doc: update blog tutorial to updated syntax and instructions (#10577)
parent
de6784a007
commit
be7c3965a9
|
@ -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...
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Loading…
Reference in New Issue