มีคำตอบที่ดีจากdani herrera อยู่แล้ว แต่ฉันต้องการที่จะอธิบายรายละเอียดเพิ่มเติม
ตามที่อธิบายไว้ในตัวเลือกที่สองวิธีแก้ปัญหาตามที่ 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
เหตุการณ์อาจเป็น:
- หัวข้อ: คาราบาวคัพรอบที่ 4
- สถานที่: แอนฟิลด์
- เวลา: 30. ตุลาคม 2562, 19:30 น. GMT
- เข้าร่วม:
- ชื่อ: ลิเวอร์พูล, เมือง: ลิเวอร์พูล
- ชื่อ: Arsenal, city: London
ตอนนี้เราต้องแก้ปัญหาจากคำถาม Django สร้างตารางกลางระหว่างโมเดลที่มีความสัมพันธ์แบบหลายต่อหลายโดยอัตโนมัติ แต่เราสามารถใช้โมเดลที่กำหนดเองและเพิ่มฟิลด์เพิ่มเติม ฉันเรียกรูปแบบนั้นว่าParticipant
:
คลาสผู้เข้าร่วม (โมเดลโมเดล):
บทบาท = (
('H', 'Home'),
('V', 'ผู้เยี่ยมชม'),
)
event = models.ForeignKey (เหตุการณ์, on_delete = models.CASCADE)
คู่แข่ง = รุ่นต่างประเทศ Keyey (คู่แข่ง, on_delete = รุ่น .CASCADE)
role = models.CharField (max_length = 1, ตัวเลือก = บทบาท)
คลาส Meta:
unique_together = (
('เหตุการณ์', 'บทบาท'),
('เหตุการณ์', 'คู่แข่ง'),
)
def __str __ (ตัวเอง):
ส่งคืน '{} - {}' รูปแบบ (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
ข้อ จำกัด ที่ไม่ซ้ำกันในขณะนี้จะ จำกัด จำนวนของคู่แข่งโดยอัตโนมัติต่อเหตุการณ์ที่สอง (เนื่องจากมีเพียงสองบทบาท: หน้าแรกและผู้เข้าชม )
ในเหตุการณ์เฉพาะ (เกมฟุตบอล) อาจมีทีมเหย้าเพียงทีมเดียวและทีมผู้เยี่ยมชมเพียงทีมเดียวเท่านั้น สโมสร ( 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 ทีมต่อเหตุการณ์
สิ่งนี้สามารถ refactored สำหรับกรณีการใช้งานที่แตกต่างกัน สมมติว่ากิจกรรมของเราคือการแข่งขันว่ายน้ำและแทนที่จะเป็นบ้านและผู้มาเยือนเรามีเลน 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 ฉันหวังว่าคำตอบนี้จะช่วยเพิ่มมูลค่าให้กับผู้อ่าน