doc: update blog tutorial to updated syntax and instructions (#10577)

pull/10878/head^2
Hank G 2021-07-21 10:33:16 -04:00 committed by GitHub
parent de6784a007
commit be7c3965a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 92 additions and 50 deletions

View File

@ -49,13 +49,12 @@ V projects can be created anywhere and don't need to have a certain structure:
```bash ```bash
mkdir blog mkdir blog
cd blog v init
touch blog.v
``` ```
First, let's create a simple hello world website: First, let's create a simple hello world website:
```v ```v oksyntax
// blog.v // blog.v
module main module main
@ -66,9 +65,11 @@ struct App {
} }
fn main() { fn main() {
vweb.run(&App{}, 8081) app := App{}
vweb.run(app, 8081)
} }
['/index']
pub fn (mut app App) index() vweb.Result { pub fn (mut app App) index() vweb.Result {
return app.text('Hello world from vweb!') return app.text('Hello world from vweb!')
} }
@ -185,30 +186,6 @@ Now let's display some articles!
We'll be using V's builtin ORM and a SQLite database. We'll be using V's builtin ORM and a SQLite database.
(V ORM will also support MySQL, Postgre, and SQL Server soon.) (V ORM will also support MySQL, Postgre, and SQL Server soon.)
Create a SQLite file with the schema:
```sql
-- blog.sqlite
drop table if exists Article;
create table Article (
id integer primary key,
title text default "",
text text default ""
);
insert into Article (title, text) values (
"Hello, world!",
"V is great."
);
insert into Article (title, text) values (
"Second post.",
"Hm... what should I write about?"
);
```
Run the file with `sqlite3 blog.db < blog.sqlite`.
Add a SQLite handle to `App`: Add a SQLite handle to `App`:
@ -219,7 +196,7 @@ import vweb
struct App { struct App {
vweb.Context vweb.Context
mut: pub mut:
db sqlite.DB db sqlite.DB
} }
``` ```
@ -231,16 +208,42 @@ Add the `init_server()` method where we'll connect to a database:
```v oksyntax ```v oksyntax
// blog.v // blog.v
pub fn (mut app App) init_server() { pub fn (mut app App) init_server() {
db := sqlite.connect(':memory:') or { panic(err) } app.db = sqlite.connect(':memory:') or { panic(err) }
db.exec('create table `Article` (id integer primary key, title text default "", text text default "")') sql app.db {
db.exec('insert into Article (title, text) values ("Hello, world!", "V is great.")') create table Article
db.exec('insert into Article (title, text) values ("Second post.", "Hm... what should I write about?")') }
app.db = db
first_article := Article{
title: 'Hello, world!'
text: 'V is great.'
}
second_article := Article{
title: 'Second post.'
text: 'Hm... what should I write about?'
}
sql app.db {
insert first_article into Article
insert second_article into Article
}
} }
``` ```
Code in the `init_server()` function is run only once during app's startup, so we are going Code in the `init_server()` function is run only once during app's startup, so we are going
to have one DB connection for all requests. to have one DB connection for all requests. Modify the main method to call the `init_server()`
function before adding it to the vweb system:
```v oksyntax
fn main() {
mut app := App{}
app.init_server()
vweb.run(app, 8081)
}
```
Because `init_server()` modifies properties of the app struct we now have to make it mutable
with the `mut` keyword.
Create a new file `article.v`: Create a new file `article.v`:
@ -250,7 +253,7 @@ Create a new file `article.v`:
module main module main
struct Article { struct Article {
id int id int [primary; sql: serial]
title string title string
text string text string
} }
@ -262,11 +265,15 @@ pub fn (app &App) find_all_articles() []Article {
} }
``` ```
Notice that the `Article` structure conforms to the same structure and naming as
the database table in the creation SQL statement. Also we need to add ORM decorators
to our primary key to let it know that it is the primary key and it should auto-increment
Let's fetch the articles in the `index()` action: Let's fetch the articles in the `index()` action:
```v ignore ```v ignore
// blog.v // blog.v
pub fn (app &App) index() vweb.Result { pub fn (app App) index() vweb.Result {
articles := app.find_all_articles() articles := app.find_all_articles()
return $vweb.html() return $vweb.html()
} }
@ -319,7 +326,7 @@ pub fn (app &App) retrieve_article() ?Article {
V ORM uses V's optionals for single values, which is very useful, since V ORM uses V's optionals for single values, which is very useful, since
bad queries will always be handled by the developer: bad queries will always be handled by the developer:
```v oksyntax ```v ignore
// article.v // article.v
article := app.retrieve_article(10) or { article := app.retrieve_article(10) or {
app.text('Article not found') app.text('Article not found')
@ -347,10 +354,10 @@ Create `new.html`:
</html> </html>
``` ```
```v oksyntax ```v ignore
// article.v // article.v
import vweb import vweb
['/new_article'; post]
pub fn (mut app App) new_article() vweb.Result { pub fn (mut app App) new_article() vweb.Result {
title := app.form['title'] title := app.form['title']
text := app.form['text'] text := app.form['text']
@ -372,12 +379,25 @@ pub fn (mut app App) new_article() vweb.Result {
> Untyped `form['key']` is temporary. Very soon Vweb will accept query and form > Untyped `form['key']` is temporary. Very soon Vweb will accept query and form
parameters via function arguments: `new_article(title, text string) {`. parameters via function arguments: `new_article(title, text string) {`.
The decorator on our function tells vweb the path to our endpoint, `/new_article`,
and that it is an HTTP POST type operation.
We need to update `index.html` to add a link to the "new article" page: We need to update `index.html` to add a link to the "new article" page:
```html ```html
<a href='/new'>New article</a> <a href='/new'>New article</a>
``` ```
Next we need to add the HTML endpoint to our code like we did with `index.html`:
```v ignore
['/new']
pub fn (mut app App) new() vweb.Result {
return $vweb.html()
}
```
Re-running this code will now allow us to add new posts to our blog endpoint
### JSON endpoints ### JSON endpoints
@ -391,14 +411,34 @@ in V is very simple:
import vweb import vweb
import json import json
['/articles'; get]
pub fn (mut app App) articles() vweb.Result { pub fn (mut app App) articles() vweb.Result {
articles := app.find_all_articles() articles := app.find_all_articles()
return app.json(json.encode(articles)) return app.json(json.encode(articles))
} }
``` ```
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/articles_json.png?raw=true"> <img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/articles_json.png?raw=true">
### Persistent data
If one wants to persist data they need to use a file instead of memory SQLite Database.
Replace the `init_server()` function with this instead:
```v oksyntax
// blog.v
pub fn (mut app App) init_server() {
app.db = sqlite.connect('blog.db') or { panic(err) }
sql app.db {
create table Article
}
}
```
As we can see it attempts to open a file in the current directory named `blog.db`.
If the database file doesn't exist it will create it. The second command will
create the table `Article` if none exists already. Now every time the
app is run you will see the articles created from the previous executions
To be continued... To be continued...

View File

@ -1,7 +1,7 @@
module main module main
struct Article { struct Article {
id int id int [primary; sql: serial]
title string title string
text string text string
} }

View File

@ -13,7 +13,9 @@ pub mut:
} }
fn main() { fn main() {
vweb.run(&App{}, 8081) mut app := App{}
app.init_server()
vweb.run(app, 8081)
} }
/* /*
@ -27,6 +29,7 @@ pub fn (app &App) index_html() vweb.Result {
return $vweb.html() return $vweb.html()
} }
*/ */
['/index']
pub fn (app &App) index() vweb.Result { pub fn (app &App) index() vweb.Result {
articles := app.find_all_articles() articles := app.find_all_articles()
return $vweb.html() return $vweb.html()
@ -34,17 +37,16 @@ pub fn (app &App) index() vweb.Result {
pub fn (mut app App) init_server() { pub fn (mut app App) init_server() {
app.db = sqlite.connect('blog.db') or { panic(err) } app.db = sqlite.connect('blog.db') or { panic(err) }
app.db.create_table('article', [ sql app.db {
'id integer primary key', create table Article
"title text default ''", }
"text text default ''",
])
} }
pub fn (mut app App) before_request() { pub fn (mut app App) before_request() {
app.user_id = app.get_cookie('id') or { '0' } app.user_id = app.get_cookie('id') or { '0' }
} }
['/new']
pub fn (mut app App) new() vweb.Result { pub fn (mut app App) new() vweb.Result {
return $vweb.html() return $vweb.html()
} }
@ -69,10 +71,10 @@ pub fn (mut app App) new_article() vweb.Result {
return app.redirect('/') return app.redirect('/')
} }
pub fn (mut app App) articles() { ['/articles'; get]
pub fn (mut app App) articles() vweb.Result {
articles := app.find_all_articles() articles := app.find_all_articles()
x := json.encode(articles) return app.json(json.encode(articles))
app.json(x)
} }
fn (mut app App) time() { fn (mut app App) time() {