I have a relationship table in a rails application called edit_privileges
, in which the User
is the "editor" and a number of other classes are "editable". Let's say that two of those classes are Message
and Comment
.
My EditPrivilege
model uses the following code:
belongs_to :editor, :class_name => "User"
belongs_to :editable, :polymorphic => true
And User
, of course
has_many :edit_privileges, :foreign_key => "editor_id"
In order to determine if a user has edit privileges for a certain model, I can't do the normal query:
user.edit_privileges.find_by_editable_id(@message.id)
because if the user has edit privileges to edit a comment with the same id as @message
, the query will return true with the wrong edit privilege record from the table.
So, I tried doing these options:
user.edit_privileges.find(:all, :conditions => ["editable_id = ? AND editable_type ?", @message.id, @message.class.to_s])
user.edit_privileges.where(:editable_id => @message.id, :editable_type => @message.class.to_s)
which works great at finding the right record, but returns an array instead of an object (an empty array []
if there is no edit privilege). This is especially problematic if I'm trying to create a method to destroy edit privileges, since you can't pass .destroy
on an array.
I figure appending .first
to the two above solutions returns the first object and nil
if the result of the query is an empty has, but is that really the best way to do it? Are there any problems with doing it this way? (like, instead of using dynamic attribute-based finders like find_by_editabe_id_and_editable_type
)
Use find(:first, ...)
instead of find(:all, ...)
to get one record (note it might return nil while find will raise an RecordNotFound exception). So for your example:
user.edit_privileges.find(:first, :conditions => { :editable_id => @message.id, :editable_type => @message.class.to_s })
BTW, if you're on more edge rails version (3.x), Model.where(...).first
is the new syntax:
user.edit_privileges.where(:editable_id => @message.id, :editable_type => @message.class.to_s).first