On Thursday, 11 December 2014 09:34:12 UTC-6, Jason FB wrote: > > Jorge, > > Although I have heard of people using a has_many in some cases when the > actual domain model might want a has_one (for example, if your business > rule is that the association can have only one at a time and you want to > keep the old join table records around with an "expired" field), generally > I would recommend using has_one where has_one is appropriate. > > has_one shouldn't preclude you from being able to create many-to-many > relationships on the table B (or rather from B->C), nor should it disable > you from being able to join together multiple eager loading associations in > your finder. > > It will however change the naming of your relationships, and it may mean > you wind up with some law-of-demeter violations down the road. > > In short, I would recommend using a has_one unless you have a compelling > reason to do it with has_many. Two examples of compelling reasons: 1) You > want to preserve old join records like I said above, or 2) You will want to > change this relationship to many-to-many soon (within 3 months) and you > want to plan for that change. > > Take a step back and consider that all Rails associations are doing are > adding "utility" methods to your object. How you use them, and how elegant > you find the code that is produced, is subjective and at the end of the day > still in your court. So use them as best you can to make the most elegant > code you can. (Performance considerations aside) > > On a side note, I would strongly encourage you to look into Active Model > serializer to encapsulate the serialization logic. > > Finally, I would note that one little trick you can do to enforce a > has_one-like behavior while actually using has_many is in your join model > you could use validates_uniqueness_of to enforce that the join table has 1 > and only 1 record for a User, like so: > > class Memberships > belongs_to :user > belongs_to :club > validates_uniqueness_of :user > end >
This won't work, unfortunately - `validates_uniqueness_of` is expecting a column name. Swapping in :user_id would, though. > > > On Dec 11, 2014, at 1:20 AM, Jorge M. <li...@ruby-forum.com <javascript:>> > wrote: > > Hello everyone, > > I came across with the following case and I was wondering if using a > has_many through association is a valid solution. > > Let's say we have three models: A, B and C. The association between > model A and model B is a 1 to 1 association with B having a Foreign Key > to A. Now, the association between B and C is a 1 to many association > with C having a Foreign Key to B. There's an action where I need to > create an xml representation of some of the properties of each instance > in a collection of A objects. Additionally, for each instance of A, I > need to append to its serialized representation two properties from > each instance of C. This C instance is associated somehow to the A > instance through an instance of B. > > To create the xml for each instance of A, I will need to iterate over > each A instance, for each A instance I will need to read two > attributes of its corresponding C instances. I want to avoid > unnecessary trips to the database, therefore, I would like to load the > A instances together with its somehow corresponding C instances outside > the loop for the xml creation. I thought to add a has_many through > association in A like this: > > class A < ActiverRecord::Base > has_many :cs, through: :b > end > > This is missing a part. The `through` option takes the name of another association on the same class. If I'm reading you correctly, your models look something like this: class A < ActiveRecord::Base has_one :b has_many :cs, through: :b end class B < ActiveRecord::Base belongs_to :a has_many :cs end class C < ActiveRecord::Base belongs_to :b end In this case, you can certainly load As together with the corresponding Cs using `include`: a_ids = [1,2,3,4] # presumably you start with ids, or some other data sufficient to find records all_the_as = A.include(:cs).find(a_ids) # this loads the specified A records, together with all their corresponding C records The above code will load ALL the A records at once. If this isn't a good idea (maybe a_ids has 30k ids in it...) then read up on find_each / find_in_batches to do things more efficiently. --Matt Jones > so later I could do the following: > > as = A.all.includes(:cs) > as.each do |a| > xml.prop_1 a.prop_1 > xml.prop_2 a.prop_2 > a.cs.each do |c| > xml.prop_3 = c.prop_3 > xml.prop_4 = c.prop_4 > end > end > > that way, I avoid some unnecessary trips to the database. However, I am > not sure if this approach is correct. According to the Rails > documentation, the examples about the has_many :through association > illustrates its usage when B has a 1 to many association to both A and > C, in other words, a Foreign Key to A and C respectively. The > association in my case between A, B and C is different. So I am not > sure if adding a has_many through in my case would be a valid solution. > I checked the final result and apparently it's working as expected, but > I would like to know if I am missing something with this approach. > > I am going to look at the rails source code to try to find out if this > approach is valid or not, but in the meantime any help to confirm if it > is acceptable to use a has_many through, why and why not, would be > really appreciated. > > -- > Posted via http://www.ruby-forum.com/. > > -- > You received this message because you are subscribed to the Google Groups > "Ruby on Rails: Talk" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to rubyonrails-ta...@googlegroups.com <javascript:>. > To post to this group, send email to rubyonra...@googlegroups.com > <javascript:>. > To view this discussion on the web visit > https://groups.google.com/d/msgid/rubyonrails-talk/894be5e163e3de789c9c732b5a912826%40ruby-forum.com > . > For more options, visit https://groups.google.com/d/optout. > > > ---- > > Jason Fleetwood-Boldt > te...@datatravels.com <javascript:> > http://www.jasonfleetwoodboldt.com/writing > > All material © Jason Fleetwood-Boldt 2014. Public conversations may be > turned into blog posts (original poster information will be made > anonymous). Email ja...@datatravels.com <javascript:> with > questions/concerns about this. > > -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-talk+unsubscr...@googlegroups.com. To post to this group, send email to rubyonrails-talk@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/rubyonrails-talk/655d36a5-e480-4a6c-aa62-3bc8a6604902%40googlegroups.com. For more options, visit https://groups.google.com/d/optout.