Read only Ecto relation?

I’m working with Ecto changesets to manipulate changes to a schema. The trick is that the schema has some relations. I’m wondering if there’s a way to define a belongs_to relationship so it is considered read-only? In other words, I need to look at that relationship to make business decisions and customize the changeset. But when it comes to passing the changeset to Repo.update/2, I want that relation to be ignored so no database updates/deletes are attempted. For now, I am using Ecto.Changeset.delete_change/3 to remove the change prior to saving. The options available for :belongs_to (e.g. :on_replace) don’t seem to support a kind of “ignore” behavior.

Here’s a more concrete example with some common database models. Imagine I need to update the Comments on a Post where each comment has an Author. I know the author’s username (the username is used as a foreign key inside the Comment), but I don’t have the full record loaded and I want to avoid a database query. In order to do the business logic involved in attaching comments to the post, I have to stub out the Comment’s author relation with an %Author{} struct because some of the business logic evaluates author attributes. The comments are calculated (some are added, some are removed), and I used Changeset.put_assoc/4 to attach the full list of comments to the post and then save the post via Repo.update(post_changeset). The scope of this update should be limited to only the post and its comments.

Does that make sense? The actual database models are different, but I hope the above explains the shape of them. The important thing is that the one schema should never be changed – in my example, the author records should never be updated on the basis of a comment.

I’m fine with my delete_change solution, but it got me wondering if there were a more elegant way to define the relationship as “one-way” or “read-only” with respect to updates/deletes.

Thanks for any insights!

1 Like

If you control all the calling code then you can just exclude the relation from all of the changeset functions.

That does not compute for me, if you need the author’s attributes then surely fetching their attributes from the DB is not such a huge deal? But you seem to have implied that you only need their username.

But again, as above, you can just ignore the author relation in the changeset you’re using to persist your changes. Your business code can recurse the changesets: Post → Comment → Author and use e.g. Comment.update_only_own_attributes_changeset and that should be good enough, no?

I won’t argue elegance so to me it’s a tie between your solution and just explicitly using a changeset function that ignores the author in the comment’s changeset.

2 Likes

You can try submitting your request on the Ecto mailing list.