<p>已经有一个来自<strong><a href="https://stackoverflow.com/users/842935/dani-herrera">dani herrera</a></strong>的伟大的<a href="https://stackoverflow.com/a/58614958/3848833">answer</a>,但是我想进一步详细说明它。在</p>
<p>如第二个选项所述,OP所需的解决方案是更改设计并成对实现两个唯一约束。与篮球比赛的类比很实际地说明了这个问题。在</p>
<p>我用足球(或足球)比赛作为例子,而不是篮球比赛。足球比赛(我称之为<code>Event</code>)由两个团队进行(在我的模型中,一个团队是<code>Competitor</code>)。这是一个多对多关系(<code>m:n</code>),其中{<cd4>}限制为两个在这种特殊情况下,该原理适用于无限个数。在</p>
<p>以下是我们的模型的外观:</p>
<pre><code>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
</code></pre>
<p>事件可以是:</p>
<ul>
<li>标题:卡拉宝杯,第四轮</li>
<li>地点:安菲尔德</li>
<li>时间:30。2019年10月19:30 GMT</li>
<li>参加人员:
<ul>
<li>名称:利物浦,城市:利物浦</li>
<li>名称:阿森纳,城市:伦敦</li>
</ul></li>
</ul>
<p>现在我们要从问题上解决问题。Django自动在具有多对多关系的模型之间创建一个中间表,但是我们可以使用自定义模型并添加更多字段。我把这个模型称为<code>Participant</code>:</p>
^{pr2}$
<p><code>ManyToManyField</code>有一个选项<code>through</code>,它允许我们指定中间模型。让我们在模型<code>Event</code>中更改它:</p>
<pre><code>class Event(models.Model):
title = models.CharField(max_length=200)
venue = models.CharField(max_length=100)
time = models.DateTimeField()
<strong>participants = models.ManyToManyField(
Competitor,
related_name='events', # if we want to retrieve events for a competitor
through='Participant'
)</strong>
def __str__(self):
return self.title
</code></pre>
<p>独特的限制条件现在会自动将每个赛事的参赛者数量限制在两个(因为只有两个角色:<strong>主场</strong>和<strong>观众</strong>)。在</p>
<p>在一个特定的项目(足球比赛)中,只能有一个主队和一个客队。俱乐部(<code>Competitor</code>)可以作为主队或客队出现。在</p>
<p>我们现在如何管理所有这些事情?像这样:</p>
<pre><code>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)
</code></pre>
<p>我们已经将<code>Participant</code>作为内联添加到<code>EventAdmin</code>中。当我们创建新的<code>Event</code>时,我们可以选择主团队和访问者团队。因此,{cd2>每一个事件可以添加更多的条目。在</p>
<p>这可以针对不同的用例进行重构。假设我们的项目是游泳比赛,而不是主场和游客,我们有1至8泳道。我们只需重构<code>Participant</code>:</p>
<pre><code>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())
</code></pre>
<p>通过此修改,我们可以进行以下活动:</p>
<ul>
<li><p>标题:国际泳联2019,50米仰泳男子决赛</p>
<ul>
<li>地点:南部大学水上运动中心</li>
<li>时间:28。2019年7月,20:02 UTC+9</li>
<li><p>参加人员:</p>
<ul>
<li>姓名:Michael Andrew,城市:美国爱丁堡,角色:lane 1</li>
<li>姓名:Zane Waddell,城市:南非布隆方丹,角色:lane 2</li>
<li>姓名:叶夫根尼·里洛夫,城市:俄罗斯诺沃特洛伊茨克,角色:3号车道</li>
<li>姓名:Kliment Kolesnikov,城市:俄罗斯莫斯科,角色:车道4</li>
</ul>
<p>//以此类推,从第5道到第8道(来源:<a href="https://en.wikipedia.org/wiki/Swimming_at_the_2019_World_Aquatics_Championships_%E2%80%93_Men%27s_50_metre_backstroke" rel="nofollow noreferrer">Wikipedia</a></p></li>
</ul></li>
</ul>
<p>一个游泳者在炎热的天气里只能出现一次,而一条泳道在炎热的天气里只能被占用一次。在</p>
<p>我把代码放到GitHub:<a href="https://github.com/cezar77/competition" rel="nofollow noreferrer">https://github.com/cezar77/competition</a>。在</p>
<p>再说一遍,所有的学分都归丹尼·赫雷拉所有。我希望这个答案能为读者提供一些附加值。在</p>