编辑 (这里有一个更有吸引力的简化答案。原始的详细答案在下面一行。)
我编写了一个模块django_dot_filter.py,它有助于编写更自然、更易读的查询过滤器。表达式以符号 V 开头,名称以点分隔:
from django_dot_filter import V
QuerySet.filter(V.myfk.child.onetoone.another.manytomany
.relation.monster.relationship
.mycustomlookup == ':P')
我把它读成带有字段的“这个未知的Variable”......因此我使用了字母V。该类实际上只是一个符号,后面可以跟点、方法、运算符等,所有内容都由. 分隔,而不是__。
支持标准可读的关系运算符,如<、<=、== 或!=,还支持括号和布尔运算符&、|、~。
Queryset.filter((V.some_related.my_field >= 10)
| ~V.field_x.startswith('Y') & (V.date_field.year() == 2017)
& V.price.range(10, 100))
每个查找都可以用经典的方式编写,例如属性V.date_field.year == 2017 或方法V.date_field.year() == 2017。许多查找作为带有参数V.my_field.regex(r'^[abc]') 而不是my_field__regex=value 的方法更具可读性。看到.date() 是一种查找方法,而.date 是一个字段的约定对我来说更具可读性。
这不是魔法。只有带有参数或关系运算符的方法是每次查找的最后部分。没有参数的方法只是一个符号,它是一个查找。具有价值的东西总是跟随。表达式被编译为 Q 表达式,包括布尔表达式。它们可以在类似的项目中轻松重用,保存到变量等,而 exclude(..) 条件而不是缺少 != 运算符的可重用性较低。
(目前不知道不支持的功能。已经写了一些测试。如果我得到足够的反馈,它可以打包。比经典的好name=value稍微冗长一点,适合简单的情况。
一个不同的答案,如果您喜欢具有可能相关字段的长链的可读过滤器,即使它们很复杂。
我今天写了一个简单的模块django_dot_filter.py,它允许对相关模型上的字段使用点语法并使用运算符 ==、!=、、>= 表示状况。可以使用位运算符~ | & 作为布尔运算符,类似于 Q objects 使用,但由于运算符优先级,比较必须用括号括起来。它的灵感来自 SQLAlchemy 和 Pandas 中使用的语法。
文档字符串:
class V(...):
"""
Syntax suger for more readable queryset filters with "." instead "__"
The name "V" can be understand like "variable", because a shortcut for
"field" is occupied yet.
The syntax is very similar to SQLAlchemy or Pandas.
Operators < <= == != >= > are supperted in filters.
>>> from django_dot_filter import V
>>>
>>> qs = Product.objects.filter(V.category.name == 'books',
>>> V.name >= 'B', V.name < 'F',
>>> (V.price < 15) | (V.date_created != today),
>>> ~V.option.in_(['ABC', 'XYZ'])
>>> )
This is the same as
>>> qs = Product.objects.filter(category__name='books',
>>> name__gte='B', name__lt='F',
>>> Q(price__lt=15) | ~Q(date_created=today),
>>> ~Q(option__in=['ABC', 'XYZ'])
>>> )
"""
(类“V”如果与点一起使用,会自动创建一个新实例。所有元素在关系运算符或.in_(iterable)方法后编译为Q表达式并再次删除实例。)
来自测试的一些示例
# this is V. syntax compiled Q syntax
test_eq(V.a.b.c == 1, Q(a__b__c=1))
test_eq(V.a == 1, Q(a=1))
test_eq(V.a != 1, ~Q(a=1))
test_eq(V.a < 2, Q(a__lt=2))
test_eq(V.a <= 3, Q(a__lte=3))
test_eq(V.a > 'abc', Q(a__gt='abc'))
test_eq(V.a >= 3.14, Q(a__gte=3.14))
test_eq((V.a == 1) & (V.b == 2), Q(a=1) & Q(b=2))
test_eq((V.a == 1) | (V.b == 2), Q(a=1) | Q(b=2))
test_eq((V.a == 1) | ~(V.b == 2), Q(a=1) | ~Q(b=2))
# method "in_(..)" is used because the word "in" is reserved.
test_eq(V.first_name.in_([1, 2]), Q(first_name__in=[1, 2]))
test_eq(~V.a.in_(('Tim', 'Joe')), ~Q(a__in=('Tim', 'Joe')))
# this should be eventually improved to support all lookup
# functions automatically e.g. by ".contains('abc')" instead of "=="
test_eq(V.a.contains == 'abc', Q(a__contains='abc'))
这是一个受您的问题启发的小玩笑,但它确实有效。我记得一些关于(核心开发人员?模糊记忆)的旧讨论,如果 Django 是一个新项目,语法name__operator=value 将不会再次使用。它非常简洁,但可读性较差。有两种官方语法已经来不及了。