Inheritance with Rails - ruby-on-rails

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

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

Possible to alias a belongs_to association in Rails?

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

find_or_create on a has many through relationship

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

Active Model Serializers belongs_to

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

How do I join multiple tables?

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"})

Resources