python - 如何使用 Django REST Framework 在 INSERT 之前停止 SELECT

标签 python django postgresql performance django-rest-framework

我用过 Django REST Framework公开仅供其他服务使用的 API 以发布新数据。它基本上只接受 json 并将其插入数据库。就这样。

这是一个相当大的数据源(有时超过 100 条记录/秒),所以我需要稍微调整一下。

所以我记录了运行的 (PostgreSQL) 查询,我看到每个 POST 都给出了 3 个查询:

2019-10-01 11:09:03.320 CEST [23983] postgres@thedb LOG:  statement: SET TIME ZONE 'UTC'
2019-10-01 11:09:03.322 CEST [23983] postgres@thedb LOG:  statement: SELECT (1) AS "a" FROM "thetable" WHERE "thetable"."id" = 'a7f74e5c-7cad-4983-a909-49857481239b'::uuid  LIMIT 1
2019-10-01 11:09:03.363 CEST [23983] postgres@thedb LOG:  statement: INSERT INTO "thetable" ("id", "version", "timestamp", "sensor", [and 10 more fields...]) VALUES ('a7f74e5c-7cad-4983-a909-49857481239b'::uuid, '1', '2019-10-01T11:09:03.313690+02:00'::timestamptz, 'ABC123', [and 10 more fields...])

我将 INSERT 的数据库调得很快,但是 SELECT 很慢。所以我想从系统中删除 SELECT。我将这一行添加到序列化程序中:

id = serializers.UUIDField(validators=[])

但它仍然执行 SELECT。有人知道如何防止 SELECT 发生吗?

获取完整信息;完整的序列化器现在看起来像这样:

import logging
from rest_framework import serializers
from .models import TheData

log = logging.getLogger(__name__)


class TheDataSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = TheData
        fields = [
            'id',
            'version',
            'timestamp',
            'sensor',
            [and 10 more fields...]
        ]


class TheDataDetailSerializer(serializers.ModelSerializer):

    id = serializers.UUIDField(validators=[])

    class Meta:
        model = TheData
        fields = '__all__'

编辑

按照 frankie567 的要求,ViewSet:

class TheDataViewSet(DetailSerializerMixin, viewsets.ModelViewSet):
    serializer_class = serializers.TheDataSerializer
    serializer_detail_class = serializers.TheDataDetailSerializer

    queryset = TheData.objects.all().order_by('timestamp')

    http_method_names = ['post', 'list', 'get']

    filter_backends = [DjangoFilterBackend]
    filter_class = TheDataFilter

    pagination_class = TheDataPager

    def get_serializer(self, *args, **kwargs):
        """ The incoming data is in the `data` subfield. So I take it from there and put
        those items in root to store it in the DB"""
        request_body = kwargs.get("data")
        if request_body:
            new_request_body = request_body.get("data", {})
            new_request_body["details"] = request_body.get("details", None)
            request_body = new_request_body
            kwargs["data"] = request_body

        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)

最佳答案

经过一些挖掘,我能够看到这种行为的来源。如果你看Django Rest Framework code :

    if getattr(model_field, 'unique', False):
        unique_error_message = model_field.error_messages.get('unique', None)
        if unique_error_message:
            unique_error_message = unique_error_message % {
                'model_name': model_field.model._meta.verbose_name,
                'field_label': model_field.verbose_name
            }
        validator = UniqueValidator(
            queryset=model_field.model._default_manager,
            message=unique_error_message)
        validator_kwarg.append(validator)

我们看到,如果 uniqueTrue(在您的情况下,我猜您将 UUID 字段定义为主键),DRF 会自动添加一个 唯一验证器。此验证器执行 SELECT 请求以检查该值是否不存在。

它附加到您在字段的 validators 参数中定义的那些,所以这就是您所做的无效的原因。

那么,我们该如何规避呢?

第一次尝试

class TheDataDetailSerializer(serializers.ModelSerializer):
    # ... your code

    def get_fields(self):
        fields = super().get_fields()
        fields['id'].validators.pop()
        return fields

基本上,我们在 id 字段生成后删除验证器。肯定有更聪明的方法来做到这一点。在我看来,DRF 在这个问题上可能过于自以为是了。

第二次尝试

class TheDataDetailSerializer(serializers.ModelSerializer):
    # ... your code

    def build_standard_field(self, field_name, model_field):
        field_class, field_kwargs = super().build_standard_field(field_name, model_field)
        if field_name == 'id':
            field_kwargs['validators'] = []
        return field_class, field_kwargs

当生成字段参数时,如果我们要生成 id 字段,请设置一个空的 validators 列表。

关于python - 如何使用 Django REST Framework 在 INSERT 之前停止 SELECT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58181943/

相关文章:

python - Django:尝试选择随机结果,但获取对象没有 len() 错误

python - 如何在 python 中启动子进程而不是等待它返回

json - PostgreSQL 选择到 JSON 数组

json - 更新 jsonb 列

python - 将不同文件夹中的多个 csv 文件中的选定列合并到单个 csv 文件中

python - Flask看不到环境变量

python - 刷新只是一个应用程序而不是整个项目

sql - 根据sql中的月份和年份选择一行

python - 使用 openCV for python 进行反投影

python - 将最大值时间戳放入 PySpark 的数组中