New to Ruby on Rails - model/migration clarification? - ruby-on-rails

Sorry for the basic, basic question but I'm having some trouble understanding the RoR doc.
say I have 3 models--Students, Classes, and Enrolled_in. If it isn't clear, students will enroll in classes, so Enrolled_in should have Students and Classes as foreign keys. I generated the models for each of these, but I'm confused what I should put into the associated migration file vs. the associated model file for each table. Do I specify the columns of the table in the migration file, and the key constraints in models?
If someone could clarify this, or tell me how they would solve the example question I posted, that would be really helpful. Thanks.

Firstly, to follow the convention the model name should be EnrolledIn, not Enrolled_in. Better yet, change it to Enrollment or something that has a definite meaning as a noun. And you're also going to run into trouble trying to use Class (which is already a ruby object) as a model name. Perhaps change it to Course or something similar.
That aside, you should define all columns in your migrations. You can define the enrolled_ins table like this:
add_table :enrolled_ins do |t|
t.references :student
t.references :course # I'm using course instead of class as noted above
end
The references shortcut will add a :student_id and :course_id as integer fields.
In your model files you'll have:
# student.rb
class Student < ActiveRecord::Base
has_many :enrolled_ins
has_many :courses, :through => :enrolled_ins
end
# course.rb
class Course < ActiveRecord::Base
has_many :enrolled_ins
has_many :students, :through => :enrolled_ins
end
# enrolled_in.rb
class EnrolledIn < ActiveRecord::Base
belongs_to :student
belongs_to :course
end

Related

Using foreign_key in rails associations has_many

Could somebody explain me is my code correct.
I'm trying to get foreign_key option in rails associations.
I have 2 models:
Book and Author
Book db schema:
name
user_id
Author db schema:
name
My models:
class Author < ApplicationRecord
has_many :books, foreign_key: :user_id
end
class Book < ApplicationRecord
belongs_to :author, foreign_key: :user_id
end
Here I don't understand why we should define foreign_key in both models. Is it necessarily?
If you have used the table and column names that Rails expects, then you do not need to explicitly define the foreign_key. In your case, if the foreign key column was named author_id, then you could get by quite simply:
class Author < ApplicationRecord
has_many :books
end
class Book < ApplicationRecord
belongs_to :author
end
However, in your case, the foreign key column is not named according to what Rails expects, so you have needed to explicitly define the foreign key column name. That's fine, but it does make a little more work for you.
In cases where you have explicitly defined the foreign key, you should define it for both associations. Your has_many association will not work without it.
In addition, you should define the inverse association:
class Author < ApplicationRecord
has_many :books, foreign_key: :user_id, inverse_of: :author
end
class Book < ApplicationRecord
belongs_to :author, foreign_key: :user_id, inverse_of: :books
end
Defining the inverse_of can cause ActiveRecord to make fewer queries, and gets rid of a few surprise behaviors. For an explanation of inverse_of, see Exploring the :inverse_of Option on Rails Model Associations by Ryan Stenberg

Linking many existing models to one new one. Ruby on Rails

So I am making an app that reviews books, articles and the like.
I have created the backbone of the app by creating models, views, controllers etc for Piece(the book or article), Section(self explanatory), Subsection, and Subsubsection.
I want to add a new model into the mix, a "Links" model (which will just be a link to another source or website). My issue is that I don't know how to make ALL of my previously stated models have "Links". I want each of The above models to have access and CRUD capabilities to their "Links", but so far all i have read about is has_many or has_and_belongs_to_many.
As far as I understand, those kinds of relations only relate ONE model to ONE other model, even if Piece might have many Sections, it only relates these two models.
I guess the Links model would have to have an obligatory piece_id, but then optional id's such as: section_id, subsection_id depending on where the link was. So if in Chapter 3 of my first book i want to add a link, it would have an obligatory piece_id=1 and then a section_id=3, but then no subsection_id or subsubsection_id.
So how do I go about creating a model such that it belongs to several other models? Or is this even possible?
https://github.com/kingdavidek/StuddyBuddy
Ok, it sounds like essentially you want a polymorphic association
class Link
belongs_to :linkable, polymorphic: true
end
class Piece
has_many :links, as: :linkable
end
Link would need linkable_id integer column and linkable_type string column. You can then use it in the same way as an ordinary has_many to belongs_to association
if i wanted to create a new Link in a Subsection, it would belong to
Subsection, but also to Section and Piece because of the nested
relationship
This bit rails can't help with, you'd need to write your own method to find all the links in the chain of items.
This is a pretty good use case for polymorphic associations. For simplicity lets start out with a one to many relationship:
class Link < ActiveRecord::Base
belongs_to :linkable, polymorphic: true
end
class Piece < ActiveRecord::Base
has_many :links, as: :linkable
end
class Section < ActiveRecord::Base
has_many :links, as: :linkable
end
Here the links table will have linkable_id (int) and linkable_type (string) columns. One important thing to take note of here is that linkable_id is not a true foreign key from the RBDMS point of view. Rather ActiveRecord resolves which table the relation points to when it loads the relation.
If we want to cut the duplication we can create a module which contains the desired behavior. Using ActiveSupport::Concern cuts a lot of the boilerplate code involved in creating such a module.
class Link < ActiveRecord::Base
belongs_to :linkable, polymorphic: true
end
# app/models/concerns/linkable.rb
module Linkable
extend ActiveSupport::Concern
included do
has_many :links, as: :linkable
end
end
class Piece < ActiveRecord::Base
include Linkable
end
class Section < ActiveRecord::Base
include Linkable
end
So how would we make a polymorpic many to many relation?
class Link < ActiveRecord::Base
has_many :linkings
end
# this is the join model which contains the associations between
# Links and the "linkable" models
class Linking < ActiveRecord::Base
belongs_to :link
belongs_to :linkable, polymorphic: true
end
# app/models/concerns/linkable.rb
module Linkable
extend ActiveSupport::Concern
included do
has_many :links, through: :linkings, as: :linkable
end
end
class Piece < ActiveRecord::Base
include Linkable
end
class Section < ActiveRecord::Base
include Linkable
end
On a side note - a better way to build a hierarchy between sections would be to use a single Section model and give it a self joining relationship.
class Section < ActiveRecord::Base
belongs_to :parent, class_name: 'Section'
has_many :children, class_name: 'Section',
foreign_key: 'parent_id'
end

Enforcing uniqueness of a model in has_many though

I have a User model, which has_many Dish through Recommendation. I would like to enforce uniqueness of Dish, as well as uniqueness of Recommendation.
How should I go about this in ActiveRecord?
In my dish.rb:
validate_uniqueness_of :dish_name
What I would like to have is: when an user recommends a dish, create a new dish if it does not exist, then create recommendation. If the dish already exists, then just create recommendation and point to existing dish.
Do I need to handle these situations manually (i.e., checking existence of dish in controller), or ActiveRecord has a way to handle it internally?
Update:
validate_uniqueness_of :dish_name only checks and return error message if the dish was created there. It probably won't create new recommendation that points to existing dish.
You could always .find_or_create_by_<attribute> to find the dish to begin with
As I see, more than one user can recommend the same dish.
Your models should look like:
Class User < ActiveRecord::Base
has_many :recommendations
has_many :dishes, :through => :recommendations
end
Class Dish < ActiveRecord::Base
has_many :recommendations
has_many :users, :through => :recommendations
end
So your Recommendations table in database should have two columns (beside it's id and timestamps) called user_id and dish_id . To validate that a user doesn't recommend
the same dish twice, do:
Class Recommendations < ActiveRecord::Base
belongs_to :dish
belongs_to :user
validates_uniqueness_of :dish_id, :scope => :user_id
end
And i didn't know about the .find_or_create_by method that Dan recommended, so definetly try to use something like that.
Hope i helped :)

Rails - Polymorphic association join table

I am currently trying to set up a model structure that seems quite simple, but I haven't quite got it down.
I have a model payment that can belong to either a customer or a supplier (which can both have many payments).
My question is simply whether I need to manually create an interface table to allow this, or if declaring the polymorphic associations will do this for me?
e.g. I have:
class Payment < ActiveRecord::Base
belongs_to :payment_originator, :polymorphic => true
end
class Customer < ActiveRecord::Base
has_many :payments, :as => :payment_originator
end
class Supplier < ActiveRecord::Base
has_many :payments, :as => :payment_originator
end
Is this enough, or do I also need to use a generator to manually create the payment_originator model?
Thanks!
As far as the models go, this is good enough. You just need to migrate a :payment_originator_type and :payment_originator_id to the payments table. The associations you defined above will automatically fill these in for you.

In Ruby on Rails, how can a model "has_many" and "belong_to" using a different field other than primary ID?

I am trying building a simple project from scratch using Rails 3. Usually, the models are like:
class Student < ActiveRecord::Base
has_many :awards
end
class Award < ActiveRecord::Base
belongs_to :student
end
and we use the award.id and student.id to get the corresponding records.
But what if it is
class Company < ActiveRecord::Base
has_many :stock_quotes
end
class StockQuote < ActiveRecord::Base
belong_to :company
end
In this case, we can use the symbol of the company, such as MSFT or GOOG to identify the company, instead of using the company.id. For example, in stock_quotes, we can store the symbol of the company directly instead of using company.id. In this case, is there a way to specify it in the models?
http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/belongs_to
http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many
Check out :primary_key and :foreign_key options
In addition to Slawosz' answer, this question about non-integer primary keys is also relevant to your question. IMHO, it would be easier to just use integer id's like in the example of Award and Student.
Slawosz has the answer right. To be verbose (and for the next time I search this) it should be like this:
#company
has_many :stock_quotes, :primary_key => :symbol
#stock_quote
belongs_to :company, :foreign_key => :symbol

Resources