6

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',)
ukejoe
  • 737
  • 2
  • 9
  • 20

3 Answers3

5

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',)
therealak12
  • 1,178
  • 2
  • 12
  • 26
  • 2
    Don't forget `required=False` as explained [here](https://stackoverflow.com/a/29621808/9078867) – Will Feb 21 '21 at 20:49
1

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.

user1575045
  • 55
  • 1
  • 9
  • Thanks for the reply. How do I go about doing that? I see that the docs say you can do that, but don't give an example. – ukejoe Sep 16 '14 at 15:17
0

Sorry for late reply, i have somewhat similar models and it is working, by overriding save_object method, the is_valid() must be returning false (have you checked it), anyway saving objects using a serializer is for very basic use cases thus limited, best way would be to save those objects explicitly in the view.

user1575045
  • 55
  • 1
  • 9