Skip to content

Add ability to configure the behavior of writing to a non-writable field#4734

Open
green-david wants to merge 1 commit into
elixir-ecto:masterfrom
green-david:dg-writable-violation-handling
Open

Add ability to configure the behavior of writing to a non-writable field#4734
green-david wants to merge 1 commit into
elixir-ecto:masterfrom
green-david:dg-writable-violation-handling

Conversation

@green-david

Copy link
Copy Markdown

This PR adds the ability to change the default behavior of silently ignoring changes to non-writable fields as discussed on the mailing list.

Attempting to write to a non-writable field can lead to code which appears to update the field but actually doesn't, leading to confusing behavior or hidden bugs. By allowing the user to specify that a warning should be logged or an exception raised, these errant writes can be detected and remedied.

Comment thread lib/ecto/repo/schema.ex
Comment on lines +641 to +650
message = "attempted to write to non-writable field #{inspect(name)} during #{action}"

case on_writable_violation do
:raise ->
raise ArgumentError, message
:warn ->
Logger.warning(message)
_ ->
:ok
end

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The message here likely needs to be more robust and contain additional information, open to feedback. This is just a simple message for now to illustrate the functionality.

Comment thread lib/ecto/schema.ex
Comment on lines +704 to +709
* `:on_writable_violation` - Defines what action to take when performing an insert or update
attempts to modify a field that should not be modified according to it's `:writable` value.
Must be one of `:nothing`, `:warn`, or `:raise`. If set to `:nothing`, the modification is
silently ignored. If set to `:warn`, the modification is ignored and a warning is logged. If set
to `:raise`, an exception is raised and the operation is aborted. If `:writable` is set to `:always`,
`:on_writable_violation` must be set to `:nothing`. Defaults to `:nothing`.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this as "the path of least resistance" to illustrate the functionality, but I am not confident this is the desired way forward as to the public facing API of how to configure this behavior.

Some other options:

writable: [when: :never, on_violation: :raise]
writable: {:never, :raise}
writable: [:never, :raise]

Open to suggestions :)

@josevalim

Copy link
Copy Markdown
Member

Looking at the code, I like the on_writable_violation approach. However, I wonder if the implementation should be module.__schema__(:on_writable_violation). It should be much simpler and we only need to invoke it if there is a violation indeed.

@josevalim

Copy link
Copy Markdown
Member

WDYT?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants