如果我有一个项目的单独模型和与该项目相关的图像的单独模型,我如何将它们组合到查询中?

If I have a separate models for an item and a separate model for the images relating to that item, how can I combine these into a query?

我正在尝试在卡片视图中显示我的查询集中的项目。我有一个 Item 模型和一个单独的 ItemImage 模型,以允许为单个项目上传多个图像。每个图像还有一个 属性 is_primary 来确定它是否是将显示在动态生成的卡片中的图像。

我已成功显示来自我的项目模型查询集的项目详细信息。但是,我很难理解如何将 itemimage.url 属性与我传递到模板中的 all_items 上下文结合起来。如何确保我的图像查询集中的图像与分配给它们的项目的 pk 相匹配,并且也是该项目的主要照片?

views.py

from django.shortcuts import render, redirect

# Create your views here.

from django.http import HttpResponse
from django.contrib.auth.forms import UserCreationForm
from django.contrib import messages 
from .models import Item, ItemImage
    
def home(request):
    all_items = Item.objects.all()
    item_images = ItemImage.objects.all() ItemImage.objects.filter(is_primary=True)
    context = {'items': all_items}
    return render(request,'items/home.html',context)
    

models.py

from django.db import models
from django.utils import timezone
from decimal import Decimal
# Create your models here.

class Item(models.Model):
    name = models.CharField(max_length=70)
    date_added = models.DateTimeField(default=timezone.now)
    price = models.DecimalField(max_digits=9, decimal_places=2, default=Decimal('0.00'))
    description = models.CharField(max_length=300)
    def __str__(self):
        return self.name
        

class ItemImage(models.Model):
    url = models.CharField(max_length=2000)
    is_primary = models.BooleanField(default=False)
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    

home.html

{% extends "base.html" %}

{% block content %}
<div class="content-section d-flex flex-row">
    {% if items%}
        {% for item in items %}
    
        <div class="card" style="width: 18rem;">
              <img class="card-img-top" src="{{item.image_ur}}" alt="Card image cap">
              <div class="card-body">
                <h5 class="card-title">{{item.name}}</h5>
                <p class="card-text">{{item.description}}</p>
                <a href="#" class="btn btn-primary">Add to cart</a>
              </div>
        </div>
        
        
        {% endfor %}
    {% else %} <h2> No items available. </h2>
    {% endif %}
</div>

{% endblock content %}

您可以使用 Prefetch object [Django-doc] 为每个 Item 获取主 ItemImages,其中:

from django.db.models import Prefetch

def home(request):
    all_items = Item.objects.prefetch_related(
        Prefetch(
            'itemimage_set',
            ImageItem.objects.<b>filter(is_primary=True)</b>
            to_attr='primary_images'
        )
    )
    context = {'items': all_items}
    return render(request,'items/home.html',context)

在您看来,您可以使用 item.primary_images 获取与 item 相关的主要图像,因此:

<div class="content-section d-flex flex-row">
  {% for item in items %}
    <div class="card" style="width: 18rem;">
      {% <b>for img in item.primary_images</b> %}
        <img class="card-img-top" src="{{ img.url }}" alt="Card image cap">
      {% <b>endfor</b> %}
      <div class="card-body">
        <h5 class="card-title">{{item.name}}</h5>
        <p class="card-text">{{item.description}}</p>
        <a href="#" class="btn btn-primary">Add to cart</a>
      </div>
    </div>
  {% empty %}
    <h2> No items available. </h2>
  {% endfor %}
</div>

Note: Django has a {% for … %} … {% empty %} … {% endfor %} template tag [Django-doc] to make rendering a message when a collection is empty more convenient.

预取解决方案对我有用。我尝试的另一种解决方案是进行原始查询。可能有一个更简洁的基于模型的解决方案,但现在原始 SQL 对我来说更具可读性。

def home(request):
    all_items = Item.objects.raw("SELECT items_item.id, items_item.name, items_item.description, items_item.price, items_item.date_added, items_itemimage.url AS imgurl FROM items_item LEFT JOIN items_itemimage ON items_item.id = items_itemimage.item_id WHERE items_itemimage.is_primary = true ORDER BY date_added DESC")
    context = {'items': all_items}
    return render(request,'items/home.html',context)