Diesel для Rust: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Что такое Diesel и зачем он нужен?

Diesel — это мощная, безопасная и расширяемая ORM (Object-Relational Mapping) библиотека для языка программирования Rust. Она предоставляет типобезопасный способ взаимодействия с реляционными базами данных, такими как PostgreSQL, MySQL и SQLite. В отличие от многих других ORM, Diesel не использует магию или рефлексию во время выполнения. Вместо этого он генерирует весь необходимый код на этапе компиляции, что гарантирует, что ваши запросы корректны и не приведут к ошибкам во время выполнения.

Основные причины использовать Diesel:

  • Типобезопасность: Ошибки в SQL-запросах (например, неверное имя столбца или несоответствие типов) обнаруживаются на этапе компиляции, а не в production.
  • Производительность: Diesel генерирует высокооптимизированный код, который работает на уровне рукописных SQL-запросов.
  • Расширяемость: Вы можете легко добавлять поддержку новых типов данных, операторов и функций.
  • Миграции: Встроенная система миграций позволяет управлять схемой базы данных как кодом.
  • Безопасность: Diesel автоматически экранирует параметры запросов, предотвращая SQL-инъекции.

Diesel идеально подходит для проектов, где важна надежность, производительность и безопасность данных. Он широко используется в веб-приложениях, API-сервисах, инструментах для обработки данных и любых других Rust-проектах, требующих взаимодействия с базами данных.

Установка Diesel

Установка Diesel состоит из двух частей: добавление библиотеки в проект и установка CLI-инструмента для работы с миграциями.

1. Добавление в Cargo.toml

Откройте файл Cargo.toml вашего проекта и добавьте зависимости. Выберите соответствующий бэкенд базы данных (или несколько):

[dependencies]
diesel = { version = "2.1", features = ["postgres"] }  # Для PostgreSQL
# diesel = { version = "2.1", features = ["mysql"] }   # Для MySQL
# diesel = { version = "2.1", features = ["sqlite"] }  # Для SQLite

2. Установка CLI (Command Line Interface)

CLI-инструмент diesel_cli используется для создания и выполнения миграций. Установите его через cargo:

# Для PostgreSQL
cargo install diesel_cli --no-default-features --features postgres

# Для MySQL
cargo install diesel_cli --no-default-features --features mysql

# Для SQLite
cargo install diesel_cli --no-default-features --features sqlite

# Для всех бэкендов
cargo install diesel_cli

3. Настройка подключения

Создайте файл .env в корне проекта для хранения строки подключения:

DATABASE_URL=postgres://username:password@localhost/diesel_demo

Затем инициализируйте Diesel в вашем проекте:

diesel setup

Эта команда создаст файл diesel.toml, папку migrations и попытается создать базу данных, если она не существует.

Быстрый старт с примером кода

Давайте создадим простое приложение для управления пользователями.

1. Создание миграции

Создадим таблицу users:

diesel migration generate create_users

Отредактируйте файлы миграции:

up.sql:

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR NOT NULL,
    email VARCHAR NOT NULL UNIQUE,
    age INTEGER NOT NULL DEFAULT 0,
    created_at TIMESTAMP NOT NULL DEFAULT NOW()
);

down.sql:

DROP TABLE users;

Примените миграцию:

diesel migration run

2. Определение модели

Создайте файл src/models.rs:

use diesel::prelude::*;
use chrono::NaiveDateTime;

#[derive(Queryable, Selectable)]
#[diesel(table_name = crate::schema::users)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct User {
    pub id: i32,
    pub name: String,
    pub email: String,
    pub age: i32,
    pub created_at: NaiveDateTime,
}

#[derive(Insertable)]
#[diesel(table_name = crate::schema::users)]
pub struct NewUser {
    pub name: String,
    pub email: String,
    pub age: i32,
}

3. Определение схемы

Diesel автоматически генерирует файл схемы. Обычно он находится в src/schema.rs:

// @generated automatically by Diesel CLI.

diesel::table! {
    users (id) {
        id -> Int4,
        name -> Varchar,
        email -> Varchar,
        age -> Int4,
        created_at -> Timestamp,
    }
}

4. Основной код

В src/main.rs:

use diesel::prelude::*;
use dotenvy::dotenv;
use std::env;

mod models;
mod schema;

use models::{NewUser, User};
use schema::users;

fn main() {
    dotenv().ok();

    let database_url = env::var("DATABASE_URL")
        .expect("DATABASE_URL must be set");
    let mut connection = PgConnection::establish(&database_url)
        .expect(&format!("Error connecting to {}", database_url));

    // Создание нового пользователя
    let new_user = NewUser {
        name: "Alice".to_string(),
        email: "alice@example.com".to_string(),
        age: 30,
    };

    let inserted_user = diesel::insert_into(users::table)
        .values(&new_user)
        .get_result::<User>(&mut connection)
        .expect("Error inserting user");

    println!("Inserted user: {:?}", inserted_user);

    // Получение всех пользователей
    let all_users = users::table
        .load::<User>(&mut connection)
        .expect("Error loading users");

    for user in all_users {
        println!("User: {} ({})", user.name, user.email);
    }
}

Основные методы и функции Diesel

1. diesel::insert_into

Что делает: Начинает построение INSERT запроса. Позволяет вставлять одну или несколько записей в таблицу.

Параметры:

  • table — ссылка на таблицу (например, users::table)

Рабочий пример:

use diesel::insert_into;

let new_user = NewUser { name: "Bob".into(), email: "bob@test.com".into(), age: 25 };
let result = insert_into(users::table)
    .values(&new_user)
    .execute(&mut connection)?;

2. .get_result / .get_results

Что делает: Выполняет запрос и возвращает одну (get_result) или несколько (get_results) записей. Используется после insert_into, update, delete или select.

Параметры:

  • connection — изменяемая ссылка на соединение с БД

Рабочий пример:

let inserted_user: User = diesel::insert_into(users::table)
    .values(&new_user)
    .get_result(&mut connection)?;

let all_users: Vec<User> = users::table
    .get_results(&mut connection)?;

3. diesel::update

Что делает: Начинает построение UPDATE запроса. Позволяет обновлять существующие записи.

Параметры:

  • target — таблица или подзапрос, определяющий, какие записи обновлять

Рабочий пример:

use diesel::update;

let updated_user = update(users::table.find(1))
    .set(users::name.eq("Updated Name"))
    .get_result::<User>(&mut connection)?;

4. diesel::delete

Что делает: Начинает построение DELETE запроса. Удаляет записи из таблицы.

Параметры:

  • target — таблица или подзапрос, определяющий, какие записи удалять

Рабочий пример:

use diesel::delete;

let deleted_count = delete(users::table.find(1))
    .execute(&mut connection)?;

// Удаление по условию
let deleted = delete(users::table.filter(users::age.lt(18)))
    .execute(&mut connection)?;

5. .filter

Что делает: Добавляет условие WHERE к запросу. Позволяет фильтровать записи по заданным критериям.

Параметры:

  • predicate — выражение, возвращающее булево значение (например, users::age.gt(21))

Рабочий пример:

let adults = users::table
    .filter(users::age.ge(18))
    .load::<User>(&mut connection)?;

let named_users = users::table
    .filter(users::name.eq("Alice"))
    .load::<User>(&mut connection)?;

6. .order / .order_by

Что делает: Добавляет сортировку ORDER BY к запросу. Позволяет упорядочить результаты по одному или нескольким столбцам.

Параметры:

  • column — столбец для сортировки (можно использовать .asc() или .desc())

Рабочий пример:

let sorted_users = users::table
    .order(users::age.desc())
    .load::<User>(&mut connection)?;

let sorted_by_name = users::table
    .order((users::name.asc(), users::age.desc()))
    .load::<User>(&mut connection)?;

7. .limit / .offset

Что делает: .limit ограничивает количество возвращаемых записей, .offset пропускает указанное количество записей. Используется для пагинации.

Параметры:

  • n — целое число (i64)

Рабочий пример:

let first_ten = users::table
    .limit(10)
    .load::<User>(&mut connection)?;

let next_ten = users::table
    .limit(10)
    .offset(10)
    .load::<User>(&mut connection)?;

8. .select

Что делает: Позволяет выбрать только определенные столбцы из таблицы. По умолчанию Diesel выбирает все столбцы, соответствующие структуре.

Параметры:

  • columns — кортеж или список столбцов для выборки

Рабочий пример:

let names = users::table
    .select(users::name)
    .load::&                    

Рекомендаций