wilbbe01
wilbbe01

Reputation: 1971

How can I limit what m2m items are displayed in django template-side?

I have a queryset in a view limiting on an m2m field. The query appears to work correctly and gets the right parent objects, but when I loop through the parent.object_set.all in the template I see m2m objects that were technically not included in the initial query (or shouldn't be anyway), but now included because they are part of parent's child m2m object_set.

To try put this in a more generic example...I'm completely making up parents and children:

class Parent(models.Model):
    name = models.CharField(max_length=42)
    children = models.ManyToManyField(Child)

class Child(models.Model):
    ...
    # no, I'm not serious on this as a data structure, but for the example....
    male = models.BooleanField()
    female = models.BooleanField()

class ParentListView(ListView):
    ...
    def get_queryset(self):
        ...
        parents = Parent.objects.filter(children__male = True)

In the template.

{% for parent in parents %}
    {{ parent.name }}
    {% for child in parent.child_set.all %}
        {% comment %}
            oops, I got all the children of this parent....I only want those that were male from the original queryset. I need the child_set to only be those that met the criteria in that first queryset. How do I accomplish this?
        {% endcomment %}
    {% endfor %}
{% endfor %} 

Upvotes: 0

Views: 284

Answers (1)

Alasdair
Alasdair

Reputation: 309009

You can't provide filter arguments with the Django template language.

In Django 1.7+, you can use prefetch_related with a custom Prefetch object when defining the queryset in the view.

def get_queryset(self):
    ...
    parents = Parent.objects.filter(
        children__male=True
    ).prefetch_related(
        Prefetch('children', queryset=Child.objects.filter(male=True), to_attr='male_children')
    )

Note that in the example above, you probably need to use distinct() to prevent duplicated parents for each male child.

We have used the to_attr argument as recommended in the docs. In the template, we then loop through the male children.

{% for parent in parents %}
    {{ parent.name }}
    {% for child in parent.male_children %}
        {{ child }}
    {% endfor %}
{% endfor %} 

Upvotes: 1

Related Questions