在Django中获取相关对象以及如何将Prefetch与相关模型一起使用

Get related objects in Django and how to use Prefetch with related models

models.py

class Press(models.Model):
    created = models.DateTimeField(editable=False)
    title = models.CharField(max_length=500, blank=True)
    slug = models.SlugField(max_length=600, blank=True, unique=True)

    def __str__(self):
        return self.title

class Scan(models.Model):
    press = models.ForeignKey(Press, related_name='scans', on_delete=models.CASCADE)
    created = models.DateTimeField(editable=False)
    title = models.CharField(max_length=300, blank=True)
    main_scan = models.ImageField(upload_to='press/scans', blank=True)

    def __str__(self):
        return self.title

views.py

class PressListView(ListView):
    model = Press
    context_object_name = "press"
    template_name = "press.html"

    def get_context_data(self, **kwargs):
        # Call the base implementation first to get a context
        context = super().get_context_data(**kwargs)
        context['press'] = 
Press.objects.prefetch_related(Prefetch('scans', 
queryset=Scan.objects.select_related('press')))
        return context

我想在网站前端实现的是列出所有媒体,作为每个媒体的封面图片,我想使用来自 Scan 模型的第一张 main_scan 图片.

我知道我可以将 models.ImageField 添加到 Press 模型,但我不想 - 在管理员中我使用 admin.TabularInline 来自附加到 Press 模型的扫描模型.

我知道有关于预取的文档,但可能我用错了,而且在模板的前端也做错了。

问题是如何让它非常优化,性能最好,只访问数据库一次。

以前我是这样做的并且它有效,但这导致 15 个重复的 SQL 语句用于 15 个对象 - 通过使用 django-toolbar 我正在检查它,它的性能非常低效。

{% for article in press %}
{{ article.title }}<br>
<img src="{{ MEDIA_URL }}{{ article.scans.all.0.main_scan }}" alt="">
{% endfor %}

我的目标是通过预取或其他方式访问数据库一次。下面的代码不起作用,所以 views.py 是错误的,HTML 也是错误的。

{% for article in press %}
{{ article.title }}<br>
<img src="{{ MEDIA_URL }}{{ article.scans.main_image }}" alt="">
{% endfor %}

更新: 15 个副本消失了。 3 使用Prefetch()

优化后的查询

当您使用 Prefetch() 时,您应该分配一个 to_attr,这是预取对象的 list 可用的属性名称。目前,过多的查询是访问字段 press 的反向关系的结果,因为您分配了 related_name='scans'.

如果您将属性重命名为 scans_list.

之类的名称,您将能够访问预取的相关对象
press = Press.objects.prefetch_related(Prefetch(
    'scans',
    queryset=Scan.objects.select_related('press'),
    to_attr='scans_list'
))

然后在您的模板中您将能够:

{% for article in press %}
{{ article.title }}<br>
<img src="{{ MEDIA_URL }}{{ article.scans_list.0.main_scan }}" alt="">
{% endfor %}