问题
I have two classes Product
and user
which have a has_many
relationship with a polymorphic type as such.
class User < ActiveRecord::Base
has_many :pictures, as: :imageable
end
class Product < ActiveRecord::Base
has_many :pictures, as: :imageable
end
class Picture < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
end
I want to also add a new property to the user models of profile_picture
so @user.profile_picture
will return a picture object. how can this be achieved? In particular the migration to add the foreign key to users.
Edit: I've updated the user model as such
has_one :profile_picture, class_name: "Picture", foreign_key: "profile_picture_id"
and added the following migration
add_column :users, :profile_picture_id, :integer
however i dont appear to be able to set the profile picture if someone could explain what exactly i am doing wrong it would be appreciated. thanks!
回答1:
There is no direct way to achieve this when using associations properly.
The data structure is wrong. You are trying to associate one and the same model in two different ways. And the problem is: these two ways interfere with each other.
You are effectively trying to store different types of objects using the same type. A direct way (suggested in another answer) would leave you with no way to determine which are simply User
's pictures and what is his profile_picture
among them. So any possible solution would require adding a way to distinguish a profile_picture
.
Just yesterday I wrote an example on how to use "single table inheritance". It actually fits well here, you need two similar data constructs to behave in different ways. To make it work, you will need two things:
Add STI support to
pictures
table:rails g migration AddTypeToPictures type:string:index
...and make sure you add atype
column of typestring
with an index on it.rails g model ProfilePicture --no-migration --parent=Picture
This class will be created empty, but will inherit everything fromPicture
, so it should stay empty in many cases.
Then just a simple association should work on any class:
has_one :profile_picture, as: :imageable
Let's assume we added this to User
. Then the search criteria to find a User
's profile picture will be:
type
is"ProfilePicture"
imageable_type
is"User"
imageable_id
is equal toid
of the user in question
Searches have become cumbersome, right? Well, this should work, but I don't recommend all this, it has implications and alternatives:
- Whatever database column you decide to add to
ProfilePicture
, it will have to be added toPicture
even if it's not used: they are stored in the same table.
Solution: makingProfilePicture
a full-fledged model: make a table for it, make it inheritActiveRecord::Base
. - Fetching a user's profile picture will require an extra database query with three (wow!) different criteria.
Solution (recommended): placing a profile picture inside a corresponding model the same way it's done forPicture
: this will require no extra queries to fetch a profile picture at all.
回答2:
class User < ActiveRecord::Base
has_many :pictures
has_one :profile_picture, :class_name => "Picture"
end
class Product < ActiveRecord::Base
has_many :pictures
end
class Picture < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
end
来源:https://stackoverflow.com/questions/25596068/ruby-on-rails-named-class-property-of-model