New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to ignore field os struct #860

Open
DavidBM opened this Issue Apr 15, 2017 · 11 comments

Comments

Projects
None yet
6 participants
@DavidBM

DavidBM commented Apr 15, 2017

Actually I have this struct

#[derive(Clone, Debug, Queryable, Serialize, AsChangeset, Identifiable, Associations)]
#[has_many(authors)]
pub struct User {
	pub id: i32,
	pub name: String,
	pub email: String,
	#[serde(skip_serializing)]
	pub password: String,
	pub created_at: DateTime<UTC>,
	pub author: Option<Author>
}

The column author is not in the database, but when I return the model, I would like to put inside of the struct the Author (a user can or not to have an author). For that I want to tell diesel that ignores the field author. I'm searching if something like #[ignore_field].

	#[ignore_field]
	pub author: Option<Author>
@killercup

This comment has been minimized.

Member

killercup commented Apr 17, 2017

I'd call it #[diesel(skip_deserializing)] to be similar to serde. And we should probably only allow that for fields whose type implements Default.

@DavidBM

This comment has been minimized.

DavidBM commented Apr 17, 2017

Umm, I don't know if I explain correctly the idea, maybe the serde annotation in my code confuses more than helps.

What I was searching is a way to totally ignore that field in the database. That means, Diesel to load (in that case) always None in the author field. For being able to add data after query to the struct.

Other question than can help. Having this struct:

#[derive(Clone, Debug, Queryable, Serialize, AsChangeset, Identifiable, Associations)]
#[has_many(authors)]
pub struct User {
	pub id: i32,
	pub email: String,
	pub author: Vec<Author>
}

is possible to have something like #[loads_has_many(authors)] to tell diesel to load all the authors in that field?

@killercup

This comment has been minimized.

Member

killercup commented Apr 17, 2017

What I was searching is a way to totally ignore that field in the database. That means, Diesel to load (in that case) always None in the author field. For being able to add data after query to the struct.

Oh, yes, I think that's what I understood as well. I was suggesting

  1. an attribute name that would not collide with other crates,

  2. and to use the Default trait to figure out what the value of that field should be when diesel tries to deserialize into a User.

    (We could specialize this to Option<_>, but I'd rather not, as this is exactly what Default is for. Option<_>::default() == None, but also String::default() == "" and Vec::default() == vec![].)

Did I get that right?

is possible to have something like #[loads_has_many(authors)] to tell diesel to load all the authors in that field?

Currently, it's possible to get (User, Vec<Author>) but you'd have to write a bit of boilerplate to put that into a new struct. I can see why this is nice to have (and a lot of ORMs have this), but I'm not quite sure how it'd fit Diesel's design. This is usually the "feature" that leads to n+1 query bugs, and we currently try to stay away from mangling models with association data. So, I'd try to work with tuples (using type aliases liberally), and see how it goes.

@DavidBM

This comment has been minimized.

DavidBM commented Apr 17, 2017

Ok, you understand correctly :)

Thanks! I will check this frequently!

@sgrif

This comment has been minimized.

Member

sgrif commented May 9, 2017

I've definitely warmed to the idea of marking a field as ignorable for update/insert (to skip a field for update, you can always use my hilarious, hacky workaround for now. I'm not sure I see the use case for skipping something on Queryable though. If you want to populate other data after loading, it seems like it'd make more sense to have another struct that is struct Foo { data_from_database: DbModel, other_data: whatever }

@matt4682

This comment has been minimized.

matt4682 commented May 16, 2017

I think the only real use case for skipping a field on Queryable would be to imitate the classic ORM approach to associations.

Using the example code from the association docs:

let user = try!(users::find(1).first(&connection));
let posts = Post::belonging_to(&user).load(&connection);

then you would make posts a proper child of user:

user.posts = posts;

The docs say that you could pass it around as (User, Vec<Post>) but that doesn't exactly play nicely with serializing to JSON, which seems to be the initial reason behind this issue. The typical JSON representation would be:

{
   "id": 0,
   "name": "Foo",
   "posts": []
}

But passing it around as a tuple would have it represented as:

{
  "user": {
     "id": 0,
     "name": "Foo"
  },
  "posts": []
}
@TatriX

This comment has been minimized.

TatriX commented Jan 20, 2018

My use case. I have a struct:

#[derive(Deserialize, AsChangeset)]
#[table_name = "users"]
pub struct UpdateUserData {
    username: Option<String>,
    email: Option<String>,
    bio: Option<String>,
    image: Option<String>,
    #[skip_somehow_plz]
    password: Option<String>,
}

I use this struct to parse POST params and then I'd like to use it as a changeset.
But my table doesn't have a password field. It has hash field.

So I want to skip password field in the changeset and set hash manually based on password.

@carlosdp

This comment has been minimized.

Contributor

carlosdp commented Jan 29, 2018

I have the same use case as @TatriX, the reason I want the field to be in the same struct as Insertable is because I use it for field validations as well.

@TatriX

This comment has been minimized.

TatriX commented Jan 29, 2018

As far as I know this depends on #1512
Original use case currently can be fulfilled with nested structs and query with join.
Example from @weiznich:

#[derive(Queryable)]
struct Foo {
    id: i32,
    name: String,
    bar: Bar,
}

#[derive(Queryable)]
struct Bar {
     id: i32,
    name: String
}

let r: Vec<Foo> = foo::table.inner_join(bar::table).select((foo::id, foo::name, (bar::id, bar::name)).load(conn)?;

I'm ready to take this issue if you think this should be implemented.

@sgrif

This comment has been minimized.

Member

sgrif commented Jan 29, 2018

It's not blocked on that PR. It just hasn't been a priority. As I mentioned in #860 (comment) though, if/when this lands, it'll be for AsChangeset and Insertable, not Queryable.

@TatriX

This comment has been minimized.

TatriX commented Jan 30, 2018

It has a good use case to be implemented on Quaryable too. Basically it's the same as with Insertable.
I want to be able to reuse the same struct to do select queries, then fill the Option<NestedStruct> from another select and pass it to the json serializer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment