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 %}
Вернуться на верх