0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SeaORMでActiveEnumを使用する方法

Last updated at Posted at 2024-09-03

RustにはSeaORMというとても使いやすいORMがあります。
SeaORMのDocsは非常に読みやすく、Rustにある程度慣れている人であれば容易に使用することができると思います。しかし、ActiveEnumの説明箇所が個人的にわかりにくく(私の読解力がないだけです。)、かなりの時間を浪費してしまったので、備忘録として書き記します。

ActiveEnumの使い方

早速使い方を書いていきます。

この記事の内容のレポジトリ

プログラムを直接読みたい方は、この記事の内容のレポジトリを用意しているので、以下のリンクからご覧ください。

環境

OS: Zorin OS 17.1 x86_64
DB: PostgreSQL (Dockerを使用)
rustc: 1.80.1
cargo: 1.80.1

sea-orm-cliの導入

cargo install sea-orm-cli

projectの作成

今回は、sampleというプロジェクト名で作成します。

cargo new sample
cd sample

docker-compose.yamlを用意

今回はPostgreSQLを使用します。

docker-compose.yaml
version: "3.8"

services:
  postgres:
    container_name: sample_db
    image: postgres:latest
    volumes:
      - ./db:/var/lib/postgresql
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: test_db
    ports:
      - "5433:5432"

SeaORMを導入

SeaORMのバージョンは、1.0.0にしてください。

cargo add sea-orm@=1.0.0

migrationフォルダを初期化

sea-orm-cli migrate init

project内のディレクトリ構造が以下のようになっていれば、成功です。

$ tree
.
├── Cargo.lock
├── Cargo.toml
├── docker-compose.yaml
├── migration
│   ├── Cargo.toml
│   ├── README.md
│   └── src
│       ├── lib.rs
│       ├── m20220101_000001_create_table.rs
│       └── main.rs
├── src
│   └── main.rs
└── target
    ├── CACHEDIR.TAG
(以下略)

テーブルの定義

今回は以下のようなテーブルを作成します。
Category columnでActiveEnumを使用します。

ActiveEnumを使用するCategory columnは、Stringとして定義します。

// migration/src/m20220101_000001_create_table.rs
use sea_orm_migration::{prelude::*, schema::*};

#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .create_table(
                Table::create()
                    .table(Post::Table)
                    .if_not_exists()
                    .col(
                        ColumnDef::new(Post::Id)
                            .integer()
                            .not_null()
                            .auto_increment()
                            .primary_key(),
                    )
                    .col(ColumnDef::new(Post::Title).string().not_null())
                    .col(ColumnDef::new(Post::Content).string().not_null())
                    .col(ColumnDef::new(Post::Category).string().not_null())
                    .to_owned(),
            )
            .await
    }

    async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .drop_table(Table::drop().table(Post::Table).to_owned())
            .await
    }
}

#[derive(DeriveIden)]
enum Post {
    Table,
    Id,
    Title,
    Content,
    Category,
}

migration/Cargo.tomlfeaturesを以下のように編集します。

migration/Cargo.toml
[package]
name = "migration"
version = "0.1.0"
edition = "2021"
publish = false

[lib]
name = "migration"
path = "src/lib.rs"

[dependencies]
async-std = { version = "1", features = ["attributes", "tokio1"] }

[dependencies.sea-orm-migration]
version = "1.0.0"
features = [
  "runtime-tokio-native-tls",  # `ASYNC_RUNTIME` feature
  "sqlx-postgres",         # `DATABASE_DRIVER` feature
]

docker-compose up -d

コンテナを起動します。

docker-compose up -d

Migrationの実行

以下のコマンドでMigrationを実行します。

DATABASE_URL="postgres://postgres:postgres@localhost:5433/test_db" sea-orm-cli migrate refresh

Entityの生成

以下のコマンドでEntityを生成します。

sea-orm-cli generate entity \
    -u postgres://postgres:postgres@localhost:5433/test_db \
    -o entity/src

Cargo.tomlの編集

entity/Cargo.tomlを作成し、以下のように編集します。

entity/Cargo.toml
[package]
name = "entity"
version = "0.1.0"
edition = "2021"
publish = false

[lib]
name = "entity"
path = "src/mod.rs"

[dependencies.sea-orm]
version = "1.0.0"

プロジェクトの直下にあるCargo.tomlを以下の内容に編集します。

Cargo.toml
[package]
name = "sample"
version = "0.1.0"
edition = "2021"

[workspace]
members = [".", "entity", "migration"]

[dependencies]
entity = { path = "entity" }
migration = { path = "migration" }
sea-orm = { version = "=1.0.0", features = [ "sqlx-postgres", "runtime-tokio-native-tls", "macros" ] }

enumの定義

entity/src/post.rsにCategoryのenumを定義します。
enum名は、CategoryEnumにしています。Model構造体のcategoryをStringからCategoryEnumに変更することを忘れないようにしましょう。

// entity/src/post.rs
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0

use sea_orm::entity::prelude::*;

#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[sea_orm(rs_type = "String", db_type = "String(StringLen::None)")]
pub enum CategoryEnum {
    #[sea_orm(string_value = "NixOS")]
    NixOS,
    #[sea_orm(string_value = "ZorinOS")]
    ZorinOS,
    #[sea_orm(string_value = "ArchLinux")]
    ArchLinux,
    #[sea_orm(string_value = "Windows")]
    Windows,
}

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "post")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    pub title: String,
    pub content: String,
    pub category: CategoryEnum,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}

これで、Rust側ではcategoryCategoryEnumのみを受け入れるようになりました!

CRUD操作

tokioを導入します。

cargo add tokio -F full

雑なプログラムですが、CRUD操作も書き記しておきます。

// src/main.rs
use ::entity::post::{self, CategoryEnum, Entity as Post};
use sea_orm::{
    ActiveModelTrait, ColumnTrait, Database, DatabaseConnection, EntityTrait, ModelTrait,
    QueryFilter, Set,
};

#[tokio::main(flavor = "current_thread")]
async fn main() {
    // connnect database
    let db: DatabaseConnection =
        Database::connect("postgres://postgres:postgres@localhost:5433/test_db")
            .await
            .expect("Failed to connect to db");
    //insert
    let post_model: post::ActiveModel = post::ActiveModel {
        title: Set("Title".to_string()),
        content: Set("Content".to_string()),
        category: Set(CategoryEnum::NixOS),
        ..Default::default()
    };
    let post: sea_orm::InsertResult<post::ActiveModel> =
        Post::insert(post_model).exec(&db).await.unwrap();
    println!("Inserted: {:?}", post);
    //find
    let post_data: Option<post::Model> = Post::find()
        .filter(post::Column::Category.eq(CategoryEnum::NixOS))
        .one(&db)
        .await
        .unwrap();
    println!("Found post data\n{:#?}", post_data);
    //update
    match post_data {
        Some(post_data) => {
            let mut update_post_data: post::ActiveModel = post_data.into();
            update_post_data.title = Set("Updated Title".to_string());
            update_post_data.content = Set("Updated Content".to_string());
            update_post_data.category = Set(CategoryEnum::Windows);
            let updated_post: post::Model = update_post_data.update(&db).await.unwrap();
            println!("Updated post data\n{:#?}", updated_post);
        }
        None => {
            println!("No post data found");
        }
    }
    //delete
    let delete_post_data: Option<post::Model> = Post::find()
        .filter(post::Column::Category.eq(CategoryEnum::Windows))
        .one(&db)
        .await
        .unwrap();
    match delete_post_data {
        Some(delete_post_data) => {
            let deleted_post: sea_orm::DeleteResult = delete_post_data.delete(&db).await.unwrap();
            println!("Deleted post data\n{:?}", deleted_post);
        }
        None => {
            println!("No post data found");
        }
    }
}

参考サイト

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?