Has one polymorphic associtaion with conditions in rails - ruby-on-rails

I basically wanted to add a condition in the below mentioned associations
class Group < ActiveRecord::Base
has_one :post, :as => :owner, :dependent => :destroy
belongs_to :head_post, :class_name => 'Post', :dependent => :destroy, :foreign_key => 'head_post_id'
end
In posts table I have two records for each group which is post and head_post and I am differentiating between those two using a field in group 'head_post_id'
Something like
has_one :post, :as => :owner, :dependent => :destroy, :conditions => "posts.owner_id != #{self.head_post_id}"
But this doesn't seem to work

Related

saving a parent's relationship from child

I have the following relationship. I want a Post to has_one current_wrent but also has_many wrents that keeps tracks of wrent objects. I believe the issue may be related to rails being confused which relationship i'mr eferring to.
When I refer to a post.current_wrent, this is returning correctly with no errors.
Class Post
include Mongoid::Document
include Mongoid::Timestamps
...
has_one :current_wrent, :class_name => "Wrent", :inverse_of => :current_wrent, :autosave => true, :dependent => :destroy
has_many :wrents, :inverse_of => :post, :dependent => :destroy
end
Class Wrent
..
belongs_to :post, :autosave => true
..
end
When I do something like..
(in Wrent.rb)
def accept!
update_attributes!(:status => 1, :accepted => true)
post.current_wrent = self
post.available = false
post.save!
notify_user
end
and I get a "Current Wrent" is invalid error, could somebody point to me something I'm doing wrong here?
EDIT: this seems to work fine.
in wrent.rb
Class Wrent
..
belongs_to :post, :inverse_of => :wrent, :autosave => true
belongs_to :post, :inverse_of => :current_wrent
in post.rb
Class Post
...
has_one :current_wrent, :class_name => "Wrent", :inverse_of => :post
belongs_to :current_wrent, :class_name => "Wrent", :inverse_of => :post
has_many :wrents, :inverse_of => :post, :dependent => :destroy
I'm still not sure what the problem is, but now I can access post.current_wrent through the belongs_to current_wrent_id column and the problem seemed to disappear.
Your Wrent model probably has a post_id field where is stored the id of the Post that it belongs_to. But there's no field in Post to store the current_wrent.
Mongoid allows you to embed objects so what you can do is embeds_one instead has_one.
Class Post
include Mongoid::Document
include Mongoid::Timestamps
...
embeds_one :current_wrent, :class_name => "Wrent", :inverse_of => :current_wrent
has_many :wrents, :inverse_of => :post, :dependent => :destroy
end

has_many through relationship with conditions based on relationship table (Rails 2.3.5)

I have a relationship that is as follows.
companies_employee.rb
belongs_to :employee
belongs_to :company
validates_presence_of :role
employee.rb
has_many :companies_employees
has_many :companies, :through => :companies_employees
company.rb
has_many :companies_employees
has_many :managers, :through => :companies_employees, :source => :employee, conditions => {:role => "Manager"}
has_many :owners, :through => :companies_employees, :source => :employee, :conditions => {:role => "Owner"}
My problem is that when it checks the conditions, it tries to find the role column in the employees table, but the role column is in the companies_employees table.
Is there a way to make it use things in this table for the conditions?
Try something like this:
has_many :managers, :through => :companies_employees, :source => :employee, conditions => ["employees.role = 'Manager"]
has_many :owners, :through => :companies_employees, :source => :employee, conditions => ["employees.role = 'Owner"]

Complex has_many relationships and Rails

I have some accounts, and users, which are disjointed at the moment.
I need users to be able to be admins, or editors, or any (and many) accounts.
At the moment, I have this:
account.rb
has_many :memberships, :dependent => :destroy
has_many :administrators, :through => :memberships, :source => :user, :conditions => {'memberships.is_admin' => true}
has_many :editors, :through => :memberships, :source => :user, :conditions => {'memberships.is_editor' => true}
user.rb
has_many :memberships
has_many :accounts, :through => :memberships
has_many :editor_accounts, :through => :memberships, :source => :account, :conditions => {'memberships.is_editor' => true}
has_many :administrator_accounts, :through => :memberships, :source => :account, :conditions => {'memberships.is_admin' => true}
Essentially, what I am trying to acheive is a nice simple way of modelling this that works in a nice simple way. For instance, being able to do the following this would be really useful:
#account.administrators << current_user
current_user.adminstrator_accounts = [..]
etc
You should be able to do this, but it might be the notation you've used that interferes with the auto scope application:
has_many :memberships,
:dependent => :destroy
has_many :administrators,
:through => :memberships,
:source => :user,
:conditions => { :is_admin => true }
The conditions should be applied if and only if the condition keys match the column names on the association. So long as the users table doesn't have a is_admin column, this will be fine.
As a note, having multiple boolean flags for something like this can be awkward. Is it possible to be an admin and an editor? You may be better off with a simple role column and then use that:
has_many :administrators,
:through => :memberships,
:source => :user,
:conditions => { :role => 'admin' }
A multi-purpose column is often better than a multitude of single-purpose columns from an indexing perspective. You will have to index each and every one of these is_admin type columns, and often you will need to do it for several keys. This can get messy in a hurry.

has_many :through, :source, :source_type returning empty array

I got some Manager and SoccerTeam model. A manager "owns" many soccer teams; also a manager can comment on soccer teams and can comment on other managers too:
manager.rb
# Soccer teams the manager owns
has_many :soccer_teams, :dependent => :restrict
# Comments the manager has made on soccer teams or other managers
has_many :reviews, :class_name => "Comment", :foreign_key => :author_id, :dependent => :destroy
# Comments the manager has received by other managers
has_many :comments, :as => :commentable, :dependent => :destroy
# Soccer teams that have received a comment by the manager
has_many :observed_teams, :through => :comments, :source => :commentable, :source_type => "SoccerTeam"
soccer_team.rb
# The manager that owns the team
belongs_to :manager
# Comments received by managers
has_many :comments, :as => :commentable, :dependent => :destroy
# Managers that have reviewed the team
has_many :observers, :through => :comments, :source => :author, :class_name => "Manager"
comment.rb
belongs_to :commentable, :polymorphic => true
belongs_to :author, :class_name => Manager
Now, if I have a Manager commenting on a SoccerTeam I expect to find:
A Comment object in manager.reviews and in soccer_team.comments
A SoccerTeam object in manager.observed_teams
A Manager object in soccer_team.observers
While everything works well for the first and the third point, when I call manager.observed_teams I always obtain an empty array. To actually obtain the list of soccer teams a manager has commented on I need to use:
manager.reviews.collect{ |review| Kernel.const_get(review.commentable_type).find(review.commentable_id) if review.commentable_type == "SoccerTeam" }
Which is ugly. I expect the simple manager.observed_teams to work… why it doesn't?
Edit
I went a step further in understanding why it does not work. In fact, the genrated SQL is:
SELECT "soccer_teams".* FROM "soccer_teams" INNER JOIN "comments" ON "soccer_teams".id = "soccer_teams".commentable_id AND "comments".commentable_type = 'SoccerTeam' WHERE (("comments".commentable_id = 1) AND ("comments".commentable_type = 'Manager'))
While I want it to be:
SELECT "soccer_teams".* FROM "soccer_teams" INNER JOIN "comments" ON "soccer_teams".id = "comments".commentable_id AND "comments".commentable_type = 'SoccerTeam' WHERE ("comments".author_id = 1)
So the question is simple: how to obtain that query? (An heuristic attempt with :foreign_key ans :as, as expected, hasn't solved the problem!).
I think you've simply used the wrong association for observed_teams. Instead of
has_many :observed_teams, :through => :comments,
:source => :commentable, :source_type => "SoccerTeam"
Try this:
has_many :observed_teams, :through => :reviews,
:source => :commentable, :source_type => "SoccerTeam"
Also in,
has_many :reviews, :class_name => :comment,
:foreign_key => :author_id, :dependent => :destroy
:comment should be 'Comment'
and in
has_many :comments, :as => commentable, :dependent => :destroy
commmentable should be :commmentable

How can one obtain a row count from has_many :through relations with :uniq => true

This is my model:
class Tag < ActiveRecord::Base
# id, name
has_many :taggings
end
class Tagging < ActiveRecord::Base
# id, tag_id, owner_id, target_type, target_id
belongs_to :tag
belongs_to :owner, :class_name => 'User'
belongs_to :target, :polymorphic => true
validates_uniqueness_of :tag_id, :scope => [ :target_id, :target_type, :owner_id ]
end
class Asset < ActiveRecord::Base
# id, owner_id, title, type, etc
belongs_to :owner, :class_name => 'User'
has_many :taggings, :as => :target
has_many :taggers, :through => :taggings, :source => :owner, :uniq => true
has_many :tags, :through => :taggings, :uniq => true
end
class User < ActiveRecord::Base
# id, name, email, etc
has_many :assets, :foreign_key => 'owner_id'
has_many :my_taggings, :class_name => 'Tagging', :foreign_key => 'owner_id'
has_many :my_tags, :through => :my_taggings, :source => :tag, :uniq => true
has_many :taggings, :as => :target
has_many :taggers, :through => :taggings, :source => :owner, :uniq => true
has_many :tags, :through => :taggings, :uniq => true
end
All of the relations are working but I have an additional requirement that I can't find the solution for:
consider this relation in the Asset class
has_many :tags, :through => :taggings, :uniq => true
calling Asset.find( :first ).tags returns an array of Tags as expected but I need for each Tag to contain a count attribute indicating how many times the row would have appeared if :uniq => true was not specified.
eg. more than one User could apply the same Tag to an Asset. I'd like to display the tag name plus the number of users that applied it.
This should do exactly what you want.
has_many :tags_with_count, :source => :tag, :through => :taggings,
:group => "tags.id", :joins => :taggings,
:select = "tags.*, COUNT('taggings.id') AS frequency"
In terms of rows returned :group => :id will return the same set as :uniq => true, but it will also allow you to perform the calculations you want. This statement is more labour intensive than :uniq => true, so I've given it a different name allowing you to choose whether to fetch the unique tags with their grouped counts, or just the list of unique tags.
The above statement will add the frequency attribute to the records returned. Through the magic of method_missing, you can access that with #tag.frequency.
Usage:
#tags = #asset.tags_with_count
#tags.each{|tag| puts [tag.id, tag.name. tag.frequency].join "\t"}
Will print the id, name, and number of occurrences of each tag for #asset.

Resources