Reputation: 59
Ruby on Rails model logic conflicts with the relational database logic.
In Ruby on Rails, the model possessing the belongs_to
, will have the foreign key in the database. So, the model:
class Student < ApplicationRecord
belongs_to :university
end
class University < ApplicationRecord
has_many :university
end
Will have the migration file:
class CreateStudents < ActiveRecord::Migration[6.0]
t.string :name
t.references :university, foreign_key: true
end
class CreateUniversities < ActiveRecord::Migration[6.0]
t.string :name
end
And, logically, the student will have the University foreign_key in the database, like so:
Student
-id int (PK)
-name varchar
-University_id int (FK)
University
-id int (PK)
-name varchar
So good so far
The problem arises when I want accepts_nested_attributes_for
in the student model for referencing a University in the _form.html.erb
. In order to use it, the host model, in this case Student, must have a has_one
instead of a belongs_to
for the referencing table. So it would become:
class Student < ApplicationRecord
has_one :university
accepts_nested_attributes_for :university
end
Notice how, in order to use accepts_nested_attributes_for
for referencing the university, the Student model MUST replace its belongs_to :university
for a has_one :university
, and University would have a belongs_to :student
, instead of a has_many :student
.
So in the migration file, Student would lose its reference to University, and the latter would have a reference to Student instead, like so:
class CreateStudents < ActiveRecord::Migration[6.0]
t.string :name
end
class CreateUniversities < ActiveRecord::Migration[6.0]
t.string :name
t.references :student, foreign_key: true
end
And the database would forcefully be:
Student
-id int (PK)
-name varchar
University
-id int (PK)
-name varchar
-Student_id int (FK)
Which is wrong, because there should be a one to many relationship from University to Student, not the other way around.
So, is there a way to use accepts_nested_attributes_for
, without messing up the relational database logic by having to forcefully inverse the relations in the models ?
Upvotes: 0
Views: 65
Reputation: 59
I see that I have REALLY made a blunder trying to explain my doubts (it is my first question, I have learned from my mistake).
The ultimate goal was to know:
Can I use accepts_nested_attributes_for when there is a belongs_to instead of a has_one or has_many ?
So, the answer is yes:
class Student < ApplicationRecord
belongs_to :university
accepts_nested_attributes_for :university
end
In the student controller, there should be a build_:
def new
@student = Student.new
@student.build_university
end
I REALLY, REALLY, REALLY mistyped my question, and I apologize for it.
Thanks for all your answers.
Upvotes: 0
Reputation: 744
accepts_nested_attributes_for
Nested attributes allow you to save attributes on associated records through the parent.
So you might be having a slight mix up about which one you want to be the parent. If you make the student the parent with nested attributes for the university you would not be referencing the university but essentially creating a new university record or updating an existing one with the nested attributes you used in your student form.
One-to-one
Consider a Member model that has one Avatar:
class Member < ActiveRecord::Base
has_one :avatar
accepts_nested_attributes_for :avatar
end
Enabling nested attributes on a one-to-one association allows you to create the member and avatar in one go:
params = { member: { name: 'Jack', avatar_attributes: { icon: 'smiling' } } }
member = Member.create(params[:member])
member.avatar.id # => 2
member.avatar.icon # => 'smiling'
So you would only be updating/creating new University records for each student you used those nested attributes on.
Here is for one-to-many:
One-to-many
Consider a member that has a number of posts:
class Member < ActiveRecord::Base
has_many :posts
accepts_nested_attributes_for :posts
end
You can now set or update attributes on the associated posts through an attribute hash for a member: include the key :posts_attributes with an array of hashes of post attributes as a value.
For each hash that does not have an id key a new record will be instantiated, unless the hash also contains a _destroy key that evaluates to true.
params = { member: {
name: 'joe', posts_attributes: [
{ title: 'Kari, the awesome Ruby documentation browser!' },
{ title: 'The egalitarian assumption of the modern citizen' },
{ title: '', _destroy: '1' } # this will be ignored
]
}}
member = Member.create(params[:member])
member.posts.length # => 2
member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
You can also see that in the one to many example above. Are you looking to change/create a University record when you post a Student record?
If you are just looking for a reference you essentially have that in your first example and could just change your routes up a bit to have the student referenced by the university.
Then once you have the routes nested you can just do a little work to the form partial. Here is a guy doing just that
So maybe if I am way off here describe why when you create/update a Student you want to create/update a University. If that is what you want to do.
This would helps others with your context a little more and might help others understand your intent with the nested attributes.
Add your form partial for example and explain your goal.
EDIT: As a punt you can maybe look at has and belongs to many association. This tutorial talks about nested attributes with a many to many association. But since I'm not certain exactly what your after it may or may not help.
Another punt sounds like you want maybe a student's university record to change when you update them. So you would have the student belong to the university and the university to has many students but also has many university records and the student to has many/has one university record and the record to belong to both. aka: this
Then you could have the student have accepts nested attributes for the university record/s.
Upvotes: 1