Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
March 16, 2021 01:00 pm GMT

Rails validations: unique within a certain scope

Its a great idea to make your database and application validations match. If you have validates :name, presence: true in your model, you should pair it with a not null database constraint. Unique validations should be paired with a UNIQUE database index.

In real-world applications, you often have more complicated validations, but you should continue this practice whenever you can.

Something I encounter regularly is the need to have records that are unique, but within a certain scope.

Imagine you were building a typical project management tool. You might want Projects to have a unique name so they can be distinguished within your UI but you dont want the name to be globally unique. If I make a project called Onboarding, another customer should not be restricted from using that name as well.

Luckily, Rails has got us covered with a handy feature called validation scopes.

Usage

The scope option to the Rails uniqueness validation rule allows us to specify additional columns to consider when checking for uniqueness.

class Project < ApplicationRecord  belongs_to :account  has_many :tasks  validates :name, presence: true, uniqueness: { scope: :account_id }end

This rule says that the name of this project must unique, within the scope of this account. In other words, the combination of a name and account_id must be unique but you can have projects with the same name in different accounts.

As we discussed earlier, you really want to back-up your application level validations with database constraints.

In this case, youll want to do a multiple column index. You can do this in a normal Rails migration.

class CreateProject < ActiveRecord::Migration[6.0]  def change    create_table :projects do |t|      ...    end    add_index :projects, [:name, :account_id], unique: true  endend

Options

You can pass multiple columns to scope.

If you were building a dining app and wanted to enforce that a guest could only have one reservation at a restaurant per day.

class Reservation < ApplicationRecord  belongs_to :guest  belongs_to :restaurant  validates :guest_id, uniqueness: {    scope: [:restaurant_id, :reservation_date]  }end

You may wish to change the message since the defaults error message will be fairly spartan: {field} has already been taken

validates :guest_id, uniqueness: {  scope: [:restaurant_id, :reservation_date],  message: "Only one reservation per guest per day is permitted"}

Note: In PostgreSQL, the default limit for index names is 63 characters so you may find yourself needing to change the index name if your model or column names are longer.

add_index :reservations, [:guest_id, :restaurant_id, :reservation_date],  unique: true,  name: "idx_reserveration_guest_date_uniq"

Additional Resources

Rails API: Uniqueness Validations

PostgreSQL Docs: Postgres Constraints

MySql Docs: Multi-column Indexes


Original Link: https://dev.to/swanson/rails-validations-unique-within-a-certain-scope-41p2

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To