首页 > 解决方案 > Counting all items linked to foreign key

问题描述

I'm looking for a solution to combine in one view information from two models. One model is for "node" definition. Second model is "drive" definition, where I have foreign key for "node". One "node" can contain many drives. In a template I need to show all assigned drives for a given node in a list view. I'm interested in count, a number of drives assigned to given node.

I don't have any new idea how calculate this information and pass to template. Should I count this as a "pure python"? I believe there is a way in Django as this doesn't look very complex.

View:

class NodeListView(ListView):
    template_name = 'nodes/nodes_list.html'
    queryset = Node.objects.all()
    context_object_name = 'nodes'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        drives = Drive.objects.all()
        context.update({
            'drives': drives
        })
        return context

Models:

class Node(models.Model):
    name = models.CharField(max_length=32)
    .... more ....

class Drive(models.Model):
    node = models.ForeignKey(Node, related_name='nodes', on_delete=models.CASCADE)
    .... more ....

标签: djangodjango-views

解决方案


Node您可以使用相关 s 的数量来注释Drives:

from django.db.models import Count

class NodeListView(ListView):
    template_name = 'nodes/nodes_list.html'
    queryset = Node.objects.annotate(ndrives=Count('nodes'))
    context_object_name = 'nodes'

然后您可以使用以下方法渲染它:

{% for node in nodes %}
    {{ node.ndrives }}
{% endfor %}

您还可以使用以下方法枚举相关 Drive的 s:

{% for node in nodes %}
    {{ node.ndrives }}
    {% for drive in node.nodes.all %}
        {{ drive }}
    {% endfor %}
{% endfor %}

您可以通过以下方式提高效率:

from django.db.models import Count

class NodeListView(ListView):
    template_name = 'nodes/nodes_list.html'
    queryset = Node.objects.annotate(
        ndrives=Count('nodes')
    ).prefetch_related('nodes')
    context_object_name = 'nodes'

之所以这样命名nodes,是因为related_name='nodes'. 然而,这没有多大意义:related_name='drives'会更有意义,因为它处理Drives 的集合,因此您可能希望将其重命名为:

class Node(models.Model):
    name = models.CharField(max_length=32)
    # …

class Drive(models.Model):
    node = models.ForeignKey(
        Node,
        related_name='drives',
        on_delete=models.CASCADE
    )
    # …

然后视图看起来像:

from django.db.models import Count

class NodeListView(ListView):
    template_name = 'nodes/nodes_list.html'
    queryset = Node.objects.annotate(
        ndrives=Count('drives')
    ).prefetch_related('drives')
    context_object_name = 'nodes'

我们用以下方式渲染它:

{% for node in nodes %}
    {{ node.ndrives }}
    {% for drive in node.drives.all %}
        {{ drive }}
    {% endfor %}
{% endfor %}

推荐阅读