Django Rest Framework: PUT / POST does not work in case of empty optional fields in nested serializer
I am using Django ver 1.7 and Django Rest Framework version 3.1.3.
I have two models that are interconnected like this:
class Person(models.Model):
name = models.CharField(max_length=200)
GENDER_CHOICES = (
('M', 'Male'),
('F', 'Female'),
('T', 'Transgender'),
)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
address = models.CharField(max_length=1000)
work_phone = models.CharField(max_length=15, blank=True, null=True,
validators=[RegexValidator(regex='^[0-9]+$',
message='Phone number must contain only digits',
code='nomatch')])
home_phone = models.CharField(max_length=15, blank=True, null=True,
validators=[RegexValidator(regex='^[0-9]+$',
message='Phone number must contain only digits',
code='nomatch')])
mobile = models.CharField(max_length=15, blank=True, null=True,
validators=[RegexValidator(regex='^[0-9]+$',
message='Phone number must contain only digits',
code='nomatch')])
email = models.EmailField(blank=True)
date_of_birth = models.DateField()
user = OneToOneField(User, null=True, blank=True)
DOCUMENT_CHOICES = (
('DL', 'Driving License'),
('RC', 'Ration card'),
('PP', 'Passport'),
('NO', 'None'),
)
additional_document = models.CharField(max_length=2, choices=DOCUMENT_CHOICES)
document_number = models.CharField(max_length=10, blank=True, null=True)
document_scan = models.FileField(max_length=1000, blank=True, null=True)
photo = models.ImageField(blank=True,null=True)
class Admin(models.Model):
person = OneToOneField(Person)
# More fields will be added later
So Person has a OneToOne field pointing to User (Django auth_user model) and Admin has a OneToOne field pointing to Person. However, I would like to provide the end user with a single screen to update all these details in one shot (similar to combining multiple ModelForms into one template).
I have created explicit serializers to match each of these models and they have nested relationships as follows.
class UserSerializer(serializers.Serializer):
id = serializers.IntegerField(label='ID', read_only=True)
username = serializers.CharField(max_length=30)
password = serializers.CharField(max_length=128, style={'input_type': 'password'})
class PersonSerializer(serializers.Serializer):
id = serializers.IntegerField(label="ID", read_only=True)
user = UserSerializer()
name = serializers.CharField(max_length=200)
gender = serializers.ChoiceField(choices=['M', 'F', 'T'])
address = serializers.CharField(max_length=1000)
work_phone = serializers.CharField(max_length=15, required=False, allow_null=True)
home_phone = serializers.CharField(max_length=15, required=False, allow_null=True)
mobile = serializers.CharField(max_length=15, required=False, allow_null=True)
email = serializers.EmailField(required=False, allow_blank=True)
date_of_birth = serializers.DateField()
additional_document = serializers.ChoiceField(choices=['NO','DL', 'RC', 'PP'])
document_number = serializers.CharField(max_length=10, required=False, allow_null=True)
document_scan = serializers.FileField(max_length=1000, required=False, allow_null=True)
photo = serializers.ImageField(required=False, allow_null=True)
class AdminSerializer(serializers.Serializer):
id = serializers.IntegerField(label="ID", read_only=True)
person = PersonSerializer()
# More fields will be added later
Each of the serializers has an explicit create () and update () method, which I've omitted for brevity. I can see that they are not being called, so the error is probably encountered during the check itself.
When I do a GET operation, the Person endpoint or Admin api returns all nested objects correctly.
When I do a PUT or POST operation on Person, it accepts empty inputs in optional fields. However, if I keep the same fields blank when doing a PUT or POST operation on the Admin object, it ignores the following errors.
My PUT request:
{
"id": 1,
"person": {
"id": 1,
"user": {
"id": 2,
"username": "nina.sinha",
"password": "pbkdf2_sha256$12000$CjmifxpP1DDv$1622S7enIwnT+NcwhMKIEWrq8Uy7gQQJPQQueML21Sg="
},
"name": "Nina Sinha",
"gender": "F",
"address": "1234, 6th Cross, 7th Main Street",
"work_phone": "42011567",
"home_phone": "",
"mobile": "9854609658",
"email": "nina.sinha@yahoo.com",
"date_of_birth": "1985-04-03",
"additional_document": "NO",
"document_number": "",
"document_scan": null,
"photo": null
}
}
Errors:
HTTP 400 Bad Request
Content-Type: application/json
Vary: Accept
Allow: GET, PUT, PATCH, HEAD, OPTIONS
{
"person": {
"document_scan": [
"The submitted data was not a file. Check the encoding type on the form."
],
"photo": [
"The submitted data was not a file. Check the encoding type on the form."
],
"document_number": [
"This field may not be blank."
],
"user": [
"This field is required."
],
"home_phone": [
"This field may not be blank."
]
}
}
The document_scan, document_number and home_phone fields were left blank, but they are optional and the AdminSerializer should accept this.
Custom HAD fields have been entered, but I still get the "This field is required" error as if they are missing.
What am I missing here? I am facing the same problem if I am using ModelSerializers.
source to share
Looking at your code, why don't you use modelerializers instead of a serializer. At the serializer level, you can change the fields as required. You can change the required field to optional fields in the serializer and this won't be a problem at the model level.
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'password')
class PersonSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Person
fields = ('id', 'user', # specify other fields)
class AdminSerializer(serializers.ModelSerializer):
person = PersonSerializer()
class Meta:
model = Admin
fields = ('id', 'person', # specify other fields)
If you want to change the user field to optional in PersonSerializer, change the code as follows.
user = UserSerializer(required=False)
source to share