Rails relationships don't work - ruby-on-rails

I'm using Rails 3.0.1/Ruby 1.9.2
I have two tables: Product and Category.
These are the models:
class Product < ActiveRecord::Base
belongs_to :parent_category, :class_name => "Category"
end
class Category < ActiveRecord::Base
has_many :products
end
So I want to access the category of the product by calling product.parent_category, and get all products from a certain category by calling category.products.
But this doesn't work.
Rails raises an exception when I do category.products
column products.category_id does not exist
It tries to find the category_id column, but I have a parent_category_id column in my table and I want to use it.
How can I fix this?

Try this:
class Product < ActiveRecord::Base
belongs_to :parent_category, :class_name => "Category"
end
class Category < ActiveRecord::Base
has_many :products, :foreign_key => :parent_category_id
end

class Product < ActiveRecord::Base
belongs_to :parent_category, :class_name => "Category", :foreign_key => "parent_category_id"
end

Related

Best way to associate these models?

I have four models, Movies, Person, Cast, and Crew and I'm uncertain on what is best way to associate them all together. Should I use a has many through association or stick with the associations below? Any recommendations would be greatly appreciated.
I want to be able to visit a movie object's show page then be able to list the appropriate cast and crew associated with the movie. In addition, when I visit a Person's show page, I want to list of all their cast roles movies, in addition if they were part of the crew.
class Movie < ActiveRecord::Base
has_many :cast
has_many :crew
end
class Person < ActiveRecord::Base
has_many :cast
has_many :crew
end
class Cast < ActiveRecord::Base
belongs_to :movie
belongs_to :person
end
class Crew < ActiveRecord::Base
belongs_to :movie
belongs_to :person
end
class Movie < ActiveRecord::Base
has_many :cast_memberships
has_many :crew_memberships
has_many :cast_members, :through => :cast_memberships, :source => :person
has_many :crew_members, :through => :crew_memberships, :source => :person
alias_method :cast, :cast_members
alias_method :crew, :crew_members
end
class CastMembership < ActiveRecord::Base
belongs_to :movie
belongs_to :person
end
class CrewMembership < ActiveRecord::Base
belongs_to :movie
belongs_to :person
end
class Person < ActiveRecord::Base
end
class CastMember < Person
has_many :cast_memberships, :foreign_key => :person_id
has_many :roles, :through => :cast_memberships, :source => :movie
end
class CrewMember < Person
has_many :crew_memberships, :foreign_key => :person_id
has_many :jobs, :through => :crew_memberships, :source => :movie
end
> movie = Movie.create! name:"Star Wars"
> cast_member = CastMember.create! name:"Harrison Ford"
> movie.cast << cast_member
> movie.cast # [{name: "Harrison Ford"}]
> cast_member.roles # [{name: "Star Wars"}]
This isn't quite what you need--cast_member.roles should return the characters (["Han Solo"]) not the movies. You could add attributes to the cast_memberships and crew_memberships tables for character data or job descriptions.

complex query... how to join many classes in rails?

I have the following associations:
class Venue < ActiveRecord::Base
has_many :sales
end
class Sale < ActiveRecord::Base
has_many :sale_lines
has_many :beverages, through: :sale_lines
end
class SaleLine < ActiveRecord::Base
belongs_to :sale
belongs_to :beverage
end
class Beverage < ActiveRecord::Base
has_many :sale_lines
has_many :sales, through: :sale_lines
has_many :recipes
has_many :products, through: :recipes
end
class Recipe < ActiveRecord::Base
belongs_to :beverage
belongs_to :product
end
class Product < ActiveRecord::Base
has_many :recipes
has_many :beverages, through: :recipes
end
I wan't to see the quantity of products sold by each venue, so basically I have to multiply the recipe.quantity by the sale_line.quantity of an specific product.
I would like to call #venue.calc_sales(product) to get the quantity sold of product.
Inside the class Venue I am trying to calculating it by:
class Venue < ActiveRecord::Base
has_many :sales
def calc_sales(product)
sales.joins(:sale_lines, :beverages, :recipes).where('recipes.product_id = ?', product.id).sum('sale_lines.quantity * recipe.quantity')
end
end
However, I can't access the recipes in that way.
Any idea on how to achieve it?
For the joins, you have to use a Hash to join a already-joined table. It's hard to explain, but here are some examples:
Venue.joins(:sales, :beverages) : This implies that the relations :sales and :beverages are declared on the Venue model.
Venue.joins(:sales => :beverages) : This implies that the relation :sales exists on the Venue model, and the relation :beverages exists on the Sale model.
Consider this:
Venue
has_one :sale
Venue.joins(:sales) : This would not work, you have to use the exact same name as the relation between the Venue model & Sale model.
Venue.joins(:sale) : This would work because you used the same name of the relation.
Attention: You have to use the pluralized name in the where clause:
Venue.joins(:sale).where(:sales => { :id => sale.id })
^^ ^^ # See the plural
In your case, you can do something like this:
sales.joins(:sale_lines => { :beverage => :recipes })
.where(:recipes => { :product_id => product.id })
.sum('sale_lines.quantity * recipes.quantity')

Rails 4 Has_many :through join association with select

I am trying to upgrade a rails 3.0 app to rails 4.0. One of the behaviour I noticed is the relationship between the models stopped working.
Assume we have the following models:
class Student < ActiveRecord::Base
has_many :teacher_students
has_many :teachers, :through => :teacher_students, :select => 'teacher_students.met_with_parent, teachers.*'
# The Rails 4 syntax
has_many :teachers, -> { select('teacher_students.met_with_parent, teachers.*') }, :through => :teacher_students
end
class Teacher < ActiveRecord::Base
has_many :teacher_students
has_many :students, :through => :teacher_students, :select => 'teacher_students.met_with_parent, students.*'
end
class TeacherStudent < ActiveRecord::Base
belongs_to :teacher
belongs_to :student
# Boolean column called 'met_with_parent'
end
Now we are able to do:
teacher = Teacher.first
students = teacher.students
students.each do |student|
student.met_with_parent # Accessing this column which is part of the join table
end
This worked for Rails 3.0, but now on Rails 4.0 I am getting Unknown column 'met_with_parent' in 'field list' I believe Rails 4 is trying to be smart and not loading the entire given join tables.
I personally would recommend the following approach, using scopes:
class Student < ActiveRecord::Base
has_many :teacher_students
has_many :teachers, :through => :teacher_students
end
class Teacher < ActiveRecord::Base
has_many :teacher_students
has_many :students, :through => :teacher_students
scope :met_with_parent, -> { joins(:teacher_students).where('teacher_students.met_with_student = ?', true) }
end
class TeacherStudent < ActiveRecord::Base
belongs_to :teacher
belongs_to :student
end
Then you can do the following:
Teacher.first.students.met_with_parent
This allows you to maintain the relationships AND filter when needed.

access by active record query with join table record in has_many :through relation

I have item, region and category table they are join through item_region ans item_category table their relationship is as follow
class Item < ActiveRecord::Base
has_many :item_region
has_many :region, :through => :item_region
has_many :item_category
has_many :category, :through => :item_category
class Region < ActiveRecord::Base
has_many :category
has_many :item, ::through => :item_category
class ItemRegion < ActiveRecord::Base
belongs_to :item
belongs_to :region
end
class Category < ActiveRecord::Base
has_many :item, :through => :item_category
has_many :item_category
class ItemCategory < ActiveRecord::Base
belongs_to :item
belongs_to :category
end
I want to find all fields of item and category_name and region_name from region_id,category_id and item_id using join table.
Thanks.
I hope I got this one correct. What about obtaining the required item, then the desired region and category from it.
item = Item.find(item_id)
region = item.regions.where(:id => region_id)
category = item.categories.where(:id => category_id)
Another suggestion, you are better off providing plural form for your associations. Its intuitive to do object.collections for a has_many association. Note that Rails will still function with above code but that doesn't follow CoC(convention over configuration) principle of Rails. If you follow the convention, you wouldn't have to do a lot of configurations.
Check the examples here http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many
Personally, I'd prefer to have my associations in Item model as :
class Item < ActiveRecord::Base
has_many :item_regions
has_many :regions, :through => :item_regions
has_many :item_categories
has_many :categories, :through => :item_categories

Is it possible to implement has_many, :through with ActiveScaffold?

Consider the following models:
class Artist < ActiveRecord::Base
has_many :artist_events
has_many :events, :through => :artist_events
end
class Event < ActiveRecord::Base
has_many :artist_events
has_many :artists, :through => :artist_events, :order => 'artist_events.position'
end
class ArtistEvent < ActiveRecord::Base
default_scope :order => 'position'
belongs_to :artist
belongs_to :event
acts_as_list :scope => :artist
end
Is is possible to use ActiveScaffold to administer this type of relationship? The ArtistEvent model exists to define a hbtm relationship with additional attribute of position.
Thanks!
Jonathan
yes. after all, ArtistEvent is yet another model - and active scaffold can operate on it as long as you have routes and controllers on the join model.

Resources