I have a User class, and a Contact where Contact is a subclass of User. Both classes are stored in a users table.
My Contacts may or may not have an email address, while an email address is required for my Users (I have validates_presence_of :email in my User model definition).
My reasoning is that Contacts are entered by users and may later become Users when they claim their profile.
First of all, is it ok to define my
users and contacts the way I did it?
Second, how do I skip the
validate_presence_of email
validation in my contacts model?
(I'm on rails 2.3.8)
Thanks!
UPDATE:
It seems Single Table Inheritance is
designed to do exactly what I needed
the right way to skip validation for
the presence of email for my Contact
table is as follow:
validates_presence_of :email, :unless => Proc.new {|user| user.type == "Contact"}
It sounds like you should abstract out User and Contacts into two tables instead of trying to consolidate them into one. Although contacts can become users, that doesn't mean that they will (I think?).
This would also solve your validate_presence_of :email question, as the contact table/model wouldn't even have the field. It would also alleviate potential performance concerns later on, I believe. You wouldn't want to have a ton of contacts to sort through to find a registered user.
If you're dead-set on doing it in one table though, I believe you can do something like the following:
validates_presence_of :email, :unless => Proc.new {|user| user.type == "Contact"}
This is assuming that you have a user_type column, but you could replace that depending on how you're determining whether a User is a Contact.
Update:
This is how you'd correctly validate the models: Remove the validates_presence_of from the model and place it inside of this block:
with_options :unless => :user_type == "contact" do |user|
user.validates_presence_of :email
end
Related
I have a Company model and an Employer model. Employer belongs_to :company and Company has_many :employers. Within my Employer model I have the following validation:
validates :company_id, inclusion: {in: Company.pluck(:id).prepend(nil)}
I'm running into a problem where the above validation fails. Here is an example setup in a controller action that will cause the validation to fail:
company = Company.new(company_params)
# company_params contains nested attributes for employers
company.employers.each do |employer|
employer.password = SecureRandom.hex
end
company.employers.first.role = 'Admin' if client.employers.count == 1
company.save!
admin = company.employers.where(role: 'Admin').order(created_at: :asc).last
admin.update(some_attr: 'some_val')
On the last line in the example code snippet, admin.update will fail because the validation is checking to see if company_id is included in the list, which it is not, since the list was generated before company was saved.
Obviously there are ways around this such as grabbing the value of company.id and then using it to define admin later, but that seems like a roundabout solution. What I'd like to know is if there is a better way to solve this problem.
Update
Apparently the possible workaround I suggested doesn't even work.
new_company = Company.find(company.id)
admin = new_company.employers.where(role: 'Admin').order(created_at: :asc).last
admin.update
# Fails validation as before
I'm not sure I understand your question completely, but there is an issue in this part of the code:
validates :company_id, inclusion: {in: Company.pluck(:id).prepend(nil)}
The validation is configured on the class-level, so it won't work well with updates on that model (won't be re-evaluated on subsequent validations).
The docs state that you can use a block for inclusion in, so you could try to do that as well:
validates :company_id, inclusion: {in: ->() { Company.pluck(:id).prepend(nil) }}
Some people would recommend that you not even do this validation, but instead, have a database constraint on that column.
I believe you are misusing the inclusion validator here. If you want to validate that an associated model exists, instead of its id column having a value, you can do this in two ways. In ActivRecord, you can use a presence validator.
validates :company, presence: true
You should also use a foreign key constraint on the database level. This prevents a model from being saved if there is no corresponding record in the associated table.
add_foreign_key :employers, :companies
If it gets past ActiveRecord, the database will throw an error if there is no company record with the given company_id.
What is the best way to adjust your validation of a model based on a parameter or action? Say I am entering a lead into a system, so all that is required is basic contact info. But then later I may enter a new contract for that user at which point I need more detailed information. So I may need phone number when just entering a lead but once they are entering into a contract I might need their birthdate and alternate phone number.
I considered using a state machine but the user could actually enter into two contracts at the same time so state doesn't really work for this scenario.
I also considered storing the extra info with the contract but since the user can have more than one contract, it needs to live with the user so it is not redundant.
So basically when saving a contract, it would tell me that the attached user is invalid if said user doesn't have the extra fields.
Check out conditional validations:
class Person
validates_presence_of :given_name, family_name
validates_presence_of :phone_number, :email_address, :if => :registered
with_options :if => :registered do |person|
# validations in this block are scoped to a registered user
person.validates_presence_of :dob
end
end
The :if option can take:
a symbol that corresponds to a method on the class that evaluates to true or false
a proc or lambda that returns a value that evaluates to true or false
a string containing ruby code (god knows why you'd want to do that)
You also have access to an :unless option which works in a similar fashion.
You can also encapsulate the logic to determine the current state of the user and use that to determine what validation steps you can take:
class Person
validates_presence_of :email_address, :if => ->(p) { p.state == :pending_confirmation }
# I actually prefer validations in this format
validate do # stricter validations when user is confirming registration
if confirming_membership && (given_name.blank? || family_name.blank?
errors.add(:base, 'A full name is required')
end
end
def state
# your logic could be anything, this is just an example
if self.confirmed_email
:registered
elsif confirming_membership
:pending_confirmation
else
:new_user
end
end
def confirming_membership
# some logic
end
end
You can use conditional validation for example:
validates_presence_of :phone, :if => Proc.new { |p| p.lead? }
In whatever action the lead posts to, you could just do this:
#object.save(validate: false)
Then, when they need to enter the contract, leave off that validate: false option to ensure that those validations run.
Also, see this post if you want to skip only certain validations.
I have a model, let's call it Book. In rails, when the Book is saved, I validate the uniqueness of it's ISBN number. For my front end, I have a simple SpineJS app which let's me add a new book.
In SpineJS:
class App.Book extends Spine.Model
#configure 'Book', 'name', 'isbn'
#extend Spine.Model.Ajax
validate: ->
"Name required" unless #name
"ISBN required" unless #isbn
And in Rails:
class Book < ActiveRecord::Base
attr_accessible :name, :isbn
validates :name, :presence => true
validates :isbn. :presence => true, :uniqueness => true
end
My issue is that in my SpineJS app, it happily saves a new book with a duplicate ISBN number, even though the Rails server returns validation errors.
Is there some way to handle this error client-side on save?
Spine's manual claims:
If validation does fail server-side, it's an error in your client-side validation logic rather than with user input.
I don't see how this could easily work with your uniqueness requirement. It may be workable if you can load all database data that could affect validation into the client side and you can somehow avoid all multi-user race conditions.
You can catch the "ajaxError" event and tell the user to retry, although catching "ajaxError" goes against the recommendations in the manual. IIRC you may also have to do some juggling with object IDs to convince Spine that a new record was in fact not created.
Additionally, you can fire pre-emptive validation requests as the user is editing the data, but this is just for the user's convenience. In theory you can still hit a race condition where someone else creates a conflicting record just before the user hits save.
Personally I switched to Backbone since I found Spine's careless attitude to error handling too scary.
I have a form that displays inputs for 2 models, I am using accepts_nested_attributes_for.
In my main model that has the accepts_nested_attributes_for, it looks like:
class Account <
accepts_nested_attributes_for :primary_user ...
Now in my form, I have a form_for on the #account, and then have fields_for the primary_user model.
If I hit submit, for some reason all the errors for the primary_user are displayed first. I would rather have the errors display in the same order as the input fields on the web page.
Is this possible to re-order them according how they are ordered in my form_for?
Also, the error messages have 'primary username cannot be black', is it possible for me to change it to 'username cannot be blank'? I don't really need to confuse the end user with the word 'primary' as it really doesn't make sense to them, its more of an internal thing.
Not sure about the re-ordering but you can change the message for a model validations as follows:
validates :username, presence: { message: "Username cannot be blank" }
Pretty new at all this. I have a simple form for users to enter a couple pieces of information and then input their email address and push the submit button. I want to make it mandatory that they have to fill out their email address in order to push the submit button. If they don't fill out their email address they should get an error message on the email box that says the email can't be blank. I know this is super simple but I need exact help on where to put what code. I've been researching this all night and know that part of the code should go in the application_controller and other should go in the html file where the actual text_field:email is.
I'd be grateful if someone could clearly tell me what the necessary steps are for doing this. Thanks!
It should go in your model. Add this:
class Model < ActiveRecord::Base
validates_presence_of :email
end
Check this link for more info: http://guides.rails.info/activerecord_validations_callbacks.html#validates-presence-of
In Rails 2, which I would assume you are using, validations go in the model. Which is located in $Rails_app_directory/app/model/$Classname.rb
In order to add ActiveRecord validations you can use the line
validates_presence_of :email_address
You should also consider using Rails to generate a confirmation field and filtering out ill-formatted email addresses. You could accomplish the former with:
validates_confirmation_of :email_address
with this, all you need to add to your form is a text_field for :email_address_confirmation
and the latter with a regular expression such as:
validates_format_of :email_address, :with => /\A[\w\.%\+\-]+#(?:[A-Z0-9\-]+\.)+(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|jobs|museum)\z/i
From snipplr, place in your model
validates_format_of :email,
:with => /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i,
:message => 'email must be valid'