Общее использование поиска или фильтра Дизеля для выполнения удалений

Я пытаюсь использовать общие функции Diesel для сокращения повторяющихся задач, таких как удаление строки на основе первичного ключа.

У меня обычная вставка строк работает относительно быстро, но запросы на удаление кажутся довольно сложными. Я попытался решить это с помощью find() и filter(). Я также проконсультировался по аналогичным темам 1 и 2 безуспешно.

Использование find

use diesel::prelude::*;
use diesel::query_dsl::methods::FindDsl;
use std::error::Error;

pub struct DB {
    conn: SqliteConnection,
}

impl DB {
    pub fn remove_row<'a, T>(&self, table: T, pk: &'a str) -> Result<(), Box<Error>>
    where
        T: FindDsl<&'a str>,
        <T as FindDsl<&'a str>>::Output: diesel::Identifiable,
        <T as FindDsl<&'a str>>::Output: diesel::associations::HasTable,
    {
        diesel::delete(table.find(pk)).execute(&self.conn)?;
        Ok(())
    }
}

Это приводит к следующей ошибке, которую я вообще не могу интерпретировать:

error[E0275]: overflow evaluating the requirement `_: std::marker::Sized`
   --> src/db/mod.rs:103:3
    |
103 |         diesel::delete (table.find (pk)) .execute (&self.conn) ?;
    |         ^^^^^^^^^^^^^^
    |
    = help: consider adding a `#![recursion_limit="128"]` attribute to your crate
    = note: required because of the requirements on the impl of `diesel::query_dsl::filter_dsl::FilterDsl<_>` for `<<<T as diesel::query_dsl::filter_dsl::FindDsl<&'a str>>::Output as diesel::associations::HasTable>::Table as diesel::query_builder::AsQuery>::Query`
    = note: required because of the requirements on the impl of `diesel::query_builder::IntoUpdateTarget` for `<T as diesel::query_dsl::filter_dsl::FindDsl<&'a str>>::Output`
    = note: required by `diesel::delete`

Использование filter()

use diesel::prelude::*;
use diesel::query_dsl::methods::FilterDsl;
use std::error::Error;

pub struct DB {
    conn: SqliteConnection,
}

impl DB {
    pub fn remove_row<T>(&self, table: T, pk: &str) -> Result<(), Box<Error>>
    where
        T: FilterDsl<bool>,
        <T as FilterDsl<bool>>::Output: diesel::Identifiable,
        <T as FilterDsl<bool>>::Output: diesel::associations::HasTable,
    {
        diesel::delete(table.filter(id.eq(pk))).execute(&self.conn)?;
        Ok(())
    }
}

В дополнение к предыдущей ошибке, это сообщение об ошибке о том, что id не известен в структуре данных. Я могу вспомнить одну недостающую черту, которая гарантирует существование этой строки, но я не нашел ничего о таком поведении.

error[E0425]: cannot find value `id` in this scope
   --> src/db/mod.rs:117:33
    |
117 |         diesel::delete (table.filter (id.eq (pk))) .execute (&self.conn) ?;
    |                                       ^^ not found in this scope
help: possible candidates are found in other modules, you can import them into scope
    |
4   | use crate::db::schema::events::columns::id;
    |
4   | use crate::db::schema::ignored_events::columns::id;
    |
4   | use crate::db::schema::locations::columns::id;
    |
4   | use std::process::id;


error[E0275]: overflow evaluating the requirement `_: std::marker::Sized`
   --> src/db/mod.rs:117:3
    |
117 |         diesel::delete (table.filter (id.eq (pk))) .execute (&self.conn) ?;
    |         ^^^^^^^^^^^^^^
    |
    = help: consider adding a `#![recursion_limit="128"]` attribute to your crate
    = note: required because of the requirements on the impl of `diesel::query_dsl::filter_dsl::FilterDsl<_>` for `<<<T as diesel::query_dsl::filter_dsl::FilterDsl<bool>>::Output as diesel::associations::HasTable>::Table as diesel::query_builder::AsQuery>::Query`
    = note: required because of the requirements on the impl of `diesel::query_builder::IntoUpdateTarget` for `<T as diesel::query_dsl::filter_dsl::FilterDsl<bool>>::Output`
    = note: required by `diesel::delete`

person Frank Brütting    schedule 18.03.2019    source источник


Ответы (1)


Дженерики не просты. Дженерики в такой универсальной системе, как Diesel, еще сложнее.

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

В то время как я просматривал сгенерированные сообщения об ошибках, я в основном смотрел на границы типов, описываемые вызываемыми функциями / методами.

По пунктам:

  1. .find взят из _ 2_.
  2. delete требует _ 4_.
  3. Результирующий тип вызова delete - это DeleteStatement, параметризованный с T::Table и T::WhereClause. Это псевдоним настраиваемого типа DeleteFindStatement.
  4. .execute происходит от _ 11_.
use diesel::{
    associations::HasTable,
    helper_types::Find,
    query_builder::{DeleteStatement, IntoUpdateTarget},
    query_dsl::methods::ExecuteDsl,
};

type DeleteFindStatement<F> =
    DeleteStatement<<F as HasTable>::Table, <F as IntoUpdateTarget>::WhereClause>;

impl DB {
    pub fn remove_row<Tbl, Pk>(&self, table: Tbl, pk: Pk) -> Result<(), Box<Error>>
    where
        Tbl: FindDsl<Pk>,
        Find<Tbl, Pk>: IntoUpdateTarget,
        DeleteFindStatement<Find<Tbl, Pk>>: ExecuteDsl<SqliteConnection>,
    {
        let find = table.find(pk);
        let delete = diesel::delete(find);
        delete.execute(&self.conn)?;
        Ok(())
    }
}

Вам нужно будет попробовать это самостоятельно для версии на основе filter, поскольку вы не предоставили достаточно кода, чтобы определить, каким id должно быть; как показано в вашем сообщении об ошибке.

Смотрите также:

есть ли преимущество в том, чтобы сделать первичный ключ универсальным, даже если он будет &str во всех случаях?

Для меня проще использовать универсальный тип, чем вставлять кучу общих параметров времени жизни.

person Shepmaster    schedule 18.03.2019
comment
Хорошо, поэтому вставка границ признаков, упомянутых в ошибках груза, здесь не помогает. Я в основном понимаю, что вы здесь делаете, но, к сожалению, не вижу понятного пути от этих сообщений об ошибках к предлагаемому вами решению, даже при выполнении небольших шагов. Так что полагаю, нужно просто больше узнать о том, как работает Дизель. Тем не менее, большое спасибо за вашу помощь! В варианте filter все таблицы имеют столбец «id: String» первичного ключа. Как это нужно указать? И есть ли преимущество в том, чтобы сделать первичный ключ универсальным, даже если он &str во всех случаях? - person Frank Brütting; 19.03.2019