Đã có một câu trả lời tuyệt vời từ dani herrera , tuy nhiên tôi muốn giải thích thêm về nó.
Như đã giải thích trong tùy chọn thứ hai, giải pháp theo yêu cầu của OP là thay đổi thiết kế và thực hiện hai ràng buộc duy nhất theo cặp. Sự tương đồng với các trận đấu bóng rổ minh họa vấn đề theo một cách rất thực tế.
Thay vì một trận đấu bóng rổ, tôi sử dụng ví dụ với các trò chơi bóng đá (hoặc bóng đá). Một trò chơi bóng đá (mà tôi gọi nó Event
) được chơi bởi hai đội (trong mô hình của tôi là một đội Competitor
). Đây là một mối quan hệ nhiều-nhiều ( m:n
), với n
giới hạn là hai trong trường hợp cụ thể này, nguyên tắc này phù hợp với số lượng không giới hạn.
Đây là cách các mô hình của chúng tôi trông:
class Competitor(models.Model):
name = models.CharField(max_length=100)
city = models.CharField(max_length=100)
def __str__(self):
return self.name
class Event(models.Model):
title = models.CharField(max_length=200)
venue = models.CharField(max_length=100)
time = models.DateTimeField()
participants = models.ManyToManyField(Competitor)
def __str__(self):
return self.title
Một sự kiện có thể là:
- danh hiệu: Carabao Cup, vòng 4,
- địa điểm: Anfield
- thời gian: 30. Tháng 10 năm 2019, 19:30 GMT
- những người tham gia:
- tên: Liverpool, thành phố: Liverpool
- Tên: Arsenal, thành phố: Luân Đôn
Bây giờ chúng ta phải giải quyết vấn đề từ câu hỏi. Django tự động tạo một bảng trung gian giữa các mô hình có mối quan hệ nhiều-nhiều, nhưng chúng ta có thể sử dụng một mô hình tùy chỉnh và thêm các trường khác. Tôi gọi mô hình đó Participant
:
người tham gia lớp học (model.Model):
VAI TRÒ = (
('H', 'Nhà'),
('V', 'Khách truy cập'),
)
event = model.ForeignKey (Event, on_delete = model.CASCADE)
đối thủ cạnh tranh = mô hình.ForeignKey (Đối thủ cạnh tranh, on_delete = mô hình.CASCADE)
vai trò = mô hình.CharField (max_length = 1, tests = ROLES)
lớp Meta:
unique_together = (
('sự kiện', 'vai trò'),
('sự kiện', 'đối thủ cạnh tranh'),
)
def __str __ (tự):
trả về định dạng '{} - {}'. (self.event, self.get_role_display ())
Có ManyToManyField
một tùy chọn through
cho phép chúng ta chỉ định mô hình trung gian. Hãy thay đổi điều đó trong mô hình Event
:
class Event(models.Model):
title = models.CharField(max_length=200)
venue = models.CharField(max_length=100)
time = models.DateTimeField()
participants = models.ManyToManyField(
Competitor,
related_name='events', # if we want to retrieve events for a competitor
through='Participant'
)
def __str__(self):
return self.title
Các ràng buộc duy nhất bây giờ sẽ tự động giới hạn số lượng đối thủ cạnh tranh trong hai sự kiện (vì chỉ có hai vai trò: Nhà và Khách truy cập ).
Trong một sự kiện cụ thể (trò chơi bóng đá) chỉ có thể có một đội chủ nhà và chỉ có một đội khách. Một câu lạc bộ ( Competitor
) có thể xuất hiện với tư cách là đội chủ nhà hoặc là đội khách.
Làm thế nào để chúng ta quản lý tất cả những điều này trong quản trị viên? Như thế này:
from django.contrib import admin
from .models import Competitor, Event, Participant
class ParticipantInline(admin.StackedInline): # or admin.TabularInline
model = Participant
max_num = 2
class CompetitorAdmin(admin.ModelAdmin):
fields = ('name', 'city',)
class EventAdmin(admin.ModelAdmin):
fields = ('title', 'venue', 'time',)
inlines = [ParticipantInline]
admin.site.register(Competitor, CompetitorAdmin)
admin.site.register(Event, EventAdmin)
Chúng tôi đã thêm các Participant
nội tuyến trong EventAdmin
. Khi chúng tôi tạo mới, Event
chúng tôi có thể chọn đội chủ nhà và đội khách. Tùy chọn max_num
giới hạn số lượng mục nhập thành 2, do đó không thể thêm 2 đội cho mỗi sự kiện.
Điều này có thể được tái cấu trúc cho một trường hợp sử dụng khác nhau. Giả sử các sự kiện của chúng tôi là các cuộc thi bơi lội và thay vì nhà và khách, chúng tôi có các làn từ 1 đến 8. Chúng tôi chỉ tái cấu trúc Participant
:
class Participant(models.Model):
ROLES = (
('L1', 'lane 1'),
('L2', 'lane 2'),
# ... L3 to L8
)
event = models.ForeignKey(Event, on_delete=models.CASCADE)
competitor = models.ForeignKey(Competitor, on_delete=models.CASCADE)
role = models.CharField(max_length=1, choices=ROLES)
class Meta:
unique_together = (
('event', 'role'),
('event', 'competitor'),
)
def __str__(self):
return '{} - {}'.format(self.event, self.get_role_display())
Với sửa đổi này, chúng tôi có thể có sự kiện này:
Một người bơi chỉ có thể xuất hiện một lần khi trời nóng và làn đường chỉ có thể bị chiếm giữ một lần khi trời nóng.
Tôi đặt mã cho GitHub: https://github.com/cezar77/competition .
Một lần nữa, tất cả các khoản tín dụng đi đến dani herrera. Tôi hy vọng câu trả lời này cung cấp một số giá trị gia tăng cho độc giả.