has_many :through + polymorphic relationships - ruby-on-rails-3

I using rails3 and trying to build some complex associations.
I have Product, Version and Property models.
class Version < ActiveRecord::Base
belongs_to :product
has_many :specs
has_many :properties, :through => :specs
end
class Product < ActiveRecord::Base
has_many :versions
has_many :specs
has_many :properties, :through => :specs
end
class Property < ActiveRecord::Base
end
class Spec < ActiveRecord::Base
belongs_to :product
belongs_to :spec
belongs_to :version
end
It works perfect, but i want to use product and version as polymorphic relations, so table specs will have only spec_id and some_other_id, instead of spec_id, product_id, version_id.
I can't figure out where i should put :as and where :polymorphic => true. Can you help me?

How about:
class Version < ActiveRecord::Base
belongs_to :product
has_many :specs, :as => :speccable
has_many :properties, :through => :specs
end
class Product < ActiveRecord::Base
has_many :versions
has_many :specs, :as => :speccable
has_many :properties, :through => :specs
end
class Property < ActiveRecord::Base
end
class Spec < ActiveRecord::Base
belongs_to :speccable, :polymorphic => true
belongs_to :spec
end
#table: specs(id,spec_id,speccable_type,speccable_id)

Related

Using has_many through with nested namespaces

I have a nested model like so:
class Games::Player < ActiveRecord::Base
attr_accessible :user_id
belongs_to :user
has_many :games_extras_achievements_players, :class_name => 'Games::Extras::AchievementsPlayer'
has_many :games_extras_achievements, :class_name => 'Games::Extras::Achievement',:through=>:games_extras_achievements_players
validates :user_id,uniqueness: true
end
class Games::Extras::Achievement < ActiveRecord::Base
has_many :games_extras_achievements_players, :class_name => 'Games::Extras::AchievementsPlayer'
has_many :games_players, through: :games_extras_achievements_players, class_name: 'Games::Player'
end
class Games::Extras::AchievementsPlayer < ActiveRecord::Base
attr_accessible :games_extras_achievement_id, :games_player_id
belongs_to :games_extras_achievement, :class_name => 'Games::Extras::Achievement'
belongs_to :games_player, :class_name => 'Games::Player'
end
Objects on the join class work as expected.
However trying to get player -> achievement or vice versa gives an error:
> p.games_extras_achievements
Games::Extras::Achievement Load (0.3ms) SELECT "games_extras_achievements".* FROM "games_extras_achievements" INNER JOIN "games_extras_achievements_players" ON "games_extras_achievements"."id" = "games_extras_achievements_players"."games_extras_achievement_id" WHERE "games_extras_achievements_players"."player_id" = 1
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column games_extras_achievements_players.player_id does not exist
LINE 1: ...ents_players"."games_extras_achievement_id" WHERE "games_ext...
If I change the migration to use player_id like it is trying to find, I get an error stating that games_player_id does not exist
I seem to have fixed it.
I needed to use the non-namespaced column names and add a foreign key constraint.
class Games::Player < ActiveRecord::Base
attr_accessible :user_id
belongs_to :user
has_many :games_extras_achievements_players, :class_name => 'Games::Extras::AchievementsPlayer'
has_many :games_extras_achievements, :class_name => 'Games::Extras::Achievement',:through=>:games_extras_achievements_players
validates :user_id,uniqueness: true
end
class Games::Extras::Achievement < ActiveRecord::Base
has_many :games_extras_achievements_players, :class_name => 'Games::Extras::AchievementsPlayer'
has_many :games_players, through: :games_extras_achievements_players, class_name: 'Games::Player'
end
class Games::Extras::AchievementsPlayer < ActiveRecord::Base
attr_accessible :achievement_id, :player_id
belongs_to :games_extras_achievement, class_name:'Games::Extras::Achievement',foreign_key: :achievement_id
belongs_to :games_player, class_name: 'Games::Player',foreign_key: :player_id
end
Hopefully this will save someone some aggravation.

ActiveRecord::HasManyThroughNestedAssociationsAreReadonly Error in Rails Admin

I just upgraded to Rails 3.2.10 and am getting an error message that I never used to get when updating a record via RailsAdmin.
ActiveRecord::HasManyThroughNestedAssociationsAreReadonly at /admin/vendor/12/edit
Message Cannot modify association 'Vendor#categories' because it goes through more than one other association.
This is my Vendor model:
class Vendor < ActiveRecord::Base
attr_accessible :name, :description, :banner_image, :logo_image, :intro_text, :thumb_image, :category_ids, :product_ids, :user_id, :remove_banner_image, :banner_image_cache, :remove_logo_image, :logo_image_cache
mount_uploader :banner_image, ImageUploader
mount_uploader :logo_image, ImageUploader
mount_uploader :thumb_image, ImageUploader
has_many :products, :dependent => :destroy
has_many :categories, :through => :products
belongs_to :owner, :class_name => "User",
:foreign_key => "user_id"
end
This is my Category model:
class Category < ActiveRecord::Base
attr_accessible :name, :product_ids, :category_ids
has_many :category_products do
def with_products
includes(:product)
end
end
has_many :products, :through => :category_products
end
This is my Product model:
class Product < ActiveRecord::Base
attr_accessible :name, :description, :price, :vendor_id, :image, :category_ids, :sku, :remove_image, :image_cache
mount_uploader :image, ImageUploader
belongs_to :vendor
has_many :category_products do
def with_categories
includes(:category)
end
end
has_many :categories, :through => :category_products
end
This is my CategoryProduct model:
class CategoryProduct < ActiveRecord::Base
attr_accessible :product_id, :category_id, :purchases_count
belongs_to :product
belongs_to :category
validates_uniqueness_of :product_id, :scope => :category_id
end
This happens because your association is nested, meaning (from rails source) :
A through association is nested if there would be more than one join table... which is your case here.
Apparently a workaround (I didn't test) is telling Vendor it doesn’t need to autosave the association.
has_many :categories, :through => :products, :autosave => false
You can mark the association as readonly and rails_admin will then not generate the category fields in the form for vendor:
has_many :categories, -> { readonly }, through: :products

Rails 3 - WARNING: Can't mass-assign protected attributes: user_ids

I have a has_many through relationship between Course and User.
class Course < ActiveRecord::Base
belongs_to :user
has_many :enrollments, :dependent => :delete_all
has_many :users, :through => :enrollments
attr_accessible :description, :duration, :name, :prerequisites, :short_name, :start_date, :user_id
accepts_nested_attributes_for :users, :allow_destroy => true
attr_accessible :users_attributes
and User:
class User < ActiveRecord::Base
has_many :subjects, :class_name => "Course" # to get this call user.subjects
has_many :enrollments, :dependent => :delete_all
has_many :courses, :through => :enrollments
and Enrollment:
class Enrollment < ActiveRecord::Base
belongs_to :course
belongs_to :user
attr_accessible :course_id, :user_id
end
Now I'm trying to set user_ids from inside Course, using a nested form. It keeps giving me that Mass Assignment warning, and nothing is saved. I read I was supposed to add attr_accessible user_id but it still doesn't work.
Even if I do something like this from the rails console:
#c.update_attributes({:user_ids => [7,8]})
with #c being the course
Any help would be greatly appreciated,
Thank you.
It's user_ids, not user_id.
You need to add user_ids to your attr_accessible.

Rails ActiveRecord Query (Cross model)

I have an app that lets users input dates & interests that relate to those dates .I need to send them deals (a few days before the date - Via Email) that are based off of their interests and location. I have all the models setup and recording the data properly, just wondering how to query the models for the dates and then send the appropriate deal based off of the city and interests.
Notes:
*Each city and interest category has only 1 deal
*I have several different models for types of dates (Holidays, Occasions, Friends Birthdays ect).. all are pretty much identical in structure.
*All interests for each type of date are stored in person_interests.
Models:
Class User
belongs_to :province
belongs_to :city
has_many :friends_bdays
has_many :occasions
has_many :person_interests, :as => :person
has_many :interests, :through => :person_interests
has_many :user_holidays
has_many :holidays, :through => :user_holidays
has_many :anniversaries
end
class Deal < ActiveRecord::Base
belongs_to :interest
belongs_to :city
belongs_to :store
end
class Store < ActiveRecord::Base
has_many :deals
belongs_to :city
belongs_to :province
end
class PersonInterest < ActiveRecord::Base
belongs_to :interest
belongs_to :person, :polymorphic => true
end
class Interest < ActiveRecord::Base
has_many :person_interests
has_many :deals
end
class Occasion < ActiveRecord::Base
belongs_to :user
belongs_to :admin_user
has_many :person_interests, :as => :person
has_many :interests, :through => :person_interests
end
class Anniversary < ActiveRecord::Base
belongs_to :user
has_many :person_interests, :as => :person
has_many :interests, :through => :person_interests
end
class Friend_bday < ActiveRecord::Base
belongs_to :user
has_many :person_interests, :as => :person
has_many :interests, :through => :person_interests
end
You can achieve this using a variation of the solution below:
Install the squeel gem
class User
def deals(reload=false)
#deals = nil if
#deals ||= Deal.where{
( (:city => city_id) | ( :interest_id => interest_ids) ) &
:deal_date => (Time.now..3.days.from_now)
}
end
end
Now, user.deals returns the deals that will be active in next 3 days matching the user's city OR interests.
Edit 1:
Based on your comment it looks like you don't need the squeel gem. You can achieve what you want using regular AR syntax.
class User
def deals(reload=false)
#deals = nil if reload
#deals ||= Deal.where(
:city => city_id,
:interest_id => interest_ids,
:deal_date => (Time.now..3.days.from_now)
)
end
end

Has_many, through association

Warning:Total Rails Newb (TRN). This should be a pretty basic question so I'm hoping someone can spare a couple mins to help shed some light.
Let's say I have the following models: User, Group, and Member
A user can have many groups (let's say friends, family, etc)
A group can have many members, namely other users.
How would I structure this?
Initially I tried this:
class User < ActiveRecord::Base
has_many :groups
has_many :groups, :through => :members
end
class Groups < ActiveRecord::Base
has_many :users, :through => :members
belongs_to :user
end
class Member < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
However this gave me an error in User so I changed
has_many :groups, :through => :members
to
has_many :memberships, :through => :members, :source => :groups
Still getting an error about missing association when I try to do
group = Group.new
group.user.new
It will be useful: http://railscasts.com/episodes/47-two-many-to-many
class User < ActiveRecord::Base
has_many :members
has_many :groups, :through => :members
has_many :groups_as_owner, :class_name => "Group"
end
class Groups < ActiveRecord::Base
has_many :members
has_many :users, :through => :members
belongs_to :owner, :class_name => "User", :foreign_key => :user_id
end
class Member < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
basically has_many-through associations are n:m associations (join-tables) that (shall) have more attributes than just the id's of the joined record ids...
so you have a table Groups (with an id), a table Users (with an id) and a table Members (no id, but user_id and group_id)
basically, what you did is nearly correct, just think about how you access a group from a user or vice versa....
a user would first look up its member information and through that member information get access to the group information ... and vice versa for a group
so you first set up
has_many :members
and then call
has_many :groups, :through => :members
all you need is
class User < ActiveRecord::Base
has_many :members
has_many :groups, :through => :members
end
class Groups < ActiveRecord::Base
has_many :members
has_many :users, :through => :members
end
class Member < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
and you have another bug in your code above
you might want to use
user = group.users.new
instead of
user = group.user.new
Try this structure:
class User < ActiveRecord::Base
has_many :members
has_many :groups, :through => :members
end
class Groups < ActiveRecord::Base
has_many :members
has_many :users, :through => :members
end
class Member < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
Also take a look at has_and_belongs_to_many, if you don't need to do with class Member then you should use has_and_belongs_to_many. In this case don't forget to create joining table in the database