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

查看:48
本文介绍了在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   │                       
                                             │           │                       
                                             └───────────┘                       

您可以看到这里有两个通过一对联结表( EventRound 得分)。我通过在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,使我可以访问并遍历得分表中针对特定人员的所有得分。 (感谢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.

I无法弄清楚如何通过 EventRound 表到达存储在 Score 中的分数。大概我需要在 PersonDetailView get_context_data 方法中进一步操作上下文。 code>。

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天全站免登陆