Xác thực tính duy nhất của nhiều cột


193

Có cách nào để xác thực rằng một bản ghi thực tế là duy nhất và không chỉ là một cột không? Ví dụ: mô hình / bảng tình bạn không thể có nhiều bản ghi giống nhau như:

user_id: 10 | friend_id: 20
user_id: 10 | friend_id: 20

7
tha thứ cho tôi nếu tôi đang dày đặc, nhưng điều đó sẽ giúp gì trong tình huống này?
re5et

2
hãy thử sử dụng "validates_uniquety_of" trong mô hình của bạn. nếu điều này không hoạt động, hãy thử tạo một chỉ mục để bạn có thể tạo một sự di chuyển của các yếu tố bao gồm một câu lệnh như add_index: table, [: cột_a ,: cột_b] ,: unique => true)
Harry Joy

1
@HarryJoy, anh hỏi Is there a rails-way way. Và bạn cung cấp cho anh ta cách không đường ray, nhưng tiêu chuẩn. The Active Record way claims that intelligence belongs in your models, not in the database.
Màu xanh lá cây

2
Thật không may validates :field_name, unique: truelà dễ bị điều kiện chủng tộc, vì vậy mặc dù chống lại đường ray, một ràng buộc thực tế được ưa thích hơn. @HarryJoy Tôi sẽ đưa ra một câu trả lời mô tả cách ràng buộc.
Pooyan Khosravi

1
câu trả lời tốt hơn sau đó tất cả những gì được lưu ý dưới đây là một stackoverflow.com/a/34425284/1612469 vì nó mang đến một lớp khác để đảm bảo mọi thứ sẽ hoạt động chính xác
Aleks

Câu trả lời:


319

Bạn có thể phạm vi một validates_uniqueness_ofcuộc gọi như sau.

validates_uniqueness_of :user_id, :scope => :friend_id

83
Chỉ muốn thêm rằng bạn có thể vượt qua nhiều thông số phạm vi trong trường hợp bạn cần xác thực tính duy nhất trên hơn 2 trường. Tức là: scope => [: friend_id ,: group_id]
Dave Rapin

27
Lạ mà bạn không thể nói validates_uniqueness_of [:user_id, :friend_id]. Có lẽ điều này cần phải được vá?
Alexey

12
Alexey, validates_uniquety_of [: user_id ,: friend_id] sẽ chỉ thực hiện xác thực cho từng trường được liệt kê - và đó là hành vi được ghi lại và dự kiến
Nikita Hismatov 18/03/13

71
Trong Rails 4, điều này trở thành: xác thực: user_id, tính duy nhất: {scope :: friend_id}
Marina Martin

3
Bạn có thể muốn thêm một thông báo lỗi tùy chỉnh như ,: message => 'đã có người bạn này.'
laffuste

137

Bạn có thể sử dụng validatesđể xác nhận uniquenesstrên một cột:

validates :user_id, uniqueness: {scope: :friend_id}

Cú pháp xác thực trên nhiều cột là tương tự nhau, nhưng bạn nên cung cấp một mảng các trường thay thế:

validates :attr, uniqueness: {scope: [:attr1, ... , :attrn]}

Tuy nhiên , các phương pháp xác nhận được trình bày ở trên có một điều kiện cuộc đua và không thể đảm bảo tính nhất quán. Hãy xem xét ví dụ sau:

  1. bản ghi bảng cơ sở dữ liệu được coi là duy nhất bởi n trường;

  2. nhiều ( hai hoặc nhiều ) yêu cầu đồng thời, được xử lý bởi các quy trình riêng biệt ( máy chủ ứng dụng, máy chủ nhân viên nền hoặc bất cứ thứ gì bạn đang sử dụng ), truy cập cơ sở dữ liệu để chèn cùng một bản ghi vào bảng;

  3. mỗi quá trình song song xác nhận nếu có một bản ghi với cùng n trường;

  4. xác thực cho mỗi yêu cầu được thông qua thành công và mỗi quy trình tạo một bản ghi trong bảng có cùng dữ liệu.

Để tránh loại hành vi này, người ta nên thêm một ràng buộc duy nhất vào bảng db. Bạn có thể thiết lập nó với trình add_indextrợ giúp cho một (hoặc nhiều) trường bằng cách chạy di chuyển sau:

class AddUniqueConstraints < ActiveRecord::Migration
  def change
   add_index :table_name, [:field1, ... , :fieldn], unique: true
  end
end

Hãy cẩn thận : ngay cả sau khi bạn đã đặt một ràng buộc duy nhất, hai hoặc nhiều yêu cầu đồng thời sẽ cố gắng ghi cùng một dữ liệu vào db, nhưng thay vì tạo các bản ghi trùng lặp, điều này sẽ đưa ra một ActiveRecord::RecordNotUniquengoại lệ, bạn nên xử lý riêng:

begin
# writing to database
rescue ActiveRecord::RecordNotUnique => e
# handling the case when record already exists
end 

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.