How to create unique index skipping empty values in Rails + Postgres?

Having unique column in rails is easy thing to do. But what if you want to have unique column skipping null values?

Problem

Let’s imagine we have application, where invite people to your organisation by sending them invitation code. These s…

Having unique column in rails is easy thing to do. But what if you want to have unique column skipping null values?

Problem

Let’s imagine we have application, where invite people to your organisation by sending them invitation code. These should be unique for every organisation, and empty in case we did not generated it yet.

For that let’s create organisation model and migration with invitation_code field.

class CreateOrganizations < ActiveRecord::Migration[7.0]
  def change
    create_table :organizations do |t|
      t.string :invitation_code, null: true
    end
  end
end

That’s great for start. Now let’s create unique index for invitation_code:

add_index :organizations, :invitation_code, unique: true

And add validation in model:

validates :invitation_code, uniqueness: true

Okay, let’s see the result:

# for non-empty invitation_code
irb(main):004:0> org = Organization.create(invitation_code: "f00b4r")
=> #<Organization id: "a8dc7fd8-7724-445c-bed4-72c94af99151", invitation_code: "f00b4r">       
irb(main):005:0> org = Organization.create(invitation_code: "f00b4r")
=> #<Organization id: nil, invitation_code: "f00b4r">
irb(main):006:0> org.errors.messages
=> {:invitation_code=>["has already been taken"]}

It’s unique for non-empty values, but for empty?

# for empty invitation_code
irb(main):001:0> Organization.create()
=> #<Organization id: nil, invitation_code: nil>
irb(main):002:0> org = Organization.create()
=> #<Organization:0x0000ffff897f2a40 id: nil, invitation_code: nil>
irb(main):003:0> org.errors.messages
=> {:invitation_code=>["has already been taken"]}

Well… as in migration we allow invitation_code to be null, we will have an db error, when creating new record. For that we need to modify our migration.

Solution

We need to modify creating an index as well as model validation.

Let’s take a focus on migration first. We need to change just creating index with where statement, so it’s unique in scope of non null values.

add_index :organizations, :invitation_code,
  unique: true,
  where: 'invitation_code IS NOT NULL',
  name: 'unique_not_null_invitation_code'

Great, we’re all set with db. Now rails model:

validates :invitation_code, uniqueness: { allow_blank: true }

And it all set. 🎉

Test

Let’s run migration and quickly test our code in console:

# for empty invitation_code
irb(main):001:0> Organization.create()
=> #<Organization id: "88174146-19c6-40e6-b675-ea04c3e4238f", invitation_code: nil>
irb(main):002:0> Organization.create()
=> #<Organization id: "61d630d1-d244-47e4-af46-880a021a26ca", invitation_code: nil> 

Great, two organisation created with null invitation_code. Now try with non-empty code:

# for non-empty invitation_code
irb(main):003:0> Organization.create(invitation_code: "f00b4r")
=> #<Organization id: "8b2ed6e7-76db-4f76-9288-69267ebca5f7", invitation_code: "f00b4r">
irb(main):004:0> org=Organization.create(invitation_code: "f00b4r")
=> #<Organization id: nil, invitation_code: "f00b4r">
irb(main):005:0> org.errors.messages
=> {:invitation_code=>["has already been taken"]}

Works like a charm. Happy hacking! 🧑‍💻


Print Share Comment Cite Upload Translate
APA
Jędrzej Urbański | Sciencx (2024-03-29T13:29:47+00:00) » How to create unique index skipping empty values in Rails + Postgres?. Retrieved from https://www.scien.cx/2022/10/10/how-to-create-unique-index-skipping-empty-values-in-rails-postgres/.
MLA
" » How to create unique index skipping empty values in Rails + Postgres?." Jędrzej Urbański | Sciencx - Monday October 10, 2022, https://www.scien.cx/2022/10/10/how-to-create-unique-index-skipping-empty-values-in-rails-postgres/
HARVARD
Jędrzej Urbański | Sciencx Monday October 10, 2022 » How to create unique index skipping empty values in Rails + Postgres?., viewed 2024-03-29T13:29:47+00:00,<https://www.scien.cx/2022/10/10/how-to-create-unique-index-skipping-empty-values-in-rails-postgres/>
VANCOUVER
Jędrzej Urbański | Sciencx - » How to create unique index skipping empty values in Rails + Postgres?. [Internet]. [Accessed 2024-03-29T13:29:47+00:00]. Available from: https://www.scien.cx/2022/10/10/how-to-create-unique-index-skipping-empty-values-in-rails-postgres/
CHICAGO
" » How to create unique index skipping empty values in Rails + Postgres?." Jędrzej Urbański | Sciencx - Accessed 2024-03-29T13:29:47+00:00. https://www.scien.cx/2022/10/10/how-to-create-unique-index-skipping-empty-values-in-rails-postgres/
IEEE
" » How to create unique index skipping empty values in Rails + Postgres?." Jędrzej Urbański | Sciencx [Online]. Available: https://www.scien.cx/2022/10/10/how-to-create-unique-index-skipping-empty-values-in-rails-postgres/. [Accessed: 2024-03-29T13:29:47+00:00]
rf:citation
» How to create unique index skipping empty values in Rails + Postgres? | Jędrzej Urbański | Sciencx | https://www.scien.cx/2022/10/10/how-to-create-unique-index-skipping-empty-values-in-rails-postgres/ | 2024-03-29T13:29:47+00:00
https://github.com/addpipe/simple-recorderjs-demo