已经有一个伟大的答案来自达尼·埃雷拉,但是我希望它来进一步阐述。
如第二个选项所述,OP所需的解决方案是更改设计并成对实现两个唯一约束。篮球比赛的类比非常实用地说明了这个问题。
我不使用篮球比赛,而是使用足球(或足球)比赛的例子。足球比赛(我称之为Event
)是由两支球队进行的(在我的模型中,一支球队是Competitor
)。这是一个多对多关系(m:n
),n
在这种特殊情况下限制为两个,该原理适用于无限数。
这是我们模型的外观:
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杯,第4轮,
- 地点:安菲尔德
- 时间:2019年10月30日,格林尼治标准时间19:30
- 参加者:
- 名称:利物浦,城市:利物浦
- 名称:阿森纳,城市:伦敦
现在我们必须从问题中解决问题。Django自动在具有多对多关系的模型之间创建中间表,但是我们可以使用自定义模型并添加更多字段。我称那个模特Participant
:
类参与者(models.Model):
角色=(
(“ H”,“首页”),
(“ V”,“访客”),
)
event = models.ForeignKey(Event,on_delete = models.CASCADE)
竞争对手= models.ForeignKey(竞争对手,on_delete = models.CASCADE)
角色=模型。CharField(max_length = 1,choices = ROLES)
类Meta:
unique_together =(
(“事件”,“角色”),
(“事件”,“竞争对手”),
)
def __str __():
返回'{}-{}'。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
现在,唯一的约束将自动将每个事件的参赛者数量限制为两个(因为只有两个角色:Home和Visitor)。
在特定事件(足球比赛)中,只能有一个主队和一个访客队。俱乐部(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
as内联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。我希望这个答案可以为读者提供一些附加值。