How to access a field through a sub table Django

Question:

I Have a table Product and a table Variation, Variation has ManyToOne relation i.e. ForeignKey with the table.

I want only first object of Variation, so that I can render it fields, which I want.

The way I am currently implementing to call the first object in html page:

{{item.variation.price_set.first}}

Note: {{item}} is my product object or instance.

Html file:

<div class="carousel-inner">
    {% for item in TopItems %}
        {% if item.image %}
            <div class="carousel-item active">
                <img class="d-block img-fluid mx-auto m-auto" src="{{item.image.url}}" alt="{{item.name}}">
                <p>{{item.name}}</p>
                <p>{{item.variation.price_set.first}}</p>
                <p>{{item.variation_set.first}}</p>
                {% if item.variation.promotion_price_set.first%}
                <h2 class="text-primary">
                    {{item.variation.promotion_price}}
                </h2>
                <h2 class="text-primary">
                    {{item.variation.price}}
                </h2>
                {% else %}
                <h2 class="text-primary">
                    {{item.variation.price}}
                </h2>
                {% endif %}
            </div>
        {% endif %}
    {% endfor%}
</div>

Models.py

class Product(models.Model):
    name = models.CharField(max_length=255)
    short_description = models.TextField(max_length=255)
    long_description = models.TextField(max_length=255, blank=True)
    image = ResizedImageField(
        size=[550, 300], quality=70, upload_to='product_images/%Y/%m/%d')
    #image = models.ImageField(upload_to='product_images/%Y/%m/%d')
    slug = AutoSlugField(unique=True, always_update=False,
                         populate_from="name")
    top_item = models.BooleanField(default=False)
    is_available = models.BooleanField(default=True)
    category = models.ForeignKey(
        Category, related_name="product", on_delete=models.CASCADE)
    subcategory = models.ForeignKey(
        SubCategory, related_name="sub", on_delete=models.CASCADE, null=True, blank=True)
    objects = models.Manager()
    available = AvailableManager()

    class Meta:
        ordering = ("name",)  # vai ser ordenado pelo nome não o ID

    def get_absolute_url(self):
        return reverse("product:detail", kwargs={"slug": self.slug})



class Variation(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    name = models.CharField(
        max_length=80, blank=True, null=True)
    price = models.FloatField()
    promotion_price = models.FloatField(blank=True, null=True)
    stock = models.PositiveIntegerField(default=1)
    is_available = models.BooleanField(default=True)
    availableVariation = AvailableManagerVariation()
    objects = models.Manager()

    def __str__(self):
        return self.name or self.product.name

Views.py, the below method is passing all records to the template.

def get_context_data(self, **kwargs):
    context["TopItems"]= Product.objects.filter(top_item=True)

So, How can I get only first record of variaton, so that I can show respective prices?

Answers:

You can use nested loops, for accessing only first promotion price as well as price in the following way:

This is a minimal reproducible example, as not able to understand your design.

{% for item in TopItems %}

<div>
    <p> {{item.name}} </p>
    <p>{{forloop.counter}}st variation</p>

    {% for variation in item.variation_set.all  %}
    {% if forloop.counter0 < 1 %}
        <p>First variation price of  - {{variation.price}}</p> 
        {% comment  %}It will give only first price {% endcomment %}
        <p>First variation promotion price - {{variation.promotion_price}}</p>
        {% comment  %}It will give only first promotion price{% endcomment %}
    {% endif %}
    {% endfor %}
</div>
<br>
{%endfor%}

Note: There must be a space between template tags, as so write it as {% endif %} not {%endif%}, similarly for {% endfor %} not {%endfor%}.

Note: It will be better, if you pass your context keys in snake_case rather than PascalCase, so it can be passed as top_items rather than TopItems.

Answered By: Sunderam Dubey