[英]How to validate against full model data in Django REST Framework
在我的Serializer.validate
方法中,如果傳入數據中沒有設置,我需要能夠訪問attrs['field']
並回self.instance.field
,我想知道是否有一個通用模式這樣做。
以 DRF 序列化器文檔的對象級驗證部分為例:
from rest_framework import serializers
class EventSerializer(serializers.Serializer):
description = serializers.CharField(max_length=100)
start = serializers.DateTimeField()
finish = serializers.DateTimeField()
def validate(self, attrs):
"""
Check that start is before finish.
"""
if attrs['start'] > attrs['finish']:
raise serializers.ValidationError("finish must occur after start")
return attrs
(這使用了普通的Serializer
,但想象一下它是Event
ModelSerializer
的 ModelSerializer。)
當創建或更新數據中包含start
和finish
屬性的事件時,此序列化程序將按預期工作。
但是,如果您提出以下請求:
client.patch(f"/events/{event.id}", {"start": "2021-01-01"})
然后序列化器將失敗,因為嘗試訪問attrs['finish']
會導致KeyError
。 在這種情況下,我需要能夠回self.instance.finish
,因為start < finish
驗證仍然是必要的。
是否有解決此問題的通用模式?
您可以通過在所有validate
方法的開頭添加一個片段來解決此問題:
def validate(self, attrs):
full_attrs = attrs
if self.instance is not None:
full_attrs = {
**self.to_internal_value(self.__class__(self.instance).data),
**full_attrs,
}
然后使用full_attrs
代替attrs
。 這會將實例數據的序列化版本添加到attrs
。
有沒有更好的方法來實現這一點?
(這樣做的一個“缺點”是,如果數據失去完整性,它可能會阻止其他有效更新。因此,例如,如果開發人員直接更新數據庫以使event.start
晚於event.finish
,則 API 用戶將不再能夠更新event.description
因為此驗證將失敗。但我認為優點肯定大於缺點,至少對於我當前的用例而言。)
我將就這個問題提出我的看法,因為我在我的一個項目中遇到了這個問題。
我在 model 層中進行了驗證檢查,因為:
在 model 層上驗證它非常簡單。 您可以覆蓋 model class 的save
方法或clean
方法並在save
方法中運行clean
方法(或full_clean
)。 更多細節在這里。
from django.db import models
from django.core.exceptions import ValidationError
class MyModel(models.Model):
start = ...
finish = ...
...
def clean(self):
if self.finish < self.start:
raise ValidationError("Finish must occur after start")
def save(self, *args, **kwargs):
self.full_clean()
super().save(*args, **kwargs)
現在這里是關於 django 的 ValidationError 的事情。 DRF 不知道如何處理它。 如果您將一些無效數據傳遞給序列化程序,您將不會得到很好的 400 響應。 為了讓 DRF 處理錯誤,您編寫自己的自定義錯誤處理程序並將其設置為您的settings.py
中的EXCEPTION_HANDLER
。
# myapp/exceptions.py
from django.core.exceptions import ValidationError
from rest_framework.views import exception_handler
from rest_framework.response import Response
def django_error_handler(exc, context):
"""Handle django core's errors."""
# Call REST framework's default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
if response is None and isinstance(exc, ValidationError):
return Response(status=400, data=exc.message_dict)
return response
# settings.py
REST_FRAMEWORK = {
...,
'EXCEPTION_HANDLER': 'myapp.exceptions.django_error_handler'
}
我注意到您正在使用通用serializers.Serializer
化程序。序列化程序 class 用於您的序列化程序。 如果您已經有一個Event
model,使用serializers.ModelSerializer
會更容易,因為這會抽象出許多 object 創建/更新邏輯。 另一個好處是,由於它會查看模型的字段定義,它會根據您在 model 中指定字段的方式構建字段,因此您無需在序列化程序中定義字段(例如,如果字段有max_length
,它將創建具有最大長度的相應 DRF 字段)。
而是通過使用CheckConstraint在數據庫級別另外強制執行完整性,這樣您就不必擔心引入臟數據
class Meta:
constraints = [
models.CheckConstraint(
check=models.Q(
finish > models.F("start")
)
),
name="start_gt_finish",
)
]
要回答驗證的選擇,實際上沒有最佳解決方案。 如果您的應用程序是 ModelForms / Django admin 和ModelForms
的混合體,則可能很想通過調用full_clean()
調用 go 但這可能也有問題,因為您需要在某處修補它,因為validate()
不會調用full_clean()
也不會正確轉換所有 Django ValidationErrors
。
我會說您的解決方案看起來不錯並用作序列化程序驗證層,如果沒有數據庫約束,您可能會檢查attrs
中是否不存在 start 和 stop 以避免驗證檢查修補的數據是否不包含 start 或 stop
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.