Best way to do the following join in Rails 3
I have the following classes:
class Annotation < ActiveRecord::Ba开发者_运维问答se
has_many :annotation_tags
end
class Tag < ActiveRecord::Base
has_many :annotation_tags
end
class AnnotationTag < ActiveRecord::Base
belongs_to :annotation
belongs_to :tag
end
and the following join:
SELECT `annotations`.*, annotation_tags.* FROM `annotations` JOIN annotation_tags on
annotation_tags.annotation_id = annotations.id and annotation_tags.tag_id = 123
What is the best way to code this in Rails 3?
You have two options:
Use has_many, :xs, :through => :ys.
class Annotation < ActiveRecord::Base
has_many :annotation_tags
has_many :tags, :through => :annotation_tags
end
class Tag < ActiveRecord::Base
has_many :annotation_tags
has_many :annotations, :through => :annotation_tags
end
class AnnotationTag < ActiveRecord::Base
belongs_to :annotation
belongs_to :tag
end
Then you can type:
tag = Tag.find(123)
annotations = tag.annotations
Alternatively, if you don't need extra attributes on your AnnotationTag model, i.e. it's purely a join table, you can use has_and_belongs_to_many. Your join table must not have an id column, so in the migration, make sure you specify :id => false as described in the ActiveRecord Associations API docs
class Annotation < ActiveRecord::Base
has_and_belongs_to_many :tags
end
class Tag < ActiveRecord::Base
has_and_belongs_to_many :annotations
end
class AnnotationsTag < ActiveRecord::Base # First part of model name must be pluralized.
belongs_to :annotation
belongs_to :tag
end
In this case the syntax for getting all the annotations for a tag is the same.
tag = Tag.find(123)
annotations = tag.annotations
Here was my initial attempt:
tag_id = 123
Annotation.joins("JOIN #{AnnotationTag.table_name} on #{AnnotationTag.table_name}.annotation_id = #{Annotation.table_name}.id and #{AnnotationTag.table_name}.tag_id = #{tag_id}").scoped
@Cameron's is a much cleaner solution, but did require my join table class name to be changed to AnnotationsTags (note the plural).
精彩评论