简体   繁体   中英

Django: Organize objects in a dictionary by foreign key

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).

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

View

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})

Template

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

I want send in context something like:

user_products = {<category.pk>: [list of Product objects in category]}

so I can access the product list of each category without defining a for loop each time and filtering them by request.user . 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!

There is better way to do this. Use Prefetch object. Prefetch will have only filtered data for each category.

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})

And then you can do this in template

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM