状況
Django REST Framework's ModelSerializer
で検証を操作しているときに、Meta.model
フィールドが常に検証されることに気づきました必ずしもそうする必要がない場合は、User
モデルのシリアル化について次の例を見てください。
password
フィールドとconfirm_password
フィールドがあります。 2つのフィールドが一致しない場合、ユーザーを作成できません。同様に、要求されたusername
がすでに存在する場合、ユーザーを作成できません。validate
の実装がシリアライザで行われ(以下を参照)、一致しないpassword
およびconfirm_password
フィールドをキャッチしますvalidate
の実装:
def validate(self, data):
if data['password'] != data.pop('confirm_password'):
raise serializers.ValidationError("Passwords do not match")
return data
問題
ValidationError
がvalidate
によって引き上げられた場合でも、ModelSerializer
はデータベースに照会して、username
がすでに使用されているかどうかを確認します。これは、エンドポイントから返されるエラーリストで明らかです。モデルエラーと非フィールドエラーの両方が存在します。
したがって、非フィールド検証が終了してデータベースへの呼び出しを保存するまでモデル検証を回避する方法を知りたいのです。
ソリューションで試行
私はDRFのソースを調べてこれがどこで起こっているのかを理解しようとしましたが、これを機能させるためにオーバーライドする必要があるものを見つけることができませんでした。
username
フィールドに_unique=True
_が設定されている可能性が高いため、Django RESTフレームワークは、新しいユーザー名は一意です。実際にこれを確認するには、repr(serializer())
を実行します。これにより、バリデーターを含む、自動生成されたすべてのフィールドが表示されます。
検証は、文書化されていない特定の順序で実行されます
serializer.to_internal_value
_ および _field.run_validators
_ )と呼ばれるフィールドの逆シリアル化serializer.validate_[field]
_ は各フィールドに対して呼び出されますserializer.run_validation
_ の後に _serializer.run_validators
_ )serializer.validate
_ が呼び出されますしたがって、表示されている問題は、シリアライザレベルの検証の前にフィールドレベルの検証が呼び出されることです。お勧めしませんが、シリアライザーのメタで_extra_kwargs
_を設定することで、フィールドレベルのバリデーターを削除できます。
_class Meta:
extra_kwargs = {
"username": {
"validators": [],
},
}
_
ただし、自動的に生成された追加のバリデーターとともに、独自の検証でunique
チェックを再実装する必要があります。
上記のソリューションが機能しないと思います。私の場合、モデルには「first_name」と「last_name」のフィールドがありますが、APIは「name」のみを受け取ります。
Metaクラスで 'extra_kwargs'と 'validators'を設定しても効果がないようで、first_nameとlast_nameは常に必須であると見なされ、バリデーターは常に呼び出されます。 first_name/last_name文字フィールドをオーバーロードできません
anotherrepfor_first_name = serializers.CharField(source=first_name, required=False)
名前が意味をなすように。何時間もイライラした後、ModelSerializerインスタンスでバリデーターをオーバーライドできる唯一の方法は、次のようにクラス初期化子をオーバーライドすることでした(誤ったインデントは許します)。
class ContactSerializer(serializers.ModelSerializer):
name = serializers.CharField(required=True)
class Meta:
model = Contact
fields = [ 'name', 'first_name', 'last_name', 'email', 'phone', 'question' ]
def __init__(self, *args, **kwargs):
self.fields['first_name'] = serializers.CharField(required=False, allow_null=True, allow_blank=True)
self.fields['last_name'] = serializers.CharField(required=False, allow_null=True, allow_blank=True)
return super(ContactSerializer, self).__init__(*args, **kwargs)
def create(self, validated_data):
return Contact.objects.create()
def validate(self, data):
"""
Remove name after getting first_name, last_name
"""
missing = []
for k in ['name', 'email', 'question']:
if k not in self.fields:
missing.append(k)
if len(missing):
raise serializers.ValidationError("Ooops! The following fields are required: %s" % ','.join(missing))
from nameparser import HumanName
names = HumanName(data['name'])
names.capitalize()
data['last_name'] = names.last
if re.search(r'\w+', names.middle):
data['first_name'] = ' '.join([names.first, names.middle])
else:
data['first_name'] = names.first
del(data['name'])
return data
現在のドキュメントでは、文字フィールドでの空白とnullの許可はノーノーですが、これはモデルではなくシリアライザであり、APIがあらゆる種類のカウボーイによって呼び出されるため、ベースをカバーする必要があります。
また、シリアライザの検証中に制御フローがどのように流れるかを理解しようとしていて、djangorestframework-3.10.3のソースコードを注意深く調べた後、以下の要求フロー図を思いつきました。フローとフローで何が起こるかを理解しましたが、ソースから調べることができるので、あまり詳しく説明することはありません。
不完全なメソッドシグネチャは無視してください。どのメソッドがどのクラスで呼び出されるかにのみ焦点を当てます。
シリアライザクラス(MySerializer(serializers.Serializer)
)にオーバーライドされた_is_valid
_メソッドがある場合、my_serializer.is_valid()
を呼び出すと、次のようになります。
MySerializer.is_valid()
が実行されます。BaseSerializer
)_is_valid
_メソッド(super(MySerializer, self).is_valid(raise_exception)
メソッド内のMySerializer.is_valid()
など)を呼び出していると仮定すると、それが呼び出されます。MySerializer
が_serializers.Serializer
_を拡張しているため、_serializer.Serializers
_のrun_validation()
メソッドが呼び出されます。これは、最初のデータ辞書のみを検証しています。そのため、フィールドレベルの検証はまだ開始していません。validate_empty_values
_の_fields.Field
_が呼び出されます。これは、data
全体で再び発生し、単一のフィールドでは発生しません。Serializer.to_internal_method
_が呼び出されます。field.run_validation()
メソッドを呼び出します。フィールドがField.run_validation()
メソッドをオーバーライドした場合、それが最初に呼び出されます。 CharField
の場合はオーバーライドされ、Field
基本クラスの_run_validation
_メソッドを呼び出します。図のステップ6-2。Field.validate_empty_values()
を呼び出しますto_internal_value
_が呼び出されます。Field.run_validators()
メソッドが呼び出されました。これは、_validators = []
_フィールドオプションを指定してフィールドに追加するバリデーターが1つずつ実行される場所だと思いますSerializer.to_internal_value()
メソッドに戻ります。ここで、forループ内の各フィールドに対して上記を実行していることを思い出してください。これで、シリアライザーで記述したカスタムフィールドバリデーター(_validate_field_name
_などのメソッド)が実行されます。前の手順のいずれかで例外が発生した場合、カスタムバリデーターは実行されません。read_only_defaults()
validate()
メソッドはここで実行されると思います。