diff --git a/tutorials/building_a_simple_web_blog_with_vweb/README.md b/tutorials/building_a_simple_web_blog_with_vweb/README.md index 16d2d82a50..0225cb5727 100644 --- a/tutorials/building_a_simple_web_blog_with_vweb/README.md +++ b/tutorials/building_a_simple_web_blog_with_vweb/README.md @@ -49,13 +49,12 @@ V projects can be created anywhere and don't need to have a certain structure: ```bash mkdir blog -cd blog -touch blog.v +v init ``` First, let's create a simple hello world website: -```v +```v oksyntax // blog.v module main @@ -66,9 +65,11 @@ struct App { } fn main() { - vweb.run(&App{}, 8081) + app := App{} + vweb.run(app, 8081) } +['/index'] pub fn (mut app App) index() vweb.Result { 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. (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`: @@ -219,7 +196,7 @@ import vweb struct App { vweb.Context -mut: +pub mut: db sqlite.DB } ``` @@ -231,16 +208,42 @@ Add the `init_server()` method where we'll connect to a database: ```v oksyntax // blog.v pub fn (mut app App) init_server() { - db := sqlite.connect(':memory:') or { panic(err) } - db.exec('create table `Article` (id integer primary key, title text default "", text text default "")') - db.exec('insert into Article (title, text) values ("Hello, world!", "V is great.")') - db.exec('insert into Article (title, text) values ("Second post.", "Hm... what should I write about?")') - app.db = db + app.db = sqlite.connect(':memory:') or { panic(err) } + sql app.db { + create table Article + } + + 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 -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`: @@ -250,7 +253,7 @@ Create a new file `article.v`: module main struct Article { - id int + id int [primary; sql: serial] title 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: ```v ignore // blog.v -pub fn (app &App) index() vweb.Result { +pub fn (app App) index() vweb.Result { articles := app.find_all_articles() 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 bad queries will always be handled by the developer: -```v oksyntax +```v ignore // article.v article := app.retrieve_article(10) or { app.text('Article not found') @@ -347,10 +354,10 @@ Create `new.html`: ``` -```v oksyntax +```v ignore // article.v import vweb - +['/new_article'; post] pub fn (mut app App) new_article() vweb.Result { title := app.form['title'] 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 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: ```html New article ``` +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 @@ -391,14 +411,34 @@ in V is very simple: import vweb import json +['/articles'; get] pub fn (mut app App) articles() vweb.Result { articles := app.find_all_articles() return app.json(json.encode(articles)) } ``` + +### 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... diff --git a/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v b/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v index 66f86520f2..f12753241f 100644 --- a/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v +++ b/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v @@ -1,7 +1,7 @@ module main struct Article { - id int + id int [primary; sql: serial] title string text string } diff --git a/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v b/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v index 00b379abbe..b670c31e89 100644 --- a/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v +++ b/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v @@ -13,7 +13,9 @@ pub mut: } 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() } */ +['/index'] pub fn (app &App) index() vweb.Result { articles := app.find_all_articles() return $vweb.html() @@ -34,17 +37,16 @@ pub fn (app &App) index() vweb.Result { pub fn (mut app App) init_server() { app.db = sqlite.connect('blog.db') or { panic(err) } - app.db.create_table('article', [ - 'id integer primary key', - "title text default ''", - "text text default ''", - ]) + sql app.db { + create table Article + } } pub fn (mut app App) before_request() { app.user_id = app.get_cookie('id') or { '0' } } +['/new'] pub fn (mut app App) new() vweb.Result { return $vweb.html() } @@ -69,10 +71,10 @@ pub fn (mut app App) new_article() vweb.Result { return app.redirect('/') } -pub fn (mut app App) articles() { +['/articles'; get] +pub fn (mut app App) articles() vweb.Result { articles := app.find_all_articles() - x := json.encode(articles) - app.json(x) + return app.json(json.encode(articles)) } fn (mut app App) time() {