Good day!
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.
Please advise
Thanks
I think that bypassing validation is always a bad idea.
Add draft_changeset
function and use it for the draft purpose, it will avoid your database to be full of strange things 
2 Likes
I suggest you make a Draft
schema with a separate DB table with one column: metadata
, of type :map
(jsonb
in Postgres). Save drafts there as a free-form Elixir map and load them back later for further refinement.
5 Likes
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
article
|> cast(params, [:title, :published_at])
|> cast_assoc(:contents, with: &MyBlog.Content.draft_changeset/2)
end
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.
2 Likes
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.
1 Like
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.
1 Like