dani herrera 로부터 이미 큰 답변 이 있지만 더 자세히 설명하고 싶습니다.
두 번째 옵션에서 설명했듯이 OP에 필요한 솔루션은 디자인을 변경하고 두 가지 고유 제약 조건을 쌍으로 구현하는 것입니다. 농구 경기와의 비유는 문제를 매우 실용적인 방식으로 보여줍니다.
농구 경기 대신 축구 (또는 축구) 게임에서 예를 사용합니다. 축구 게임 (내가 부르는 Event
)은 두 팀 (내 모델에서 팀은 Competitor
) 에 의해 재생됩니다 . 이것은 다 대다 관계 ( m:n
) n
이며이 특별한 경우에는 2 개로 제한되며 원칙은 무제한에 적합합니다.
다음은 모델의 모습입니다.
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
이벤트는 다음과 같습니다.
- 제목 : Carabao Cup, 4 라운드,
- 장소 : 안필드
- 시간 : 2019 년 10 월 30 일 19:30 GMT
- 참가자 :
- 이름 : 리버풀, 도시 : 리버풀
- 이름 : 아스날, 도시 : 런던
이제 우리는 문제에서 문제를 해결해야합니다. Django는 다 대다 관계로 모델간에 중간 테이블을 자동으로 생성하지만 사용자 정의 모델을 사용하고 추가 필드를 추가 할 수 있습니다. 나는 그 모델을 부른다 Participant
:
참가자 (models.Model) 클래스 :
역할 = (
( 'H', '홈'),
( 'V', '방문자'),
)
이벤트 = models.ForeignKey (이벤트, on_delete = models.CASCADE)
경쟁사 = models.ForeignKey (경쟁사, on_delete = models.CASCADE)
역할 = models.CharField (max_length = 1, choices = ROLES)
메타 클래스 :
unique_together = (
( '이벤트', '역할'),
( '이벤트', '경쟁사'),
)
데프 __str__ (자체) :
return '{}-{}'. format (self.event, self.get_role_display ())
는 ManyToManyField
옵션이 through
우리가 중간 모델을 지정할 수 있습니다. 모델에서 변경해 봅시다 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
고유 제한 조건은 이제 이벤트 당 경쟁자 수를 자동으로 2 개로 제한합니다 ( 홈 과 방문자 라는 두 가지 역할 만 있기 때문 ).
특정 이벤트 (축구 경기)에는 홈 팀과 방문자 팀이 하나만있을 수 있습니다. 클럽 ( Competitor
)은 홈 팀 또는 방문자 팀으로 나타날 수 있습니다.
이제 관리자에서이 모든 것을 어떻게 관리합니까? 이처럼 :
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)
Participant
에에 인라인을 추가 했습니다 EventAdmin
. 새로 만들 때 Event
홈 팀과 방문자 팀을 선택할 수 있습니다. 이 옵션 max_num
은 항목 수를 2 개로 제한하므로 이벤트 당 2 개 팀을 추가 할 수 없습니다.
다른 사용 사례에 대해 리팩토링 할 수 있습니다. 우리의 이벤트가 수영 대회이며 가정과 방문객 대신 1 ~ 8 레인이 있다고 가정 해 봅시다 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())
이 수정으로 다음과 같은 이벤트를 가질 수 있습니다.
수영 선수는 더위에 한 번만 나타날 수 있으며 레인은 더위에 한 번만 차지할 수 있습니다.
코드를 GitHub ( https://github.com/cezar77/competition)에 넣었습니다 .
다시 모든 크레딧은 dani herrera에게 전달됩니다. 이 답변이 독자들에게 부가 가치를 제공하기를 바랍니다.