Save data in DB without changeset validation

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 :wink:

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

Thank you for your help

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