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