Django: Organize objects in a dictionary by foreign key
<p>I have this problem: I've got two models, products and categories, the categories are defined in a fixture (they won't change), and I need to display all categories in a single template, as a grid. The models are rather simple, the important thing is that Products have a foreign key pointing to Category model, and another one pointing to User model (owner).</p>

<p>I have to display all products in each category block (CSS doesn't matter, I just need to be able to bring them up) but the solution I've got so far is this</p>

<p><strong>View</strong></p>

<pre><code>def index(request):
    user_products = []
    if request.user.is_authenticated():
        user_products = Product.objects.filter(owner=request.user)
    categories = Category.objects.all()
    return render(request, 'index.html', {'user_products': user_products, 'categories': categories})
</code></pre>

<p><strong>Template</strong></p>

<pre><code>&lt;!-- This goes for each category, 12 in total --&gt;
&lt;div&gt;
    &lt;h3&gt;Category (name hardcoded)&lt;/h3&gt;
    {% for category in categories %}
        {% if category.pk == 3 %}
            &lt;ul class="product_list"&gt;
            {% for product in category.product_set.all %}
                {% if product.owner == request.user %}
                &lt;li&gt;
                    &lt;div class="product"&gt;
                        &lt;h2 class="title"&gt;{{ product.title }} &lt;/h2&gt;
                        &lt;p class="text"&gt;{{ product.text }}&lt;/p&gt;
                    &lt;/div&gt;
                &lt;/li&gt;
                {% endif %}
            {% empty %}
            {% endfor %}
            &lt;/ul&gt;
        {% endif %}
    {% endfor %}
&lt;/div&gt;
</code></pre>

<p>I want send in context something like:</p>

<pre><code>user_products = {&lt;category.pk&gt;: [list of Product objects in category]}
</code></pre>

<p>so I can access the product list of each category without defining a <em>for</em> loop each time <strong>and</strong> filtering them by <em>request.user</em>. Can you think of a better way to do this? I had to hardcode each category because they have a given order, but if you know how to display them dynamically while keeping that order it would be great. Thanks!</p>

<p>There is better way to do this. Use <a href="https://docs.djangoproject.com/en/1.10/ref/models/querysets/#prefetch-objects" rel="nofollow"><code>Prefetch</code></a> object. Prefetch will have only filtered data for each category.</p>

<pre><code>from django.db.models.query import Prefetch

def index(request):
    user_products = []
    if request.user.is_authenticated():
        user_products = Product.objects.filter(owner=request.user)
    else:
        # answering comment
        user_products = Product.objects.none()
    prefetch = Prefetch('product_set', queryset=user_products)
    categories = Category.objects.all().prefetch_related(prefetch)

    return render(request, 'index.html', {'categories': categories})
</code></pre>

<p>And then you can do this in template</p>

<pre><code>&lt;!-- You need to do this only once. No need to copy this for each category --&gt;
&lt;div&gt;
    {% for category in categories %}
    &lt;h3&gt;Category {{ category.name }}&lt;/h3&gt;
        &lt;ul class="product_list"&gt;
        {% for product in category.product_set.all %}
            &lt;li&gt;
                &lt;div class="product"&gt;
                    &lt;h2 class="title"&gt;{{ product.title }} &lt;/h2&gt;
                    &lt;p class="text"&gt;{{ product.text }}&lt;/p&gt;
                &lt;/div&gt;
            &lt;/li&gt;
        {% endfor %}
        &lt;/ul&gt;
    {% endfor %}
&lt;/div&gt;
</code></pre>

python django django-templates