How to reflect in the database a new belongs_to and has_many relationship in Ruby on Rails
Adding a belongs_to
(or any other) relationship to your model only tells active record that models are linked logically. This gives you access to methods like task.user
. For this to actually work, the instances must be linked via database fields.
This is the step you're missing: you need to create a migration that will add a column to the Task table indicating which user it belongs to.
rails g migration AddUserIdToTasks user_id:integer
Note AddUserIdToTasks can be whatever name you want. It makes no difference. You can then open db/migrations/add_user_to_tasks
and see what it does. Usually self.up will modify the database how you want it and self.down will do the opposite (so, in this case, remove the used_id).
Then to actually execute the SQL commands to alter the database table and schema, run
rake db:migrate
How do I specific belongs_to and has_many relationships when generating new models
When passed as a generator argument belongs_to
in is just an alias of references
which tells rails to create a column named blog_id
which is a foreign key:
# rails generate model Post blog:belongs_to
class CreatePosts < ActiveRecord::Migration[5.0]
def change
create_table :posts do |t|
t.belongs_to :blog, foreign_key: true
t.timestamps
end
end
end
This is the actual database column that defines the relation between two tables.
It also adds the association to the model:
class Post < ApplicationRecord
belongs_to :blog
end
Why does'nt the same work for has_many
?
The arguments for the model generators are attributes of the model. blog_id
is an actual attribute backed by a database column.has_many
is not an attribute. It's a metaprogramming method which adds a posts
method to your Blog instances. You need to add it manually to the model.
If you run rails g model Blog posts:has_many foo:bar
Rails will actually create a migration with those attributes:
class CreateBlogs < ActiveRecord::Migration[5.0]
def change
create_table :blogs do |t|
t.has_many :posts
t.bar :foo
t.timestamps
end
end
end
Rails does not type check the arguments. Of course the migration won't actually run:
undefined method `has_many' for #<ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition:0x007fd12d9b8bc8>
If you have already generated the migration just remove the line t.has_many :posts
and add has_many :posts
to app/models/blog.rb
.
Rails Converting a has_many relationship into a has and belongs to many
I suggest you to always use has_many :through
instead of HBTM.
To establish this kind of relation you'll need the following set up:
# region.rb
class Region
has_many :facility_regions
has_many :facilities, through: :facility_regions
end
# facility.rb
class Facility
has_many :facility_regions
has_many :regions, through: :facility_regions
end
# facility_region.rb
class FacilityRegion
belongs_to :facility
belongs_to :region
end
Also, of course, you'll need to create a migration:
rails g migration create_facility_regions facility_id:integer region_id:integer
# in this migration create a uniq index:
add_index :facility_regions, %I(facility_id region_id), name: :facility_region
rake db:migrate
UPD
As to migration from one database state to another one.
I think it should not be a problem.
1) Do not delete the relations you had before (leave has_many :facilities
and belongs_to :region
in models).
2) When new table is created and new associations added to the classes (which I showed) create a new migration:
rails g migration migrate_database_state
3) Write the script, which will create new records in db (to reflect the current state of things):
ActiveRecord::Base.transaction do
Facility.where.not(region_id: nil).find_each do |facility|
next if FacilityRegion.find_by(falicity_id: facility.id, region_id: facility.region_id)
FacilityRegion.create!(facility_id: facility.id, region_id: facility.region_id)
end
end
4) Put this script into last created migration and run it (or in console without migration, effect would be the same).
5) After script is successfully run, create new migration in which you delete region_id
from facilities
table and remove these associations definitions (has_many :facilities
and belongs_to :region
) from models.
It must be it. I might have made some typos or so, make sure I did not miss anything and
has_many, belongs_to relation in active record migration rails 4
You could call:
rails g model task user:references
which will generates an user_id
column in the tasks
table and will modify the task.rb
model to add a belongs_to :user
relatonship. Please note, you must to put manually the has_many :tasks
or has_one :task
relationship to the user.rb
model.
If you already have the model generated, you could create a migration with the following:
rails g migration AddUserToTask user:belongs_to
which will generate:
class AddUserToTask < ActiveRecord::Migration
def change
add_reference :tasks, :user, index: true
end
end
the only difference with this approach is, the belongs_to :user
relationship in the task.rb
model won't be created automatically, so you must create it for your own.
Creating a has_and_belongs_to_many relationship in Rails
I would like the teacher to have many users but each user to have only one teacher
You only need has_many
/ belongs_to
...
#app/models/user.rb
class User < ActiveRecord::Base
belongs_to :teacher
end
#app/models/teacher.rb
class Teacher < ActiveRecord::Base
has_many :users
end
You'll need to add a teacher_id
column in your users
table (the opposite of what you have now):
class UpdateUsers < ActiveRecord::Migration
def change
change_table :users do |t|
t.references :teacher, index: true, foreign_key: true #-> teacher_id
end
end
end
--
The error you have is that you're calling user_id
on teacher
; it should be teacher_id
on user:
@teacher = Teacher.new(name: "Phred Willard",
email: "pwillard@test.com",
phone: "1234567890",
user_ids: [@user.id,
@user2.id,
@user3.id,
@user4.id])
This should associate @teacher
with the defined @users
you've listed.
You'll also want to look at collection_singular_ids
for the has_many
association, which is why your test is failing.
Rails: has_many with extra details?
Recipes and Ingredients have a has and belongs to many relationship, but you want to store additional information for link.
Essentially what you are looking for is a rich join model. But, a has_and_belongs_to_many relationship is not flexible enough to store the additional information you require. Instead you will need to use a has_many :through relatinship.
This is how I would set it up.
recipes columns: instructions
class Recipe < ActiveRecord::Base
has_many :recipe_ingredients
has_many :ingredients, :through => :recipe_ingredients
end
recipe_ingredients columns: recipe_id, ingredient_id, quantity
class RecipeIngredients < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
end
ingredient columns: name
class Ingredient < ActiveRecord::Base
has_many :recipe_ingredients
has_many :recipes, :through => :recipe_ingredients
end
This will provide a basic representation of what you're looking to do. You may want to add a validation to RecipeIngredients to ensure that each ingredient is listed once per recipe, and a callback to fold duplicates into one entry.
Has_many through and belongs to in Rails
You're very close, you're just missing the connection. Magazine can see Subscription because Subscription has its magazine_id
. User can see Subscription because Subscription has its user_id
. Through the Subscription, Magazine and User can see each other. So you want to user through
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :magazines, through: :subscriptions
has_many :subscriptions
end
class Subscription < ActiveRecord::Base
belongs_to :user
belongs_to :magazine
end
class Magazine < ActiveRecord::Base
belongs_to :user
has_many :subscriptions
has_many :users, through: :subscriptions
end
If this doesn't work, make sure you post your schema.rb/relevant fields from the tables you mentioned.
Using has_many / belongs_to to access info in controller - Ruby on Rails
You shouldn't need to add anything to the controller show
action.
In your customer show
view you presumably have access to a @customer
object. Because of your has_many, that will have a collection @customer.orders
. So, in the view, you can do something like
<table>
<thead>
<td>Item</td>
<td>Quantity</td>
<td>Price</td>
<td>Date Placed<td>
</thead>
<% @customer.orders.each do |order| %>
<tr>
<td><%= order.item.name %></td>
<td><%= order.quantity %></td>
<td><%= order.price %></td></tr>
<td><%= order.date_placed %></td>
</tr>
<% end %>
</table>
Obviously I'm making up the possible order
fields you'd want to display, but this should give you the idea.
Rails has many and belongs to one
Add a creator_id column to your projects table for the creator relationship, and then add the associations to the models:
class User < ActiveRecord::Base
has_many :assigned_projects
has_many :projects, :through => :assigned_projects
has_many :created_projects, :class_name => "Project", :foreign_key => :creator_id
end
class Project < ActiveRecord::Base
belongs_to :user
has_many :assigned_projects
has_many :users, :through => :assigned_projects
belongs_to :creator, :class_name => "User", :foreign_key => :creator_id
end
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many
Rails Has and Belongs To Many Associations
If you just want to show parents and their children at view you don't need to create new table, just do as below:
In your Parents controller's index action( I am considering that you want to show all the parents with their children so I am using index action ) :
def index
@parents= Parent.all.includes(:children)
end
Then on your index.html.erb view :
<% @parents.each do |parent| %>
<%= parent.name%><br/>
<% parent.children.each do |child| %>
<%= child.name%><br/>
<% end %>
<% end %>
Considering that you have define association between you Parent an Child model
Related Topics
How to Check What Is Queued in Activejob Using Rspec
Vcrproxy: Record Phantomjs Ajax Calls with Vcr Inside Capybara
How to Send Mail with Rails Without a Template
How to Attach a Message to Rspec Check
Ruby on Rails and JSON Parser from Url
Ruby Merging Two Arrays into One
Ruby Request to Https - "In 'Read_Nonblock': Connection Reset by Peer (Errno::Econnreset)"
Paperclip :Style Depending on Model (Has_Many Polymorphic Images)
Ruby Concatenate Strings and Add Spaces
Bundle Install Could Not Fetch Specs from Https://Rubygems.Org/
What's the Variable Scope Within 'Class_Eval' String
No Database Connection in Rails Console
Why Are Constants from Extended Module Not Available in Class Methods Declared with Self