So I have a class Category:
class Category < ActiveRecord::Base
attr_accessible :category_id, :name
end
and a class UserCategory.
class UserCategory < ActiveRecord::Base
attr_accessible :user_id, :category_id, usercategory_id
self.table_name = 'contractor_categories'
self.primary_key = :nid
belongs_to :user, class_name: "User", foreign_key: "user_id",
:inverse_of => :categories
end
So when I do User.last.categories.first.name
I would like to get the name of the first associated category.
How should I do that without doing something like: Category.find(User.last.categories.first.category_id).name
Edit: I'm currently doing:
def name
Category.find(self.category_id).name
end
But I'm pretty sure there is a better way to do it.
In your model you can add an association
belongs_to :category
and then your name method would look like this
def name
category.name
end
or you could do User.last.categories.first.category.name which would save you creating a name method, but I'm not sure why you would prefer that.
Related
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
I have a model with a belongs_to association:
class Car < ActiveRecord::Base
belongs_to :vendor
end
So I can call car.vendor. But I also want to call car.company! So, I have the following:
class Car < ActiveRecord::Base
belongs_to :vendor
def company
vendor
end
end
but that doesn't solve the assignment situation car.company = 'ford', so I need to create another method for that. Is there a simple alias mechanism I can use for associations? Can I just use alias_method :company, :vendor and alias_method :company=, :vendor=?
No it doesn't look for company_id for instance change your code as follows
In Rails3
class Car < ActiveRecord::Base
belongs_to :vendor
belongs_to :company, :class_name => :Vendor,:foreign_key => "vendor_id"
end
In Rails4
We can use alias attribute.
alias_attribute :company, :vendor
In Rails 4, you should simply be able to add alias_attribute :company, :vendor to your model.
Short Version:
Generate model with migration
$ rails generate model Car vendor:references name:string ...
Add following line in Car model i.e car.rb file
class Car < ActiveRecord::Base
belongs_to :company, :class_name => 'Vendor', :foreign_key => 'vendor_id'
end
Now you have #car.company instance method.
For a Detailed explanation read ahead [Optional if you understood the above !!]
Detailed Version:
The model Car will have an association with the model Vendor (which is obvious). So there should be a vendor_id in the table cars.
In order to make sure that the field vendor_id is present in the cars table run the following on the command line. This will generate the right migration. The vendor:references is important. You can have any number of attributes after that.
$ rails generate model Car vendor:references name:string
Or else in the existing migration for create_table :cars just add the line t.references :vendor
class CreateCars < ActiveRecord::Migration
def change
create_table :cars do |t|
t.string :name
...
t.references :vendor
t.timestamps
end
end
end
The final thing that you need to do is edit the model Car. So add this code to your car.rb file
class Car < ActiveRecord::Base
belongs_to :company, :class_name => 'Vendor', :foreign_key => 'vendor_id'
end
After you do the third step you will get the following instance methods for the model Car provided by Rails Associations
#car.company
When you do #car.company it will return a #<Vendor ...> object. To find that #<Vendor ...> object it will go look for the vendor_id column in the cars table because you have mentioned :foreign_key => 'vendor_id'
You can set the company for a car instance by writing
#car.company = #vendor || Vendor.find(params[:id]) #whichever Vendor object you want
#car.save
This will save the id of that Vendor object in the vendor_id field of the cars table.
Thank You.
class Car < ActiveRecord::Base
belongs_to :vendor
belongs_to :company, :class_name => :Vendor
end
I have a has many through relationship in my app:
Shows has many Bands through => Lineups
Bands are unique by :name
class Show < ActiveRecord::Base
attr_accessible :city_id, :title, :dateonly, :timeonly, :image, :canceled, :venue_attributes, :bands_attributes
belongs_to :city
belongs_to :venue
has_many :lineups
has_many :bands, through: :lineups
has_and_belongs_to_many :users
end
class Lineup < ActiveRecord::Base
belongs_to :show
belongs_to :band
end
class Band < ActiveRecord::Base
attr_accessible :name, :website, :country, :state
has_many :lineups
has_many :shows, through: :lineups
validates :name, presence: true
validates_uniqueness_of :name
before_save :titleize_name
private
def titleize_name
self.name = self.name.titleize
end
end
New Bands are created like this:
(lets say we have a show record already saved called s1)
> s1.bands.new(name: "Wet Food")
> s1.save
Right now this will only save if a band named "Wet Food" doesn't already exist
In which model is the best place to do a Band.find_or_create in this relationship so that an existing band can be used if one with the same name exists?
This is generally the type of call that would go in a Controller (or maybe a service object), but not in a Model. It really depends on the particular user flow that you're trying to accomplish in your app. Basically, where ever you are already using s1.bands.new, you could use this instead :
s1.bands.where(name: 'Wet Food').first_or_create
This question pertains to AMS 0.8
I've got two models:
class Subject < ActiveRecord::Base
has_many :user_combinations
has_ancestry
end
class UserCombination < ActiveRecord::Base
belongs_to :stage
belongs_to :subject
belongs_to :user
end
And two serializers:
class UserCombinationSerializer < ActiveModel::Serializer
attributes :id
belongs_to :stage
belongs_to :subject
end
class SubjectSerializer < ActiveModel::Serializer
attributes :id, :name, :description, :subjects
def include_subjects?
object.is_root?
end
def subjects
object.subtree
end
end
When a UserCombination is serialized, I want to embed the whole subtree of subjects.
When I try to use this setup I get this error:
undefined method `belongs_to' for UserCombinationSerializer:Class
I tried changing the UserCombinationSerializer to this:
class UserCombinationSerializer < ActiveModel::Serializer
attributes :id, :subject, :stage
end
In this case I get no errors, but the subject is serialized in the wrong way - not using the SubjectSerializer.
My questions:
Shouldn't I be able to use a belongs_to relation in the serializer?
If not - how can I get the wanted behaviour - embedding the subject tree using the SubjectSerializer?
This is not really elegant but it seems to be working :
class UserCombinationSerializer < ActiveModel::Serializer
attributes :id, :stage_id, :subject_id
has_one :subject
end
I don't really like calling has_one whereas it's actually a belongs_to association :/
EDIT: Disregard my comment about has_one/belongs_to ambiguity, the doc is actually pretty clear about it: http://www.rubydoc.info/github/rails-api/active_model_serializers/frames
In Active Model Serializer 0-10-stable, belongs_to is now available.
belongs_to :author, serializer: AuthorPreviewSerializer
belongs_to :author, key: :writer
belongs_to :post
belongs_to :blog
def blog
Blog.new(id: 999, name: 'Custom blog')
end
https://github.com/rails-api/active_model_serializers/blob/0-10-stable/docs/general/serializers.md#belongs_to
So you could do:
class UserCombinationSerializer < ActiveModel::Serializer
attributes :id
belongs_to :stage, serializer: StageSerializer
belongs_to :subject, serializer: SubjectSerializer
end
What if you try with something like this:
class UserCombinationSerializer < ActiveModel::Serializer
attributes :subject,
:stage,
:id
def subject
SubjectSerializer.new(object.subject, { root: false } )
end
def stage
StageSerializer.new(object.stage, { root: false } )
end
end
The case:
tables:
teacher :id :name
course :id :name
teachercourse :id :teacher_id :course_id
How to do inner join to this 3 tables with rails?
Edit (my models):
class Course < ActiveRecord::Base
attr_accessible :name
has_many :teachercourses
has_many :teachers, through: :teachercourse
end
class Teacher < ActiveRecord::Base
attr_accessible :name
has_many :teachercourses
has_many :courses, through: :teachercourse
end
class Teachercourse < ActiveRecord::Base
attr_accessible :course_id, :teacher_id
belongs_to :course
belongs_to :teacher
end
Edit2 - where I need the join result(show action):
class CourseController < ApplicationController
def show
#not real syntax
#course=Course.find(join:teacher,teachercourse,teacher :: where course='javacourse');
end
end
Both your Teacher and Course models should also contain has_many :teachercourses
Then, if you're writing your code in the Teacher model it should be something like this:
joins(teachercourses: :course)
Edit:
If I understand the intention behind the code you posted, you're looking for all the teachers that teach in the java course. So this should work:
Teacher.joins(teachercourses: :course).where(course: {name: "javacourse"})