-
Notifications
You must be signed in to change notification settings - Fork 21.9k
Finding inverse associations automatically #9522
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Finding inverse associations automatically #9522
Conversation
/cc @tenderlove |
I support the idea as I would really like to see such behaviour be automatic (I never understood why parent.children.build, a very commo pattern, doesn't set parent on children without :inverse_of which is a somewhat less known option). That being said, I have some questions about implementation. Would this work with Also, if parent has_many children and child belongs_to parent, then when using syntax |
inverse_name = active_record.name.downcase | ||
|
||
begin | ||
klass.reflect_on_association(inverse_name.to_sym) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll need to consider that the inverse association may exclude this record via :conditions or :class_name - any others? Default scope on the associated class as well.
This gets hairy quickly. Any way to limit the complexity & scope? Perhaps ignoring associations with any exclusionary conditions at all, rather than trying to check whether they match.
@egilburg Your right, it seems like I should be excluding a bunch of cases when the association, as @jeremy said, has particular options on it. I'll also go ahead and implement some caching because it seems to have a small memory penalty in comparison with running this method multiple times. I'm going to make sure that this method isn't called when there are conditions on the association. |
I've been poking at a similar patch, but have never gotten it working quite right. Some thoughts:
|
I've done a bunch of things to improve this PR. First, I've added caching as @egilburg suggested. The results for each reflection are computed once and stored in the Second, I've added the ability for a user to opt out of having the reflection automatically find an inverse, as @al2o3cr suggested. You can now use the following syntax to opt out of the automatic inverse finding: has_many :posts, :automatic_inverse_of => false The option only works when you are given an explicit Third, I've limited the method so that that when particular options are specified on an association, the automatic inverse finding will fail. The association options that cause a failure in the automatic inverse finder are specified in: INVALID_AUTOMATIC_INVERSE_OPTIONS = [:class_name, :conditions, :through, :polymorphic, :foreign_key] These options are checked for both the current reflection and the potential inverse. As a side note: a couple of the models in the ActiveRecord tests had to be changed to |
Looking good 👍 |
Wouldn't |
@al2o3cr That's a good point. I don't really see any downside with that approach. I'll go ahead and change it. |
Does this work on both sides of a has_many relationship? Refer to issue: #8125 |
@Retistic No it doesn't, and in fact, it shouldn't because the belongs_to association isn't supposed to be invertible into a has_many. For example, see here in the documentation: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations.rb#L933. Even more clear is inside the belongs_to_association file where I'm not adding any extra functionality to inverse_of, I'm simply making sure that if an inverse can be found easily, then Rails will automatically call that inverse instead of waiting for the user to add it. |
That makes sense and this is a great idea. +1 for this pull request |
I'm up for working on getting this merged, but let's wait til 4-0-stable has been branched. |
@jonleighton - the biggest reason I can see to prefer |
@al2o3cr probably we should just check for falsey values, which both |
Letting people know this is the current state of this PR (had to hunt for it in comments)
After 4.0 has been branched we will re-visit this feature. |
This is really exciting, my original intention for There's some work in there that might be useful to look at, particularly https://github.com/h-lame/parental_control/blob/master/lib/parental_control.rb#L17 onwards. It looks like you've got everything covered, but I notice that you explicitly discard associations with the |
@h-lame Thanks for the tip! I looked at the I think this makes it possible to keep associations with the |
the results. Added tests to check to make sure that inverse associations are automatically found when has_many, has_one, or belongs_to associations are defined.
/cc @jonleighton 4-0-stable is live now, this can be reviewed now 😄 |
…on_inverses Finding inverse associations automatically
I don't think this was ready to be merged. It still has the Also it really needs some documentation (changelog entry, edits to docs in |
@jonleighton Yep, I'll put this on my bucket list. |
@jonleighton Is this done? |
If you don't define
inverse_of
in an association, then you don't get the performance benefits of the inverse associations. However, these inverses can often times be guessed automatically. I'm adding functionality that will do some simple guesses if theinverse_of
has not been set in an association.For example, let's say we have two models Post and Comment:
The code I'm adding will automatically find that the post and comments are inverses of each other, so that the result is that the above code is equivalent to this:
Note that I've removed some tests which no longer make any sense (they were asserting that inverses were nil, but the new functionality means that they actually do find an inverse automatically).