Django: How can I change my code to access ForeignKey fields through 3 tables?


I tried to get values with using three tables.

cart_c = Cart.objects.select_related('item').filter(user=2)
context = {'cart_c': cart_c}

I got QuerySet, ‘cart_c’, but I could not refer from ‘cart_c’.

So, I made three tables joined with prefetch_related().

items = Item.objects.prefetch_related('item_photo', 'cart_item').filter(cart__user=2)
context = {'items': items}

But, I do not have any good idea to access and Cart.quantity except this.

# Or [i.quantity for item in items for i in item.cart_item.all()]

I think this way is not proper and sophisticated to use in Django Template System.

It’s complicated I think.

I found a relative question in StackOverFlow, and one of the answers is same like code I wrote.


Could anyone teach me how I should change and write code to send QuerySet or dictionary to Django Template to access, Item.price, and Cart.quantity with using three tables, Item, ItemPhoto and Cart?


class Item(models.Model): # Product
    name = models.CharField("item_name", max_length=255, blank=False, default="")
    price = models.DecimalField("price",

class ItemPhoto(models.Model): # Photo
    item = models.ForeignKey(Item, on_delete=models.CASCADE,
        related_name="item_photo", verbose_name="item_id",)
    photo = models.ImageField("item_photo", blank=True, 

class Cart(models.Model): # Order
    user = models.ForeignKey(settings.AUTH_USER_MODEL, 
        on_delete=models.CASCADE, null=False,
    item = models.ForeignKey(Item, verbose_name="item_id", 
        related_name="cart_item", null=False,)
    quantity = models.PositiveSmallIntegerField("quantity", default=0)

Python: 3.9 / Django 4.1

Asked By: K-Yuuma



What you have as Cart, should actually be CartItem. A model that represents the quantity of each item that a user can put in their actual Cart. In my answer, I’m referring it as CartItem.

You can access Item’s attributes, its photo, and cart quantity in the following manner:

For Postgres, we’ve used ArrayAgg to combine the list of photos per item and annotate it on the CartItem object:

from django.contrib.postgres.aggregates import ArrayAgg

cart_item_qs = CartItem.objects.filter(user=2).select_related('item').annotate(

# You can then pass this in the context
context = {"cart_items": cart_item_qs}

Finally, in the template, you can iterate on your cart_items like:

{% for cart_item in cart_items %}
    {{ }} - {{ cart_item.item.price }}
    {% for photo in cart_item.item_photos %}
        <img src="{{ photo.url }}" />
    {% endfor %}
{% endfor %}


For MySQL, we can use Func expression to apply GROUP_CONCAT SQL function to concatenate all photo from CartItem

cart_item_qs = CartItem.objects.filter(user=2).select_related('item').annotate(
    item_photos=models.Func(models.Value('GROUP_CONCAT'), models.Value(''), function='GROUP_CONCAT')

# You can then pass this in the context
context = {"cart_items": cart_item_qs}

To parse this in the template, you’ll do something like this:

{% for cart_item in cart_items %}
    {{ }} - {{ cart_item.item.price }}
    {% for photo in",") %}
        <img src="{{ photo }}" />
    {% endfor %}
{% endfor %}

If the GROUP_CONCAT is not supported, then you can also write this query using Subquery in Django.

PS: You can use django-versatilimagefield for storing your images. It provides a lot of additional functionality for storing and retrieving images in different dimensions.

Answered By: Sanyam Khurana