Как извлекать данные из отношения «один ко многим» с помощью запросов Django

#django #django-models #django-views #django-templates #django-queryset

#django #django-модели #django-представления #django-шаблоны #django-queryset

Вопрос:

Я новичок в Django и пытаюсь получить сведения о пути к одному конкретному изображению из таблицы ProductImage, используя поле place в таблице и отображая изображение.Где place == 'Main Product Img' Product и ProductImage имеют отношение «один ко многим».

В моей базе данных есть две таблицы с именами 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)
  

Я смог написать запрос в оболочке для этого

 >>> 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'
  

Любая помощь, которая приветствуется.

Комментарии:

1. Вы не можете выполнять вызовы методов в шаблоне Django, скобки намеренно запрещены, чтобы люди не могли писать бизнес -логику в шаблоне.

2. @WillemVanOnsem значит, мне нужно создать отдельный файл для запросов?

3. нет, это ухудшило бы качество программного обеспечения на порядок.

4. @WillemVanOnsem, хорошо, теперь я понял… Большое вам спасибо… Не могли бы вы порекомендовать мне несколько ссылок для дальнейших исследований 🙂

Ответ №1:

Вы не можете выполнять вызовы методов в шаблоне 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 объект [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 %}