Run different ActiveRecord validations based on context
Tags: ruby, rails, webdev
Published: 2021-03-08 17:05:27

---
title: Run different ActiveRecord validations based on context
published: true
date: 2021-03-08 13:00:00 UTC
tags: ruby,rails,webdev
canonical_url: https://boringrails.com/tips/activerecord-validation-context
---

Sometimes want to skip certain validations on your database models. Maybe you have a multi-step wizard or want admins to have more freedom in changing data.

You might be tempted to have certain forms skip validations, but there is a better way.

Rails allows you to pass in a `context` when saving or validating a record. You can combine `context` with the `on:` option to run only certain ActiveRecord validations.

## Usage

Let’s say you want want to build a multi-step workflow for a job board. You might allow someone to create a job listing and start filling in the data, but not fully validate everything until it is time to publish the listing.

```ruby
class Listing < ApplicationRecord
  belongs_to :company
  belongs_to :user

  has_rich_text :requirements

  validates :title, presence: true, length: { maximum: 50 }

  validates :salary_range, presence: true, on: :publish
  validates :application_instructions, presence: true, on: :publish

  def publish!
    self.published_at = Time.current
    save(context: :publish)
  end
end
```

In this case, we will always require a `title` (with 50 characters max) whenever we create or edit this record. But we will only validate `salary_range` and `application_instructions` when we pass `:publish` as the validation context.

You could implement this workflow with controller actions like:

```ruby
class ListingsController < ApplicationController

  def create
    @listing = Listing.new(listing_params)

    if @listing.save
      redirect_to @listing, notice: "Listing created"
    else
      render :new, status: :unprocessable_entity
    end
  end

  def publish
    @listing = Listing.find(params[:id])

    if @listing.publish!
      redirect_to @listing, notice: "Listing published"
    else
      render :edit, status: :unprocessable_entity
    end
  end
end
```

You can also add validations that are different based on which user is making the change. Maybe you want to allow admins to give special, short usernames to your friends.

Here we can set one rule that requires six character usernames in the `:create` context (which Rails will include by default when creating a record). Then we add a rule in the `:admin` context that only requires three characters.

```ruby
class Account < ApplicationRecord
  validates :username, length: { minimum: 6 }, on: :create
  validates :username, length: { minimum: 3 }, on: :admin
end

Account.new(username: "swanson").valid? # => true
Account.new(username: "swanson").valid?(:admin) # => true

Account.new(username: "mds").valid? # => false
Account.new(username: "mds").valid?(:admin) # => true

Account.new(username: "a").valid? # => false
Account.new(username: "a").valid?(:admin) # => false
```

One downside with using Rails validation contexts is that you may not be able to use database level validations. Being able to persist partially-valid records or have conditional rules is a powerful feature, but it’s not without costs.

Think carefully about moving validations from the database constraint level to your application.

## Additional Resources

Rails Guides: [Validations :on option](https://guides.rubyonrails.org/active_record_validations.html#on)

Rails API Docs: [ActiveRecord::Validations#valid?](https://api.rubyonrails.org/classes/ActiveRecord/Validations.html#method-i-valid-3F)

* * *
[![](https://boringrails.com/images/twitter-banner.png)](https://twitter.com/_swanson)