consistency_fail
consistency_fail copied to clipboard
has_one does not always allow a unique constraint
A has_one
may be a refinement of a has_many
. In this case, there cannot and should not be a unique constraint on the foreign key. Currently the gem does not distinguish.
class Blog
has_many :posts
has_one :most_recent_post, -> { order(published_at: :desc) }, class_name: 'Post'
...
end
This could potentially be addressed by getting the class of each has_one and checking: If that class has a has_many
relationship as well then it would disqualify it for a unique constraint. Association reflections would be useful here:
- http://api.rubyonrails.org/classes/ActiveRecord/Reflection/ClassMethods.html
- http://www.rubydoc.info/docs/rails/3.0.0/ActiveRecord/Reflection/AssociationReflection
Yep, totally, makes sense.
I'm kind of wondering about similar use cases, but slightly different implementations too. When I've done this kind of thing in the past, I've used an instance method, rather than a has_one
, to implement things like #most_recent_post
(this has the side benefit that I don't have to worry about accidental deletion if someone uses the generated setter). If someone went in the other direction, and implemented #posts
as an instance method, and left the has_one
as an association, we wouldn't be able to spot it with this strategy.
That's the only concrete case I've got in mind right now, but my gut says that if there's this one use case that we've missed up until this point, there might be others as well that I'm not thinking of (which makes me just want to give users an escape hatch).
That's a good point. An escape hatch is making more sense.
@trptcolin @adsteel Bumping this old issue! We recently ran into this with two separate models that were both referencing a polymorphic field, one with has_one
, the other with has_many
. We were able to work around this by defining a getter on the model for the has_one
relationship.
For the escape hatch: are you thinking an additional option set on the has_one
relationship / uniqueness
validation? Using a comment will be hard since the gem relies on an introspection class instead of a full-blown parser. Something like:
class SomeModel
has_one :something, disable_consistency_fail_unique_constraint_check: true
# or
validates :something_else, uniqueness: true, disable_consistency_fail_unique_constraint_check: true
end
I was thinking more like a config file passed to consistency_fail where you can say “ignore this association; I know about it already.” I’d rather not require users to decorate production code with knowledge of this specific tool.
Hey @trptcolin is there a solution to this (if there's a config file it doesn't seem documented anywhere)? We have a very similar use case where a has_one association is a scoped version of a has_many association. Redefining the has_one association as an instance method is not appropriate for us because we join through this association a lot, e.g. most of the time we impose an ordering based on an attribute on the association.
@louietyj hasn't been implemented yet, but I'd love a PR for it!