Như đã đề cập trong các nhận xét, lý do "với thiết lập này cần cả hai" chỉ là bạn quên thêm các blank=True
trường FK của mình, do đó ModelForm
(một tùy chỉnh hoặc mặc định do quản trị viên tạo) sẽ khiến trường biểu mẫu được yêu cầu . Ở cấp lược đồ db, bạn có thể điền cả hai, một hoặc không một trong số các FK đó, sẽ ổn thôi vì bạn đã làm cho các trường db đó trở nên không thể (với null=True
đối số).
Ngoài ra, (cf các bình luận khác của tôi), bạn có thể muốn kiểm tra xem FK của bạn có thực sự là duy nhất không. Về mặt kỹ thuật, điều này biến mối quan hệ của bạn thành nhiều mối quan hệ thành một mối quan hệ - bạn chỉ được phép một bản ghi 'kiểm tra' duy nhất cho một GroupID hoặc SiteId nhất định (bạn không thể có hai hoặc nhiều 'kiểm tra' cho một GroupId hoặc SiteId) . Nếu đó thực sự là những gì bạn muốn, bạn có thể muốn sử dụng OneToOneField rõ ràng (lược đồ db sẽ giống nhau nhưng mô hình sẽ rõ ràng hơn và mô tả liên quan có thể sử dụng nhiều hơn cho trường hợp sử dụng này).
Như một lưu ý phụ: trong Mô hình Django, trường ForeignKey cụ thể hóa như một thể hiện mô hình có liên quan, không phải là id thô. IOW, đưa ra điều này:
class Foo(models.Model):
name = models.TextField()
class Bar(models.Model):
foo = models.ForeignKey(Foo)
foo = Foo.objects.create(name="foo")
bar = Bar.objects.create(foo=foo)
sau đó bar.foo
sẽ giải quyết foo
, không để foo.id
. Vì vậy, bạn chắc chắn muốn đổi tên của bạn InspectionID
và SiteID
các lĩnh vực cho phù hợp inspection
và site
. BTW, trong Python, quy ước đặt tên là 'all_lower_with_underscores' cho bất cứ điều gì khác ngoài tên lớp và hằng giả.
Bây giờ cho câu hỏi cốt lõi của bạn: không có cách SQL tiêu chuẩn cụ thể nào để thực thi ràng buộc "cái này hay cái kia" ở cấp cơ sở dữ liệu, do đó, nó thường được thực hiện bằng cách sử dụng một ràng buộc CHECK , được thực hiện trong mô hình Django với các ràng buộc "meta" của mô hình tùy chọn .
Điều này đang được nói, cách các ràng buộc thực sự được hỗ trợ và thi hành ở cấp db tùy thuộc vào nhà cung cấp DB của bạn ( ví dụ, MySQL <8.0.16 chỉ cần bỏ qua chúng ) và loại ràng buộc bạn sẽ cần ở đây sẽ không được thi hành ở dạng hoặc xác thực cấp độ mô hình , chỉ khi cố gắng lưu mô hình, do đó bạn cũng muốn thêm xác thực ở cấp độ mô hình (tốt nhất) hoặc xác thực cấp độ biểu mẫu, trong cả hai trường hợp trong phương thức (mô hình) hoặc clean()
phương thức biểu mẫu .
Vì vậy, để làm cho một câu chuyện dài ngắn:
trước tiên hãy kiểm tra kỹ xem bạn có thực sự muốn unique=True
ràng buộc này không và nếu có thì hãy thay thế trường FK của bạn bằng OneToOneField.
thêm một blank=True
đối số cho cả hai trường FK (hoặc OneToOne) của bạn
- thêm ràng buộc kiểm tra thích hợp trong meta mô hình của bạn - tài liệu không rõ ràng nhưng vẫn đủ rõ ràng nếu bạn biết thực hiện các truy vấn phức tạp với ORM (và nếu bạn không đến lúc bạn học ;-))
- thêm một
clean()
phương thức vào mô hình của bạn để kiểm tra xem bạn có một hoặc một trường khác và gây ra lỗi xác thực khác không
và bạn sẽ ổn thôi, giả sử RDBMS của bạn tôn trọng các ràng buộc kiểm tra tất nhiên.
Chỉ cần lưu ý rằng, với thiết kế này, Inspection
mô hình của bạn hoàn toàn vô dụng (nhưng rất tốn kém!) - bạn sẽ có được các tính năng chính xác tương tự với chi phí thấp hơn bằng cách chuyển trực tiếp FK (và các ràng buộc, xác nhận, v.v.) InspectionReport
.
Bây giờ có thể có một giải pháp khác - giữ mô hình Kiểm tra, nhưng đặt FK làm OneToOneField ở đầu kia của mối quan hệ (trong Trang web và Nhóm):
class Inspection(models.Model):
id = models.AutoField(primary_key=True) # a pk is always unique !
class InspectionReport(models.Model):
# you actually don't need to manually specify a PK field,
# Django will provide one for you if you don't
# id = models.AutoField(primary_key=True)
inspection = ForeignKey(Inspection, ...)
date = models.DateField(null=True) # you should have a default then
comment = models.CharField(max_length=255, blank=True default="")
signature = models.CharField(max_length=255, blank=True, default="")
class Group(models.Model):
inspection = models.OneToOneField(Inspection, null=True, blank=True)
class Site(models.Model):
inspection = models.OneToOneField(Inspection, null=True, blank=True)
Và sau đó bạn có thể nhận được tất cả các báo cáo cho một Trang web hoặc Nhóm cụ thể với yoursite.inspection.inspectionreport_set.all()
.
Điều này tránh việc phải thêm bất kỳ ràng buộc hoặc xác nhận cụ thể nào, nhưng với chi phí của một mức độ bổ sung bổ sung ( join
mệnh đề SQL, v.v.).
Giải pháp nào trong số đó là "tốt nhất" thực sự phụ thuộc vào ngữ cảnh, vì vậy bạn phải hiểu ý nghĩa của cả hai và kiểm tra xem bạn thường sử dụng mô hình của mình như thế nào để tìm ra phương pháp phù hợp hơn với nhu cầu của riêng bạn. Theo như tôi quan tâm và không có nhiều bối cảnh (hoặc nghi ngờ), tôi muốn sử dụng giải pháp với các mức độ ít hơn, nhưng YMMV.
NB liên quan đến các mối quan hệ chung: chúng có thể hữu ích khi bạn thực sự có nhiều mô hình liên quan có thể và / hoặc không biết trước mô hình nào mà người ta sẽ muốn liên quan đến chính bạn. Điều này đặc biệt hữu ích cho các ứng dụng có thể tái sử dụng (nghĩ các tính năng "bình luận" hoặc "thẻ", v.v.) hoặc các ứng dụng có thể mở rộng (khung quản lý nội dung, v.v.). Nhược điểm là nó làm cho việc truy vấn nặng hơn nhiều (và khá không thực tế khi bạn muốn thực hiện các truy vấn thủ công trên db của mình). Từ kinh nghiệm, họ có thể nhanh chóng trở thành một bot / mã writ và hoàn hảo, vì vậy tốt hơn là giữ chúng khi không có giải pháp nào tốt hơn (và / hoặc khi việc bảo trì và thời gian chạy không phải là vấn đề).
2 xu của tôi.
Inspection
lớp, sau đó phân lớp thànhSiteInspection
vàGroupInspection
cho các phần không phổ biến.