Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upClarification on `update` #26
Comments
This comment has been minimized.
|
Yeah, you wouldn't want to make the columns This popped into my head as a possible solution. Thoughts? #[changeset_for(users)]
pub struct UserChanges {
id: i32,
#[optional_update]
first_name: Option<String>,
#[optional_update]
last_name: Option<String>,
#[optional_update]
email: Option<String>,
}Could also specify it at the struct level. #[changeset_for(users, behavior_when_none="skip")]
pub struct UserChanges {
// ...
}In the short term, I would just do 3 queries to work around this. |
sgrif
added this to the 0.2 milestone
Nov 29, 2015
sgrif
added
hole in api
discussion desired
labels
Nov 29, 2015
This comment has been minimized.
|
So I think this is what the impl AsChangeset<users> for UserChanges {
type Changeset = Vec<Box<Changeset<Target=users>>>;
fn as_changeset(self) -> Changeset {
let changes = Vec::new();
// push any fields we always update
if let Some(first_name) = self.first_name {
changes.push(Box::new(users::first_name.eq(first_name)))
}
// repeat for each field
changes
}
}I don't think there's a way to do this without boxing, and also using a |
added a commit
that referenced
this issue
Nov 29, 2015
This comment has been minimized.
|
I've pushed up the required |
This comment has been minimized.
|
Trying to build pub struct UserChanges {
id: i32,
first_name: Option<String>,
last_name: Option<String>,
email: Option<String>,
}
use self::users::dsl::*;
impl AsChangeset for UserChanges {
type Changeset = Vec<Box<Changeset<Target=users::table>>>;
fn as_changeset(self) -> Changeset<Target=users::table> {
let changes = Vec::new();
if let Some(first_name) = self.first_name {
changes.push(Box::new(first_name.eq(first_name)))
}
changes
}
}
table! {
users {
id -> Serial,
first_name -> VarChar,
last_name -> VarChar,
email -> VarChar,
}
}produces the error:
|
This comment has been minimized.
|
And trying to use
|
This comment has been minimized.
|
Your return type should be |
This comment has been minimized.
|
And you'll need |
This comment has been minimized.
|
The compiler is still sad for the same ( impl AsChangeset for UserChanges {
type Changeset = Vec<Box<Changeset<Target=users::table>>>;
fn as_changeset(self) -> Self::Changeset> {
let mut changes = Vec::new();
if let Some(first_name) = self.first_name {
changes.push(Box::new(first_name.eq(first_name)))
}
changes
}
} |
This comment has been minimized.
|
|
This comment has been minimized.
|
Whoops, I need to add some |
This comment has been minimized.
|
Just pushed an update. This compiles, and should work as a workaround (it's what I'll have the annotation generate) impl AsChangeset for UserChanges {
type Changeset = Vec<Box<Changeset<Target=users::table>>>;
fn as_changeset(self) -> Self::Changeset {
let mut changes: Vec<Box<Changeset<Target=users::table>>> = Vec::new();
if let Some(first_name) = self.first_name {
changes.push(Box::new(
users::first_name.eq(first_name).as_changeset()
))
}
if let Some(last_name) = self.last_name {
changes.push(Box::new(
users::last_name.eq(last_name).as_changeset()
))
}
if let Some(email) = self.email {
changes.push(Box::new(
users::email.eq(email).as_changeset()
))
}
changes
}
} |
This comment has been minimized.
|
I was able to get update to work implementing AsChangeset, however I was only able to get it to build using execute_returning_count. I could not get it to return the result using query_one. This was the error that I was getting:
and the same error with Query:
|
This comment has been minimized.
|
@mfpiccolo I got let changeset = UserChanges {
id: user_id,
first_name: body.get("first_name").map(|attr| attr[0].to_owned()),
last_name: body.get("last_name").map(|attr| attr[0].to_owned()),
email: body.get("email").map(|attr| attr[0].to_owned()),
};
let command = query_builder::update(users.filter(id.eq(changeset.id))).set(changeset);
let result: Option<User> = connection.query_one(command).unwrap(); |
This comment has been minimized.
|
I think we're seeing rust-lang/rust#28894 strike again. The actual issue is that |
This comment has been minimized.
|
Yep that was it. Passing the command itself and not the borrowed command was the issue. |
sgrif
modified the milestones:
0.3,
0.2
Nov 30, 2015
This comment has been minimized.
bwo
commented
Dec 1, 2015
|
This may be old news the people conversing here, but opaleye's approach to insert/update seems like a reasonably nice one, at least in haskell-land: https://github.com/tomjaguarpaw/haskell-opaleye/blob/master/Doc/Tutorial/TutorialManipulation.lhs#L48-L88 |
This comment has been minimized.
|
@bwo Yeah, I'm going to basically give the ability to choose between the current behavior and that behavior. I don't want to stop supporting putting |
This comment has been minimized.
|
I'm going to change the behavior of I will likely introduce an additional type in the future to represent wanting to assign null (which when deserializing from JSON will require the key being present, and the value being |
added a commit
that referenced
this issue
Dec 4, 2015
sgrif
referenced this issue
Dec 4, 2015
Merged
`#[changeset_for]` now skips optional fields when `None` #47
This comment has been minimized.
|
First pass at correcting this for those interested: #47 |
added a commit
that referenced
this issue
Dec 4, 2015
added a commit
that referenced
this issue
Dec 4, 2015
sgrif
closed this
in
#47
Dec 4, 2015
This comment has been minimized.
Drakulix
commented
Jan 19, 2016
|
This is actually quite a major issue for my use case. I use my changeset struct as a proxy object for the actual database object, that is also cached and updates the records on Drop. I do not need fields, that get optionally updated, I am updating the whole record anyway. I need a way to clear the nullable fields. Implementing AsChangeset manually is quite verbose, an additional option for the old behaviour would be very nice. |
This comment has been minimized.
|
FWIW it sounds like you don't actually need to use |
This comment has been minimized.
Drakulix
commented
Jan 19, 2016
|
well that is still quite large and verbose, as my struct has a lot of fields. I will go with that for now, but it would be nice to be able to just use |
This comment has been minimized.
|
Yeah, I'll probably add an option for the behavior you want at some point. We just need to be careful to make sure the API is sufficiently clear what the difference is, and make sure its a wide enough use case to justify being in the core library, as we don't want to end up with 18-billion options (e.g. after this I wouldn't be surprised for someone to have a struct where they want one |
This comment has been minimized.
Drakulix
commented
Jan 19, 2016
|
I totally understand that, but this recent change just moved the api hole to the less common use case, although there is a quite easy option to work around that. Another Alternative might be to add a new enum that might either be Null or a value, that can be instanciated from an Option. |
This comment has been minimized.
|
Yeah, I've considered that in the past as well. There's a similar gap for inserts, where there's no way to insert For just this case though, I think I'm fine with something like |
This comment has been minimized.
Drakulix
commented
Jan 19, 2016
|
Because I am not much a database guy I try to abstract away most of the database internals and just have a nice struct representation of my internal database structures. And that change would allow me easily to have one struct to rule all use cases without write much additional code. |
This comment has been minimized.
This comment has been minimized.
Drakulix
commented
Jan 19, 2016
|
@sgrif Thanks! |
mcasper commentedNov 29, 2015
So far
updateis the hardest to wrap my head around, and I was hoping for some clarification/guidance.In the context of an HTTP API, with a users table that looks like:
how would you go about implementing a Users#update action?
My first instinct is to have something like:
Such that you end up updating whatever valid params the user sends, but that requires having
Nullablecolumns in the table macro, when the columns aren't actually nullable.Keep up all the great work!