やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Rust自習(diesel 3 SQLite3 ORM)

 データを更新する。

成果物

コード

 前回のコードを修正・追記する。

schema.rs

table! {
    posts (id) {
        id -> Integer,
        title -> Text,
        body -> Text,
        is_published -> Bool,
    }
}
  • idの型をNullable<Integer>からInteger
  • is_publishedの型をTextからBool

src/bin/publish_post.rs

 新規作成する。

extern crate diesel;
extern crate diesel_hello;

use self::diesel::prelude::*;
use self::diesel_hello::*;
use std::env::args;

fn main() {
    use diesel_hello::schema::posts::dsl::{posts, is_published};

    let id = args()
        .nth(1)
        .expect("publish_post requires a post id")
        .parse::<i32>()
        .expect("Invalid ID");
    let connection = establish_connection();

    let _ = diesel::update(posts.find(id))
        .set(is_published.eq(true))
        .execute(&connection)
        .unwrap_or_else(|_| panic!("Unable to find post {}", id));

    let post: models::Post = posts
        .find(id)
        .first(&connection)
        .unwrap_or_else(|_| panic!("Unable to find post {}", id));

    println!("Published post {}", post.title);
}

確認

$ sqlite3 ./db.sqlite3
SQLite version 3.22.0 2018-01-22 18:45:57
Enter ".help" for usage hints.
sqlite> select * from posts;
1|記事のタイトル|記事の本文。
複数行を書く。
Ctrl+dキーで終了。
最終行。
|f

 is_published列の値がfである。

実行

$ cargo run --bin publish_post 1
...
Published post 記事のタイトル

 これで公開フラグを立てた。DBファイルを確認してみる。

$ sqlite3 /tmp/work/Rust.Diesel.20190727130150/src/2/diesel_hello/db.sqlite3
SQLite version 3.22.0 2018-01-22 18:45:57
Enter ".help" for usage hints.
sqlite> select * from posts;
1|記事のタイトル|記事の本文。
複数行を書く。
Ctrl+dキーで終了。
最終行。
|1

 f1になった。

 てっきりtだと思ったのだが。なら初期値はfでなく0のほうが良さそう。まあいいか。

NG集

失敗1

失敗1

 公式getting startedページのコードを使ったがダメだった。

src/bin/publish_post.rs

extern crate diesel_demo;
extern crate diesel;

use self::diesel::prelude::*;
use self::diesel_demo::*;
use self::models::Post;
use std::env::args;

fn main() {
    use diesel_demo::schema::posts::dsl::{posts, published};

    let id = args().nth(1).expect("publish_post requires a post id")
        .parse::<i32>().expect("Invalid ID");
    let connection = establish_connection();

    let post = diesel::update(posts.find(id))
        .set(is_published.eq(true))
        .get_result::<Post>(&connection)
        .expect(&format!("Unable to find post {}", id));
    println!("Published post {}", post.title);
}

 ポイントはupdate()is_publishedフラグをfalseからtrueに変更する。

既存DB確認

$ sqlite3 ./db.sqlite3
SQLite version 3.22.0 2018-01-22 18:45:57
Enter ".help" for usage hints.
sqlite> select * from posts;
1|記事のタイトル|記事の本文。
複数行を書く。
Ctrl+dキーで終了。
最終行。
|f
  • 既存データはf(false)である
  • id1である

実行

cargo run --bin publish_post 1
error[E0277]: the trait bound `bool: diesel::Expression` is not satisfied
  --> src/bin/publish_post.rs:17:27
   |
17 |         .set(is_published.eq(true))
   |                           ^^ the trait `diesel::Expression` is not implemented for `bool`
   |
   = note: required because of the requirements on the impl of `diesel::expression::AsExpression<diesel::sql_types::Text>` for `bool`

 前回、IDが1のレコードを追加した。ので、引数に1を渡す。

失敗2

失敗2

 githubのコードを使った。これが成功コードの元になった。

src/bin/publish_post.rs

extern crate diesel;
extern crate diesel_hello;

use self::diesel::prelude::*;
use self::diesel_hello::*;
use std::env::args;

fn main() {
    use diesel_demo_step_3_sqlite::schema::posts::dsl::{posts, is_published};

    let id = args()
        .nth(1)
        .expect("publish_post requires a post id")
        .parse::<i32>()
        .expect("Invalid ID");
    let connection = establish_connection();

    let _ = diesel::update(posts.find(id))
        .set(is_published.eq(true))
        .execute(&connection)
        .unwrap_or_else(|_| panic!("Unable to find post {}", id));

    let post: models::Post = posts
        .find(id)
        .first(&connection)
        .unwrap_or_else(|_| panic!("Unable to find post {}", id));

    println!("Published post {}", post.title);
}
error[E0277]: the trait bound `bool: diesel::Expression` is not satisfied
  --> src/bin/publish_post.rs:19:27
   |
19 |         .set(is_published.eq(true))
   |                           ^^ the trait `diesel::Expression` is not implemented for `bool`
   |
   = note: required because of the requirements on the impl of `diesel::expression::AsExpression<diesel::sql_types::Text>` for `bool`

 schema.rsをみてみると、is_published -> Bool,となっていた。対して私のコードはText

schema.rs

table! {
    posts (id) {
        id -> Nullable<Integer>,
        title -> Text,
        body -> Text,
        is_published -> Text,
    }
}

 is_published -> Bool,となっていた。is_publishedTextBoolに修正して再度コンパイルする。

 またエラー。

error[E0277]: the trait bound `i32: diesel::deserialize::FromSql<diesel::sql_types::Nullable<diesel::sql_types::Integer>, diesel::sqlite::Sqlite>` is not satisfied
  --> src/bin/publish_post.rs:25:10
   |
25 |         .first(&connection)
   |          ^^^^^ the trait `diesel::deserialize::FromSql<diesel::sql_types::Nullable<diesel::sql_types::Integer>, diesel::sqlite::Sqlite>` is not implemented for `i32`
   |
   = help: the following implementations were found:
             <i32 as diesel::deserialize::FromSql<diesel::sql_types::Integer, DB>>
             <i32 as diesel::deserialize::FromSql<diesel::sql_types::Integer, diesel::sqlite::Sqlite>>
   = note: required because of the requirements on the impl of `diesel::Queryable<diesel::sql_types::Nullable<diesel::sql_types::Integer>, diesel::sqlite::Sqlite>` for `i32`
   = note: required because of the requirements on the impl of `diesel::Queryable<(diesel::sql_types::Nullable<diesel::sql_types::Integer>, diesel::sql_types::Text, diesel::sql_types::Text, diesel::sql_types::Bool), diesel::sqlite::Sqlite>` for `(i32, std::string::String, std::string::String, bool)`
   = note: required because of the requirements on the impl of `diesel::Queryable<(diesel::sql_types::Nullable<diesel::sql_types::Integer>, diesel::sql_types::Text, diesel::sql_types::Text, diesel::sql_types::Bool), diesel::sqlite::Sqlite>` for `diesel_hello::models::Post`
   = note: required because of the requirements on the impl of `diesel::query_dsl::LoadQuery<diesel::SqliteConnection, diesel_hello::models::Post>` for `diesel::query_builder::SelectStatement<diesel_hello::schema::posts::table, diesel::query_builder::select_clause::DefaultSelectClause, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause<diesel::expression::operators::Eq<diesel_hello::schema::posts::id, diesel::expression::bound::Bound<diesel::sql_types::Nullable<diesel::sql_types::Integer>, i32>>>, diesel::query_builder::order_clause::NoOrderClause, diesel::query_builder::limit_clause::LimitClause<diesel::expression::bound::Bound<diesel::sql_types::BigInt, i64>>>`

 schema.rsを見比べてみると、id -> Integer,だった。対して私の環境は以下。

table! {
    posts (id) {
        id -> Nullable<Integer>,
        title -> Text,
        body -> Text,
        is_published -> Bool,
    }
}

 Nullableという余計なものがついている。これを取り去りIntegerにして再実行。

 成功!

$ cargo run --bin publish_post 1
...
Published post 記事のタイトル

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux

前回まで