Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework optional nested serializer field in ModelSerializer

I have two models: FacebookAccount and Person. There's a OneToOne relationship like so:

class FacebookAccount(models.Model):
    person = models.OneToOneField(Person, related_name='facebook')
    name = models.CharField(max_length=50, unique=True)
    page_id = models.CharField(max_length=100, blank=True)

I created a PersonSerializer which has a facebook field set to a FacebookSerializer I created:

class FacebookSerializer(serializers.ModelSerializer):
    class Meta:
        model = FacebookAccount
        fields = ('name', 'page_id',)

class PersonSerializer(serializers.ModelSerializer):
    facebook = FacebookSerializer(required=False)

    class Meta:
        model = Person
        fields = ('id', 'name', 'facebook',)

I then created a view to create a new Person along with a new FacebookAccount instance for a POST request:

class PersonCreate(APIView):

    def post(self, request):
        # Checking for something here, doesn't affect anything
        if 'token' in request.DATA:
            serializer = PersonSerializer(data=request.DATA)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

This works fine and well when my POST data has a facebook object with a unique name attribute in it. However, what if the facebook information is optional? I set "required=False" in the facebook field on the PersonSerializer to account for this, but I get a 400 error when my POST data does not contain a facebook object. The error says that the "name" field for facebook is required.

If I set "blank=True" on the name field in the FacebookAccount model and don't provide and facebook info in my POST data, a blank FacebookAccount record is created which is expected but not desired.

Does anyone know how to fix this? Thanks.

edit:

I tried overriding the save_object method on the PersonSerializer, but it looks like that method isn't called. I tried working my way back through the methods that are called, and it looks like get_field is the last one:

class PersonSerializer(serializers.ModelSerializer):
    facebook = FacebookSerializer(required=False)

    def get_default_fields(self):
        field = super(PersonSerializer, self).get_default_fields()
        print field
        return field

    def get_pk_field(self, model_field):
        print 'MF'
        mf = super(PersonSerializer, self).get_pk_field(model_field)
        print mf
        return mf

    def get_field(self, model_field):
        print 'FIELD'
        mf = super(PersonSerializer, self).get_field(model_field)
        print mf
        return mf

    # not called
    def get_validation_exclusions(self, instance=None):
        print '**Exclusions'
        exclusions = super(PersonSerializer, self).get_validation_exclusions(instance=None)
        print exclusions
        return exclusions

    # not called
    def save_object(self, obj, **kwargs):
        if isinstance(obj, FacebookAccount):
            print 'HELLO'
        else:
            print '*NONONO'
        return super(PersonSerializer, self).save_object(obj, **kwargs)

    class Meta:
        model = Person
        fields = ('id', 'name', 'stripped_name', 'source', 'facebook',)
like image 577
ukejoe Avatar asked Sep 16 '14 05:09

ukejoe


People also ask

What is difference between serializer and ModelSerializer?

The ModelSerializer class is the same as a regular Serializer class, except that: It will automatically generate a set of fields for you, based on the model. It will automatically generate validators for the serializer, such as unique_together validators. It includes simple default implementations of .

How do you pass extra context data to Serializers in Django REST Framework?

In function-based views, we can pass extra context to serializer with “context” parameter with a dictionary. To access the extra context data inside the serializer we can simply access it with “self. context”. From example, to get “exclude_email_list” we just used code 'exclude_email_list = self.

Do we need Serializers in Django REST Framework?

Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.

What is Serializers SerializerMethodField ()?

Serializer Method Field It gets its value by calling a method on the serializer class it is attached to. It can be used to add any sort of data to the serialized representation of your object. SerializerMethodField gets its data by calling get_<field_name> . Example: class UserSerializer(serializers.


2 Answers

Simply pass allow_null=True when declaring the nested serializer in the parent serializer, like this:

class FacebookSerializer(serializers.ModelSerializer):
    class Meta:
        model = FacebookAccount
        fields = ('name', 'page_id',)

class PersonSerializer(serializers.ModelSerializer):
    facebook = FacebookSerializer(allow_null=True, required=False)

    class Meta:
        model = Person
        fields = ('id', 'name', 'facebook',)
like image 181
therealak12 Avatar answered Dec 06 '22 04:12

therealak12


Well, that is the limitation of django-restframework when specifying reverse relation as a field, because it creates a list from the fields specified , required=false only directs the serializer to save the default null value for that related field, to resolve it you can override save_object method of serializer and remove facebook field if its null.

like image 37
user1575045 Avatar answered Dec 06 '22 04:12

user1575045