Please help me get the include right.
Poem
has_many :awards
has_one :overall_ranking
Award
belongs_to :poem
# before
# has_one :overall_ranking, :foreign_key => :poem_id
## SOLUTION
# after
has_one :overall_ranking, :foreign_key => :poem_id, :primary_key => :poem_id
OverallRanking
belongs_to :poem
update: Award.all(:include => [:overall_ranking]) # works with SOLUTION
Please note that I cannot depend on Poem#id as users may delete the poem, but if it's a winner, I make a copy within Award, so I must depend only on Award#poem_id Thank you!
Your problem is that:
has_one :overall_ranking, :foreign_key => :poem_id
means that Award has one OverallRanking and that the ID of Award in has_one relation is in column poem_id, i.e. your logic is wrong.
It would make more sense if you would just use:
#award.poem.overall_rating
Or in find:
Award.all(:include => [:poem => {:overall_ranking}])
You might want to give some more information but I hope this helps:
You might want to try joins.
For example:
Award.all(:joins => :overall_ranking, :conditions => ['some_attribute_from_overall_ranking=?', true])
So this will find awards and include the overall_ranking.
There is some confusion for me.
Award
belongs_to :poem
has_one :overall_ranking, :foreign_key => :poem_id
Here you are using the same id for both relation. This means that you try to retrieve the overall ranking with the poem id.
If I translate to sql you say something like:
overall_ranking.id = poem_id
I think this is wrong.
If you like to have the same overall_ranking for award and poem you can write something like this:
Award.rb
belongs_to :poem
has_one :overall_ranking, :through=>:poem
You can include like
Award.all(:include => [:overall_ranking])
or nested
Award.all(:include => [{:poem=>:overall_ranking}])
Update
1.Your has one association is set up incorrectly.
Please see: http://blog.hasmanythrough.com/2007/1/15/basic-rails-association-cardinality
Award
belongs_to :poem
belongs_to :overall_ranking, :foreign_key => :poem_id
OverallRanking
belongs_to :poem
has_one :award
You always should have belongs_to at the model where you store the referencing id.
2. But this not resolves your problem in your logic.
With this you will still has association between Award#poem_id = OverallRanking#id. You should have Award#poem_id = OverallRanking#poem_id.
I suggest to add overall_ranking_id to Award and things become much cleaner.
Related
I have the following models:
class Product < ActiveRecord::Base
has_many :product_recommendation_sets, :dependent => :destroy
has_many :recommendation_sets, :through => :product_recommendation_sets
end
class RecommendationSet < ActiveRecord::Base
has_many :product_recommendation_sets, :dependent => :destroy
has_many :products, :through => :product_recommendation_sets
has_many :recommendations
end
class Recommendation < ActiveRecord::Base
belongs_to :recommendation_set
end
And am adding recommendations recommendations_set like so:
p = Product.find_by_wmt_id(product) || Product.create( ItemData.get_product_data(product) )
recommendation = find_by_rec_id(rec_id) || create( ItemData.get_product_data(rec_id) )
rec_set = RecommendationSet.find_or_create_by_rating_set_id_and_model_version_and_product_id(rating_set.id, model_version, p.id)
sec_set.update_attributes(
:rating_set_id => rating_set.id,
:product_id => p.id,
:model_version => model_version,
:notes => note
)
sec_set.recommendations << recommendation
sec_set.save
prs = ProductRecommendationSet.find_or_create_by_recommendation_set_id_and_rating_set_id_and_product_id(rec_set .id, rating_set.id, p.id,)
prs.update_attributes(
:recommendation_set_id => rec_set.id,
:rating_set_id => rating_set.id,
:product_id => p.id
)
This works as expected, however my problem is that I have multiple recommendation_sets which belong to multiple products, and each of the recommendation_sets may have the same recommendation. By saving each recommendation to a recommendation_set as I am currently doing, if two recommendation_sets have the same recommendation, only one of the sets will add that recommendation. Is there anyway of saving each recommendation to multiple recommendation_sets using a secondary id, such as save by recommendation_id_and_product_id, or would I need to change this releationship to a has_many :through?
Based on your clarification, I think you basically have a many-to-many relationship between RecommendationSet and Recommendation. Presently, you have a one-to-many.
There are a couple of options:
Use the has_and_belongs_to_many method in both models to describe the relationship;
Manually create a "join" model and then give both RecommendationSet and Recommendation a has_many to this join model (with two corresponding belongs_to lines in the join model pointing to the other two models);
A has_many ... :through style, like you mentioned
Note that the first two options require you to have a join table.
If you require additional information on the join table/model, I tend to go with the 2nd option. Otherwise, either the first or third are perfectly valid.
Ryan Bates of RailsCasts made an episode about this here: http://railscasts.com/episodes/47-two-many-to-many
And some more information from the Rails documentation: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Many-to-many
In short, if you don't need extra info on the join, I think your idea of the has_many ... :through is perfectly fine.
Let me know whether that helps
Both Attendment & Vouching:
belongs_to :event
belongs_to :account
Therefore: 1 to 1 relationship between attendments and vouchings.
Is there a way to do this without my thinking too much?
# attendment
has_one :vouching :through => [:event, :account]
Note: I don't mind thinking too much, actually.
Yeah i don't think you can use a has_one for this. Assuming I'm reading this correctly, you have two models:
Attendment
Vouching
They both store an event_id and account_id. You want to know from the attendment model, what vouching shares the same event and account as the attendment. I think the easiest solution for this is to write a method inside your attendment.rb file.
class Attendment < ActiveRecord::Base
# belong to statements go here
def voucher
Voucher.where(:event_id => self.event_id, :account_id => self.account_id).first
end
end
I'm using Rails 3 and I've got a one to many association I'm trying to define: A user can have many subject families assigned to him/her, but a subject family can only be assigned to one user.
Here's what I have defined:
class User
has_many :subject_families
class SubjectFamily
belongs_to :assignee, :class_name => "User", :foreign_key => 'assigned_to'
I added a migration that does this:
change_table(:subject_families) do |t|
t.integer :assigned_to
end
I'm getting an exception when I try to do:
u = User.first
s = u.subject_families
Here's the exception:
Invalid column name 'user_id'.: SELECT [subject_families].* FROM [subject_families] WHERE ([subject_families].user_id = 1)
I was expecting this to be using subject_families.assigned_to rather than user_id but lo and behold I was disappointed in this expectation. Can anyone see what I might have missed here? I've googled this a lot and from what I can see this SHOULD work.
I believe you also need to specify the :foreign_key option on the has_many association declaration in your User model.
class User
has_many :subject_families, :foreign_key => 'assigned_to'
You need to specify :foreign_key => 'assigned_to' in the has_many relationship on User as well.
I have the following classes:
class Region < ActiveRecord::Base
has_many :geographical_relations, :as => :contained
has_many :geographical_units, :as => :container, :class_name => "GeographicalRelation"
end
class GeographicalRelation < ActiveRecord::Base
belongs_to :container, :polymorphic => true
belongs_to :contained, :polymorphic => true
end
class Country < ActiveRecord::Base
has_many :geographical_relations, :as => :contained
end
And I want to be able to, from a Country record, get all the Regions in which it is contained:
c = Country.find(1)
c.regions #=> should return all the regions in which c is contained
For now I've created the following method:
def regions
self.geographical_relations.where(:container_type => "Region").map{|relation| relation.container}
end
But I wonder if there's any way to set an "has_many" association to do this for me.
Cheers.
EDIT:
after trying the alternatives suggested in the comments I only got nice ActiveRecord errors.
The has_many_polymorphs gem seems to be a good way to do this, but for Rails 3 it is not 'officially' supported so for my case it is not a good option.
So I will work with methods like the one described above, putting them in modules and including the respective modules inside each "container"/"contained" models. This seems to work ok. =) Only change I made, to avoid N+1 Queries was adding 'includes':
def regions
self.geographical_relations.includes(:container).where(:container_type => "Region").map{|relation| relation.container}
end
Hopefully this works nicely and fast... =P =)
Anyway if anyone has an answer to solve this I'll look forward to see it! =)
Thanks all!
I know there are plenty of resources on this but I'm having a tough time relating any of them to my situation so I was hoping someone could help me clarify how this works:
Basically I have a model Action, (which gets created anytime a user does something that affects another user, like commenting on their article or voting on someones photo, for example), these actions will be listed in the users dashboard page as all the actions that have taken place that relate to them, like a stream... sort of like Github's "News Feed"
I've decided to go with creating a polymorphic association, here is what my model looks like:
class Action < ActiveRecord::Base
belongs_to :instigator, :polymorphic => true
belongs_to :victim, :polymorphic => true
end
I used instigator and victim because anyone can create these actions, which in turn always affect another user, here is my User model
class User < ActiveRecord::Base
has_many :actions, :as => :instigator
has_many :actions, :as => :victim
end
And this is where I think I'm going wrong, because ultimately I want to have a query which when I run something like User.find(1).actions to actually return all the instances in which the user is both an instigator or a victim, I think I can't have both of those have_many's in there, because when used like this I only get the instances where the user is the victim.
Here is my migration:
create_table :actions do |t|
t.references :instigator, :polymorphic => true
t.references :victim, :polymorphic => true
t.string :action_performed
t.references :resource, :polymorphic => true
t.timestamps
end
Thanks for any help, I always love the great suggestions and help the SO community gives.
This reminds of classic Friendship model problem. Polymorphic association is besides the point.
Rails version agnostic solution:
class User < ActiveRecord::Base
has_many :instigator_actions, :class_name => "Action", :as => :instigator
has_many :victim_actions, :class_name => "Action", :as => :victim
has_many :actions, :finder_sql => '
SELECT a.*
FROM actions a
WHERE (a.instigator_type = "User" AND instigator_id = #{id}) OR
(a.victim_type = "User" AND victim_id = #{id})'
end
While creating the Actions create them using one of the first two associations.
u1.instigator_actions.create(:victim => u2)
OR
u1.victim_actions.create(:instigator => u2)
At the same time you can get a list of actions associated with an user using the actions association.
u1.actions
Firstly I suggest you use roles through Single table Inheritance. In your user table , you can have a type column which identifies someone as an instigator or as a victim. (Of course if someone is both , he will have 2 rows , so you will have to make sure you dont have the name as the primary key.)
So now you have a more structured layout. Coming to the polymorphism problem,try using a different interface. As in,
class Action < ActiveRecord::Base
belongs_to :actionable, :polymorphic => true
end
actionable need not be a separate class. Its just a name given to the interface.Like wise on the other side of the association.
The Rails Way by Obie Fernandez gives you a clear picture on this, so you can refer it for more dope on polymorphic associations.