Es gibt bereits eine großartige Antwort von Dani Herrera , aber ich möchte darauf näher eingehen .
Wie in der zweiten Option erläutert, besteht die vom OP geforderte Lösung darin, das Design zu ändern und zwei eindeutige Einschränkungen paarweise zu implementieren. Die Analogie zu den Basketballspielen verdeutlicht das Problem auf sehr praktische Weise.
Anstelle eines Basketballspiels verwende ich ein Beispiel für Fußballspiele. Ein Fußballspiel (das ich es nenne Event) wird von zwei Teams gespielt (in meinen Modellen ist es ein Team Competitor). Dies ist eine Viele-zu-Viele-Beziehung ( m:n), die nin diesem speziellen Fall auf zwei begrenzt ist. Das Prinzip ist für eine unbegrenzte Anzahl geeignet.
So sehen unsere Modelle aus:
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
Ein Ereignis könnte sein:
- Titel: Carabao Cup, 4. Runde,
- Veranstaltungsort: Anfield
- Zeit: 30. Oktober 2019, 19:30 GMT
- Teilnehmer:
- Name: Liverpool, Stadt: Liverpool
- Name: Arsenal, Stadt: London
Jetzt müssen wir das Problem aus der Frage lösen. Django erstellt automatisch eine Zwischentabelle zwischen den Modellen mit einer Viele-zu-Viele-Beziehung. Wir können jedoch ein benutzerdefiniertes Modell verwenden und weitere Felder hinzufügen. Ich nenne das Modell Participant:
Klassenteilnehmer (models.Model):
ROLES = (
('H', 'Zuhause'),
('V', 'Besucher'),
)
event = models.ForeignKey (Event, on_delete = models.CASCADE)
Wettbewerber = Modelle.ForeignKey (Wettbewerber, on_delete = Modelle.CASCADE)
Rolle = Modelle.CharField (max_length = 1, Auswahl = ROLES)
Klasse Meta:
unique_together = (
('Ereignis', 'Rolle'),
('Veranstaltung', 'Konkurrent'),
)
def __str __ (Selbst):
return '{} - {}'. format (self.event, self.get_role_display ())
Das ManyToManyFieldhat eine Option through, mit der wir das Zwischenmodell angeben können. Lassen Sie uns das im Modell ändern 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
Die eindeutigen Einschränkungen begrenzen jetzt automatisch die Anzahl der Teilnehmer pro Veranstaltung auf zwei (da es nur zwei Rollen gibt: Heim und Besucher ).
In einem bestimmten Ereignis (Fußballspiel) kann es nur eine Heimmannschaft und nur eine Besucherteams geben. Ein Verein ( Competitor) kann entweder als Heimmannschaft oder als Besucherteam auftreten.
Wie verwalten wir jetzt all diese Dinge im Admin? So was:
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)
Wir haben das Participantals Inline in das hinzugefügt EventAdmin. Wenn wir neue erstellen Event, können wir die Heimmannschaft und die Besucherteams auswählen. Die Option max_numbegrenzt die Anzahl der Einträge auf 2, daher können nicht mehr als 2 Teams pro Event hinzugefügt werden.
Dies kann für andere Anwendungsfälle überarbeitet werden. Nehmen wir an, unsere Veranstaltungen sind Schwimmwettkämpfe und statt Heim und Besucher haben wir die Bahnen 1 bis 8. Wir überarbeiten nur Folgendes 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())
Mit dieser Modifikation können wir dieses Ereignis haben:
Ein Schwimmer kann nur einmal in einem Lauf auftreten, und eine Bahn kann nur einmal in einem Lauf besetzt werden.
Ich habe den Code an GitHub gesendet: https://github.com/cezar77/competition .
Alle Credits gehen wieder an dani herrera. Ich hoffe, diese Antwort bietet den Lesern einen Mehrwert.