2

I have a Parent, Name, and Kid Models :

models.py

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


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


class Kid(models.Model):
    family = models.ForeignKey(Parent)
    name = models.ForeignKey(Name)
    age = models.IntegerField()
    city = models.CharField(max_length=250)

and I have a view function to display Parent objects only if Kid objects related to the Parent are all in the dictionary:

views.py

def index(request):
    patterns = [
        {'name__title': 'samy', 'age__lt': 15, 'city': 'paris'},
        {'name__title': 'sally', 'age__gt': 20, 'city': 'london'}
    ]
    filter_q = reduce(operator.or_, map(lambda p: Q(**p), patterns))
    qs = Kid.objects.filter(filter_q).values_list('id', 'family_id')
    family_ids = set()
    child_ids = list()
    for child_id, family_id in qs:
        family_ids.add(family_id)
        child_ids.append(child_id)
    incomplete_family_ids = set(Kid.objects.exclude(id__in=child_ids).filter(family_id__in=family_ids).values_list('family_id', flat=True).distinct())
    complete_family_ids = family_ids - incomplete_family_ids
    parents = Parent.objects.filter(id__in=complete_family_ids)
    template = 'index.html'
    context = {'parents': parents}
    return render(request, template, context)

What if I want to replace the pattern dictionary with models objects, like:

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


class PatternItems(models.Model):
    name = models.ForeignKey(Name)
    age = models.integer()
    city = models.CharField(max_length=250)
    pattern = models.ForeignKey(Pattern) 

so instead of the dictionary. I wonder if its possible to select a pattern from Pattern models to display Parent objects if all it's related Kid objects are in PatternItems using a Pattern object.

the whole idea is to make the filter dynamic and allow users to make their own filters .

Help?

2 Answers 2

3

You can replace this piece of code:

patterns = [
    {'name__title': 'samy', 'age__lt': 15, 'city': 'paris'},
    {'name__title': 'sally', 'age__gt': 20, 'city': 'london'}
]
filter_q = reduce(operator.or_, map(lambda p: Q(**p), patterns))

with a loop in which you add as many filters as you like:

# init patterns as a list of PatternItems
patterns = [ PatternItem(name=Name(title='samy'), age=15, city='paris'),
             PatternItem(name=Name(title='sally'), age=20, city='london'),
           ]
# iterate through the list normally
filters = []
for pt_item in patterns:
    filters.append(Q(name__title=pt_item.name.title) & 
                    Q(age__lt=pt_item.age) & Q(city=pt_item.city))

filter_q = filters[0]
for f in filters[1:]:
    filter_q |= f

Another way of getting patterns could be evaluating a QuerySet, e.g. using filter():

# any_column__gte and value here are just examples
patterns = PatternItem.objects.filter(any_column__gte=value)
# iterate through the patterns using .all()
filters = []
for pt_item in patterns.all():
    ...

The only problem with this approach is that you have to use the same comparison (i.e. age__lt) for each PatternItem in the list.

Please note: I'm using loops instead of the reduce() function because it doesn't exist in Python 3.

Sign up to request clarification or add additional context in comments.

5 Comments

where do I specify the pattern name ?
@DjangoGuy in the above code, you specify the name by setting PatternItem.name to an existing Name instance. Or -simpler- you can change your PatternItem.name field to a CharField and use Q(name__title=pt_item.name) instead.
Let's say I have a pattern called 'Adults' that has a X number of PatternItems related to it. where do I write 'Adults' in the code you wrote?
@DjangoGuy assuming that, inside Adults, the PatternItems are in a queryset named PatternItems, change the 3rd line to for pr_item in Adults.PatternItems.all():
do I have to import something before using filter_q? cause my editior doesn't recognize it . I also get this error 'ModelBase' object is not iterable for pt_item in Pattern:
0

So Here is the answer for my question that probably was not clear. @Paolo Stefan code was so close to what I needed.

view.py

patterns = PatternItems.objects.filter(pattern__title='adults')
filters = []
for pt_item in patterns.all():
    filters.append(Q(name__title=pt_item.name.title) &
                   Q(age__lt=pt_item.age) & Q(city=pt_item.city))

filter_q = filters[0]
for f in filters[1:]:
    filter_q |= f
qs = Kid.objects.filter(filter_q).values_list('id', 'family_id')
family_ids = set()
child_ids = list()
for child_id, family_id in qs:
    family_ids.add(family_id)
    child_ids.append(child_id)
incomplete_family_ids = set(Kid.objects.exclude(id__in=child_ids).filter(family_id__in=family_ids).values_list('family_id', flat=True).distinct())
complete_family_ids = family_ids - incomplete_family_ids
parents = Parent.objects.filter(id__in=complete_family_ids)
template = 'index.html'
context = {'parents': parents}
return render(request, template, context)

The only difference is : patterns = PatternItems.objects.filter(pattern__title='adults')

which filters the patternitems with the pattern needed 'adults'.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.