178

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

user_id: 10 | friend_id: 20 
user_id: 10 | friend_id: 20 
+7

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

+2

thử sử dụng "validates_uniqueness_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 mà bạn có thể tạo ra một sự di trú của các feild, bao gồm một câu lệnh như add_index: table, [: column_a,: column_b],: unique => true) –

+1

@HarryJoy, anh ta hỏi ' Có một cách đường ray'. Và bạn cung cấp cho anh ta cách không rails, nhưng tiêu chuẩn. 'Cách hoạt động ghi lại rằng trí thông minh thuộc về các mô hình của bạn, không phải trong cơ sở dữ liệu.' – Green

Trả lời

306

Bạn có thể gọi số validates_uniqueness_of như sau.

validates_uniqueness_of :user_id, :scope => :friend_id 
+78

Chỉ muốn thêm rằng bạn có thể vượt qua nhiều tham số phạm vi trong trường hợp bạn cần phải xác thực tính duy nhất trên hơn 2 trường. I E. : scope => [: friend_id,: group_id] –

+25

Thật lạ khi 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

+11

Alexey, validates_uniqueness_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à nó được ghi lại và hành vi mong đợi –

32

Bạn có thể cần những ràng buộc thực sự trên db, bởi vì xác thực bị điều kiện chủng tộc.

validates_uniqueness_of :user_id, :scope => :friend_id 

Khi bạn duy trì một cá thể người dùng, Rails sẽ xác thực mô hình của bạn bằng cách chạy truy vấn SELECT để xem có bất kỳ hồ sơ người dùng nào đã tồn tại với user_id được cung cấp không. Giả sử hồ sơ chứng minh là hợp lệ, Rails sẽ chạy câu lệnh INSERT để tiếp tục người dùng. Điều này rất hữu ích nếu bạn đang chạy một cá thể đơn lẻ của một máy chủ web/quy trình đơn lẻ.

Trong trường hợp hai quy trình/chủ đề đang cố tạo người dùng có cùng user_id cùng một lúc, tình huống sau có thể phát sinh. Race condition with validates

Với chỉ mục duy nhất trên db tại chỗ, tình huống trên sẽ phát ra như sau. Unique indexes on db

trả lời lấy từ bài viết trên blog này - http://robots.thoughtbot.com/the-perils-of-uniqueness-validations

+3

Và làm thế nào để chúng ta đạt được các ràng buộc phía DB (của sự hạn chế duy nhất giữa nhiều cột) với hệ thống di chuyển Rails? –

+0

http://stackoverflow.com/questions/4123610/how-to-implement-a-unique-index-on-two-columns-in-rails –

+0

@LeeHanKyeol để làm điều đó bạn có thể làm điều gì đó như def change add_index : người dùng,: email, duy nhất: đúng kết thúc kết thúc –

113

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

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

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

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

Tuy nhiên, các phương pháp xác thực được hiển thị ở trên có điều kiện chủng tộc 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 cho là duy nhất bởi n trường;

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

  3. mỗi quy trình song song xác thực nếu có bản ghi với cùng các trường n;

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

Để tránh loại hành vi, ta nên thêm một độc đáo hạn để db bảng.Bạn có thể đặt nó với add_index helper cho một (hoặc nhiều) lĩnh vực (s) bằng cách chạy di cư sau:

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

Caveat: ngay cả sau khi bạn đã thiết lập một hạn chế duy nhất, hai hoặc nhiều yêu cầu đồng thời sẽ cố gắng viết cùng một dữ liệu để db, nhưng thay vì tạo hồ sơ trùng lặp, điều này sẽ nâng cao một ActiveRecord::RecordNotUnique ngoại lệ, mà bạn nên xử lý riêng biệt:

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

Cảm ơn bạn !! – nuc

0

DB Hạn chế:

add_index :friendships, [:user_id, :friend_id], unique: true

Các vấn đề liên quan