簡體   English   中英

如何在 Django REST 框架中驗證完整的 model 數據

[英]How to validate against full model data in Django REST Framework

TL;博士

在我的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。)

當創建或更新數據中包含startfinish屬性的事件時,此序列化程序將按預期工作。

但是,如果您提出以下請求:

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 層中進行了驗證檢查,因為:

  1. 您不再需要在序列化程序層上運行驗證檢查。
  2. 驗證邏輯更接近數據庫層,因此如果有人決定使用 django 的 ORM 並從后端創建對象(例如導入腳本),您不必擔心會創建“壞”數據。
  3. 您的驗證邏輯更接近正在創建/保存 object 的代碼,因此更易於調試

在 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM