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.

+3


source to share


1 answer


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)

      

0


source







All Articles