Active record uniqueness validations with a given scope
Table of Contents
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!