Has_many of different models association in Rails - ruby-on-rails

I have a few different models which I would like to add multiple images to.
I have an image model with belongs_to associations set up to the different owning models (each of these owning models has has_many :images defined).
I would like to know what's the appropriate migration I should create in order to add an image_ids column to each of owning models.
I assume something like this...
rails g migration AddImagesToBusinesses images businesses image_ids:integer
However, I'm confused as I believe that you can only make one association this way and it would need to be completed by adding a column to the images table to identify the id of the model it belongs to (here there are a few different models).
Thank you for your help.

As you concern about relationship of image to other model. You should try polymorphic associations like this.
Generate the Image model:
class CreateImages < ActiveRecord::Migration
def change
create_table :images do |t|
t.string :file_id
t.boolean :featured
t.references :imageable, polymorphic: true, index: true
t.timestamps null: false
end
end
end
Update the Image model:
class Image < ActiveRecord::Base
attachment :file
belongs_to :imageable, polymorphic: true
end
Add association to other models like this
class Model < ActiveRecord::Base
has_many :images, as: :imageable, dependent: :destroy
accepts_attachments_for :images, attachment: :file
end
For more details you Ruby on Rails Guide.

I think you need Polymorphic associations.
See documentaions here.

Related

2 different Model share 1 unique Photo Model

Hello Rails Community !
I don't know how to structure my different models.
I have 2 differents models : cars and house
Theses models can have multiple photos.
My questions are :
Is it possible to use 1 photo model for the cars and house or I need to create 1 cars_photos model and 1 house_photos model
If it's possible, how can I generate my Photo model ?
=> Option 1
rails g model Photo name:string, description:text car:references house:references
Car.rb
has_many :photos
House.rb
has_many :photos
Photo.rb
belongs_to :car
belongs_to :house
The problem with this option is that a photo will have to be linked with a car AND with a house. Witch is not good.
=> I want a photo be linked with a car OR with a house
I don't know how to proceed...
Thx !
This is almost the exact prototypical polymorphic association from the Rails guides
$ rails g model Photo name:string description:text imageable:references{polymorphic}:index
Produces this migration file
class CreatePhotos < ActiveRecord::Migration[5.1]
def change
create_table :photos do |t|
t.string :name
t.text :description
t.references :imageable, polymorphic: true
t.timestamps
end
end
end
t.references :imageable, polymorphic: true is going to give you two columns on your photos table: imageable_id:integer which will be the id column of the associated object, and imageable_type:string which will be the stringified class name of the associated object. This allows photos to interface with and belong to any model on one association.
Then your models should look like this
class Photo < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
class Car < ApplicationRecord
has_many :photos, as: :imageable
end
class House < ApplicationRecord
has_many :photos, as: :imageable
end
You can add a Photo to a Car with Car.find(params[:car_id]).photos.create and assign a Car to a Photo with Photo.new imageable: Car.find(params[:car_id])
Yes, you can reuse photos for both cars and house.
There are two major gems
for photo uploading: paperclip and carrierwave.
Have a look at them before you continue with your modeling!

Joined Table Confusion - Ruby on Rails

I'm a little befuddled on how to create a joined table in ROR.
Let's say there are 2 tables:
User
Book
In order to create a joined table user_book, my initial impression was that you did this:
rails g migration user_book
But I've been told that to create an actual joined table, I generate an actual model:
rails g model user_book
Is that correct?
And if the second one is correct, then once I've created it, I open it and add both the book_id, and user_id to the migrated file, and then run rake db:migrate command. Is this correct?
EDIT:
Just wanted to added that
User model would have this: has_many :books, through: :user_book
Book model would have this: has_many :users, through: :user_book
If you want to use has_many through:, you need a table to handle that. As well as a model object to represent that table.
How you go about creating it (rails g model or rails g migration) doesn't ultimately matter. All those commands do is create either a model AND a migration file, or just a migration file. Personally, I would do the rails g model, since you need both.
You'd just add a user_books table with 2 IDs, user_id and book_id.
Migration would look something like this:
class CreateUserBooks < ActiveRecord::Migration
def change
create_table :user_books do |t|
t.references :user, index: true, foreign_key: true, null: false
t.references :book, index: true, foreign_key: true, null: false
t.timestamps null: false
end
end
end
Models would look like this:
class User < ActiveRecord::Base
has_many :user_books
has_many :books, through: :user_books
end
class Book < ActiveRecord::Base
has_many :user_books
has_many :users, through: :user_books
end
class UserBook < ActiveRecord::Base
belongs_to :user
belongs_to :book
end

Understanding Rails Model Associations

I'm relatively new to Ruby on Rails and I'm trying to understand the way how active record associations work. So far, I thought I figured it out, but not sure anymore.
Anyway, I'm building my very own CMS and apart from all, I'll focus on my main problem. I'm having a table pages and pictures:
class CreatePages < ActiveRecord::Migration
def change
create_table :pages do |t|
t.string :name
t.integer :headline_image_id
t.timestamps
end
create_table :pictures do |t|
t.string :name
t.string :description
t.string :image
t.timestamps
end
end
end
With this I have my models:
class Page < ActiveRecord::Base
validates :name, presence: true
validates :headline_image_id, presence: true
belongs_to :headline_image, class_name: Picture, foreign_key: :headline_image_id
end
class Picture < ActiveRecord::Base
mount_uploader :image, ImageUploader
end
And that's it. Now after I create a picture and a page which has the id of a picture in the headline_image_id attribute, I can fetch that headline_image with #target_page.headline_image. Perfect, but the thing that is bothering me is the readability of the code. Wouldn't it make much more sense if I associated the two models in the Page model like this:
class Page < ActiveRecord::Base
validates :name, presence: true
validates :headline_image_id, presence: true
has_one :headline_image, class_name: Picture, foreign_key: :headline_image_id
end
If I do it like this and run #target_page.headline_image I get a SQL Constraint exception that tells me there is no headline_image_id in the pictures table.
I read all the Active Record Association tutorial on Ruby on Rails Guides and watched all the codeschool Rails courses, and I was pretty sure that everything was going to work with a has_one association...but it didn't.
Can someone please explain?
Thanks!
Rails Guides provides an explanation as to why you're experiencing the problem. Essentially, when you declare a belongs_to relationship, the foreign key appears on the table for the class declaring it. When you declare a has_one relationship, the foreign key is on the table for the class in the declaration.
Example
In this scenario, the pictures table would require a page_id foreign key.
class Page < ActiveRecord::Base
has_one :picture
end
class Picture < ActiveRecord::Base
belongs_to :page
end
In this scenario, the pages table would require a picture_id foreign key.
class Page < ActiveRecord::Base
belongs_to :picture
end
class Picture < ActiveRecord::Base
has_one :page
end
If you wanted to use a has_one association, just remove the headline_image_id column from your pages table and add a page_id column to your pictures table. You can do this in one migration. After you run the migration, change your model definitions as per my above example. Hope this helps.

Creating an auto-updating list in Rails

I have three relevant models:
class InventoryItem < ActiveRecord::Base
belongs_to :item, :foreign_key => :item_id
belongs_to :vendor
has_many :shopping_list_items
class ShoppingList < ActiveRecord::Base
has_many :shopping_list_items
belongs_to :user
end
class ShoppingListItem < ActiveRecord::Base
belongs_to :shopping_list
belongs_to :inventory_item
end
What I am trying to do is create a sidebar shopping list that will autoupdate ShoppingListItem attributes (specifically price) when a respective attribute is changed in the InventoryItem table (again, price). My thinking was to have these three classes and map ShoppingListItems directly to InventoryItems, but I'm unsure of how to proceed with that. Alternatively, is it possible to do away with the ShoppingListItem class entirely and make ShoppingList be a collection of InventoryItems specified by the user? Any input is much appreciated. Thanks in advance!
To redo my comments as a real answer, yes, it is possible to forego the ShoppingListItem model in this case, as long as you don't need to attach any data to that model itself (e.g. the time the item was added to the list). You could link your models as follows with a has_and_belongs_to_many association:
class InventoryItem < ActiveRecord::Base
belongs_to :item
belongs_to :vendor
has_and_belongs_to_many :shopping_lists
end
class ShoppingList < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :inventory_items
end
This will allow you to assign an array of inventory items to the inventory_items attribute of a shopping list, and Rails will create or delete the necessary join records automatically. More information from the Rails guides. Note that you'll still need a join table in your schema -- there just isn't a model associated with it. In your case, the migration might look like this:
create_table :inventory_items_shopping_lists, id: false do |t|
t.references :inventory_item
t.references :shopping_list
end
add_index :inventory_items_shopping_lists, :inventory_item_id
add_index :inventory_items_shopping_lists, :shopping_list_id
add_index :inventory_items_shopping_lists, [:inventory_item_id, :shopping_list_id], unique: true
Note that in order for Rails to auto-detect the table, its name should be the combined plural forms of both models in alphabetical order. Otherwise you need to specify the table name using the join_table option when defining the association.

Configure my model so that a field can reference many different types of classes

Let's say that I have a Company model and a Product model, both of which have an UserUploadedImage associated with them. I want to create my UserUploadedImage in a way such that I can write image.parent and that will reference either the Product or the Company, whichever is appropriate in that case.
I realize I can store a second column in UserUploadedImage with either Product or Company and have a conditional to look up the appropriate value. I am, however, not sure where the optimal place to put this code is, or whether there is a cleaner way of achieving my goal. Thanks!
What you need to look at is Polymorphic Associations
http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
Polymorphic Associations
A slightly more advanced twist on associations is the polymorphic association. With polymorphic associations, a model can belong to more than one other model, on a single association. For example, you might have a picture model that belongs to either an employee model or a product model.
class Picture < ActiveRecord::Base
belongs_to :imageable, :polymorphic => true
end
class Employee < ActiveRecord::Base
has_many :pictures, :as => :imageable
end
class Product < ActiveRecord::Base
has_many :pictures, :as => :imageable
end
You can think of a polymorphic belongs_to declaration as setting up an interface that any other model can use. From an instance of the Employee model, you can retrieve a collection of pictures: #employee.pictures.
Similarly, you can retrieve #product.pictures.
If you have an instance of the Picture model, you can get to its parent via #picture.imageable. To make this work, you need to declare both a foreign key column and a type column in the model that declares the polymorphic interface:
class CreatePictures < ActiveRecord::Migration
def change
create_table :pictures do |t|
t.string :name
t.integer :imageable_id
t.string :imageable_type
t.timestamps
end
end
end
This migration can be simplified by using the t.references form:
class CreatePictures < ActiveRecord::Migration
def change
create_table :pictures do |t|
t.string :name
t.references :imageable, :polymorphic => true
t.timestamps
end
end
end

Resources