在Django中创建自定义字段查找

jcd*_*yer 11 python django django-queryset

如何在Django中创建自定义字段查找

当过滤查询集,Django提供了一组您可以使用查找的:__contains,__iexact,__in,等等.我希望能够为我的经理提供新的查找,例如,有人可以说:

twentysomethings = Person.objects.filter(age__within5=25)
Run Code Online (Sandbox Code Playgroud)

并获取Person年龄在20到30之间的所有对象.我是否需要继承QuerySetManager类来执行此操作?如何实施?

Zac*_*ach 12

更灵活的方法是编写自定义QuerySet以及自定义管理器.使用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()
Run Code Online (Sandbox Code Playgroud)

这允许您链接自定义查询.所以这两个查询都是有效的:

Person.objects.in_age_range(20,30)

Person.objects.exclude(somefield = some_value).in_age_range(20, 30)
Run Code Online (Sandbox Code Playgroud)


oza*_*zan 6

而不是创建字段查找,最佳做法是创建一个管理器方法,可能看起来有点像这样:

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()
Run Code Online (Sandbox Code Playgroud)

然后使用就像这样:

twentysomethings = Person.objects.in_age_range(20, 30)
Run Code Online (Sandbox Code Playgroud)


Mat*_*ngo 6

首先,让我说没有Django机制,这意味着公开促进你想要的东西.

(编辑 - 实际上自Django 1.7以来有:https://docs.djangoproject.com/en/1.7/howto/custom-lookups/)

也就是说,如果你真的想完成这个,那么继承QuerySet并重写_filter_or_exclude()方法.然后创建一个自定义管理器,只返回你的自定义QuerySet(或猴子补丁Django QuerySet,yuck).我们在neo4django中这样做,以便在构建Neo4j特定Query对象时尽可能多地重用Django ORM查询集代码.

尝试(大致)这样的东西,改编自扎克的答案.我已经将字段查找解析的实际错误处理留给了读者:)

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()
Run Code Online (Sandbox Code Playgroud)

最后的评论 - 显然,如果你想链接自定义字段查找,这将变得非常毛茸茸.另外,我通常会在功能上更多地编写这个并使用itertools来表现,但是认为将其删除更为明确.玩得开心!


d33*_*tah 3

从 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)
Run Code Online (Sandbox Code Playgroud)

注册时,您可以直接使用Field.register_lookup(AbsoluteValueLessThan)