I use phoenix/ecto in my app.
On one of forms users can edit complex entity. In corresponding model i write ‘changeset’ function which call cast, cast_assoc and validate_required. In associated models i also have some validations.
Users ask me to add ability to save draft version of entity. In this case i want to skip any validations and save data to DB as is. When user press ‘save draft’ button, i got request with flag ‘draft’ after that i call changeset function and i want to skip validation in entity as well as in associated fields. I can add field called ‘draft’ to my model and check it in changeset function of main entity. But don’t know how to pass it to the ‘changeset’ of associated fields.
I think that bypassing validation is always a bad idea.
draft_changeset function and use it for the draft purpose, it will avoid your database to be full of strange things
I suggest you make a
Draft schema with a separate DB table with one column:
metadata, of type
jsonb in Postgres). Save drafts there as a free-form Elixir map and load them back later for further refinement.
If you are asking how to
cast_assoc for a specific changeset, that can be specified via the
with option… https://hexdocs.pm/ecto/Ecto.Changeset.html#cast_assoc/3
def draft_changeset(article, params) do
|> cast(params, [:title, :published_at])
|> cast_assoc(:contents, with: &MyBlog.Content.draft_changeset/2)
This might be enough. Depending on what you’re doing @dimitarvp 's draft suggestion or a separate service module with an Ecto multi and customized validation and persistence across multiple entities might make sense.
Hi, i encounter the same problematic. Can you explain please why you suggest this? I think it is a concern of data model. But what is the benefice of this approach versus saving the schema in a potentially invalid state and with status “draft”?
The benefit is that you still keep some form and shape of the data. It might be a draft but you likely still have at least one field that needs to be a date or an integer (basically not a string), right? If not, then storing just a single text field in the DB – without validation – would make sense.
This is a bit theoretical though. What’s your use case? We’d be able to give proper recommendation if we know that.
Thank for the quick response.
My use case is like the one of forum. A user creates a Post. Before it is posted, the Post is saved as draft.
The Post may have multiple statuses. The first is “draft”. The Post is saved without validation but because of its status, we know it is not postable.
In this case your focus should be the status field and making sure it never has an invalid value (using the
ecto_enum library and/or the new
Ecto.Enum facility will help you with this).
And the text can be saved freeform in the DB. You would also want a
published_at field for the posts which would be
nil before it is published.
Ecto.Enum is definitely a good idea. I will add it.
published_at will be implemented by a new status (Status is a relationship. Post has many statuses).
Before publishing a Post, I will validate the Post. WDYT?
Sounds pretty normal for that kind of stuff. Do it – and make sure you cover it with tests that try to introduce an invalid state.