Nested Model Validation Problem and Solution
Bài đăng này đã không được cập nhật trong 3 năm
Nested Attribute is one of the helpful feature for Ruby on Rails developers. It gives the privilege to save attributes of a record through its associated parent. You can find details about nested model and nested attribute here.
Model Validation
Validation of models is an important part of development as it ensures that only valid data are saved into model. For instace, it may be necessary for any application to ensure that every user provide valid email or mailing address. It is a good practice because it prohibits error and use of unpermitted data. But validation in nested model cause a problem. I am explaining it with an example.
Suppose we have two model User and Project where a user can have many project. So relationship between these two model is one to many. So our model description are
app/models/user.rb
class User < ActiveRecord::Base
has_many :projects
Attributes: email:sring, name:string
end
app/models/project.rb
class Project < ActiveRecord::Base
belongs_to :user
Attributes: user:reference, name:string
end
As there is a has_many relation in user model with project, we can enable nested attribute updating by
app/models/user.rb
class User < ActiveRecord::Base
has_many :projects
Attributes: email:sring, name:string
accepts_nested_attributes_for :projects
end
Now as a good and sincere developer, we will add validation to our models
app/models/user.rb
class User < ActiveRecord::Base
has_many :projects
validates_presence_of :email
validates_presence_of :name
Attributes: email:sring, name:string
accepts_nested_attributes_for :projects
end
app/models/project.rb
class Project < ActiveRecord::Base
belongs_to :user
validates_presence_of :user
validates_presence_of :name
Attributes: user:reference, name:string
end
Ok now model programming is done. We will now move to controller. We will create an user with some projects at the same time. So our user controller will be like
app/controllers/users_controller.rb
class UsersController < ApplicationController
def create
user = User.new(user_params)
if user.save
render json: { success: "Created Successfully"),
else
render json: user.errors
end
end
def user_params
params.require(:user).permit :email, :name, projects_attributes: [:name]
end
end
Our controller and models are ready and so we are ready to roll. Now we are going to create an user with this JSON request:
curl -X POST -H "Content-Type: application/json" http://localhost:3000/users -d '{"user":{"name": "Ananta Jalil", "email": "yopmail@jalil.com", "projects_attributes": [{"name": "acting"}, {"name": "Business"}]}}
But BANG!! It throws an error, right?? and the error is
{projects.user_id: [projects.attribute.user_id.blank]}
Whats The Problem
Actually Rails Validate the presence of associated records in the join record before the parent record exist in the database. So in this case validation of projects run before that existing record is assigned to a saved user. So the user_id is nil when validation of projects is run. And give us the error that user id is blank. So what's the solution?? Can't we use validation in nested models??
Solution
The answer of the above question is Yes, we can. You have to use inverse_of
to get rid of this problem. Documentation of invers_of can be found here. Its a good practise to always add this functionality in has_many, belongs_to methods in model. So adding this, our model looks
app/models/user.rb
class User < ActiveRecord::Base
has_many :projects, inverse_of: :user
validates_presence_of :email
validates_presence_of :name
Attributes: email:sring, name:string
accepts_nested_attributes_for :projects
end
app/models/project.rb
class Project < ActiveRecord::Base
belongs_to :user, inverse_of: :projects
validates_presence_of :user
validates_presence_of :name
Attributes: user:reference, name:string
end
So now we can create our user object with associated project. So after running the above JSON request for creating user we get
{ "success": "Created Successfully")}
Problem solved na??
ha5
All rights reserved