referencing two models to each other - ruby-on-rails

i'm going through one of ror beginners books, and it has this example on references:
class Article < ActiveRecord::Base
attr_accessible :body, :excerpt, :location, :published_at, :publisher, :title
validates :title, :presence => true
validates :body, :presence => true
belongs_to :user
has_and_belongs_to_many :categories
end
class Category < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :articles
end
so these two models are meant to be connected by habtm in both ways, just like in the code above. but, the book also tells me i should provide such a migration:
class CreateArticlesCategories < ActiveRecord::Migration
def up
create_table :articles_categories, :id => false do |t|
t.integer :article
t.integer :category
end
end
def down
drop_table :articles_categories
end
end
My question: why? why do i need to create this migration and not a model articles_categories?

tl;dr; You need the extra model when you're using a has_many through, you don't need it for the habtm association
The has_and_belongs_to_many association is meant to be used when you don't really care about the table in the middle (here it's articles_categories). What I mean by "don't" really care is that, there are no extra data needed in this table. It's only here to tell you which categories are related to a given article, and which articles are related to a given category.
For that purpose the has_and_belongs_to_many association is great cause you don't have to create the model, it only needs the join table.
If somehow you need some extra data in that table, like tags, dates, status, or anything, then it would be really handy to have a dedicated model, so you could explicitly manipulate it.
For those cases, you should use the has_many :through association
If you need more information, I suggest you read the rails guide about association, it's a good start. And of course if my explanation is not clear enough, please tell me, and I'll add more details.

Related

Rails: Create migrations for has_many and has_one

I have Question, Option and Answer models as follows:
class Question < ApplicationRecord
belongs_to :user
has_many :options
has_one :answer
end
class Option < ApplicationRecord
belongs_to :question
has_many :answers
end
class Answer < ApplicationRecord
belongs_to :question
belongs_to :option
end
I have one migration files for Question and Option models like this:
class CreateQuestions < ActiveRecord::Migration[5.2]
def change
create_table :questions do |t|
t.text :body
t.references :user, foreign_key: true
t.timestamps
end
end
end
class CreateOptions < ActiveRecord::Migration[5.2]
def change
create_table :options do |t|
t.references :question, foreign_key: true
t.timestamps
end
end
end
If my understanding is correct, I have a migration here for belongs_to association. My doubt is, are these migration files enough to create has_many associations or do I need to add any extra conditions in migrations files? If yes, please tell me what to add. I referred the following link:
[https://stackoverflow.com/questions/35771847/rails-survey-style-application-show-all-answers-on-option][1]
[1]: Rails survey style application - Show all answers on option but I did not understand whether I need to add extra line for has_many and has_one associations.
Your migrations are correct, because if you think of your models as in database tables, you will never store the 'has_many' option somewhere. That is merely for the human understanding, as well as for ActiveRecord.
So an option in your example belongs to a question, hence we have to store the ID of that question in the record of the answer. In the question migration however, we don't store any information regarding the option, it is enough that the option "knows" which question it belongs to. (And same for user and question).
Only in the model you can then specify - as you did - the 'has_many' options. This will allow you later to call 'question.options` to retrieve all options that belong to a question.

Has_many of different models association in 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.

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.

Associating custom model with Spree

I want to add Service category, same like Spree::Product, for that I have to define some associations, as below
class Service < ActiveRecord::Base
has_many :images, -> { order(:position) }, as: :viewable, class_name: "Spree::Image", dependent: :destroy
has_many :taxons, class_name: "Spree::Taxon", dependent: :destroy
validates :name, presence: true,
length: { minimum: 5 }
end
Now, first, is this the right method to define such category or should i use some other convention to define Service, and for :taxons association, should I define migration to add service_id column in spree_taxons table?
There is a matter of design there, Spree uses a model to join Taxons and Products, you should create it and name it services_taxon, the migration should look something like this:
class CreateServiceTaxon < ActiveRecord::Migration
def change
create_table :service_taxon do |t|
t.integer :service_id
t.integer :taxon_id
end
end
end
And on the Service model you should add:
class ServiceTaxon < ActiveRecord::Base
belongs_to :service, :class_name => 'Service', :foreign_key => 'service_id'
belongs_to :taxon, :class_name => 'Spree::Taxon', :foreign_key => 'taxon_id'
end
Another thing i should point out is that if you need some functionality that is already created by the spree team on the product model, you should really consider using theirs, or at least try to extend the product model.
You would need a new join model such as ServiceTaxons instead of adding a service_id to Spree::Taxon. If you look at how Products are linked to Taxons it's through the spree_product_taxons table.
The more important part is whether or not you need a new Service class. You would be better off having your services as just products. Products are deeply ingrained into the Spree system that you are creating a lot of work for yourself trying to implement another model that exists side-by-side with it.

How to model a "can belong to A or B" relationship in Rails?

I'm a newbie to RoR - I have three models: Customer, Job and Note. Customers have Jobs, and both Customers and Jobs can have Notes. Is there a special way to handle this type of relationship in Rails, or will it work if I just have a normal belongs_to relationship with Note?
The issue that concerns me is the note having fields for both customer_id and job_id but only a single one will ever be used for a single record (i.e. a specific Note can refer to either a Job or a Customer, but never both), and it doesn't feel like good database design to have a column that will be null half of the time.
Am I over thinking this, or is there something that's not clear to me?
I'd suggest using a polymorphic association as it's more flexible and expanadable, and easier to enforce. The model required is below:
class Note < ActiveRecord::Base
belongs_to :notable, :polymorphic => true
end
class Customer < ActiveRecord::Base
has_many :notes, :as => :notable
end
class Job < ActiveRecord::Base
has_many :notes, :as => :notable
end
with migration
create_table :notes do |t|
t.references :notable, :polymorphic => {:default => 'Photo'}
end
For details on a polymorphic association, I'd suggest google

Resources