How to assign a has_many/belongs_to relation properly in Rails ActiveRecord?
There is not difference between the 3 following:
my_article.author_id = author_one.id
my_article.save
# same as
my_article.author = author_one
my_article.save
# same as
author_one.articles << my_article
To set the owner of a particular post, the most common and readable way would be:
post.author = author
post.save
OR shorter version:
post.update_attributes(author_id: author.id) # call an implicit save
Rails ActiveRecord relationships - has many and belongs to associations
It depends whether you want a join model or not. A join model lets you hold extra information against the association between two other models. For example, perhaps you want to record a timestamp of when the article was tagged. That information would be recorded against the join model.
If you don't want a join model, then you could use a simple has_and_belongs_to_many
association:
class Article < ActiveRecord::Base
has_and_belongs_to_many :tags
end
class Tag < ActiveRecord::Base
has_and_belongs_to_many :articles
end
With a Tagging
join model (which is a better name than ArticleTag
), it would look like this:
class Article < ActiveRecord::Base
has_many :taggings
has_many :tags, :through => :taggings
end
class Tag < ActiveRecord::Base
has_many :taggings
has_many :articles, :through => :taggings
end
class Tagging < ActiveRecord::Base
belongs_to :article
belongs_to :tag
end
- A Guide to Active Record Associations
Proper way to set up models and relationships for a rails app, has_many through
Try:
<p>
<strong>Participants:</strong>
<% @game.users.each do |user| %>
<br><%= user.name %>
<% end %>
</p>
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 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
Get items with Rails relations
As things stand, your relations are incomplete and so Rails won't work properly. If User has_many
Tickets then Tickets must belong_to
(or at least has_one
) User. Alternatively, User can have_many
Tickets through
Group, which seems more likely in this case.
However, even then, it's not clear what your Group model is doing. Particularly, it's not clear how you intend it to relate to User - this looks like quite a complex relationship.
To start with, though, try and set the models up like this:
class Ticket < ApplicationRecord
belongs_to :group
end
class Group < ApplicationRecord
belongs_to :user
has_many :tickets, dependent: :destroy
end
class User < ApplicationRecord
has_many :groups, dependent: :destroy
has_many :tickets, through: :groups
end
(You'll see that I've also inherited these models from ApplicationRecord, which is how I've always done it.)
If you set it up as above, you can get your ticket records with a simple @user.tickets
.
If this works, you can then add the extra HABTM relationship for Groups and Users. But be aware that HABTM relationships can be complex and there are good and bad ways to use them.
(If the primary relationship you really want is Groups > Users > Tickets then let me know and I can adjust accordingly.)
How to force a has_many association using a different column as foreign key
You need to specify a different primary key for the relationship if you wish to achieve what you are looking to do.
To clarify, this is not the same as changing the primary_key
of the model. This way is only changing the primary key used by the relationship. Please see the bottom of this post for examples.
I changed the keys from both using custom_id
and changed one to foo_id
. This way you have a better idea of what is going on between the models. You can use both custom_id
if you wish, but I would suggest keeping the rails norm of foo_id
for the belongs_to association.
If you want to use both of custom_id you'll have to add some specific foreign_keys
Here are the models:
Foo
class Foo < ApplicationRecord
has_many :bars,
primary_key: :custom_id,
foreign_key: :foo_id
end
Bar
class Bar < ApplicationRecord
belongs_to :foo,
primary_key: :custom_id
end
The Migrations
CreateFoos
class CreateFoos < ActiveRecord::Migration[5.2]
def change
create_table :foos do |t|
t.integer :custom_id, index: {unique: true}
t.timestamps
end
end
end
CreateBars
class CreateBars < ActiveRecord::Migration[5.2]
def change
create_table :bars do |t|
t.integer :foo_id, index: true
t.timestamps
end
end
end
Here is the updated Test that should now be passing:
Test
require 'test_helper'
class BarTest < ActiveSupport::TestCase
test "the truth" do
foo = Foo.new(id: 1, custom_id: 100)
bar = Bar.new(foo: foo)
assert bar.foo_id == foo.custom_id
# bar.foo_id = 100
# foo.custom_id = 100
end
end
Examples
Foo.find(1) #<Foo id: 1, custom_id: 100>
Bar.first #<Bar id: 1, foo_id: 100>
Bar.first.foo = #<Foo id: 1, custom_id: 100>
Bar.first.foo == Foo.find(1) # true
As you can see, this method does not change the primary key of Foo
itself. It changes the primary key the relationship between Foo
and Bar
uses. Bar is realated to foo via custom_id: 100
, but foo is still found with it's id: 1
key, not its custom_id
key.
How do I set up a has many through for two different associations between the the same models
It doesn't sound like you need a has_many through association for users hosting events. Something like this should work sufficiently for that (in user.rb) if you have a hosted_by_id
column on your events
table:
has_many :hosted_events, class_name: "Event", foreign_key: "hosted_by_id"
For attending events assuming a join class with columns attendee_id
and event_id
:
class AttendeeEvent < ActiveRecord::Base
belongs_to :attendee, class_name: "User"
belongs_to :event
end
You can add the following association to user.rb:
has_many :attendee_events, foreign_key: "attendee_id"
has_many :attended_events, through: :attendee_events, source: :event
The source: :event
option indicates that the target objects for this association are found from the event
association on the joining object.
The associations in event.rb are then:
belongs_to :hosted_by, class_name: "User"
has_many :attendee_events
has_many :attendees, through: :attendee_events
Rails: has_many through between two models and also a has_many/belongs to on the same models
I think something like this may solve your problem:
# User
has_many :answered_questions
has_many :questions, :through => :answered_questions
has_many :created_questions, class_name: Question, foreign_key: :author_id
# Question
has_many :answered_questions
has_many :users, :through => :answered_questions
belongs_to :author, class_name: User
# Answered questions
belongs_to :question
belongs_to :user
Related Topics
How to Pass Content to Jekyll Default Converter After Custom Conversion
Unable to Delete File from Amazon S3 Using Ruby Script
Using Ruby and Mechanize to Fill in a Remote Login Form Mystery
Algorithm to Print All Valid Combations of N Pairs of Parenthesis
Multipart Response in Ruby/Rack
How to Run an Excel MACro from Ruby
Search for "Enabled" Users in Net-Ldap for Ruby
Rails Gem to Break a Paragraph into Series of Sentences
How to Listen to Stdin Input Without Pausing My Script
Is There a More Concise Way to Call an Outside Method on a Map in Ruby
What Is the Equivalent to Rspec's 'It "Should …", Focus: True Do' in Minitest/Spec
Detect Similar Sounding Words in Ruby
Routing Error No Route Matches [Get] "/Static_Pages/Home", Tutorial
Is It a Good Idea to Pass a Huge String as an Argument to Sidekiq Worker