python - Django JSONField过滤查询集,其中过滤值是注释的总和值
问题描述
如何正确编写过滤器代码,使其仅返回未售罄的动物。
我正在使用 POSTGRES db、python3.6 和 Django 2.1.7(目前有 v2.2a1、v2.2b1 预发布版本)
我的问题是Django JSONField 过滤的扩展, 它过滤过滤器中的硬编码值。
我的案例需要过滤器中的注释值。
models.py
我知道模型可以优化,但我已经有 3 年多以来的大量记录
from django.db import models
from django.contrib.postgres.fields import JSONField
class Animal(models.Model):
data = models.JSONField(verbose_name=_('data'), blank=True)
class Sell(models.Model):
count = models.IntegerField(verbose_name=_('data'), blank=True)
animal = models.ForeignKey('Animal',
on_delete=models.CASCADE,
related_name="sales_set",
related_query_name="sold"
)
在我的 api 中,我只想退回那些还有东西要卖的动物
animal = Animal(data={'type':'dog', 'bread':'Husky', 'count':20})
我要过滤的应该类似于 animal.data['count'] > sum(animal.sales_set__count
Animal.objects.annotate(animals_sold=Sum('sales_set__count'))
.filter(data__contains=[{'count__gt': F('animals_sold')}])
用上面的代码我得到builtins.TypeError
TypeError: Object of type 'F' is not JSON serializable
如果我删除它,F
它不会过滤animals_sold的值,而是过滤文本'animals_sold'并且它没有任何帮助。
Animal.objects.annotate(animals_sold=Sum('sales_set__count'))
.filter(data__contains=[{'count__gt': F('animals_sold')}])
编辑1:这里还有一个可以链接的主题: Postgres: values query on json key with django
编辑 2:这里是一些附加代码,其中包含相关 django 票证中建议的自定义转换类
from django.db.models.constants import LOOKUP_SEP
from django.db.models import F, Q, Prefetch, Sum
from django.db.models import IntegerField, FloatField, ExpressionWrapper
from django.db.models.functions import Cast
from django.contrib.postgres.fields import JSONField
from django.contrib.postgres.fields.jsonb import KeyTransform, KeyTextTransform
class KeyIntegerTransform(KeyTransform): # similar to KeyTextTransform
""" trasnform the data.count to integer """
operator = '->>'
nested_operator = '#>>'
output_field = IntegerField()
class KeyIntTransformFactory:
""" helper class for the JSONF() """
def __init__(self, key_name):
self.key_name = key_name
def __call__(self, *args, **kwargs):
return KeyIntegerTransform(self.key_name, *args, **kwargs)
class JSONF(F):
""" for filtering on JSON Fields """
def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
rhs = super().resolve_expression(query, allow_joins, reuse, summarize, for_save)
field_list = self.name.split(LOOKUP_SEP)
for name in field_list[1:]:
rhs = KeyIntegerTransform(name)(rhs)
return rhs
到目前为止我尝试过的查询集过滤:
q = q.filter(data__contains={'count__gt':JSONF('sold_count_sum')})
# err: Object of type 'JSONF' is not JSON serializable
q = q.filter(sold_count_sum__lt=Cast(JSONF('data_count'), IntegerField()))
# err: operator does not exist: text ->> unknown
q = q.filter(sold_count_sum__lt=Cast(JSONF('data__count'), IntegerField()))
# err: 'KeyIntegerTransform' takes exactly 1 argument (0 given)
q = q.filter(sold_count_sum__lt=KeyIntegerTransform('count', 'data'))
# err: operator does not exist: text ->> unknown
q = q.filter(sold_count_sum__lt=F('data__count'))
# err: operator does not exist: text ->> unknown
q = q.filter(sold_count_sum__lt=F('data_count'))
# err: operator does not exist: text ->> unknown
q = q.filter(sold_count_sum__lt=JSONF('data_count'))
# err: operator does not exist: text ->> unknown
q = q.filter(sold_count_sum__lt=JSONF('data__count'))
# err: 'KeyIntegerTransform' takes exactly 1 argument (0 given)
q = q.filter(sold_count_sum__lt=JSONF('data', 'count'))
# err: JSONF.__init__() takes 2 params
解决方案
queryset = Animal.objects.annotate(
json=Cast(F('data'), JSONField()),
sold_count_sum = Sum('sold__count'),
sold_times = Count('sold'),
).filter(
Q(sold_times=0) | Q(sold_count_sum__lt=Cast(
KeyTextTransform('count', 'json'), IntegerField())
),
# keyword filtering here ...
# client = client
)
这对我有用,但它可以用一个好的 JSONF 字段进行优化
我们还可以(重新)移动json
注释并使用转换版本data
(可能有一些性能改进):
queryset = Animal.objects.annotate(
sold_count_sum = Sum('sold__count'),
sold_times = Count('sold'),
).filter(
Q(sold_times=0) | Q(sold_count_sum__lt=Cast(
KeyTextTransform('count', Cast(
F('data'), JSONField())), IntegerField()
)
),
# keyword filtering here ...
# client = client
)
推荐阅读
- java - 在 LWGL 中渲染一个正方形
- azure-devops - YAML 应该如何在我的构建管道中使用 Docker 容器(sidecar 服务)
- django - 管理中的 Django 横幅
- selenium - 获取错误方法发送键未定义类型 webelement
- json - ASP.NET Core 3 - Serilog 如何在 appsettings.json 文件中配置 Serilog.Sinks.Map?
- reactjs - 将数据传递给 react-vega
- android - 评级栏在回收站视图中为 0
- swift - Firebase 和 Google 身份验证构建失败 => API_UNAVAILABLE(macCatalyst) ==> “预期 ','”
- localhost - 将 Instagram 基本显示 API 与 Hugo 和 localhost 等静态站点结合使用?
- diagrammer - 使用 DiagrammeR 节点和边而不是 graphviz 创建流程图