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

发布于 2024-08-20 03:42:01 字数 479 浏览 3 评论 0原文

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

过滤查询集时,django 提供了一组可以使用的查找:__contains__iexact__in 等。我希望能够为我的经理提供新的查找,因此,例如,有人可能会说:

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

取回年龄在 20 到 30 岁之间的所有 Person 对象。我是否需要将其子类化QuerySetManager 类来执行此操作?它将如何实施?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

清旖 2024-08-27 03:42:02

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

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)

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)
诠释孤独 2024-08-27 03:42:02

首先,我要说的是,没有适当的 Django 机制来公开促进你想要的东西。

(编辑 - 实际上从 Django 1.7 开始就有: https://docs .djangoproject.com/en/1.7/howto/custom-lookups/ )

也就是说,如果你真的想要完成这个,子类QuerySet 并重写 _filter_or_exclude() 方法。然后创建一个自定义管理器,仅返回您的自定义QuerySet(或猴子补丁Django的QuerySet,恶心)。我们在 neo4django 中执行此操作,以便在构建特定于 Neo4j 的同时重用尽可能多的 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()

最后的评论 - 显然,如果您想链接自定义字段查找,这将变得非常棘手。另外,我通常会写得更实用一些,并使用 itertools 来提高性能,但我认为将其省略会更清楚。玩得开心!

First, let me say that there is no Django machinery in place that's meant to publicly facilitate what you'd like.

(Edit - actually since Django 1.7 there is: 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.

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

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!

千紇 2024-08-27 03:42:02

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

:注册时,您只需使用 Field.register_lookup(AbsoluteValueLessThan) 即可。

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:

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.

满地尘埃落定 2024-08-27 03:42:01

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

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文