Rails + Postgres에서 빈 값을 건너뛰는 고유 인덱스를 만드는 방법은 무엇입니까?

9621 단어 postgresrailsruby
레일에 고유한 기둥을 갖는 것은 쉬운 일입니다. 하지만 null 값을 건너뛰는 고유한 열을 갖고 싶다면 어떻게 해야 할까요?

문제



초대 코드를 전송하여 사람들을 조직으로 초대하는 애플리케이션이 있다고 가정해 보겠습니다. 이들은 모든 조직에 대해 고유해야 하며 아직 생성하지 않은 경우 비어 있어야 합니다.

이를 위해 invitation_code 필드를 사용하여 조직 모델 및 마이그레이션을 생성해 보겠습니다.

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


시작하기에 좋습니다. 이제 invitation_code에 대한 고유 인덱스를 생성해 보겠습니다.

add_index :organizations, :invitation_code, unique: true


그리고 모델에 유효성 검사를 추가합니다.

validates :invitation_code, uniqueness: true


자, 결과를 보자:

# 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"]}


비어 있지 않은 값에는 고유하지만 비어 있습니까?

# 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"]}


음... 마이그레이션에서 invitation_code가 null이 되도록 허용하므로 새 레코드를 만들 때 db 오류가 발생합니다. 이를 위해서는 마이그레이션을 수정해야 합니다.

해결책



인덱스 생성 및 모델 유효성 검사를 수정해야 합니다.

먼저 마이그레이션에 초점을 맞추겠습니다. null이 아닌 값의 범위에서 고유하도록 where 문으로 인덱스를 생성하는 것만 변경해야 합니다.

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


좋아, 우리는 모두 db로 설정되었습니다. 이제 레일 모델:

validates :invitation_code, uniqueness: { allow_blank: true }


그리고 모든 것이 설정되었습니다. 🎉

테스트



마이그레이션을 실행하고 콘솔에서 코드를 빠르게 테스트해 보겠습니다.

# 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> 


좋습니다. nullinvitation_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"]}


매력처럼 작동합니다. 행복한 해킹! 🧑‍💻

좋은 웹페이지 즐겨찾기