如何正确查询 Django 中的外键?

How to correctly query to a Foreign Key in Django?

我有一组模型ClusterServerService,它们通过外键一对多建立关系,如下片段:

class Server(models.Model):
    cluster = models.ForeignKey(Cluster, null=True, on_delete=models.PROTECT)
    shortname = models.CharField(max_length=20, null=False)
    .
    .
    .


class Service(models.Model):
    host_server = models.ForeignKey(Server, null=False, on_delete=models.PROTECT)
    name = models.CharField(max_length=20, null=False)
    technology = models.CharField(max_length=20, null=False)
    .
    .
    .

现在我正在尝试在查询中获取一组 Servers,以及相关的 Services 使用

def infra(request, cluster_name):

    cluster = Cluster.objects.get(name__iexact=cluster_name)
    servers = Server.objects.filter(cluster_id=cluster.id).order_by('shortname')
    services = Service.objects.filter(host_server__in=servers).order_by('id')

    template = 'webapp/infra.html'
    context = {
        'cluster':cluster,
        'servers':servers,
        'services':services,
        }
    return render(request, template, context)

但是,在视图中,我在单个 QuerySet 中获取 所有服务,而不是每个 Server 的 QuerySet 仅包含 Services 关联有了它。

作为解决方法,我使用了以下方法:

<h5>Services</h5>
  <ul class="list-group">
    {% for service in services %}
      {% if service.host_server_id == server.id %}
        <li class="list-group-item">{{ service.name }}</li>
      {% endif %}
    {% endfor %}
  </ul>

但我觉得必须有一种方法可以更优雅地遍历每个服务器的服务,而不是每次都遍历所有服务然后决定是否应该取消显示。

是否有可能改进或者这个初始方法是否正确?

谢谢!

为您的 host_server 字段定义一个 related_name 并使用 Django's Reverse ForeignKey Manager 为每个服务器获取相关服务。

注意:如果没有 related_name,可以从 service_set

访问反向管理器

models.py

class Service(models.Model):
    host_server = models.ForeignKey(Server, null=False, on_delete=models.PROTECT, related_name="services")

views.py

def infra(request, cluster_name):

    cluster = Cluster.objects.get(name__iexact=cluster_name)
    # we use `prefetch_related` to speedup the query
    servers = Server.objects.filter(cluster_id=cluster.id).prefetch_related('services').order_by('shortname')
    return render(request, 'webapp/infra.html', {
        'cluster':cluster,
        'servers':servers,
    })

infra.html


  <ul class="list-group">
    {% for server in servers %}
      <li class="list-group-item">{{ server.name }}
        <ul>
        {% for service in server.services.all %}
          <li class="list-item">{{ service.name }}</li>
        {% endfor %}
        </ul>
      </li>
    {% endfor %}
  </ul>