How do you create custom field lookups in Django?
如何在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:
在过滤查询集时,django提供了一组查找,您可以使用它们:__contains、__iexact、__in等。我想为我的经理提供一个新的查找,例如,有人可能会说:
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?
然后把所有年龄在20到30岁的人都拿回来。我需要对QuerySet或Manager类进行子类化吗?如何实施?
4 个解决方案
#1
1
As of Django 1.7, there is a simple way to implement it. Your example is actually very similar to the one from the documentation:
对于Django 1.7,有一种简单的实现方法。你的例子和文档中的例子非常相似:
from django.db.models import Lookup
class AbsoluteValueLessThan(Lookup):
lookup_name = 'lt'
def as_sql(self, qn, connection):
lhs, lhs_params = qn.compile(self.lhs.lhs)
rhs, rhs_params = self.process_rhs(qn, connection)
params = lhs_params + rhs_params + lhs_params + rhs_params
return '%s < %s AND %s > -%s' % (lhs, rhs, lhs, rhs), params
AbsoluteValue.register_lookup(AbsoluteValueLessThan)
While registering, you can just use Field.register_lookup(AbsoluteValueLessThan)
instead.
注册时,您可以使用Field.register_lookup(AbsoluteValueLessThan)代替。
#2
12
A more flexible way to do this is to write a custom QuerySet as well as a custom manager. Working from ozan's code:
更灵活的方法是编写自定义查询集和自定义管理器。工作从ozan的代码:
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)
#3
6
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)
#4
6
First, let me say that there is no Django machinery in place that's meant to publicly facilitate what you'd like.
首先,我要说的是,目前还没有Django机器可以公开地为您想要的东西提供便利。
(Edit - actually since Django 1.7 there is: https://docs.djangoproject.com/en/1.7/howto/custom-lookups/ )
(编辑——实际上从Django 1.7开始有:https://docs.djangoproject.com/en/1.7/howto/custom-lookups/)
That said, if you really want to accomplish this, subclass QuerySet
and override the _filter_or_exclude()
method. Then create a custom manager that only returns your custom QuerySet
(or monkey-patch Django's QuerySet
, yuck). We do this in neo4django to reuse as much of the Django ORM queryset code as possible while building Neo4j-specific Query
objects.
也就是说,如果您真的想实现这个目标,那么子类QuerySet并覆盖_filter_or_rejection()方法。然后创建一个定制的管理器,它只返回定制的QuerySet(或monkey-patch Django的QuerySet, yuck)。我们在neo4django中这样做是为了在构建neo4j特定的查询对象时尽可能多地重用Django ORM queryset代码。
Try something (roughly) like this, adapted from Zach's answer. I've left actual error handling for the field lookup parsing as an exercise for the reader :)
尝试一些(粗略地)类似的东西,改编自Zach的答案。我将字段查找解析的实际错误处理留给了读者:)
class PersonQuerySet(models.query.QuerySet):
def _filter_or_exclude(self, negate, *args, **kwargs):
cust_lookups = filter(lambda s: s[0].endswith('__within5'), kwargs.items())
for lookup in cust_lookups:
kwargs.pop(lookup[0])
lookup_prefix = lookup[0].rsplit('__',1)[0]
kwargs.update({lookup_prefix + '__gte':lookup[1]-5,
lookup_prefix + '__lt':lookup[1]+5})
return super(PersonQuerySet, self)._filter_or_exclude(negate, *args, **kwargs)
class PersonManager(models.Manager):
def get_query_set(self):
return PersonQuerySet(self.model)
class Person(models.Model):
age = #...
objects = PersonManager()
Final remarks - clearly, if you want to chain custom field lookups, this is going to get pretty hairy. Also, I'd normally write this a bit more functionally and use itertools for performance, but thought it was more clear to leave it out. Have fun!
最后的注意事项——显然,如果您想要链接自定义字段查找,这将变得非常麻烦。此外,我通常会在功能上写得更详细一些,并使用itertools进行性能测试,但我认为应该更清楚地将其排除在外。玩得开心!
#1
1
As of Django 1.7, there is a simple way to implement it. Your example is actually very similar to the one from the documentation:
对于Django 1.7,有一种简单的实现方法。你的例子和文档中的例子非常相似:
from django.db.models import Lookup
class AbsoluteValueLessThan(Lookup):
lookup_name = 'lt'
def as_sql(self, qn, connection):
lhs, lhs_params = qn.compile(self.lhs.lhs)
rhs, rhs_params = self.process_rhs(qn, connection)
params = lhs_params + rhs_params + lhs_params + rhs_params
return '%s < %s AND %s > -%s' % (lhs, rhs, lhs, rhs), params
AbsoluteValue.register_lookup(AbsoluteValueLessThan)
While registering, you can just use Field.register_lookup(AbsoluteValueLessThan)
instead.
注册时,您可以使用Field.register_lookup(AbsoluteValueLessThan)代替。
#2
12
A more flexible way to do this is to write a custom QuerySet as well as a custom manager. Working from ozan's code:
更灵活的方法是编写自定义查询集和自定义管理器。工作从ozan的代码:
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)
#3
6
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)
#4
6
First, let me say that there is no Django machinery in place that's meant to publicly facilitate what you'd like.
首先,我要说的是,目前还没有Django机器可以公开地为您想要的东西提供便利。
(Edit - actually since Django 1.7 there is: https://docs.djangoproject.com/en/1.7/howto/custom-lookups/ )
(编辑——实际上从Django 1.7开始有:https://docs.djangoproject.com/en/1.7/howto/custom-lookups/)
That said, if you really want to accomplish this, subclass QuerySet
and override the _filter_or_exclude()
method. Then create a custom manager that only returns your custom QuerySet
(or monkey-patch Django's QuerySet
, yuck). We do this in neo4django to reuse as much of the Django ORM queryset code as possible while building Neo4j-specific Query
objects.
也就是说,如果您真的想实现这个目标,那么子类QuerySet并覆盖_filter_or_rejection()方法。然后创建一个定制的管理器,它只返回定制的QuerySet(或monkey-patch Django的QuerySet, yuck)。我们在neo4django中这样做是为了在构建neo4j特定的查询对象时尽可能多地重用Django ORM queryset代码。
Try something (roughly) like this, adapted from Zach's answer. I've left actual error handling for the field lookup parsing as an exercise for the reader :)
尝试一些(粗略地)类似的东西,改编自Zach的答案。我将字段查找解析的实际错误处理留给了读者:)
class PersonQuerySet(models.query.QuerySet):
def _filter_or_exclude(self, negate, *args, **kwargs):
cust_lookups = filter(lambda s: s[0].endswith('__within5'), kwargs.items())
for lookup in cust_lookups:
kwargs.pop(lookup[0])
lookup_prefix = lookup[0].rsplit('__',1)[0]
kwargs.update({lookup_prefix + '__gte':lookup[1]-5,
lookup_prefix + '__lt':lookup[1]+5})
return super(PersonQuerySet, self)._filter_or_exclude(negate, *args, **kwargs)
class PersonManager(models.Manager):
def get_query_set(self):
return PersonQuerySet(self.model)
class Person(models.Model):
age = #...
objects = PersonManager()
Final remarks - clearly, if you want to chain custom field lookups, this is going to get pretty hairy. Also, I'd normally write this a bit more functionally and use itertools for performance, but thought it was more clear to leave it out. Have fun!
最后的注意事项——显然,如果您想要链接自定义字段查找,这将变得非常麻烦。此外,我通常会在功能上写得更详细一些,并使用itertools进行性能测试,但我认为应该更清楚地将其排除在外。玩得开心!