Rails: polymorphic assoziation, has_many :through - ruby-on-rails

Im not sure i understand rails polymorphic.
In Java you can create Objects from the same Objecttype:
http://www.fh-kl.de/~guenter.biehl/lehrgebiete/java2/j2-08-Dateien/abb.8.10.jpg
Person trainer = new Trainer()
Person sportler = new Trainer()
In Rails http://guides.rubyonrails.org/association_basics.html#polymorphic-associations:
In this example: picture can be from an employee or from a product, sounds strange because this is not realy the same type.
Do i understand the real purpose: to save objects in the same container an array of person or image?
In my rails project: I have several person: sportsmen, trainer and guest. They are sons of person (inheritance).
I think i meet the inheritance reason.
There is another class named exercise.
Sportsmen and trainer can create exercises.
So i want to use polymorphic. Exercises can be from trainer or sportsmen. Like in the example of the rails page, images can be from employee or a product.
Do i meet the best practise?
How do i implement a has_many :through with polymorphy?
It is not possible to use a habtm assoziation with polymorphic.
You have to define a additional class, but how exactly?

I think you want single table inheritance (STI) models, not a polymorphic relationship.
See this article http://www.alexreisner.com/code/single-table-inheritance-in-rails and these stackoverflow answers Rails - Single Table Inheritance or not for Applicant/Employee relationship Alternative to Rails Single Table Inheritance (STI)?

Just to make it clear, you should use polymorphic associations when you have a model that may belong to many different models on a single association.
Suppose, you want to be able to write comments for users and stories. You want both models to be commendable. Here's how this could be declared:
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
end
class Employee < ApplicationRecord
has_many :comment, as: :commentable
end
class Product < ApplicationRecord
has_many :comment, as: :commentable
end
To declare the polymorphic interface (commendable) you need to declare both a foreign key column and a type column in the model.
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.text :body
t.integer :commentable_id
t.string :commentable_type
t.timestamps
end
add_index :comments, :commentable_id
end
end
You can check more details about associations here.

Related

What is the best way to implement belongs to many logic in Rails?

For example I have model Match and model Player. What I want is for Player to be able to participate in many matches.
So it should look smth like this:
#match_first = Match.first
#match_last = Match.last
#match_first.players
#player1, player3, player4
#match_last.players
#player1, player4, player5
Both matches can have same players simultanously.
In your Match model:
has_many: :players, through: :appearances
In your Player model:
has_many: :matches, through: :appearances
class Appearance < ActiveRecord::Base
belongs_to :match
belongs_to :player
You could have extra attributes on appearance like 'goals_scored'. The through model doesn't have to be called Appearance... it could be Attendance or MatchPlayer or PlayerMatch, but you can see that the last two names are constraining, because out of this association you can also see all the matches a player appeared in.
Rails provides two ways to accomplish the many-to-many relationship. The first, which #zoot's answer describes, is has_many :through. This is a great option if the relationship is a Model in its own right (i.e., needs additional attributes or validations).
If the relationship between the two entities is direct such that you don't really need a third model, you can use the has_and_belongs_to_many association.
In your Match model:
has_and_belongs_to_many :players
In your Player model:
has_and_belongs_to_many :matches
When you use has_and_belongs_to_many you do need to create a join table that holds the relationship. The migration for that (assuming a relatively recent version of Rails) might look like:
class CreateJoinTableMatchesPlayers < ActiveRecord::Migration
def change
create_join_table :matches_players, :users do |t|
t.index [:match_id, :player_id], unique: true
end
end
end
There's a section in the Active Record Associations guide that talks about how to choose between the two approaches.

Form control-group for rails polymorphic association

In my rails app I have following models:
class Book < ApplicationRecord
belongs_to :bookable, polymorphic: true
end
class Student < ApplicationRecord
has_many :books, as: :bookable
end
class Library < ApplicationRecord
has_many :books, as: :bookable
end
When creating a new book object in form.html.erb, how I could map t.integer :bookable_id and t.string :bookable_type to specific object?
Ideally I would use grouped_collection_select and pull first Student and Library and below their ids.
Thanks in advance.
When creating a new book object in form.html.erb, how I could map
t.integer :bookable_id and t.string :bookable_type to specific object?
Well, you can use the collection_select or grouped_collection_select (according to your requirements) to display Students and Libraries to store the values for bookable_id.
For bookable_type, Rails under the hood reads the class names of the instances and captures the class name and stores it as a value for bookable_type. For example, consider the below approach
#student = Student.find(1)
#student.books.create
The above code snippet will create a record in books table with bookable_id = 1 and bookable_type = "Student"

What is the process of creating a joining table which resolves a many-to-many relationship using migrations?

I would like to understand the process which can be followed to create models and migrations which in turn create a joining table within the database to resolve a many-to-many relationship. For instance. If i have a course table, a students table and i want to create a joining table called studies with the id from course and students along with extra data such as the grade and date started. Exactly What is the process for doing this?
If i am to use the generate model command for each of the above 3 table names, this will create a separate migration for all of them. Even if i go into the models and add the relevant associations this will not affect how the database is created? Please could you give me some guidance on what steps i must take in order to create the required foreign key relationships here with some examples?
Use a has_many :through association. Set it up manually.
Step 1: Generate your models separately. The joining model Study will contain foreign keys, so remember to include the columns.
rails g model Course title:string
rails g model Student name:string
rails g model Study course_id:integer student_id:integer start_date:date grade:string
Step 2: Set up associations:
# models/course.rb
class Course
has_many :studies
has_many :students, through: :studies
end
# models/student.rb
class Student
has_many :studies
has_many :courses, through: :studies
end
# models/study.rb
class Study
belongs_to :course
belongs_to :student
end
Step 3: Run migrations, restart server (if necessary), and that's it. Rails will handle the rest.
Accessing attributes in the joining table may require careful timing to ensure the correct object is being accessed, since the joining object is not returned via Rails' built-in methods, ie #course.students. Check out this answer for some ideas.
Read the guide for more information.
for many-to-many relationships use:
class Car < ActiveRecord::Base
has_and_belongs_to_many :tires
end
class Tire < ActiveRecord::Base
has_and_belongs_to_many :cars
end
Then you create a migration like this:
class CreateCarsAndTires < ActiveRecord::Migration
def change
create_table :cars do |t|
t.string :name
end
create_table :tires do |t|
t.string :something
end
create_table :cars_tires do |t|
t.belongs_to :car
t.belongs_to :tire
t.string :additional_dataA //optional
t.int :something_else //optional
end
end
end
It is very important that you name your join table in the migration in alphabetical order (c in cars comes before t for tires) as ActiveRecord will look in has_many_and_belongs_to relations for a table which is named this way pluralized-classA_pluralized_classB like apples_bananas vs bananas_apples which would not work and you would have to add the table name to your classes and it goes against the convention over configuration paradigm.
Hope it helps.

Class inheritance in Ruby on Rails

I'm building this little app with ruby on rails.
In the app, I'm having the following models strutter which I'm not sure whether it's the best ror practice. (I'm actually a Java developer)
There is a person class which I use it as a super class, and there are staff class and customer classes inherits from the person class. To achieve this, I have:
class Person < ActiveRecord::Base
end
class Staff < ActiveRecord::Base
belongs_to :person, :polymorphic => true, :dependent => :destroy
end
class CreateStaffs < ActiveRecord::Migration
def self.up
create_table :staffs do |t|
t.references :person, :polymorphic => true, :null => false
....
Firstly, what I did works fine, but am i doing the best thing?
The next thing I'm trying to do is to create a form which creates person, staff objects and link them. And I'm stuck on having two models on a single form.
Does anyone have suggestions?
Thanks,
Kevin Ren
What you're doing here is not to use Person as a superclass of Staff, but you create a relation between them. You want to look at single-table inheritance instead. You essentially want this:
class Person < ActiveRecord::Base
end
Class Staff < Person
end
In addition you need to have a "type" field in your Person table that Rails uses to figure out which model a given record belongs to. See the docs for ActiveRecord for more info.

Associate one model to two different models?

Hey guys. I'm trying to build this Picture Battle site, (where you chose the picture you prefer) and I had two models. Pictures, and Battles.
So Each Picture has_many Battles, but Each Battle belongs to two pictures. How do I associate it.. I was thinking something like "belongs_to_many" but apparently that doesn't exist.
from what i see this could be easily done by using a has_and_belongs_to_many association
You should set up a has_many :through relationship if you need to work with the relationship model as an independent entity. If you don’t need to do anything with the relationship model, which is probably the case, it may be simpler to set up a has_and_belongs_to_many relationship
here's how you do the HABTM:
class Picture < ActiveRecord::Base
has_and_belongs_to_many :battles
end
and
class Battle < ActiveRecord::Base
has_and_belongs_to_many :pictures
end
then you can call picture.battles and battle.pictures
you will also need to create a new migration that looks like this
class CreateBattlesPicturesJoinTable < ActiveRecord::Migration
def self.up
create_table :battles_pictures, :id => false do |t|
t.integer :battle_id
t.integer :picture_id
end
end
def self.down
drop_table :battles_pictures
end
end
more info here
It is a many-to-many association. You can achieve it through a join model. Check has_many in the API docs

Resources