在 Django DetailView 中遍历多个外键 [英] Traverse multiple foreign keys in Django DetailView

查看:15
本文介绍了在 Django DetailView 中遍历多个外键的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开展一个项目,以记录在一系列射箭比赛中的所有得分.他们在许多赛事和许多不同的射箭比赛中被许多人射中.(射箭中的回合"是一种特殊类型的比赛.为了简单起见,我们假设有两种:室内和室外.)

I'm working on a project to record all scores shot at a bunch of archery events. They are shot by many people at many events and many different archery rounds. (A "round" in archery is a particular type of competition. For the sake of simplicity let's say there are two: indoor and outdoor.)

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

Here's a basic ER diagram of the relevant part of my database model:

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

您可以看到这里有两个多对多关系在使用两个连接表(EventRoundScore)解决.我通过在 models.py 中指定through"表和through_fields"来手动创建这些连接表.

You can see that there are two ManyToMany relationships at work here resolved with two junction tables (EventRound and Score). I'm creating these junction tables manually by specifying the "through" table and "through_fields" in models.py.

我创建了一个 PersonDetailView,它允许我访问和遍历 Score 表中特定人员的所有分数.(感谢 Jaberwocky 和他在 Detailview 对象关系上的解决方案)

I've created a PersonDetailView that allows me to access and iterate through all of the scores in the Score table for a specific person. (Thanks to Jaberwocky and his solution at 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 %}

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

The trouble comes when I try the same strategy with the Events and Rounds. I'd like to show all the scores associated with a particular event or round and include details about the person who shot the score.

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

I can't figure out how to reach through the EventRound table and get to the scores stored in Score. Presumably, I need to further manipulate the context in the get_context_data method of the PersonDetailView.

关于如何做到这一点有什么想法吗?

Any ideas about how to do this?

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

Update: Here's a portion of my models.py that includes the tables referenced in this post.

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 %}

这篇关于在 Django DetailView 中遍历多个外键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆