Search code examples
pythondjangodjango-rest-frameworkdjango-serializer

DRF - ModelSerializer with a non-model write_only field


I have the following Model, Serializer and View. My aim is to pass a custom string like

referrer = "pid=email&af_sub1=ui_1120&c=xyz"

in the POST method (RegisterViewSet below) and then to the viewset/serializer to use this information to fill in the referral_campaign, referral_media and inviting_user

So: 1. referrer is write_only field 2. referrer is not a field in the model, but the info would be used to populate fields in the model

How to achieve this in the DRF way?

Model

class User(AbstractUser):     
    first_name = models.CharField(max_length=100, null=True, blank=True)
    last_name = models.CharField(max_length=100, null=True, blank=True)

    # social/viral feature related fields
    referral_campaign = models.CharField(default="", max_length = 200, help_text="Campaign that led to the user signup")
    referral_media_source = models.CharField(default="", max_length = 200, help_text="Campaign that led to the user signup")
    inviting_user = models.ForeignKey('self', help_text="Inviting user", null=True, blank=True)

Serializer

class UserSerializer(serializers.ModelSerializer):

class Meta:
    model = User
    fields = ('first_name', 'last_name',)
    write_only_fields = ('first_name', 'last_name', 'referrer')     #how to use this 'referrer' field to populate the Model fields?
    read_only_fields = ('id',)

View

class RegisterViewSet(generics.CreateAPIView):

    model = User
    serializer_class = UserSerializer
    permission_classes = [
        permissions.AllowAny
    ]

Solution

  • First, you need to define this field in the serializer:

    class UserSerializer(serializers.ModelSerializer):
        referrer = serializers.CharField(max_length=300, allow_blank=True)
        class Meta:
            model = User
            fields = ('id', 'first_name', 'last_name', 'referrer')
            write_only_fields = ('first_name', 'last_name', 'referrer')     #how to use this 'referrer' field to populate the Model fields?
            read_only_fields = ('id',)
    

    (I am not really sure if you need 'first_name', 'last_name' in write_only_fields, as this means you will get only ID in response, but this depends on your requirements)

    Now, you need to override the serializer restore_object method:

    def restore_object(self, attrs, instance=None):
        referrer = attrs.pop('referrer')
        # parse referrer to referral_campaign, referral_media_source, inviting_user 
        ...
        instance = super(UserSerializer, self).restore_object(attrs, instance=instance)
        instance.referral_campaign = referral_campaign
        instance.referral_media_source = referral_media_source
        instance.inviting_user = inviting_user
        return instance
    

    (This answer assumes DRF 2)