如何使用 Django 查询从一对多关系中获取数据

How to fetch data from one to many relationship using Django queries

我是 Django 的新手,正在尝试使用 place[=47 从 ProductImage table 获取一张特定图像的路径详细信息=] 字段中的 table 并显示 image.Where place == 'Main Product Img' Product 和 ProductImage 是一对多的关系。

我的数据库有两个 table 名为 Product(id, name, price,..)ProductImage(id, productid, image, place) 其中包含三张图片('Main Product IMG', 'Sub Img 1', 'Sub Img 2')

这是我的models.py

from django.db import models
from django.contrib.auth.models import User


class Product(models.Model):
    name = models.CharField(max_length=200)
    desc = models.TextField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    discount = models.DecimalField(max_digits=4, decimal_places=0)

    def __str__(self):
        return self.name

class ProductImage(models.Model):
    PLACEHOLDER= (
        ('Main Product Image', 'Main Product Image'),
        ('Sub Img 1', 'Sub Img 1'),
        ('Sub Img 2', 'Sub Img 2'),
    )
    product = models.ForeignKey(Product, related_name='image_set', on_delete=models.CASCADE)
    image = models.ImageField(upload_to='images/')
    place = models.CharField(max_length=20, choices=PLACEHOLDER)

    def __str__(self):
        return str(self.id)

views.py

def menu(request):
    products = Product.objects
    images = ProductImage.objects

    context = {'products': products, 'images': images}
    return render(request, 'store/menu.html', context)

模板

{% for product in products.all %}

  <a href="#">

    <img src="{{ images.filter(product__id=product.id, place='Main Product Image')[0].image.url }}" class="img-fluid mt-3">

    {% if product.discount != 0 %}
      <span class="bonus">{{ product.discount }}% off</span>
    {% endif %}
  </a>

settings.py

STATIC_URL = '/static/'

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),

]

# Media files

MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR

urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('menu', views.menu, name='menu'),
]
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

我能够在 shell 中编写查询来做到这一点

>>> images = ProductImage.objects
>>> images.filter(product__id='1', place='Main Product Image')[0].image
<ImageFieldFile: images/img3.jpg>

其余操作正常。我已经尝试解决这个问题好几个小时了,但一直

TemplateSyntaxError at /menu
Could not parse the remainder: '(product__id=product.id, place='Main Product Image')[0].image.url'
from 'images.filter(product__id=product.id, place='Main Product Image')[0].image.url'

感谢任何帮助。

不能在Django模板中进行方法调用,括号是故意不允许的,以防止人们在其中编写业务逻辑模板。

你可以定义一个方法,比如在Product模型中:

class Product(models.Model):
    name = models.CharField(max_length=200)
    desc = models.TextField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    discount = models.DecimalField(max_digits=4, decimal_places=0)

    @property
    def main_product_image(self):
        return self.image_set.filter(place='Main Product Image').first()

    def __str__(self):
        return self.name

然后用以下方式渲染:

<img src="{{ product.main_product_image.image.url }}"; class="img-fluid mt-3">

但这会导致 N+1 问题,如果允许使用参数调用方法,这也会导致 N+1问题。

您可以使用 Prefetch object [Django-doc] 来批量加载主要图像:

from django.db.models import Prefetch

def menu(request):
    products = Product.objects.prefetch_related(
        Prefetch(
            'image_set',
            ProductImage.objects.filter(place='Main Product Image'),
            to_attr='main_images'
        )
    )

    context = {'products': products}
    return render(request, 'store/menu.html', context)

然后你可以渲染它:

{% for product in products %}
    <img src="{{ product.main_images.0.image.url }}" class="img-fluid mt-3">;
    …
{% endfor %}