I have the following classes:
class Annotation < ActiveRecord::Base
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).
Related
I'm using Rails 4.
I have an Anomaly Records class called Ar that inherits from the following classes as follows:
class RecordBase < ActiveRecord::Base
self.abstract_class = true
end
class ArAndEcrBase < RecordBase
self.abstract_class = true
# Relations
belongs_to :originator, class_name: 'User', foreign_key: 'originator_id'
has_many :attachments
end
class Ar < ArAndEcrBase
end
I want to share some relations with a class that handles another type of records in the Ar subclass however the has_many relationship doesn't work.
The following works:
> Ar.last.originator
=> #<User id: 1, ...
The following crashes:
> Ar.last.attachments
Mysql2::Error: Unknown column 'attachments.ar_and_ecr_base_id'
For some reason the has_many relationship doesn't work well. It should look for column attachments.ar_id and not attachments.ar_and_ecr_base_id
Am I doing something wrong? Or is this a Rails bug?
Atm the only way to get the code working is to move the has_many relation to the Ar class:
class Ar < ArAndEcrBase
has_many :attachments
end
If you want several models to have association to the same other model you probably need a polymorphic association
class Picture < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
end
class Employee < ActiveRecord::Base
has_many :pictures, as: :imageable
end
class Product < ActiveRecord::Base
has_many :pictures, as: :imageable
end
My data model is fellows, the note and hashtag's relationship is many to many
class Note < ActiveRecord::Base
attr_accessible :title
attr_accessible :content
attr_accessible :created_at
default_scope -> { order(created_at: :desc) }
has_and_belongs_to_many :hashtags
end
class Hashtag < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :notes
end
class NoteHashtag < ActiveRecord::Base
t.belongs_to :note, index: true
t.belongs_to :hashtag, index: true
end
I want to inquire the sql like:
select Note.* from Note inner join NoteHashtag on Note.id=NoteHashtag.note inner join Hashtag on NoteHastag.hashtag=Hashtag.id where Hashtag.name="test"
How to convert the sql into the datamodel operation in ruby on rails4?
I try to use the:
#note=Note.joins(:hashtag).where(name: "test")
and the error is:
ActionView::Template::Error (Association named 'hashtag' was not found on Note;perhaps you misspelled it?):
You need has_many :through associations if you are going to explicitly define the join model NoteHashtag. If you delete that model, you can do #notes=Note.joins(:hashtag).where(name: "test") and ActiveRecord will generate the query you are expecting.
Otherwise you could do
class Note < ActiveRecord::Base
...
has_many :note_hashtags
has_many :hashtags, through: :note_hash_tags
end
class Hashtag < ActiveRecord::Base
attr_accessible :name
end
class NoteHashtag < ActiveRecord::Base
belongs_to :note
end
Then #notes = Note.joins(:note_hashtags).joins(:hash_tags).where(name: "test) would work.
Note that both of these will return more than one note.
You can get records from many to many relationship by doing this:-
#note = Note.joins(:hashtags).where('hashtags.name' => 'test')
Why don't you address this relationship originating from the proper model? Define your models like this:
class Note < ActiveRecord::Base
...
has_many :note_hashtags
has_many :hashtags, through: :note_hashtags
end
class Hashtag < ActiveRecord::Base
attr_accessible :name
has_many :note_hashtags
has_many :notes through: :notes_hashtags
end
class NoteHashtag < ActiveRecord::Base
belongs_to :note
belongs_to :hashtag
end
And then query like this:
Hashtag.where(name: 'Test').notes
my Post object has post_categories array
Post.post_categories = []
For my all Post object i need to filter posts by post_categories which include 1
like this
Post.all.where :post_categories.include? 1
how can i do
these are models
class Post < ActiveRecord::Base
has_many :categorizes
has_many :post_categories, :through=>:categorizes
accepts_nested_attributes_for :post_categories
end
class PostCategory < ActiveRecord::Base
has_many :categorizes
has_many :posts, :through=>:categorizes
end
class Categorize < ActiveRecord::Base
belongs_to :post
belongs_to :post_category
end
You can use a join and filter on the ID field of the joined table:
Post.joins(:post_categories).where('post_categories.id' => 1)
I have two models with a has many through association between them like below:
TipoDocumento < ActiveRecord::Base
has_many :dependencias
has_many :TipoRequisitos, :through => :dependencias
...
end
TipoRequisito < ActiveRecord::Base
has_many :dependencias
has_many :TipoDocumentos, :through => :dependencias
...
end
Dependencia < ActiveRecord::Base
belongs_to: TipoDocumento
belongs_to: TipoRequisito
...
end
The id's attributes for the join model Dependencia are TipoDocumento_id and TipoRequisito_id.
Now, when I try this in the rails console:
x = TipoDocumento.find(1)
x.TipoRequisitos
I get this error:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: dependencia.tipo_documento_id: SELECT "tipo_requisitos".* FROM "tipo_requisitos" INNER JOIN "dependencia" ON "tipo_requisitos"."id" = "dependencia"."TipoRequisito_id" WHERE "dependencia"."tipo_documento_id" = 1
also if I try the opposite with TipoRequisito it's the same.
It seems that Rails is changing somehow the TipoDocumento_id column name for tipo_documento_id when it performs the query. So, I tried to change the id's column's names from their CamelCase to their snake_case, but I get the analog error (Cannot find TipoDocumento_id or TipoRequisito_id.)
I don't see what's wrong.
You need to follow the Rails convention and use down-cased names when you refer to models when defining relations:
TipoDocumento < ActiveRecord::Base
has_many :dependencias
has_many :tipo_requisitos, through: :dependencias
...
end
TipoRequisito < ActiveRecord::Base
has_many :dependencias
has_many :tipo_documentos, :through => :dependencias
...
end
Dependencia < ActiveRecord::Base
belongs_to :tipo_documento
belongs_to :tipo_requisito
...
end
you need to lower-case it, like this:
x = TipoDocumento.find(1)
x.tipo_requisitos
Please also check: http://guides.rubyonrails.org/association_basics.html
I am using single table inheritance in my project. Instead of explaining more, I'll give the code:
# person_profile.rb
class PersonProfile < ActiveRecord::Base
belongs_to :Person
end
# company_profile.rb
class CompanyProfile < ActiveRecord::Base
belongs_to :Company
end
# person.rb
class Person < User
has_one :PersonProfile
end
# company.rb
class Company < User
has_one :CompanyProfile
end
This seems to me like it should work fine. In one of my views I try if #person.PersonProfile == nil which makes perfect sense to me. But Rails doesn't like it:
Mysql::Error: Unknown column 'person_profiles.person_id' in 'where clause': SELECT * FROM `person_profiles` WHERE (`person_profiles`.person_id = 41) LIMIT 1
Rails is looking for person_id in the table person_profiles, but there is only a user_id in that table. What is the best way to fix this bug?
You can use the :foreign_key option of has_one to specify the key.
For example:
has_one :person, :foreign_key => "user_id"
See this reference.
The models specified in your model associations should be in lowercase with each word being separated by an underscore. So:
class PersonProfile < ActiveRecord::Base
belongs_to :person
end
class CompanyProfile < ActiveRecord::Base
belongs_to :company
end
class Person < User
has_one :person_profile
end
class Company < User
has_one :company_profile
end
i had to specify the foreign_key as 'user_id' because it thinks its 'person_id' by default.
class Person < User
has_one :person_profile, :foreign_key => 'user_id'
end
class Company < User
has_one :company_profile, :foreign_key => 'user_id'
end