如何在生产环境中使用Rocket运行Diesel迁移?

3

我需要在基于 Rocket 的应用程序中的生产环境中运行 Diesel 数据库迁移。通常有两种方法可以执行数据库迁移:

  1. 在应用程序启动时。
  2. 与应用程序启动分开。

我更喜欢第二种选项,即通过使用应用程序二进制文件的 --migrate 标志来调用,但由于目标应用程序相当简单,第一种方式也可以胜任。

在 Diesel 问题跟踪器中有一个关于在生产环境中运行迁移的线程,并提供了如何执行迁移的建议:

  1. diesel_migrations 添加到您的依赖项中
  2. 在您的 crate 中包含 extern crate diesel_migrations,并确保用 #[macro_use] 进行修饰
  3. 在代码开头添加 embed_migrations!()
  4. 要运行迁移,请使用 embedded_migrations::run(&db_conn)

main.rs 中我做了如下修改:

#![feature(proc_macro_hygiene, decl_macro)]

#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_migrations;

#[macro_use]
extern crate rocket;
#[macro_use]
extern crate rocket_contrib;

#[database("my_db_name")]
pub struct DbConn(diesel::PgConnection);

fn main() {
    // Update database
    embed_migrations!();
    embedded_migrations::run(&DbConn);
    // Launch the app
    ...
}

这会导致以下错误:

error[E0277]: the trait bound `fn(diesel::r2d2::PooledConnection<<diesel::PgConnection as rocket_contrib::databases::Poolable>::Manager>) -> DbConn {DbConn}: diesel::Connection` is not satisfied
  --> src/main.rs:30:30
   |
29 |     embed_migrations!();
   |     --------------------
   |     |
   |     required by this bound in `main::embedded_migrations::run`
30 |     embedded_migrations::run(&DbConn);
   |                              ^^^^^^^ the trait `diesel::Connection` is not implemented for `fn(diesel::r2d2::PooledConnection<<diesel::PgConnection as rocket_contrib::databases::Poolable>::Manager>) -> DbConn {DbConn}`
   |
   = note: required because of the requirements on the impl of `diesel_migrations::MigrationConnection` for `fn(diesel::r2d2::PooledConnection<<diesel::PgConnection as rocket_contrib::databases::Poolable>::Manager>) -> DbConn {DbConn}`
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

如何解决这个问题?

你不应该使用连接类型而是连接实例来调用 run 函数,例如:let db_conn = whatever; embedded_migrations::run(&db_conn); - Jmb
1
我不确定为什么您要在 diesel 问题跟踪器中寻找那些不完整的信息,而不是使用对应的文档(https://docs.rs/diesel_migrations/1.4.0/diesel_migrations/macro.embed_migrations.html)。 此外,您需要一个实现Connection的实例,因此,请根据 rocket 文档(因为我在那里看到了他们神奇的宏)建议执行任何操作,或者只需在应用程序启动时建立一个新的 PgConnection 来运行迁移。 - weiznich
2个回答

10
在谷歌上搜索了一些内容,找到了这个可以工作的示例here
关键代码。
use rocket::Rocket;
use rocket::fairing::AdHoc;

// This macro from `diesel_migrations` defines an `embedded_migrations` module
// containing a function named `run`. This allows the example to be run and
// tested without any outside setup of the database.
embed_migrations!();

#[database("sqlite_database")]
pub struct DbConn(SqliteConnection);

fn run_db_migrations(rocket: Rocket) -> Result<Rocket, Rocket> {
    let conn = DbConn::get_one(&rocket).expect("database connection");
    match embedded_migrations::run(&*conn) {
        Ok(()) => Ok(rocket),
        Err(e) => {
            error!("Failed to run database migrations: {:?}", e);
            Err(rocket)
        }
    }
}

fn rocket() -> Rocket {
    rocket::ignite()
        .attach(DbConn::fairing())
        .attach(AdHoc::on_attach("Database Migrations", run_db_migrations))
        .mount("/", StaticFiles::from("static/"))
        .mount("/", routes![index])
        .mount("/todo", routes![new, toggle, delete])
        .attach(Template::fairing())
}

请注意,自2.0.0版本发布以来,此内容已更改-https://github.com/Diesel-rs/Diesel/blob/master/guide_drafts/migration_guide.md#diesel_migration-rewrite - Igbanam

5

在SS_Rebelious的回答基础上,这是我为Rocket 0.5所做的工作:

#[macro_use]
extern crate rocket;
#[macro_use]
extern crate diesel_migrations;

use capt_server::api::routes;
use diesel_migrations::embed_migrations;
use envy;
use rocket::fairing::AdHoc;
use rocket::{Build, Rocket};
use rocket_sync_db_pools::database;

#[database("sqlite_db")]
pub struct Db(diesel::SqliteConnection);

// This macro from `diesel_migrations` defines an `embedded_migrations` module
// containing a function named `run`. This allows the example to be run and
// tested without any outside setup of the database.
embed_migrations!();

#[launch]
fn rocket() -> _ {
    rocket::build()
        .attach(Db::fairing())
        .attach(AdHoc::try_on_ignite("Database Migrations", migrate))
        .mount("/", routes())
}

async fn migrate(rocket: Rocket<Build>) -> Result<Rocket<Build>, Rocket<Build>> {
    let db = Db::get_one(&rocket).await.expect("database connection");
    db.run(|conn| match embedded_migrations::run(&*conn) {
        Ok(()) => Ok(rocket),
        Err(e) => {
            error!("Failed to run database migrations: {:?}", e);
            Err(rocket)
        }
    })
    .await
}

经过多天的尝试,您的解决方案完美地解决了我的问题。谢谢! - Flashito

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接