Re: [Rails] RoR Associations - has_many :through valid or invalid usage?

2014-12-12 Thread Jason Fleetwood-Boldt
of course!

No piece of code that I write on a mailing list has actually been run, it’s 
just all from my brain. Take all advice with a skeptical eye for 
omissions/errors like that. 


> On Dec 12, 2014, at 12:31 AM, Matt Jones  wrote:
> 
> This won't work, unfortunately - `validates_uniqueness_of` is expecting a 
> column name. Swapping in :user_id would, though.
> 

-- 
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/0B6DB6E3-2766-4C27-9269-2B48417A7B42%40datatravels.com.
For more options, visit https://groups.google.com/d/optout.


Re: [Rails] RoR Associations - has_many :through valid or invalid usage?

2014-12-11 Thread Matt Jones


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. > 
> 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 an

Re: [Rails] RoR Associations - has_many :through valid or invalid usage?

2014-12-11 Thread Jason Fleetwood-Boldt
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 would enforce that a user is a member of only 1 club at a time, but keeps 
the club_id out of the User model (which is nice & clean). In the future, I 
could add "acts_as_paranoid" to this model (using 
https://github.com/radar/paranoia), and then I'd still keep those old join 
records in the table marked as "delete_at" if I wanted a history of the user's 
old memberships. (And since paranoia will monkeypatch all manner of finder 
methods, it won't conflict with the validates_uniqueness_of so I will achieve 
the effect of enforcing that a User is a member of only 1 club at a time and  I 
will have a history of the clubs they belongs to in the past)

Hope this helps.
Jason




> On Dec 11, 2014, at 1:20 AM, Jorge M.  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
> 
> 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
> re

[Rails] RoR Associations - has_many :through valid or invalid usage?

2014-12-10 Thread Jorge M.
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

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-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/894be5e163e3de789c9c732b5a912826%40ruby-forum.com.
For more options, visit https://groups.google.com/d/optout.