2013-07-07 40 views
11

Sử dụng Django 1.5.1. Python 2.7.3.Django độc đáo cùng nhau hạn chế thất bại?

Tôi muốn thực hiện một ràng buộc duy nhất với nhau bằng một trường khóa ngoài và một trường slug. Vì vậy, trong meta mô hình của tôi, tôi đã làm

foreign_key = models.ForeignKey("self", null=True, default=None) 
slug = models.SlugField(max_length=40, unique=False) 

class Meta: 
    unique_together = ("foreign_key", "slug") 

Tôi thậm chí đã kiểm tra mô tả bảng trong Postgres (9.1) và ràng buộc được đưa vào bảng cơ sở dữ liệu.

-- something like 
"table_name_foreign_key_id_slug_key" UNIQUE CONSTRAINT, btree (foreign_key_id, slug) 

Tuy nhiên, tôi vẫn có thể lưu vào bảng cơ sở dữ liệu ngoại tuyến của chuỗi Không có/null và trùng lặp.

Ví dụ,

tôi đầu vào và có thể tiết kiệm

# model objects with slug="python" three times; all three foreign_key(s) 
# are None/null because that is their default value 
MO(slug="python").save() 
MO(slug="python").save() 
MO(slug="python").save() 

Vì vậy, sau khi sử dụng unique_together, tại sao tôi có thể vẫn đầu vào ba trong số hàng có giá trị giống nhau không?

Tôi chỉ đoán là có thể phải làm với giá trị mặc định là Không cho trường ngoại lệ, bởi vì trước unique_together, khi tôi chỉ có duy nhất = True trên slug, mọi thứ đều hoạt động tốt. Vì vậy, nếu đó là trường hợp, những gì giá trị mặc định tôi nên có mà chỉ ra một giá trị null, nhưng cũng duy trì các ràng buộc duy nhất?

Trả lời

16

Trong Postgresql NULL không bằng bất kỳ NULL nào khác. Do đó các hàng bạn tạo ra không giống nhau (từ quan điểm của Postgres).

Cập nhật

Bạn có một vài cách để đối phó với nó:

  • Cấm giá trị Null cho chính nước ngoài và sử dụng một số giá trị mặc định
  • Override các save phương pháp mô hình của bạn để kiểm tra xem không có hàng nào tồn tại
  • Thay đổi tiêu chuẩn SQL :)
+1

làm cách nào để sửa lỗi này? Có một giá trị tôi có thể gán cho lĩnh vực khóa ngoại hoặc một cái gì đó như duy nhất trên slug nếu khóa nước ngoài là NULL? – Derek

+0

Vui lòng xem cập nhật. – Yossi

+0

Có một giá trị mặc định cho khóa ngoại trong bưu phí sao cho giá trị sẽ không bao giờ được sử dụng một cách tự nhiên? – Derek

1

Thêm phương thức clean vào mô hình của bạn để bạn có thể chỉnh sửa hàng hiện tại.

def clean(self): 
    queryset = MO.objects.exclude(id=self.id).filter(slug=self.slug) 
    if self.foreign_key is None: 
     if queryset.exists(): 
      raise ValidationError("A row already exists with this slug and no key") 
    else: 
     if queryset.filter(foreign_key=self.foreign_key).exists(): 
      raise ValidationError("This row already exists") 

Hãy coi chừng, clean (hoặc full_clean) không được gọi bởi phương pháp mặc định save.

NB: nếu bạn đặt mã này theo phương thức save, biểu mẫu cập nhật (như trong quản trị viên) sẽ không hoạt động: bạn sẽ gặp lỗi traceback do ngoại lệ ValidationError.

0

Chỉ cần tự tạo chỉ số thứ trên slug lĩnh vực, nhưng chỉ cho giá trị NULL trong foreign_key_id:

CREATE INDEX table_name_unique_null_foreign_key 
    ON table_name (slug) WHERE foreign_key_id is NULL 

Xin lưu ý, rằng Django không hỗ trợ này, vì vậy mà không hình thức tùy chỉnh/xác nhận mô hình mà bạn sẽ nhận được IntegrityError tinh khiết/500.

có thể trùng lặp của Create unique constraint with null columns

0

Như hobbyte đề cập, "Trong PostgreSQL NULL không bằng bất kỳ NULL khác. Vì vậy các hàng bạn tạo ra không giống nhau (từ quan điểm Postgres')."

Một cách khác có thể để giải quyết thách thức này là thêm xác thực tùy chỉnh ở cấp chế độ xem trong phương thức form_valid.

Trong views.py:

def form_valid(self, form): 
 

 
    --OTHER VALIDATION AND FIELD VALUE ASSIGNMENT LOGIC-- 
 

 
    if ModelForm.objects.filter(slug=slug,foreign_key=foreign_key: 
 
    form.add_error('field', 
 
     forms.ValidationError(_("Validation error message that shows up in your form. "), 
 
     code='duplicate_row',)) 
 
    return self.form_invalid(form)

Cách tiếp cận này là hữu ích nếu bạn đang sử dụng quan điểm giai cấp dựa, đặc biệt là nếu bạn đang tự động gán giá trị cho các lĩnh vực mà bạn muốn ẩn từ người dùng.

Ưu điểm:

  • Bạn không cần phải tạo ra các giá trị mặc định dummy trong cơ sở dữ liệu
  • Bạn vẫn có thể sử dụng các hình thức cập nhật (xem câu trả lời Toff của)

Nhược điểm: - Đây không bảo vệ chống lại các hàng trùng lặp được tạo trực tiếp ở cấp cơ sở dữ liệu. - Nếu bạn sử dụng phụ trợ quản trị của Django để tạo đối tượng MyModel mới, bạn sẽ cần phải thêm cùng một logic xác thực này vào biểu mẫu quản trị viên của mình.

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