I have an issue with a model. I have a model that is updated through 2 forms (as 2 people need to enter separate data). Form 1 contains the first half of the required data and therefor only that data needs to be validated there.
I am having trouble finding a way to validate only the data entered in form 1. Below you'll find my subscription.rb model file.
class Subscription < ActiveRecord::Base
# werkgever form
if form_id == 'form1'
validates :email, presence: true
end
# werknemer form
if form_id == 'form2'
validates :name, presence: true
validates :city presence: true
end
end
I need the if statements to contain something that would make it so that only the values beloging to that form are validated so that i dont get errors on form2 when updating and vice versa.
I hope this is clear enough. Any help is appreciated.
Thanks.
You could set validations with condition:
attr_accessor :form_type
validates :email, presence: true, :if => :werkgever_form?
def werkgever_form?
form_type == 'werkgever'
end
:form_type is a virtual attribute, which is not saved in the database and needed only for validations. You can set this attribute as a hidden field in each form:
<%= form.hidden_field :form_type, 'werkgever' %>
attr_accessor :form_type
validates :email, presence: true, if: :check_if_form_one
validates :name, presence: true, unless: :check_if_form_one
validates :city presence: true, unless: :check_if_form_one
def check_if_form_one
/* Add your condition here
example: form_type == 'form1' */
end
You can set form_type from controller method or view page.
I have 2 forms.
Form 1 I have 10 fields which I am validating.
Form 2 but it only contains 2 fields.
The model is same.
What I need to do is:
To validate fields when they are submitted by forms. If I am not posting any fields it should not validate in model. If I post 5 fields it should validate 5 fields. If I post 2 fields it should validate only 2 not all of them.
So form 1 all 10 should be validated, form 2 only 2 should validate not rest of 8.
Here is my code:
validates :teacher_number, :title, :name, :gender, :location, :dob,
:contact_mobile, :contact_home, :street, :city, :state, :zip_code, :country,
presence: true
validates :teacher_number, uniqueness: {scope: :school_id}
validate :teacher_number_existance, :on => :create
validate :school_existance, :on => :create
Below is my attempt which successfully works fine but its bulk of code that somewhat a bad practice.
validates :teacher_number, presence: true, if: "teacher_number && teacher_number.blank?"
validates :title, presence: true, if: "title && title.blank?"
validates :name, presence: true, if: "name && name.blank?"
validates :gender, presence: true, if: "gender && gender.blank?"
validates :location, presence: true, if: "location && location.blank?"
validates :dob, presence: true, if: "dob && dob.blank?"
validates :contact_mobile, presence: true, if: "contact_mobile && contact_mobile.blank?"
validates :contact_home, presence: true, if: "contact_home && contact_home.blank?"
validates :street, presence: true, if: "street && street.blank?"
validates :city, presence: true, if: "city && city.blank?"
validates :state, presence: true, if: "state && state.blank?"
validates :zip_code, presence: true, if: "zip_code && zip_code.blank?"
validates :country, presence: true, if: "country && country.blank?"
validates :teacher_number, uniqueness: {scope: :school_id}, if: "teacher_number && teacher_number.blank?"
validate :teacher_number_existance, :on => :create, if: "self.teacher_number && self.teacher_number.blank?"
validate :school_existance, :on => :create, if: "self.teacher_number && self.teacher_number.blank?"
EDIT
UPDATED MY QUESTION.
I see two ways for this:
Some hidden param in form and attr_accesor in model to turn off validation.
Use save(validate: false) for save from that second form.
Next, you can do it like this
if validate_object?
validates :email, presence: true
validates :variant, presence: true
end
You can use some patterns like form object.
But you have to remember that this object will be invalid in future too.
If you want different validations of data in different circumstances, you should not be validating on the model. You should validate elsewhere: either in the view using HTML form validation, or in the controller using Rails' Strong Params.
I think you should use HTML form validation to manage this, because you're worried about the record missing attributes. Strong Params is more useful in case you are worried about people supplying forbidden values for record attributes.
Here is how you would use HTML form validation to manage this (example using HAML):
= form_for #model do |f|
= f.text_input(:teacher_number, required: true)
...all your other inputs...
= f.submit 'Submit Form'
Here is how you would use Strong Params to constrain the number of things you can get:
class ModelsController < ApplicationController
def form_action_1
Model.create(form_1_params)
end
def form_action_2
Model.create(form_2_params)
end
private
def form_1_params
# let's permit all the things you want for the first form
params.require(:model).permit(:teacher_number, :title, ...)
end
def form_2_params
# let's permit only the few things you want in the second form
params.require(:model).permit(:only, :a, :few, :things)
end
end
I'm not sure exactly what you're asking, but perhaps this approach would work. Write your validations like this:
validates :teacher_number,
:title,
:name,
:gender,
:location,
:dob,
:contact_mobile,
:contact_home,
:street,
:city,
:state,
:zip_code,
:country,
presence: true,
on: :form2
validates :teacher_number, uniqueness: {scope: :school_id}
validate :teacher_number_existance, :on => :create
validate :school_existance, :on => :create
The on: :form2 near the bottom of the first validator means the validations will only run if you explicitly ask them to run.
So in your controller action for saving form2, you would have to do this:
if valid?(:form2) && #model.save
The other validators will run when you save, as normal. Using the on: parameter of validates for your own symbols (as opposed to the built-in ones) is covered in the Rails Guide for validations, but it's easy to miss. I didn't notice it myself until recently. I hope this helps.
Finally, after so going back and forth below solution worked well for me.
before_validation :strip_validations
def strip_validations
['teacher_number', 'title', 'name', 'gender', 'location', 'dob', 'contact_mobile', 'contact_home', 'street', 'city', 'state', 'zip_code', 'country'].each do |attr|
errors.add("Teacher", {attr => " #{attr} can't be blank"}) if send(attr.to_sym) && send(attr.to_sym).blank?
end
end
I have a study that can have participants. I have a simple_form where the user can add participants. It looks a bit like a table:
name | company | email OR mobile | timezone
name | company | email OR mobile | timezone
name | company | email OR mobile | timezone
By default, the screen has three fieldset rows, and the user can add more rows if needed. Each row is one participant.
I would like my participant model to validate only the rows that have been filled out, and ignore rows that are blank because even though we are showing three by default to the user, not all three are required fields.
Here's the relevant portion of app/models/participants.rb.
class Participant < ApplicationRecord
belongs_to :study
validates :name, presence: true
validates :company, presence: true
validates :time_zone, presence: true
if :channel == 'sms'
validates :mobile_number, presence: true
elsif :channel == 'email'
validates :email, presence: true
end
end
In participants_controller.rb I have:
def index
3.times { #study.participants.build } if #study.participants.length.zero?
end
The problem is that I get an error because simple_form thinks that all three fields are required, and not just the first row.
Rails' validators accept conditions:
validates :mobile_number, presence: true, if: Proc.new { |p| p.study.channel == 'sms' }
validates :email, presence: true, if: Proc.new { |p| p.study.channel == 'email' }
By default all inputs are required. When the form object includes
ActiveModel::Validations (which, for example, happens with Active
Record models), fields are required only when there is presence
validation. Otherwise, Simple Form will mark fields as optional. For
performance reasons, this detection is skipped on validations that
make use of conditional options, such as :if and :unless.
And of course, the required property of any input can be overwritten
as needed:
<%= simple_form_for #user do |f| %>
<%= f.input :name, required: false %>
<%= f.input :username %>
<%= f.input :password %>
<%= f.button :submit %>
<% end %>
Try to put all the inputs as required: false. That should allow skip simple_form validations and the data came into the controller and the model can be filtered or/and validated and every other things you want to do before persist.
In the model class you can use several ways of validations for example:
you also can use the :if and :unless options with a symbol corresponding to the name of a method that will get called right before validation happens. This is the most commonly used option.
for example
class Participant < ApplicationRecord
belongs_to :study
validates :name, presence: true
validates :company, presence: true
validates :time_zone, presence: true
validates :mobile_number, presence: true if: :channel_is_sms?
validates :email, presence: true if: :channel_is_email?
def channel_is_sms?
channel == "sms"
end
def channel_is_email?
channel == "email"
end
end
or also you can use custom validator where you do all that you need validate. for example
class MyValidator < ActiveModel::Validator
def validate(record)
unless record.channel == 'sms'
...
... actions here
...
end
end
end
class Person
include ActiveModel::Validations
validates_with MyValidator
end
I have added class ContactRequest in home_helper.rb
module HomeHelper
class ContactRequest
include ActiveModel::Model
attr_accessor :name, :email, :phone, :message, :captcha
validates :name, presence: true,length: {in:2..255}
validates :email, presence: true, length: {in:6..255}
validates :message, presence: true
validates :phone, presence: true
validates :captcha, presence: true
end
end
This class basically is to be used to bind dat from a JSON POST to an object which then I can use for
Verifying Captcha
Sending the contact information from this object as an email via SendGrid
hence, I do not have any needs to persist the information.
However I get an error when the relevant method of the controller is called
def contact
#contactReq = ContactRequest.new(JSON.parse(params[:json]))
logger.debug "ContactRequest: #{#contactReq.attributes.inspect}"
for this line I see in the logs
NameError (uninitialized constant HomeController::ContactRequest):
app/controllers/home_controller.rb:6:in `contact'
isn't the home_helper available to the view and this controller? Or should I put this class in concerns directory?
You need to add the module name in front of the classname HomeHelper:
def contact
#contactReq = HomeHelper::ContactRequest.new(JSON.parse(params[:json]))
...
end
I have model Order:
class Order < ActiveRecord::Base
has_one :shipping_address
has_and_belongs_to_many :books
validates :first_name, :surename, :email, :street1, :country, :zipcode, presence: true
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
validates :zipcode, numericality: true
accepts_nested_attributes_for :shipping_address
end
and model Book:
class Book < ActiveRecord::Base
DEFAULT_PRICE = 55.15
NEXT_BOOK_PERCENT = 5
has_and_belongs_to_many :pages
has_and_belongs_to_many :orders
validates :name, presence: {message: "Name can't be blank."}
validates_length_of :name, minimum: 3, maximum: 12, message: "Sorry, we can't create this book right now. Please contact us for further information."
validate :same_letter_validation
validate :validates_for_non_alphabetic
before_save :compile
#......
end
Also I have table books_orders (book_id, order_id)
When I try do delete order from RailsAdmin panel I get next error:
NoMethodError in RailsAdmin::Main#delete
undefined method `orders_books' for #
It says that error in this line:
- #abstract_model.each_associated_children(object) do |association, child|
Have you defined that "orders_books" method anywhere in your code? If so, can you please add it to your question. If you haven't, then the root cause of your issue is just that, that you're calling the "orders_books" method but it is not yet defined
Given that you reference "#books_orders" in your question, I believe it likely that you just swapped "books_orders" and "orders_books" at some point in your code
Thanks. It's bug of a Rails 4.1.1. I have update it to 4.1.4 and all works OK.