【问题标题】:Django Rest Framework filter on the base of JSONField nested objects基于 JSONField 嵌套对象的 Django Rest Framework 过滤器
【发布时间】:2019-07-11 17:40:05
【问题描述】:

我正在使用 Python(3)、Django(1.11) 和 DRF 开发一个项目,其中我必须在 json 对象字段的基础上过滤数据,该字段在 db 模型中保存为 JSONFIELD .

这是我尝试过的:

# model.py

from django.db import models
import jsonfield

class MyModel(models.Model):
    id = models.CharField(primary_key=True, max_length=255)
    type = models.CharField(max_length=255)
    props = jsonfield.JSONField()
    repo = jsonfield.JSONField()
    created_at = models.DateTimeField()
# serializers.py

class MyModelSerializer(serializers.ModelSerializer):
    props = serializers.JSONField()
    repo = serializers.JSONField()

    class Meta:
        model = EventModel
        fields = "__all__"
# JSON object
{
  "id":4633249595,
  "type":"PushEvent",
  "props":{
    "id":4276597,
    "login":"iholloway",
    "avatar_url":"https://avatars.com/4276597"
  },
  "repo":{
    "id":269910,
    "name":"iholloway/aperiam-consectetur",
    "url":"https://github.com/iholloway/aperiam-consectetur"
  },
  "created_at":"2016-04-18 00:13:31"
}
# views.py

class PropsEvents(generics.RetrieveAPIView):
    serializer_class = MyModelSerializer

    def get_object(self):
        print(self.request.parser_context['kwargs']['id'])
        queryset = MyModel.objects.filter(data__props__id=self.request.parser_context['kwargs']['id'])
        obj = get_object_or_404(queryset)
       return obj

它应该通过props ID 返回 MyModel 记录并且应该能够 返回所有 MyModel objects 的 JSON 数组,其中 props ID/mymodel/props/<ID> 的 GET 请求。如果请求的props 没有 如果存在,则 HTTP 响应代码应为 404,否则,响应 代码应为 200。 JSON 数组应按升序排序 按 MyModel ID。

当我向这个视图发送请求时,它返回一个错误:

> django.core.exceptions.FieldError: Unsupported lookup 'id' for JSONField or join on the field not permitted.
> [18/Feb/2019 10:37:39] "GET /events/actors/2790311/ HTTP/1.1" 500 16210

那么,如何根据id of props 过滤对象?

请帮帮我! 提前致谢!

【问题讨论】:

标签: python django django-rest-framework django-filter drf-queryset


【解决方案1】:

您正在寻找的功能是可能的,不幸的是它不是那么简单。据我所知,jsonfield 包不支持它,但您必须使用Postgres as your database backend and use its internal JSONField。我认为您可以选择以下之一:

  • 切换到 django.contrib.postgres.fields.JSONField 并在所有环境中使用 Postgres 作为您的数据库后端(然后支持此类查找)
  • 使数据遵循一定的架构,并将 JSONField 更改为单独的模型和表
  • 使用混合存储解决方案和 JSON 文档的专用解决方案
  • 将需要查询的字段提取到模型中 - 启用查询,但将非结构化数据保留在 JSONField 中。
class MyModel(models.Model):
    id = models.CharField(primary_key=True, max_length=255)
    type = models.CharField(max_length=255)
    props = jsonfield.JSONField()
    props_id = models.IntegerField(null=True)
    repo = jsonfield.JSONField()
    repo_id = models.IntegerField(null=True)
    created_at = models.DateTimeField()

然后手动设置id值或者在模型的save()中设置:

def save(self, *args, **kwargs):
    self.repo_id = self.repo.get("id")
    self.props_id = self.props.get("id")
    return super().save(*args, **kwargs)

【讨论】:

  • 我们可以使用sqlite3 来实现这一点,因为这是使用sqlite3 的要求吗?
  • 恐怕不是,这仅由 postgres 原生支持,因此它不在主要的 django 字段中。要继续使用 sqlite,您需要尝试其他解决方案之一,或者例如将您需要查询的字段提取到单独的模型字段中。我会用这个想法更新答案。
  • 嗨@mfrackowiak,在创建新对象时,porps_id 不会从self.props.get("id") 自动添加,当我从输入对象中删除`null=True` 时,它是必需的props_id。我无法更改输入对象结构,那么如何强制它从save() 添加props_id
【解决方案2】:

你应该使用

from django.contrib.postgres.fields import JSONField

代替

import jsonfield

然后我认为一切都应该正常工作

【讨论】:

  • 现在它为sqlite3.InterfaceError: Error binding parameter 0 - probably unsupported type.返回sqlite3.InterfaceError: Error binding parameter 0 - probably unsupported type.
  • sqlite3 不太适合生产环境,如果您使用 postgresql 作为数据库并且不适用于其他数据库,则此解决方案有效
猜你喜欢
  • 2015-09-21
  • 2017-10-19
  • 1970-01-01
  • 2017-04-09
  • 1970-01-01
  • 2021-09-21
  • 1970-01-01
  • 2014-07-07
  • 1970-01-01
相关资源
最近更新 更多