views:

211

answers:

2

How do you create custom field lookups in Django?

When filtering querysets, django provides a set of lookups that you can use: __contains, __iexact, __in, and so forth. I want to be able to provide a new lookup for my manager, so for instance, someone could say:

twentysomethings = Person.objects.filter(age__within5=25)

and get back all the Person objects with an age between 20 and 30. Do I need to subclass the QuerySet or Manager class to do this? How would it be implemented?

+4  A: 

Rather than creating a field lookup, best practice would be to create a manager method, that might look a little bit like this:

class PersonManger(models.Manager):
    def in_age_range(self, min, max):
        return self.filter(age__gte=min, age__lt=max)

class Person(models.Model):
    age = #...

    objects = PersonManager()

then usage would be like so:

twentysomethings = Person.objects.in_age_range(20, 30)
ozan
+4  A: 

A more flexible way to do this is to write a custom QuerySet as well as a custom manager. Working from ozan's code:

class PersonQuerySet(models.query.QuerySet):
    def in_age_range(self, min, max):
        return self.filter(age__gte=min, age__lt=max)

class PersonManager(models.Manager):
    def get_query_set(self):
         return PersonQuerySet(self.model)

    def __getattr__(self, name):
        return getattr(self.get_query_set(), name)

class Person(models.Model):
    age = #...

    objects = PersonManager()

This allows you to chain your custom query. So both these queries would be valid:

Person.objects.in_age_range(20,30)

Person.objects.exclude(somefield = some_value).in_age_range(20, 30)
Zach
That's excellent! I'm still kinda hoping there's some API for hooking into the lookup field syntax, to take advantage of the rlationship traversal logic. I'll keep digging. In the meantime, I think this gets me a step closer to understanding how the pieces tie together. Thanks!
jcdyer