Django - Отображение списка товаров со спецификациями по внешнему ключу
У меня есть модель Product, и модель ProductCustomField, которая хранит все спецификации для каждого продукта. Она имеет внешний ключ к Product.
Я просто хочу иметь возможность перечислить каждый продукт вместе с характеристиками этого продукта. Сейчас в коде для каждого товара отображаются характеристики всех товаров.
модель
class Product(models.Model):
name = models.CharField(max_length=255, unique=True)
def __str__(self):
return self.name[:50]
class ProductCustomField(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
key = models.CharField(max_length=255)
value = models.CharField(max_length=255)
def __str__(self):
return self.key[:50]
view
def product_lines_view(request):
context = {
'product_list': Product.objects.all(),
'spec_list': ProductCustomField.objects.all(),
}
return render(request, 'product_lines/product_list.html', context)
шаблон
{% for product in product_list %}
<h2>{{ product.name }}</h2>
<ul>
{% for spec in spec_list %}
<li>{{ spec.key }}</li>
<li>{{ spec.value }}</li>
</ul>
{% endfor %}
{% endfor %}
Вам не нужно загружать каждую спецификацию и передавать ее в spec_list, поэтому вы видите все спецификации несколько раз.
Ваш вид может выглядеть следующим образом:
def product_lines_view(request):
context = {
'product_list': Product.objects.all(),
}
return render(request, 'product_lines/product_list.html', context)
И ваш шаблон может выглядеть так:
{% for product in product_list %}
<h2>{{ product.name }}</h2>
<ul>
{% for spec in product.productcustomfield_set.all %}
<li>{{ spec.key }}</li>
<li>{{ spec.value }}</li>
{% endfor %}
</ul>
{% endfor %}
Объясняется это тем, что Django автоматически устанавливает атрибут обратного отношения, который состоит из имени модели, с которой оно связано, и суффикса _set. Таким образом, сделав это, вы получите все ProductCustomField, которые имеют отношение с каждым конкретным товаром.
Вы можете изменить это поведение, используя ключевое слово related_name, сделав в models.py:
class ProductCustomField(models.Model):
product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
related_name="specs"
)
key = models.CharField(max_length=255)
value = models.CharField(max_length=255)
def __str__(self):
return self.key[:50]
Таким образом, вы сможете обращаться к шаблону следующим образом:
{% for product in product_list %}
<h2>{{ product.name }}</h2>
<ul>
{% for spec in product.specs.all %}
<li>{{ spec.key }}</li>
<li>{{ spec.value }}</li>
{% endfor %}
</ul>
{% endfor %}
Вы должны знать, что это изменение, и все места, которые в настоящее время используют productcustomfield_set, больше не будут работать и должны быть изменены на specs.
Вот документация по ключевому слову django related_name: https://docs.djangoproject.com/en/4.0/ref/models/fields/#django.db.models.ForeignKey.related_name
Во-первых, вам не нужен "spec_list" в вашем контексте:
context = {
'product_list': Product.objects.all(),
}
Просто измените шаблон:
{% for product in product_list %}
<h2>{{ product.name }}</h2>
<ul>
{% for spec in product.productcustomfield_set.all %}
<li>{{ spec.key }}</li>
<li>{{ spec.value }}</li>
</ul>
{% endfor %}
{% endfor %}