DRF - Raise Exception if any defined field is None

2019-08-17 18:02发布

I need to serialize model to JSON. Then send this JSON to one API.

But this API requires some fields to be not None.

I have a list of these fields. In this case, let's say it's just ['telephone'] but it can be much more.

For example:

class UserSerializer(serializers.ModelSerializer):
    telephone = serializers.CharField(source='userprofile.telephone')
    class Meta:
        model = User
        fields = ['first_name','last_name','telephone']

Serialization:

>>> UserSerializer(user).data
>>> {'first_name':'Michael','last_name':'Jackson','telephone':None}

Since API requires some fields like telephone, I want UserSerializer to raise ValidationError when the required field is None.

So in this case I couldn't serialize user because telephone is None.

I tried many things including adding required=True to the telephone but nothing works.

Is there a way to validate serialized data? Note that I'm not talking about deserialization.

3条回答
【Aperson】
2楼-- · 2019-08-17 18:10

Why validation not working?

The validation process undergoes only while Deserialization process (input is a dict like object) and you are trying a Serialization process. In the case of Serialization, DRF assumes the given object is a valid one and hence it doesn't require a validation.

Source DRF-serializers


How can we make this happen?

Method-1
Make your user object to a user_data (dict object) and pass it to the serializer and run the validation.

user = User.objects.get(id=1)
dict_user_data = {"first_name": user.first_name, "last_name": user.last_name, "telephone": user.userprofile.telephone}
user_serializer = UserSerializer(data=dict_user_data)
user_serializer.is_valid(True)
user_serializer.data


Method-2
Override the to_representation() method

class UserSerializer(serializers.ModelSerializer):
    telephone = serializers.CharField(source='userprofile.telephone')

    class Meta:
        model = User
        fields = ['first_name', 'last_name', 'telephone']

    def to_representation(self, instance):
        data = super().to_representation(instance)
        for field, value in data.items():
            if value is None:
                raise SomeExceptionHere({field: "can't be None"})
        return data
查看更多
走好不送
3楼-- · 2019-08-17 18:27

What you want is not to validate data for de-serialization, but to validate for serialization. Expressions like required=True are all used to validate data for de-serialization. Serialization is handled in to_representation method of Serializer class (which is the base for ModelSerializer)

What you can do is, override to_representation, and have the method raise an exception if a required field value is None.

You may need to further subclass default DRF classes like Field (for a serializer field) and use your custom classes to be able to provide your functionality in a systematic manner. You do not want to rely on required=True for this, because it is used for another purpose already (for de-serialization validation).

What I suggest is, subclass Field class, add a property like "required_for_read", and define your serializer field with this property, using your custom field class. Then, in your overridden to_representation method, look for this property, and raise an exception if the field has this property as True but its value is None

查看更多
男人必须洒脱
4楼-- · 2019-08-17 18:31

You don't need to. DRF serializers can do that right out the box. If a field is setted to be null=False or required=True, just do this.

data = UserSerializer(data=user_data)
data.is_valid(raise_exception=True

And that is it. A 400 error will be raised.

If you want, you can tweak the error message:

data = UserSerializer(data=user_data)
try:
    data.is_valid(raise_exception=True)
except ValidationError as e:
   return HttpResponse(status_code=400, content=e)
查看更多
登录 后发表回答