代码之家  ›  专栏  ›  技术社区  ›  jcdyer Anand S Kumar

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

  •  11
  • jcdyer Anand S Kumar  · 技术社区  · 15 年前

    如何创建自定义 field lookups 在Django?

    在筛选查询集时,Django提供了一组可以使用的查找: __contains , __iexact ,请 __in 等等。我希望能够为我的经理提供新的查找,例如,有人可以说:

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

    然后回来 Person 年龄在20到30岁之间的物体。我需要将 QuerySet Manager 要上课吗?如何实施?

    4 回复  |  直到 10 年前
        1
  •  1
  •   d33tah    10 年前

    从Django1.7开始,有一个简单的方法来实现它。您的示例实际上与 the documentation :

    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)
    

    注册时,您可以使用 Field.register_lookup(AbsoluteValueLessThan) 相反。

        2
  •  12
  •   Zach    15 年前

    更灵活的方法是编写自定义查询集和自定义管理器。根据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()
    

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

    Person.objects.in_age_range(20,30)
    
    Person.objects.exclude(somefield = some_value).in_age_range(20, 30)
    
        3
  •  6
  •   ozan    15 年前

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

    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()
    

    使用方法如下:

    twentysomethings = Person.objects.in_age_range(20, 30)
    
        4
  •  6
  •   Andy Baker    10 年前

    首先,我要说的是,没有任何Django机器可以公开地为你想要的东西提供便利。

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

    也就是说,如果你 真正地 想要完成这个,子类 QuerySet 并覆盖 _filter_or_exclude() 方法。然后创建一个只返回自定义 查询集 (或猴子补丁Django's 查询集 ,哎呀!我们这样做 neo4django 在构建特定于NEO4J的查询集代码时,尽可能多地重用django-orm查询集代码 Query 物体。

    尝试类似这样的(粗略的)东西,根据扎克的答案改编。我将字段查找解析的实际错误处理留给读者作为练习:)

    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()
    

    最后一句话——很明显,如果你想链接自定义的字段查找,这将变得相当棘手。另外,我通常会写得更实用一些,并使用itertools来提高性能,但我认为更清楚的是不要写了。玩得高兴!

    推荐文章