Reputation: 109
I need to validate a field before the create method
In my _form.html.erb I have two models, one is the owner model, and the other is a model I create to have other arguments, I need to validate those arguments before getting in the create method, I can use an if, but it is not the best practice to do it.
def create
@customer = Customer.new(customer_params)
#read the city name, since this is requested by city name (string) and it shoud be "id" in the system
city = city_params()
@customer.city_id = City.find_by(name: city["name"]).id
respond_to do |format|
if @customer.save
format.html { redirect_to @customer, notice: 'Customer was successfully created.' }
format.json { render action: 'show', status: :created, location: @customer }
else
format.html { render action: 'new' }
format.json { render json: @customer.errors, status: :unprocessable_entity }
end
end
end
I need to validate the the city name, because the customer owner must have the city_id, and the _form requests the name (string), so I need to find the city but previously I need to validate the city name has a value and it exists,
How can I validate this in the model ?
Upvotes: 1
Views: 923
Reputation: 76784
before_validate
You could use the before_validate
callback in your model:
#app/models/customer.rb
Class Customer < ActiveRecord::Base
attr_accessor :city_name
before_validate :set_city
private
def set_city
city_id = City.find_by(name: city_name).id
end
end
--
Custom Validation Method
I think the bottom line is you'll be best using a custom validation method for this. You basically want to return the user to the form with an error saying "City not found" or similar; which is entirely within the remit of a custom validation method:
#app/models/customer.rb
Class Customer < ActiveRecord::Base
validate :check_city_id
private
def check_city_id
errors.add(:city_id, "City doesn't exist") unless City.try city_id
end
end
--
System
This kind of issue can be handled by simply giving the user options to select the id
at input; rather than selecting by name
:
#app/views/customers/new.html.erb
<%= form_for @customer do |f| %>
<%= f.select :city_id, City.all.collect {|p| [ p.name, p.id ] } %>
<% end %>
I think your method of giving the user the ability to pick a city name
, and then validating in the backend is very inefficient; whilst giving the user a rigid set of options to select a buyer by city is far more robust
Upvotes: 0
Reputation: 18090
If I were you, I would start out by keeping all of this logic in the controller and use a filter to find the city:
class CustomersController < ApplicationController
before_action :find_city, only: [:create, :update]
def create
@customer = Customer.new(customer_params)
#read the city name, since this is requested by city name (string) and it shoud be "id" in the system
@customer.city_id = @city.try(:id) # This returns `nil` if the city was not found
respond_to do |format|
if @customer.save
format.html { redirect_to @customer, notice: 'Customer was successfully created.' }
format.json { render action: 'show', status: :created, location: @customer }
else
format.html { render action: 'new' }
format.json { render json: @customer.errors, status: :unprocessable_entity }
end
end
end
private
def find_city
@city = City.find_by(name: params[:city][:name]) # No need for strong parameters for this
end
end
Then make sure you're validating the presence of city_id
in your Customer
class:
class Customer < ActiveRecord::Base
validates :city_id, presence: true
end
Later on, if you find that you need this logic to be extracted from the controller, then consider looking at creating a service object or a form object. Because this is a simple case (only 2 classes are involved), I would hold off on creating those constructs for now though. The controller layer is sufficient enough to handle this simple logic.
Why not move the logic directly into the model? I can tell you from experience that you do not want to mess your model up with tons of logic involving other model classes. Customer
should not really know much about City
in my opinion.
Upvotes: 2
Reputation: 160
We have something called callbacks http://api.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html
..using this we can trigger our required validations in the model.
You can create your own logic of validation like example
before_create :method_name
def method_name
your logic.....example: validates :city_name ..
end
Upvotes: -1