Как остановить SELECT перед вставкой с Django REST Framework - PullRequest
2 голосов
/ 01 октября 2019

Я использовал Django REST Framework для предоставления API, который используется только другим сервисом для POST новых данных. Это в основном просто берет 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)

1 Ответ

1 голос
/ 01 октября 2019

После некоторого копания я смог увидеть, откуда происходит это поведение. Если вы посмотрите на код Django Rest Framework :

    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)

Мы увидим, что если unique равно True (что в вашем случае, как яПредположим, вы определили поле UUID в качестве первичного ключа), DRF автоматически добавляет UniqueValidator. Этот валидатор выполняет запрос 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

При генерации аргументов поля установите пустое значение validatorsсписок, если мы генерируем поле id.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...