Active Record uses the new_record? instance method to determine whether an object is already in the database or not.
The following methods trigger validations, and will save the object to the database only if the object is valid:
- create
- create!
- save
- save!
- update
- update_attributes
- update_attributes!
The bang versions (e.g. save!) raise an exception if the record is invalid. The non-bang versions don’t:
save and update_attributes return false, create and
update just return the objects.
The following methods skip validations, and will save the object to the database regardless of its validity. They should be used with caution.
- decrement!
- decrement_counter
- increment!
- increment_counter
- toggle!
- update_all
- update_attribute
- update_counters
Note that save also has the ability to skip validations if passed
:validate => false as argument. This technique should be used with caution.
- save(:validate => false)
Note that an object instantiated with new will not report errors even if it’s technically invalid, because validations are not run when using
new.
Validation Helpers
validates_acceptance_of
This validation is very specific to web applications and this ‘acceptance’ does not need to be recorded anywhere in your database (if you don’t have a field for it, the helper will just create a virtual attribute).
validates_acceptance_of can receive an :accept option, which determines the value that will be considered acceptance. It defaults to “1”, but you can change this.
class Person < ActiveRecord::Base validates_acceptance_of :terms_of_service, :accept => 'yes' end
validates_associated
class Library < ActiveRecord::Base has_many :books validates_associated :books end
class Person < ActiveRecord::Base validates_confirmation_of :email end
validates_confirmation_of
class Person < ActiveRecord::Base validates_confirmation_of :email end
<%= text_field :person, :email %> <%= text_field :person, :email_confirmation %>
validates_exclusion_of
class Account < ActiveRecord::Base validates_exclusion_of :subdomain, :in => %w(www), :message => "Subdomain %{value} is reserved." end
This example uses the :message option to show how you can include the attribute’s value.
validates_format_of
class Product < ActiveRecord::Base validates_format_of :legacy_code, :with => /\A[a-zA-Z]+\z/, :message => "Only letters allowed" end
validates_inclusion_of
class Coffee < ActiveRecord::Base validates_inclusion_of :size, :in => %w(small medium large), :message => "%{value} is not a valid size" end
validates_length_of
- :minimum – The attribute cannot have less than the specified length.
- :maximum – The attribute cannot have more than the specified length.
- :in (or :within) – The attribute length must be included in a given interval. The value for this option must be a range.
- :is – The attribute length must be equal to the given value.
:wrong_length, :too_long, and :too_short
class Person < ActiveRecord::Base validates_length_of :bio, :maximum => 1000, :too_long => "%{count} characters is the maximum allowed" end
validates_numericality_of
class Player < ActiveRecord::Base validates_numericality_of :points validates_numericality_of :games_played, :only_integer => true end
* :greater_than * :greater_than_or_equal_to * :equal_to * :less_than * :less_than_or_equal_to * :odd * :even
validates_presence_of
If you want to be sure that an association is present, you’ll need to test whether the foreign key used to map the association is present, and not the associated object itself.
class LineItem < ActiveRecord::Base belongs_to :order validates_presence_of :order_id end
Since false.blank? is true, if you want to validate the presence of a boolean field you should use
validates_inclusion_of :field_name, :in => [true, false].
validates_uniqueness_of
class Holiday < ActiveRecord::Base validates_uniqueness_of :name, :scope => :year, :message => "should happen once per year" end
class Person < ActiveRecord::Base validates_uniqueness_of :name, :case_sensitive => false end
validates_with
class Person < ActiveRecord::Base validates_with GoodnessValidator end class GoodnessValidator < ActiveModel::Validator def validate if record.first_name == "Evil" record.errors[:base] << "This person is evil" end end end
The validator class has two attributes by default:
- record – the record to be validated
- options – the extra options that were passed to validates_with
class Person < ActiveRecord::Base validates_with GoodnessValidator, :fields => [:first_name, :last_name] end class GoodnessValidator < ActiveRecord::Validator def validate if options[:fields].any?{|field| record.send(field) == "Evil" } record.errors[:base] << "This person is evil" end end end
validates_each
It doesn’t have a predefined validation function. You should create one using a block, and every attribute passed to
validates_each will be tested against it. In the following example, we don’t want names and surnames to begin with lower case.
class Person < ActiveRecord::Base validates_each :name, :surname do |model, attr, value| model.errors.add(attr, 'must start with upper case') if value =~ /\A[a-z]/ end end
Common Validation Options
:allow_nil
class Coffee < ActiveRecord::Base validates_inclusion_of :size, :in => %w(small medium large), :message => "%{value} is not a valid size", :allow_nil => true end
:allow_blank
:message
:on
The default behavior for all the built-in validation helpers is to be run on save (both when you’re creating a new record and when you’re updating it)
Conditional Validation
Using a Symbol with :if and
:unless
class Order < ActiveRecord::Base validates_presence_of :card_number, :if => :paid_with_card? def paid_with_card? payment_type == "card" end end
Using a String with :if and
:unless
You can also use a string that will be evaluated using eval and needs to contain valid Ruby code.
class Person < ActiveRecord::Base validates_presence_of :surname, :if => "name.nil?" end
Using a Proc with :if and :unless
class Account < ActiveRecord::Base validates_confirmation_of :password, :unless => Proc.new { |a| a.password.blank? } end
Creating Custom Validation Methods
You must then register these methods by using one or more of the validate,
validate_on_create or validate_on_update class methods, passing in the symbols for the validation methods’ names.
You can pass more than one symbol for each class method and the respective validations will be run in the same order as they were registered.
class Invoice < ActiveRecord::Base validate :expiration_date_cannot_be_in_the_past, :discount_cannot_be_greater_than_total_value def expiration_date_cannot_be_in_the_past errors.add(:expiration_date, "can't be in the past") if !expiration_date.blank? and expiration_date < Date.today end def discount_cannot_be_greater_than_total_value errors.add(:discount, "can't be greater than total value") if discount > total_value end end
You can even create your own validation helpers and reuse them in several different models.
ActiveRecord::Base.class_eval do def self.validates_as_choice(attr_name, n, options={}) validates_inclusion_of attr_name, {:in => 1..n}.merge(options) end end
Simply reopen ActiveRecord::Base and define a class method like that. You’d typically put this code somewhere in
config/initializers. You can use this helper like this:
class Movie < ActiveRecord::Base validates_as_choice :rating, 5 end
Working with Validation Errors
Of course, calling errors.clear upon an invalid object won’t actually make it valid: the
errors collection will now be empty, but the next time you call valid? or any method that tries to save this object to the database, the validations will run again. If any of the validations fail, the
errors collection will be filled again.
<%= form_for(@product) do |f| %> <%= f.error_messages %> <p> <%= f.label :description %><br /> <%= f.text_field :description %> </p> <p> <%= f.label :value %><br /> <%= f.text_field :value %> </p> <p> <%= f.submit "Create" %> </p> <% end %>
<%= error_messages_for :product %>
<%= f.error_messages :header_message => "Invalid product!", :message => "You'll need to fix the following fields:", :header_tag => :h3 %>
- .field_with_errors – Style for the form fields and labels with errors.
- #errorExplanation – Style for the div element with the error messages.
- #errorExplanation h2 – Style for the header of the div element.
- #errorExplanation p – Style for the paragraph that holds the message that appears right below the header of the
div element. - #errorExplanation ul li – Style for the list items with individual error messages.
Scaffolding for example generates public/stylesheets/scaffold.css, which defines the red-based style you saw above.
Customizing the Error Messages
HTML
The way form fields with errors are treated is defined by ActionView::Base.field_error_proc. This is a
Proc that receives two parameters:
- A string with the HTML tag
- An instance of ActionView::Helpers::InstanceTag.
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance| if instance.error_message.kind_of?(Array) %(#{html_tag}<span class="validation-error"> #{instance.error_message.join(',')}</span>).html_safe else %(#{html_tag}<span class="validation-error"> #{instance.error_message}</span>).html_safe end end
Callback Registration
class User < ActiveRecord::Base validates_presence_of :login, :email before_validation :ensure_login_has_a_value protected def ensure_login_has_a_value if login.nil? self.login = email unless email.blank? end end end
class User < ActiveRecord::Base validates_presence_of :login, :email before_create {|user| user.name = user.login.capitalize if user.name.blank?} end
10 Available Callbacks
10.1 Creating an Object
- before_validation
- after_validation
- before_save
- after_save
- before_create
- around_create
- after_create
10.2 Updating an Object
- before_validation
- after_validation
- before_save
- after_save
- before_update
- around_update
- after_update
10.3 Destroying an Object
- before_destroy
- after_destroy
- around_destroy
after_initialize can be useful to avoid the need to directly override your Active Record
initialize method.
The after_find callback will be called whenever Active Record loads a record from the database.
after_find is called before after_initialize if both are defined.
They have no before_* counterparts, and the only way to register them is by defining them as regular methods. If you try to register
after_initialize or after_find using macro-style class methods, they will just be ignored. This behaviour is due to performance reasons, since
after_initialize and after_find will both be called for each record found in the database, significantly slowing down the queries.
class User < ActiveRecord::Base def after_initialize puts "You have initialized an object!" end def after_find puts "You have found an object!" end end >> User.new You have initialized an object! => #<User id: nil> >> User.first You have found an object! You have initialized an object! => #<User id: 1>
Running Callbacks
- create
- create!
- decrement!
- destroy
- destroy_all
- increment!
- save
- save!
- save(false)
- toggle!
- update
- update_attribute
- update_attributes
- update_attributes!
- valid?
Additionally, the after_find callback is triggered by the following finder methods:
- all
- first
- find
- find_all_by_attribute
- find_by_attribute
- find_by_attribute!
- last
The after_initialize callback is triggered every time a new object of the class is initialized.
Skipping Callbacks
- decrement
- decrement_counter
- delete
- delete_all
- find_by_sql
- increment
- increment_counter
- toggle
- update_all
- update_counters
class User < ActiveRecord::Base has_many :posts, :dependent => :destroy end class Post < ActiveRecord::Base after_destroy :log_destroy_action def log_destroy_action puts 'Post destroyed' end end >> user = User.first => #<User id: 1> >> user.posts.create! => #<Post id: 1, user_id: 1> >> user.destroy Post destroyed => #<User id: 1>
Conditional Callbacks
the same to validation.
Callback Classes
class PictureFileCallbacks def after_destroy(picture_file) File.delete(picture_file.filepath) if File.exists?(picture_file.filepath) end end
class PictureFile < ActiveRecord::Base after_destroy PictureFileCallbacks.new end
class PictureFileCallbacks def self.after_destroy(picture_file) File.delete(picture_file.filepath) if File.exists?(picture_file.filepath) end end
class PictureFile < ActiveRecord::Base after_destroy PictureFileCallbacks end
You can declare as many callbacks as you want inside your callback classes.
Observers
rails generate observer User
class UserObserver < ActiveRecord::Observer def after_create(model) # code to send confirmation email... end end
Observers are conventionally placed inside of your app/models directory and registered in your application’s
config/application.rb file. For example, the UserObserver above would be saved as
app/models/user_observer.rb and registered in config/application.rb this way:
# Activate observers that should always be running config.active_record.observers = :user_observer
As usual, settings in config/environments take precedence over those in
config/application.rb. So, if you prefer that an observer doesn’t run in all environments, you can simply register it in a specific environment instead.
sharing observers
class MailerObserver < ActiveRecord::Observer observe :registration, :user def after_create(model) # code to send confirmation email... end end