DjangoGuy DjangoGuy - 22 days ago 5
Python Question

Filter objects based on related objects

So I have a form where user can post

Parent
details also form for Kid for a different model.

what i need is to allow users to access list of
Parent
objects by filtering their related objects
Kid


let's say a filter to list Parents objects that has children named
X
older than
Z
live in
Y
city AND has no children named
X
who is younger than
Z
live in
Y
city .

models.py :

class Parent(models.Model):
title = models.CharField(max_length=250)


class Kid(models.Model):
cities = (
('city1', city1),
('city2', city2)
)
family = models.ForeignKey(Parent)
title = models.CharField(max_length=250)
age = models.CharField(max_length=250)
city = models.CharField(choices=cities)


Views.py :

def index(request):
my_pattern = (
(name=samy, age_lt=15, city=paris),
)
filter_ids = Kid.objects.filter(my_pattern).values_list('parents_id', flat=True).distinct()
exclude_ids = Kid.objects.exclude(my_pattern).values_list('parents_id', flat=True).distinct()
parents = Parent.objects.filter(id__in=filter_ids).exclude(id__in=exclude_ids)
template = 'index.html'
context = {'parents': parents}
return render(request, template, context)


as the views.py shows i listed the pattern as a tuple but didn't work!

i am also wondering if this is the way to do django Q objects except for adding Q in the beginning!

Answer

my_pattern should be dictionary, and you need to use ** unpacking in filter and exclude.

def index(request):
    my_pattern = {'name': 'samy', 'age__lt': 15, 'city': 'paris'}
    filter_ids = Kid.objects.filter(**my_pattern).values_list('family_id', flat=True).distinct()
    exclude_ids = Kid.objects.exclude(**my_pattern).values_list('family_id', flat=True).distinct()
    parents = Parent.objects.filter(id__in=filter_ids).exclude(id__in=exclude_ids)
    template = 'index.html'
    context = {'parents': parents}
    return render(request, template, context)

If you have mutiple patterns, then you need to use Q objects

import operator
from functools import reduce

def index(request):
    patterns = [
        {'name': 'samy', 'age__lt': 15, 'city': 'paris'},
        {'name': 'sally', 'age__gt': 20, 'city': 'london'}
    ]
    filter_q = reduce(operator.or_, map(lambda p: Q(**p), patterns))
    filter_ids = Kid.objects.filter(filter_q).values_list('family_id', flat=True).distinct()
    exclude_ids = Kid.objects.exclude(filter_q).values_list('family_id', flat=True).distinct()
    parents = Parent.objects.filter(id__in=filter_ids).exclude(id__in=exclude_ids)
    template = 'index.html'
    context = {'parents': parents}
    return render(request, template, context)