在Django DetailView中遍历多个外键



我正在做一个项目,记录一系列射箭比赛的所有成绩。他们在许多项目和许多不同的射箭比赛中被许多人射杀。(射箭比赛中的"圆形"是一种特殊的比赛类型。为了简单起见,我们假设有两种:室内和室外。(

以下是我的数据库模型的相关部分的基本ER图:

┌───────────┐         ┌───────────┐       ┌────────────────┐        ┌───────────┐
│           │        ╱│           │╲      │                │╲       │           │
│  Person   │──────┼──│   Score   │──┼────│   EventRound   │──┼─────│   Event   │
│           │        ╲│           │╱      │                │╱       │           │
└───────────┘         └───────────┘       └────────────────┘        └───────────┘
╲│╱                            
┼                             
│                             
┌───────────┐                       
│           │                       
│   Round   │                       
│           │                       
└───────────┘                       

您可以看到,这里有两个ManyToMany关系,它们通过两个连接表(EventRoundScore(来解决。我通过在models.py.中指定"through"表和"through_fields"来手动创建这些连接表

我创建了一个PersonDetailView,它允许我访问并迭代特定人员的Score表中的所有分数。(感谢Jaberwocky和他在Detailview Object Relations的解决方案(

# views.py
class PersonDetailView(DetailView):
model = Person
queryset = Person.objects.all()
template_name = 'person_detail.html'
def get_context_data(self, **kwargs):
context = super(PersonDetailView, self).get_context_data(**kwargs)
context['scores'] = Score.objects.filter(person=self.get_object()).order_by('-event_round__date')
return context
# person_detail.html
{% block content %}
<h1>Results for {{ person }}</h1>
<table>
<tr><th>Division</th><th>Score</th><th>Date</th><th>Event</th><th>Round</th></tr>
{% for score in scores %}
<tr>
<td>{{ score.division }}</td>
<td>{{ score.pretty_score }}</td>
<td>{{ score.event_round.date|date:"M d, Y" }}</td>
<td>{{ score.event_round }}</td>
<td>{{ score.event_round.round }}</td>
</tr>
{% endfor %}
</table>
{% endblock content %}

当我在事件和回合中尝试同样的策略时,麻烦就来了我想显示与特定事件或回合相关的所有分数,并包括得分者的详细信息

我不知道如何通过EventRound表获得存储在Score中的分数。据推测,我需要在PersonDetailViewget_context_data方法中进一步操作context

有什么办法吗?

更新:这是我的models.py的一部分,其中包括本文中引用的表。

from django.db import models
from datetime import date
from django.urls import reverse
from django.utils import timezone

class Person(models.Model):
"""
Contains information about competitors who have scores in the database.
"""
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=75)
birthdate = models.DateField(blank=True, null=True)
slug = models.SlugField(null=False, unique=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
verbose_name_plural = "People"
ordering = ['last_name', 'first_name']
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
def get_absolute_url(self):
return reverse('person_detail', kwargs={'slug', self.slug})

class Event(models.Model):
name = models.CharField(max_length=100)
start_date = models.DateField(null=True)
end_date = models.DateField(null=True)
location = models.ForeignKey("Location", on_delete=models.CASCADE)
slug = models.SlugField(null=False, unique=True)
scoring_method = models.ForeignKey("ScoringMethod", on_delete=models.CASCADE)
event_type = models.ForeignKey("EventType", blank=True, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['name']
def __str__(self):
return f"{self.name}"
def get_absolute_url(self):
return reverse('event_detail', kwargs={'slug', self.slug})
@property
def date_range(self):
if self.start_date is None:
return "Unknown"
elif self.start_date == self.end_date:
return f"{self.start_date.strftime('%b %d, %Y')}"
else:
return f"{self.start_date.strftime('%b %d, %Y')} – {self.end_date.strftime('%b %d, %Y')}"

IN_OR_OUT_CHOICES = [
("Indoor", "Indoor"),
("Outdoor", "Outdoor"),
]

class Round(models.Model):
name = models.CharField(max_length=75)
description = models.TextField()
slug = models.SlugField(null=False, unique=True)
organization = models.ForeignKey("Organization", on_delete=models.CASCADE)
is_retired = models.BooleanField("Retired", default=False)
in_or_out = models.TextField(
"Indoor/Outdoor",
max_length=30,
choices=IN_OR_OUT_CHOICES,
)
events = models.ManyToManyField(
Event,
through="EventRound",
through_fields=('round', 'event'),
related_name="rounds",
)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['organization', 'name']
def __str__(self):
return "%s" % (self.name)
def get_absolute_url(self):
return reverse('round_detail', kwargs={'slug', self.slug})

class EventRound(models.Model):
date = models.DateField(null=True)
event = models.ForeignKey("Event",
on_delete=models.CASCADE,
related_name="event_rounds",
)
round = models.ForeignKey(
"Round",
on_delete=models.CASCADE,
related_name="event_rounds",
)
participants = models.ManyToManyField(
Person,
through="Score",
through_fields=('event_round', 'person'),
related_name="event_rounds",
)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
verbose_name_plural = "Event-Rounds"
ordering = ['-event']
def __str__(self):
return "%s" % (self.event)

DISTANCE_UNIT_CHOICES = [
("yd", "Yards"),
("m", "Meters"),
]

class Score(models.Model):
person = models.ForeignKey(
"Person",
on_delete=models.CASCADE,
related_name="scores",
)
event_round = models.ForeignKey(
"EventRound",
on_delete=models.CASCADE,
related_name="scores",
)
score = models.PositiveSmallIntegerField()
x_count = models.PositiveSmallIntegerField(blank=True, null=True)
age_division = models.ForeignKey("AgeDivision", on_delete=models.CASCADE)
equipment_class = models.ForeignKey("EquipmentClass", on_delete=models.CASCADE)
gender = models.ForeignKey("Gender", on_delete=models.CASCADE)
distance = models.CharField(max_length=10, blank=True)
distance_unit = models.CharField(max_length=10, choices=DISTANCE_UNIT_CHOICES)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return "%s - %s" % (self.person, self.event_round)

使用相关模型的字段进行筛选

由于您只是简单地显示分数,因此最好从Score型号开始工作

# Get scores by Event for all rounds and dates
Score.objects.filter(event_name__event=event)
# Get scores by Event-Round for all dates
Score.objects.filter(event_name__event=event, event_name__round=round)
# Get scores from one Event-Round in a specific date
Score.objects.filter(event_name__event=event, event_name__round=round, event_name__date=date)

应用于您的用例

个人得分:

# view.py
class PersonDetailView(DetailView):
model = Person      
queryset = Person.objects.all()
template_name = 'person_detail.html' 
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
person = self.get_object()
context['person'] = person
context['scores'] = Score.objects.filter(person=person)
return context
# person_detail.html
{% block content %}
<h1>Results for {{ person }}</h1>
<table>
<tr><th>Division</th><th>Score</th><th>Date</th><th>Event</th><th>Round</th></tr>
{% for score in scores %}
<tr>
<td>{{ score.division }}</td>
<td>{{ score.pretty_score }}</td>
<td>{{ score.event_round.date|date:"M d, Y" }}</td>
<td>{{ score.event_round.event }}</td>
<td>{{ score.event_round.round }}</td>
</tr>
{% endfor %}
</table>
{% endblock content %}

按事件划分的分数:

# view.py
class EventDetailView(DetailView):
model = Event       
queryset = Event.objects.all()
template_name = 'event_detail.html' 
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
event = self.get_object()
context['event'] = event
context['scores'] = Score.objects.filter(event_round__event=event)
return context
# event_detail.html
{% block content %}
<h1>Results for {{ event }}</h1>
<table>
<tr><th>Division</th><th>Score</th><th>Date</th><th>Round</th><th>Person</th></tr>
{% for score in scores %}
<tr>
<td>{{ score.division }}</td>
<td>{{ score.pretty_score }}</td>
<td>{{ score.event_round.date|date:"M d, Y" }}</td>
<td>{{ score.event_round.round }}</td>
<td>{{ score.person}}</td>
</tr>
{% endfor %}
</table>
{% endblock content %}

按回合划分的分数:

# view.py
class RoundDetailView(DetailView):
model = Round   
queryset = Round.objects.all()
template_name = 'round_detail.html' 
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
round = self.get_object()
context['round'] = round
context['scores'] = Score.objects.filter(event_round__round=round)
return context

# round_detail.html
{% block content %}
<h1>Results for {{ round }}</h1>
<table>
<tr><th>Division</th><th>Score</th><th>Date</th><th>event</th><th>Person</th></tr>
{% for score in scores %}
<tr>
<td>{{ score.division }}</td>
<td>{{ score.pretty_score }}</td>
<td>{{ score.event_round.date|date:"M d, Y" }}</td>
<td>{{ score.event_round.event}}</td>
<td>{{ score.person}}</td>
</tr>
{% endfor %}
</table>
{% endblock content %}

最新更新