Best way to do the following join in Rails 3 - ruby-on-rails

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

Rails 4 has_many association not working when model has 2 subclasses

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

How to use join to select many to many relationship model in ruby on rails4?

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

Ruby on Rails - use where with array of object

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)

Problems with CamelCase and underscore Rails 3

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

Rails Inheritance with relationships problem

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

Resources