我想通过 django queryset 获取 RangeField 的上限。
当您可以访问 python 对象时,
my_model.size_range.upper
就可以工作。不在查询引擎内部,无法使用 F
、When
、Case
...
class MyModel(models.Model):
size_range = IntergerRangeField()
MyModel.objects.all().annotate(new_upper=F('size_range__upper') + 1)
FieldError: Unsupported lookup 'upper' for IntegerRangeField or join on the field not permitted.
有什么想法吗?
我找到了一个半解决方案:
from django.db.models.functions import Upper
MyModel.objects.all().annotate(
upper=Upper('size_range')
).annotate(
new_upper=F('upper') + 1
)
虽然感觉很笨拙,但至少有用
如果有人更了解吗?我确信可以直接查询原始值。
使用 django,您可以注册 自定义查找。
IntegerRangeField.register_lookup(MyCustomLookup)
但是您无法直接在
Upper
或 RangeField
上注册 IntegerRangeField
,因为默认情况下循环使用与 output_field
相同的字段(例如:您想要的类型不是 IntegerRangeField
而是 IntegerField
) 。 output_field
必须明确。
使用
IntegerRangeField
自定义查找 Upper
如下所示:
from django.contrib.postgres.fields import IntegerRangeField
from django.db.models.functions import Upper
class IntegerRangeUpper(Upper):
output_field = IntegerRangeField.base_field()
IntegerRangeField.register_lookup(IntegerRangeUpper)
MyModel.objects.all().annotate(
new_upper=F('size_range__upper') + 1
)
由于在所有类型的范围字段上使用此功能可能很有用,并且通过查找手动注册范围字段的所有排列会非常重复,因此该过程可以推广。
此代码使用
base_field
作为 output_field
创建基于原始查找类的新查找类,并将它们注册到字段中。
import itertools
from django.contrib.postgres.fields import IntegerRangeField, BigIntegerRangeField, DateRangeField, DateTimeRangeField, DecimalRangeField
from django.db.models.functions import Lower, Upper
def register_range_lookup(
field, lookup_class
):
class CustomLookup(lookup_class):
output_field = field.base_field()
field.register_class_lookup(CustomLookup)
for range_field, lookup_class in itertools.product(
[
IntegerRangeField,
BigIntegerRangeField,
DateRangeField,
DateTimeRangeField,
DecimalRangeField,
],
[Lower, Upper],
):
register_range_lookup(range_field, lookup_class)
此外,
__lower
和__upper
现在可以像这样用于过滤:
# Gets all instances where the lower bounds are greater than 5
MyModel.objects.all().filter(
size_range__lower__gt=5
)