Why did a NewPost struct have to be created to actually add a new Post? Is it generally not possible to have the result type of a query be used to update the same records?
Update yes. It's possible to use the same struct for create, too if you really want, but you'd need to wrap `id` in an `Option`.
Generally we're designed for the idea that you'll have a separate struct for read, create, and update. We also try and design for the latter two mapping roughly to web forms, as this is how most large applications end up wanting to be structured from my experience.
The main thing I'm trying to avoid is lumping all your logic in a single place, and ending up in many of the same rabbit holes that Active Record has led us to (which is at least partially my fault)
I guess that makes sense. The types should be able to ensure you don't have any errors even with so many "redundant" types, but my first thought is of how much boiler plate will result. Like, I would guess most record types will be very similar between create, update, and retrieve.
> The types should be able to ensure you don't have any errors even with so many "redundant" types
Correct. Sorry, I should have been clearer in my last comment. The reason you'd want a separate type for insert is because wrapping your id in an `Option` would be painful for anything except insert.
> but my first thought is of how much boiler plate will result.
Yeah, if you're coming from Active Record (which is the other project I maintain), we'll probably seem a little bit verbose. My goal here is to be 80% as nice to get started with, but nudge users towards significantly more maintainable code as their product grows.
> I would guess most record types will be very similar between create, update, and retrieve.
Early on, yes. 2 years in, not so much.
> Also, what about upsert?
This is something I'd like to add support for in the future. This comment made me realize I didn't have a tracking issue, so here it is: https://github.com/sgrif/diesel/issues/196
We went through a somewhat similar process at an old job. Entities don't have an ID before they've been saved, but do after. Other things can change over their lifecycle too. At one point, we had separate types for each stage in the lifecycle, a bit like your different types for insert and read, but that makes it impossible to write any kind of code which is stage-agnostic - so for example you need a separate to-string function for each stage.
We were working in Scala, and I had a go at solving this using a higher-kinded type for the ID and other changing fields:
The idea is that if you say that the ID is of type 'T of String, where T is some subtype of Option', then you can bind T to None for entities that haven't been saved, Some for ones which have, and Option where you have a mixture. You then have one type definition, with which you can write stage-agnostic code, but static guarantees about whether there's an ID.
You can go further and use type constraints to enforce the order of lifecycle stages: if your object can only be saved to the database once it's been given a timestamp, say, you can make sure that the ID type can only be Some if the timestamp type is also Some.
So maybe when Rust gets HKTs it'll be time for a new release!
I guess it's because I'm coming from KDB ETL work when I think about database stuff that I don't share this perspective. But in KDB, the database has no notion of non-null columns or auto-ids or anything.