Sudah ada jawaban bagus dari dani herrera , namun saya ingin menguraikannya lebih lanjut.
Sebagaimana dijelaskan dalam opsi kedua, solusi seperti yang disyaratkan oleh OP adalah mengubah desain dan mengimplementasikan dua kendala unik berpasangan. Analogi dengan pertandingan bola basket menggambarkan masalah dengan cara yang sangat praktis.
Alih-alih pertandingan bola basket, saya menggunakan contoh dengan permainan sepak bola (atau sepak bola). Sebuah permainan sepak bola (yang saya sebut itu Event
) dimainkan oleh dua tim (dalam model saya tim adalah Competitor
). Ini adalah hubungan banyak ke banyak (m:n
), dengan n
terbatas pada dua dalam kasus khusus ini, prinsipnya cocok untuk jumlah yang tidak terbatas.
Berikut tampilan model kami:
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
Suatu acara dapat:
- judul: Piala Carabao, babak ke-4,
- tempat: Anfield
- waktu: 30. Oktober 2019, 19:30 GMT
- peserta:
- nama: Liverpool, kota: Liverpool
- nama: Arsenal, kota: London
Sekarang kita harus menyelesaikan masalah dari pertanyaan. Django secara otomatis membuat tabel perantara antara model dengan hubungan banyak ke banyak, tetapi kita dapat menggunakan model khusus dan menambahkan bidang lebih lanjut. Saya menyebut model itu Participant
:
Peserta kelas (model. Model):
ROL =
('H', 'Rumah'),
('V', 'Pengunjung'),
)
event = models.ForeignKey (Event, on_delete = models.CASCADE)
kompetitor = models.ForeignKey (Pesaing, on_delete = models.CASCADE)
role = models.CharField (max_length = 1, choices = ROLES)
kelas Meta:
unique_together = (
('acara', 'peran'),
('acara', 'pesaing'),
)
def __str __ (mandiri):
return '{} - {}'. format (self.event, self.get_role_display ())
The ManyToManyField
memiliki opsi through
yang memungkinkan kita untuk menentukan model menengah. Mari kita ubah itu dalam model 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
Batasan unik sekarang akan secara otomatis membatasi jumlah pesaing per peristiwa menjadi dua (karena hanya ada dua peran: Rumah dan Pengunjung ).
Dalam acara tertentu (pertandingan sepak bola) hanya ada satu tim tuan rumah dan satu tim tamu. Klub ( Competitor
) dapat muncul sebagai tim tuan rumah atau sebagai tim pengunjung.
Bagaimana kita mengelola semua hal ini sekarang di admin? Seperti ini:
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)
Kami telah menambahkan Participant
sebagai sebaris dalam EventAdmin
. Saat kami membuat yang baru, Event
kami dapat memilih tim tuan rumah dan tim tamu. Opsi max_num
membatasi jumlah entri menjadi 2, oleh karena itu tidak lebih dari 2 tim dapat ditambahkan per acara.
Ini dapat di refactored untuk kasus penggunaan yang berbeda. Katakanlah acara kami adalah kompetisi renang dan alih-alih rumah dan pengunjung, kami memiliki jalur 1 hingga 8. Kami hanya memperbaiki 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())
Dengan modifikasi ini kita dapat memiliki acara ini:
Perenang hanya bisa muncul sekali dalam panas, dan jalur hanya bisa ditempati sekali dalam panas.
Saya memasukkan kode ke GitHub: https://github.com/cezar77/competition .
Sekali lagi, semua kredit jatuh ke dani herrera. Saya harap jawaban ini memberikan nilai tambah bagi pembaca.