I've read multiple questions about this, but have yet to find an answer that works for my situation.
I have 3 models: Apps, AppsGenres and Genres
Here are the pertinent fields from each of those:
Apps
application_id
AppsGenres
genre_id
application_id
Genres
genre_id
The key here is that I'm not using the id field from those models.
I need to associate the tables based on those application_id and genre_id fields.
Here's what I've currently got, but it's not getting me the query I need:
class Genre < ActiveRecord::Base
has_many :apps_genres, :primary_key => :application_id, :foreign_key => :application_id
has_many :apps, :through => :apps_genres
end
class AppsGenre < ActiveRecord::Base
belongs_to :app, :foreign_key => :application_id
belongs_to :genre, :foreign_key => :application_id, :primary_key => :application_id
end
class App < ActiveRecord::Base
has_many :apps_genres, :foreign_key => :application_id, :primary_key => :application_id
has_many :genres, :through => :apps_genres
end
For reference, here is the query I ultimately need:
#apps = Genre.find_by_genre_id(6000).apps
SELECT "apps".* FROM "apps"
INNER JOIN "apps_genres"
ON "apps"."application_id" = "apps_genres"."application_id"
WHERE "apps_genres"."genre_id" = 6000
UPDATED Try this:
class App < ActiveRecord::Base
has_many :apps_genres, :foreign_key => :application_id
has_many :genres, :through => :apps_genres
end
class AppsGenre < ActiveRecord::Base
belongs_to :genre, :foreign_key => :genre_id, :primary_key => :genre_id
belongs_to :app, :foreign_key => :application_id, :primary_key => :application_id
end
class Genre < ActiveRecord::Base
has_many :apps_genres, :foreign_key => :genre_id
has_many :apps, :through => :apps_genres
end
With query:
App.find(1).genres
It generates:
SELECT `genres`.* FROM `genres` INNER JOIN `apps_genres` ON `genres`.`genre_id` = `apps_genres`.`genre_id` WHERE `apps_genres`.`application_id` = 1
And query:
Genre.find(1).apps
generates:
SELECT `apps`.* FROM `apps` INNER JOIN `apps_genres` ON `apps`.`application_id` = `apps_genres`.`application_id` WHERE `apps_genres`.`genre_id` = 1
Related
The following are my three models: many users can each have many products (and vice versa) through an associations model.
class Product < ActiveRecord::Base
has_many :associations
has_many :users, :through => :associations
end
class User < ActiveRecord::Base
has_many :associations
has_many :products, :through => :associations
has_many :medium_associated_products, :class_name => "Product", :through => :associations, :source => :product, :conditions => ["associations.strength = ?", "medium"]
has_many :strong_associated_products, :class_name => "Product", :through => :associations, :source => :product, :conditions => ["associations.strength = ?", "strong"]
end
class Association < ActiveRecord::Base
belongs_to :user
belongs_to :product
end
To add a "medium" association.strength product to the user, I usually do:
user.products << product #associations.strength is by default "medium"
My question is how would I do the same thing and add a product to the user but with "strong" association.strength initalized?
You can do the same with strong by
user.strong_associated_products << product
You may need to set to your relations this way:
class User < ActiveRecord::Base
has_many :medium_associations, class_name: 'Association', condition: ["associations.strength = ?", "medium"]
has_many :strong_associations, class_name: 'Association', condition: ["associations.strength = ?", "strong"]
has_many :medium_associated_products, class_name: 'Product', through: :medium_associations, source: :product
has_many :strong_associated_products, class_name: 'Product', through: :strong_associations, source: :product
end
Adding onto #sonnyhe2002's answer. I ended up using a callback such as
has_many :strong_associations, class_name: 'Association', condition: ["associations.strength = ?", "strong"], add_before: :set_the_strength
and then
def set_the_strength(obj)
obj[:strength] = "strong"
end
So, I'm trying to test my models associations with the following test:
it 'retrieve items registered under person' do
p = FactoryGirl.create(:person)
o = FactoryGirl.create(:order, customer: p)
o.customer.should == p
i = FactoryGirl.create(:item)
o.items << i
o.save
p.items.count.should == 1
end
My models:
class Person < AR:Base
has_many :orders, :as => :customer
has_many :items, :through => :orders
end
class Order < AR:Base
has_many :items
belongs_to :customer, :class_name => Person
end
class Item < AR:Base
belongs_to :order
has_one :customer, :class_name => Person, :through => :order
end
But when I run the test it gives me the following error:
SQLite3::SQLException: no such column: orders.customer_type: SELECT COUNT(*) FROM "items" INNER JOIN "orders" ON "items"."order_id" = "orders"."id" WHERE "orders"."customer_id" = 1 AND "orders"."customer_type" = 'Person'
What am I doing wrong?
Update:
The problem was on the ':as => :customer' bit. But my real problem was with the test. I should have assigned an order in the creation of an item.
It's because the :as option specifies a polymorphic interface. This explains "orders"."customer_type" = 'Person' in the where clause. I think what you mean to do is:
class Person < ActiveRecord::Base
has_many :orders, :foreign_key => :customer_id
has_many :items, :through => :orders
end
See the :as option in the guide.
I think Item should belong_to customer through order
class Item < AR:Base
belongs_to :order
belongs_to :customer, :class_name => Person, :through => :order
end
i have Author entity which belongs_to User. User has_many posts. Please advice how can i show recent_posts on Author entity from User.
class User < ActiveRecord::Base
has_many :posts, :foreign_key => "author_id"
end
class Post < ActiveRecord::Base
attr_accessible :title, :content
belongs_to :author, :class_name => "User"
end
class Author < ActiveRecord::Base
belongs_to :user
has_many :recent_posts, :through => :user,
:class_name => "Post",
:limit => 3,
:order => "updated_at desc"
end
How recent_post should be done? Raw sql?
You want the :source option to has_many, which you use to specify the association on the other model, like so:
has_many :recent_posts, :through => :user, :source => :posts, :limit => 3, :order => 'updated_at desc'
I have the Course model which has a number of has many through associations with the User model with join table CourseUser. The join table has an attribute type_str which specifies which role the user takes on. I have added validation to ensure that only one record is present in the join table for each course, user pair. The problem is ensuring that this record is updated if it is already present, rather than adding a new one which of course makes validation fail.
User class:
class User < ActiveRecord::Base
...
has_many :courses_enrolled_on, :through => :course_enrollees, :source => :course, :conditions => { :course_users => { :type_str => "enrollee" } }
has_many :course_users
has_many :courses, :through => :course_users, :source => :course, :readonly => true
end
Course class
class Course < ActiveRecord::Base
has_many :course_enrollees, :conditions => { :type_str => "enrollee" }, :class_name => CourseUser
has_many :enrollees, :through => :course_enrollees, :source => :user
has_many :course_users
has_many :users, :through => :course_users, :source => :user, :readonly => true
end
Course class:
class CourseUser < ActiveRecord::Base
belongs_to :course
belongs_to :user
validates_uniqueness_of :course_id, :scope => :user_id
end
This should be easy. I think I must be getting caught up on naming.
Both a 'manager' and a 'subordinate' (employee) are of class "Person".
Here's what I have:
class Person < ActiveRecord::Base
has_many :person_manager_assignments
has_many :managers, :through => :person_manager_assignments
has_many :subordinates, :through => :person_manager_assignments
end
class PersonManagerAssignment < ActiveRecord::Base
has_one :subordinate, :class_name => "Person", :foreign_key => "id", :primary_key => 'person_id'
has_one :manager, :class_name => "Person", :foreign_key => "id", :primary_key => 'manager_id'
end
Which works great for checking and assigning managers.
I'm caught on the part about subordinates. It returns the Person's self, instead of their subordinates:
p.subordinates
Person Load (0.5ms) SELECT "people".* FROM "people" INNER JOIN "person_manager_assignments" ON "people"."id" = "person_manager_assignments"."person_id" WHERE "person_manager_assignments"."person_id" = 15973
See the bit where in the WHERE clause where it's matching "person_id"? I need that to be "manager_id", but messing with the PersonManagerAssignment associations foreign_key and primary_key values doesn't seem to help.
Any ideas?
Answer is essentially here: http://railscasts.com/episodes/163-self-referential-association
So I think you need this:
class Person < ActiveRecord::Base
has_many :person_manager_assignments
has_many :managers, :through => :person_manager_assignments
has_many :subordinate_relationships, :class_name=>"PersonManagerAssignment", :foreign_key=>"manager_id"
has_many :subordinates, :through => :subordinate_relationships, :source=>:person
end
and
class PersonManagerAssignment < ActiveRecord::Base
belongs_to :person
belongs_to :manager, :class_name=>"Person"
end
Rock on.
I'm going to guess that your PersonManagerAssignment table has a person_id and a manager_id and associations in the model like has_one :person and has_one :manager. If that's the case, I'll recommend changing this association
has_one :person
to this
has_one :subordinate, :class_name => "Person", :foreign_key => "person_id"
and then your has_many :subordinates should work as expected.
class Person < ActiveRecord::Base
has_many :subordinates :through => :person_manager_assignments
has_many :managers, :through => :person_manager_assignments
end
class PersonManagerAssignment < ActiveRecord::Base
belongs_to :subordinate, :class_name => 'Person'
belongs_to :manager, :class_name => 'Person'
end