I'm slightly confused about a polymorphic association I've got. I need an Article model to have a header image, and many images, but I want to have a single Image model. To make matters even more confusing, the Image model is polymorphic (to allow other resources to have many images).
I'm using this association in my Article model:
class Article < ActiveRecord::Base
has_one :header_image, :as => :imageable
has_many :images, :as => :imageable
end
Is this possible? Thanks.
I tried this, but then header_image returns one of the images. Simply because the images table doesn't specify a different image use type (header_image vs. normal image). It simply says: imageable_type = Image for both uses. So if there's no information stored about the use type, ActiveRecord cannot differentiate.
Yep. That's totally possible.
You might need to specify the class name for header_image, as it can't be inferred. Include :dependent => :destroy too, to ensure that the images are destroyed if the article is removed
class Article < ActiveRecord::Base
has_one :header_image, :as => :imageable, :class_name => 'Image', :dependent => :destroy
has_many :images, :as => :imageable, :dependent => :destroy
end
then on the other end...
class Image < ActiveRecord::Base
belongs_to :imageable, :polymorphic => true
end
Related
I have a product, and a product can have many images. This is through an associations table. However, I would like a product to have one main image.
I know this is very easy to do with a method in the model, but I want it to be an association so that I can preload it in the query by using include.
Models:
class Product < ActiveRecord::Base
has_many :image_associations, :as => :imageable
has_many :images, :through => :image_associations
end
class ImageAssociation < ActiveRecord::Base
belongs_to :image
belongs_to :imageable, :polymorphic => true
end
class Image < ActiveRecord::Base
has_many :image_associations
end
In the ImageAssociation table, there is a boolean column called 'feature' which associates an image as the 'main' image.
One of the ways I've thought about doing this is adding a main_image_id column to the products table, and then adding to the Image model:
belongs_to :image, :class => "Image", :foreign_key => "main_image_id"
However, this doesn't allow for any fallback to the other has_many images if the main image is nil -- which I'd like the association to load.
Which is why I was hoping for something in the images model like:
has_one :image, :through => :images, :conditions => 'feature = true', :order => 'created_at DESC'
But that gives me an association error.
Is there any way I can edit that has_one, or do I really need to run a rake task to push an image into every main_image_id field, and then add a validation for future products to make sure a main image has been added?
EDIT:
Should I be using a has_and_belongs_to_many association instead?
You're almost there, I think, although I'm not so clear on polymorphic associations. I think you want the following:
has_one :main_image_assoc, class => "ImageAssociation", :conditions => 'feature = true', :order => 'created_at DESC', :as => :imageable
has_one :image, :through => :main_image_assoc
I a while ago i read this article: http://pivotallabs.com/users/nick/blog/articles/275-advanced-proxy-usage-part-i which is talking about AR proxy etc..
The author pointed what is my issue now.
This example will describe it.
class Gallery
has_many :images, :class_name => 'Image'
has_many :my_images, :class_name => 'Image' #, :conditions => "images.user_id == current_user.id" # FIX THIS
end
and here
class Image
belongs_to :user
belongs_to :gallery
end
Notice: I use PostgreSQL
So when you access my all images with a gallery: Gallery.first.includes(:my_images)
If i want to return all images: Gallery.first.includes(:images)
So now how to pass the current user to the conditions in the has_many?
Edit
User
has_many :images
has_many :galleries, through: :images
If i understand well, you want to get something like :
current_user.galleries.first.images
If so, you would just need to create a has_many association between User and Gallery through a third model, possibly named UserGallery.
More information on has many through :
http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association
A question regarding Rails associations.
Consider the following models:
People
Events
Images
People and events can have many images.
Images uploaded to an event, need to have the ability to be associated with multiple people.
This means that there are two relationships between people and images.
One where images are uploaded directly on the person. And one where a person is tagged in an event.
Can there also be a relationship between a person and an event based on the fact they were tagged in one (or multiple) images in an event? In this regard, it's a sort of image tagging system where associations are created based on what event people are tagged in.
Wondering what is the best practice in Rails to create this association? Any help or advice is greatly appreciated!
I am not absolutely sure about best practice,
but there is a chance to solve your case in simple way, as far as i understand you:
We have:
Event ... has_many :photos; Photo ... belongs_to :event
and solution is to create some intermediary ActiveRecord class
People ... has_many :photos, :through photo_mappings
PhotoMapping ... belongs_to :user; belongs_to :photo
Have a nice AR associations best practice1
In rails it is definitely possible to define a join-table that has extra fields. So in this case I would define the following table:
class LinkedImage
belongs_to :person
belongs_to :image
OWNER=1
TAGGED=2
validates :relation_type, :inclusion => {:in => [OWNER, TAGGED]}
end
This table would link an image to a person, and has an extra field relation_type (you could think of a more appropriate name maybe), which now can have two values: 1 (for the OWNER, meaning the image was directly uploaded to the person), and 2 (the person is tagged in the image). Aside from the relation, maybe you want to store something extra, like the position in the image, an extra comment, then you could easily add that here as well.
And the person would look like:
class Person
has_many :linked_images
has_many :uploaded_images, :class_name => Image,
:through => :linked_images,
:conditions => "relation_type=#{LinkedImage::OWNER}"
has_many :tagged_in_images, :class_name => Image,
:through => :linked_images,
:conditions => 'relation_type=#{LinkedImage::TAGGED}"
end
and the code for Image could be similar.
I hope this helps.
I don't know if this is the best approach, but I would do something like this:
class User < ActiveRecord::Base
has_many :images, :as => :owner
has_many :tags
has_many :tagged_images, :through => :tags, :source => :image
has_many :tagged_events, :finder_sql => %q( SELECT `events`.* FROM `events`, `images` WHERE `events`.id = `images`.owner_id AND `images.owner_type ="Event" `
WHERE (`images`.id IN (#{tagged_image_ids.join(',')}) ))
end
class Event < ActiveRecord::Base
has_many :images, :as => :owner
has_many :tagged_users, :finder_sql => %q( SELECT `users`.* FROM `users`, `images` WHERE `users`.id = `images`.owner_id AND `images.owner_type ="User" `
WHERE (`images`.id IN (#{image_ids.join(',')}) ))
end
class Tag < ActiveRecord::Base
belongs_to :user
belongs_to :image
end
class Image < ActiveRecord::Base
belongs_to :owner, :polymorphic => true
has_many :tags
has_many :tagged_users, :through => :tags, :source => :user
end
Note: Using finder_sql is not the best in Rails, because for example it doesn't allow you to add new tagged images just using the relationship, but it works fine for reading.
I am trying to make my image_maps get destroyed when either a product or image is deleted. Here is the code.
class ImageMap < ActiveRecord::Base
belongs_to :imageable, :polymorphic => true
belongs_to :image
end
class Product < ActiveRecord::Base
has_many :image_maps, :as => :imageable
has_many :images, :through => :image_maps
end
class Image < ActiveRecord::Base
has_many :image_maps, :as => :imageable, :dependent => :destroy
end
Right now the image_maps do not get destroyed when you delete an image and i still need to figure out how to get it to work for products too.
I think that your dependent should go to the ImageMap model.
If i remember correctly, this is what Rails checks on the associated models to see whether they need to be destroyed.
Please bear with me for a moment as I try to explain exactly what I would like to achieve.
In my Ruby on Rails application I have a model called Page.
It represents a web page.
I would like to enable the user to arbitrarily attach components to the page. Some examples of "components" would be Picture, PictureCollection, Video, VideoCollection, Background, Audio, Form, Comments.
Currently I have a direct relationship between Page and Picture like this:
class Page < ActiveRecord::Base
has_many :pictures, :as => :imageable, :dependent => :destroy
end
class Picture < ActiveRecord::Base
belongs_to :imageable, :polymorphic => true
end
This relationship enables the user to associate an arbitrary number of Pictures to the page. Now if I want to provide multiple collections i would need an additional model:
class PictureCollection < ActiveRecord::Base
belongs_to :collectionable, :polymorphic => true
has_many :pictures, :as => :imageable, :dependent => :destroy
end
And alter Page to reference the new model:
class Page < ActiveRecord::Base
has_many :picture_collections, :as => :collectionable, :dependent => :destroy
end
Now it would be possible for the user to add any number of image collections to the page.
However this is still very static in term of the :picture_collections reference in the Page model. If I add another "component", for example :video_collections, I would need to declare another reference in page for that component type.
So my question is this:
Do I need to add a new reference for each component type, or is there some other way? In Actionscript/Java I would declare an interface Component and make all components implement that interface, then I could just have a single attribute :components which contains all of the dynamically associated model objects.
This is Rails, and I'm sure there is a great way to achieve this, but its a tricky one to Google. Perhaps you good people have some wise suggestions. Thanks in advance for taking the time to read and answer this.
I believe you can use just
class Page < ActiveRecord::Base
has_many :components, :as => :attachable, :dependent => :destroy
end
class Picture < ActiveRecord::Base
belongs_to :attachable, :polymorphic => true
end
class PictureCollection < ActiveRecord::Base
belongs_to :attachable, :polymorphic => true
has_many :pictures, :as => :imageable, :dependent => :destroy
end
and so on...