我用过 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)
我们看到,如果 unique
是 True
(在您的情况下,我猜您将 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/