Stay Ahead in Ruby!
From development processes to the most useful gems, get it all straight in your inbox. Join our Ruby blog today!

Skip to main content

Active record uniqueness validations with a given scope

Attention! This article might be outdated, refer to latest documentation if solution does not work.
Active record uniqueness validations with a given scope - cover image

When are uniqueness validations used? #

Web apps are used to solve different complex everyday issues, such as purchases at online web stores, table bookings at a restaurant or a co-working space. For the application to function properly, only comprehensively agreed and verified data should be stored and displayed.

Sometimes there is a need to set uniqueness constraints in the relationship between different objects. Active record validations are a convenient and readable way to set association uniqueness constraints at the model level of the rails app.

Rails validations “are database agnostic, cannot be bypassed by end users, and are convenient to test and maintain.”

Examples #

Let’s say there is a need to create a time management app. In this case, we would have a user who would like to set tasks daily.

class User < ApplicationRecord
  has_many :tasks
end
class Task < ApplicationRecord
  belongs_to :user
end

Also we would like to help user distinguish tasks with different priority so that the user can only have one highly prioritized task per day. Rails uniqueness validation with :scope option can help us achieve that.

class Task < ApplicationRecord
  belongs_to :user

  validates :priority, uniqueness: { scope: [:user_id, :day] }, if: -> { priority == 3 }
end

What if we were to create an application for booking airline tickets? It would be nice to set up validation so that users can’t book a seat if it’s already booked for that date.

class Flight < ApplicationRecord
  has_many :air_ticket_bookings
end

class User < ApplicationRecord
  has_many :air_ticket_bookings
end
class AirTicketBooking < ApplicationRecord
  belongs_to :flight
  belongs_to :user

  validates :flight_id, uniqueness: { scope: [:seat_number, :date] }
end

It will not be amiss to set the same restrictions at the database level:

class AddUserPriorityUniqIndexToTasks < ActiveRecord::Migration[7.0]
  def change
    add_index :tasks, [:priority, :user_id, :day], unique: true, where: 'priority = 3', name: 'idx_task_priority_user_uniq'
  end
end

class AddUniqIndexToAirTicketBookings < ActiveRecord::Migration[7.0]
  def change
    add_index :air_ticket_bookings, [:flight_id, :seat_number, :date], unique: true, name: 'idx_flight_air_ticket_uniq'
  end
end

Errors handling #

You can also pass a message option which can later be displayed in your controller’s flash window.

...
validates :priority, uniqueness: { scope: [:user_id, :day],
                                   message: 'only one highly prioritized task per day' }, if: -> { priority == 3 }
...

...
validates :flight_id, uniqueness: { scope: [:seat_number, :date],
                                    message: 'the seat is already booked for this date' }
...

We are ready to provide expert's help with your product
or build a new one from scratch for you!

Contact MobiDev’s tech experts!
MobiDev Ruby Team
Author
Petro Halimurka
Ruby Developer. Loves ruby for its expressiveness. Likes to learn new technologies and features.

comments powered by Disqus