Using SeaOrm, I want to create a model that has no relations. Essentially, a DB with one table.
This seems like it should be super easy, but the documentation doesn't cover this and the DeriveEntityModel macro requires all the boilerplate for entity relations to be present.
What I want is:
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "device")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(column_name = "uuid")]
pub uuid: Uuid,
#[sea_orm(column_name = "location")]
pub location: Option<String>,
#[sea_orm(column_name = "lastHeard")]
pub lastHeard: Option<DateTime>
}
And the error I get is:
cannot find type `Relation` in this scope
help: you might have meant to use the associated type: `Self::Relation`rustc(E0412)
the trait bound `models::device::ActiveModel: sea_orm::ActiveModelBehavior` is not satisfied
the trait `sea_orm::ActiveModelBehavior` is not implemented for `models::device::ActiveModel`
I'm thinking there must be another macro to use, one that doesn't require relations, but I can't find it in the docs.
thanks for trying SeaORM. Try to define an empty Relation enum like below.
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "device")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(column_name = "uuid")]
pub uuid: Uuid,
#[sea_orm(column_name = "location")]
pub location: Option<String>,
#[sea_orm(column_name = "lastHeard")]
pub lastHeard: Option<DateTime>
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
Related
I know that there is a similar question here, but I've not been able to make it fit my use case.
I have a Model struct that's nested into other structs. The model can have two different types of Config objects, a ModelConfig or a SeedConfig. They are nearly identical save for a few fields. As it stands now, I need two concrete implementations of Model (SeedModel and ModelModel) in order to change the config field, resulting in duplication of all the methods and trait implementations.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct MetaModel {
pub model: Model
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Model {
pub name: String,
pub config: Option<ModelConfig>
}
What I've tried:
Using Generics: This pushes the generic type up the chain and results in very complex definitions and areas where I don't have the context to create the parent struct (i.e. the MetaModel definition has no access to the Model definition at creation).
This eventually results in a the type parameter C is not constrained by the impl trait, self type, or predicates unconstrained type parameter error
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct MetaModel<C> {
pub model: Model<C>
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Model<C> {
pub name: String,
pub config: Option<C>
}
Trait Objects: This doesn't work because serde cannot serialize trait objects
pub trait Config {}
pub struct ModelConfig;
impl Config for ModelConfig {}
pub struct SeedConfig;
impl Config for SeedConfig {}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Model {
pub name: String,
pub config: Option<Box<dyn Config>
}
What I'd like to do:
impl OtherTrait for Model {
type Value = Model;
fn build_model(&self, m: DerivedMeta) -> Result<Self::Value, &'static str> {
Ok(Model {
// Either a SeedConfig or a ModelConfig
})
}
}
What I would do is use a combination of #[serde(flatten)] and #[serde(untagged)]:
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
struct Config {
members: i32,
shared_by: String,
both: i64,
#[serde(flatten)]
specific: SpecificConfig,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(untagged)]
enum SpecificConfig {
SeedConfig {
some_members: i16,
unique_to: String,
seed_config: u64,
},
ModelConfig {
other_members: i8,
not_shared_with_above: u32,
},
}
Serde explanation of flatten:
Flatten the contents of this field into the container it is defined in.
This removes one level of structure between the serialized representation and the Rust data structure representation.
Serde explanation of untagged:
There is no explicit tag identifying which variant the data contains. Serde will try to match the data against each variant in order and the first one that deserializes successfully is the one returned.
By combining these two, we get the following behavior:
flatten allows all shared fields and specific fields to be on the same level in the config
untagged allows us to avoid adding an explicit tag in the config
all shared properties are directly accessible
only specific properties require matching the specific enum
I am trying to make an Actix API with Diesel, and in making the first endpoint (/books/create), I am having trouble trying to get the inserted value back into my code.
This is my insert:
use diesel::prelude::*;
use crate::models::Book;
use crate::schema::books;
use crate::db;
pub fn create_book(book: &Book) -> Book {
let mut connection: SqliteConnection = db::establish_connection();
let result: QueryResult<Book> = diesel::insert_into(books::table)
.values([book])
.get_result::<Book>(&mut connection);
result.unwrap()
}
This is the error, which I wrapped in a pastebin since it's too long for Stackoverflow: https://pastebin.com/Evq6tUYq
I wonder if it can be a problem of the schema or model, but they seem to be okay'ish and don't have mismatching values, as far as I know:
use diesel::prelude::*;
use serde::{Serialize, Deserialize};
use crate::schema::books;
#[derive(Queryable, Serialize, Deserialize, Insertable)]
#[diesel(table_name = books)]
pub struct Book {
pub id: i32,
pub title: String,
pub author: String,
pub creation_date: Option<String>,
pub publishing_house: Option<String>,
pub release_date: Option<String>,
pub cover_image: Option<String>
}
diesel::table! {
books (id) {
id -> Integer,
title -> Text,
author -> Text,
publishing_house -> Nullable<Text>,
release_date -> Nullable<Text>,
cover_image -> Nullable<Text>,
creation_date -> Nullable<Text>,
}
}
Where is the error? What's missing?
It's impossible to answer this question as it is missing important details like the used diesel version, and the enabled feature flags for diesel.
The underlying problem is that .get_result() uses INSERT INTO … RETURNING SQL statements internally. These are only supported by Sqlite 3.35 or newer. As of this support for such statements is behind a separate diesel feature flag. You need to enable the returning_clauses_for_sqlite_3_35 feature flag.
That by itself will not fix the issue. .values([book]) creates a array with one element and tries to insert that. This will cause diesel to use a batch insert statement, which normally allows to insert more than one element at once. While this is supported for the sqlite backend it cannot be used in combination with the returning support described above, as SQLite is missing another SQL feature here (using the DEFAULT keyword to signal that a certain value should be replaced by the corresponding database default value). In your case this can be fixed by just calling .values(&book), which constructs an ordinary single row insert.
You cant use Queryable and Insertable trait in the same Book struct you must separate them into 2 different structs.
#[derive(Queryable, Serialize, Deserialize)]
pub struct Book {
pub id: i32,
pub title: String,
pub author: String,
pub creation_date: Option<String>,
pub publishing_house: Option<String>,
pub release_date: Option<String>,
pub cover_image: Option<String>
}
#[derive(Insertable)]
#[diesel(table_name = books)]
pub struct BookInsert {
pub id: i32,
pub title: String,
pub author: String,
pub creation_date: Option<String>,
pub publishing_house: Option<String>,
pub release_date: Option<String>,
pub cover_image: Option<String>
}
I am using the Diesel crate to perform some database work. In some tables, two columns of the table should be treated together as a single key.
This pattern is repeated in many places in the database, so it would be nice to avoid a heap of repeated copy-paste code to handle this. However, I can't convince Diesel to automatically produce a type I can use in queries or inserts.
Consider the table
table! {
records (iid) {
iid -> Integer,
id_0 -> BigInt,
id_1 -> BigInt,
data -> Text,
}
}
and the ideal types
#[derive(Debug, Copy, Clone, FromSqlRow)]
pub struct RecordId {
id_0: i64,
id_1: i64,
}
#[derive(Queryable, Debug)]
pub struct Record {
pub iid: i32,
pub id: RecordId,
pub data: String,
}
This code compiles OK, but when I try to use it I get an error, for example:
pub fn find(connection: &SqliteConnection) -> types::Record {
records
.find(1)
.get_result::<types::Record>(connection)
.unwrap()
}
produces:
error[E0277]: the trait bound `(i32, types::RecordId, std::string::String): diesel::Queryable<(diesel::sql_types::Integer, diesel::sql_types::BigInt, diesel::sql_types::BigInt, diesel::sql_types::Text), _>` is not satisfied
--> src/main.rs:76:21
|
76 | records.find(1).get_result::<types::Record>(connection).unwrap()
| ^^^^^^^^^^ the trait `diesel::Queryable<(diesel::sql_types::Integer, diesel::sql_types::BigInt, diesel::sql_types::BigInt, diesel::sql_types::Text), _>` is not implemented for `(i32, types::RecordId, std::string::String)`
|
= help: the following implementations were found:
<(A, B, C) as diesel::Queryable<(SA, SB, SC), __DB>>
<(A, B, C) as diesel::Queryable<diesel::sql_types::Record<(SA, SB, SC)>, diesel::pg::Pg>>
= note: required because of the requirements on the impl of `diesel::Queryable<(diesel::sql_types::Integer, diesel::sql_types::BigInt, diesel::sql_types::BigInt, diesel::sql_types::Text), _>` for `types::Record`
= note: required because of the requirements on the impl of `diesel::query_dsl::LoadQuery<_, types::Record>` for `diesel::query_builder::SelectStatement<types::records::table, diesel::query_builder::select_clause::DefaultSelectClause, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause<diesel::expression::operators::Eq<types::records::columns::iid, diesel::expression::bound::Bound<diesel::sql_types::Integer, i32>>>>`
If I create a version which does not contain the RecordId but has the sub-pieces directly, then there is no error:
pub struct RecordDirect {
pub iid: i32,
pub id_0: i64,
pub id_1: i64,
pub data: String,
}
// ...
pub fn find_direct(connection: &SqliteConnection) -> types::RecordDirect {
records
.find(1)
.get_result::<types::RecordDirect>(connection)
.unwrap()
}
Similarly, I can manually implement the Queryable trait and that works OK too,
#[derive(Debug)]
pub struct RecordManual {
pub iid: i32,
pub id: RecordId,
pub data: String,
}
impl Queryable<records::SqlType, diesel::sqlite::Sqlite> for RecordManual {
type Row = (i32, i64, i64, String);
fn build(row: Self::Row) -> Self {
RecordManual {
iid: row.0,
id: RecordId {
id_0: row.1,
id_1: row.2,
},
data: row.3,
}
}
}
// ...
pub fn find_manual(connection: &SqliteConnection) -> types::RecordManual {
records
.find(1)
.get_result::<types::RecordManual>(connection)
.unwrap()
}
This case is ugly to maintain and I could not work out how to get it working for insertion — manually implementing Insertable seems a little trickier than Queryable.
To make this easier for anyone looking at it to play with, I've created a repository containing an almost compiling small reproducer containing the code blocks from this post. (Normally I'd put it up on the rust-playground, but that doesn't support diesel). You can find that code at https://github.com/mikeando/diesel_custom_type_demo.
Is there a way to make the #[derive(Queryable)] (and #[derive(Insertable)]) work for these kinds of cases?
A minimal reproducer for the failing initial case is:
#[macro_use]
extern crate diesel;
use diesel::prelude::*;
mod types {
use diesel::deserialize::Queryable;
use diesel::sqlite::SqliteConnection;
table! {
records (iid) {
iid -> Integer,
id_0 -> BigInt,
id_1 -> BigInt,
data -> Text,
}
}
#[derive(Debug, Copy, Clone, FromSqlRow)]
pub struct RecordId {
id_0: i64,
id_1: i64,
}
// Using a RecordId in a Record compiles, but
// produces an error when used in an actual query
#[derive(Queryable, Debug)]
pub struct Record {
pub iid: i32,
pub id: RecordId,
pub data: String,
}
}
use types::records::dsl::*;
pub fn find(connection:&SqliteConnection) -> types::Record {
records.find(1).get_result::<types::Record>(connection).unwrap()
}
Is there a way to make the #[derive(Queryable)] (and #[derive(Insertable)]) work for these kinds of cases?
For #[derive(Insertable)] this is simply possible by adding a #[diesel(embedded)] to your id field and a #[derive(Insertable)] on both structs. See the documentation of Insertable for details.
For #[derive(Queryable)] this is not possible because Queryable is supposed to be a plain mapping from query result to struct, with the basic assumption that the "shape" of the output remains the same (at least for the derive).
I get this error:
#[derive(Insertable, Queryable, Identifiable, Debug, PartialEq)]
^^^^^^^^^^ the trait `diesel::Expression` is not implemented for `std::string::String`
when I try to compile this struct:
#[derive(Insertable, Queryable, Identifiable, Debug, PartialEq)]
#[table_name = "example_table"]
pub struct SigninLog {
pub id: i32,
pub user_group: UserRoleEnum,
pub created_at: Option<SystemTime>,
pub optional_data: Option<String>
}
Is it because it contains a custom enum or an Option<String>? And if that is the problem how can I solve it?
In general, in order to be able to #[derive(X)] for a struct, all its members must implement X as well. This might not be the case with Insertable, because it is not a standard trait, but you might want to verify this; in your case Insertable is not implemented for the String within optional_data; it is implemented for Option<T>, so the Option enclosing it is not an issue.
You might want to implement Insertable manually; I haven't used diesel, though - there might be a simpler way to do this.
I use this data structure in a project:
#[derive(Serialize, Deserialize)]
pub enum Field {
last_name(String),
first_name(String),
/* etc. */
}
#[derive(Serialize, Deserialize)]
pub struct Update {
pub id: Id,
pub field: Field,
}
The enum is not really useful by itself, I use it for deserialization of JSON. So is it possible to do something like that?
#[derive(Serialize, Deserialize)]
pub struct PersonUpdate {
pub id: Id,
pub field: enum {
last_name(String),
first_name(String),
}
}
It is not possible, you must give it a name, like you did in the first example.