Home > Enterprise >  Django DRF: Take extra input from user using serializer but not present in the model
Django DRF: Take extra input from user using serializer but not present in the model

Time:03-10

I have one ModelSerializer class which is used for user input & validation. I am using it with a ModelViewset which automatically creates a new entry in the database using the serializer. Now what I want to do is that I want to have a extra field in the serializer say roles which will be given by the user. It is an array of roles for a user but this field is not present in the model & I want to manually add this roles entry in the auth_user_groups table. So I wrote my serializer like this,

class UserCreateSerializer(serializers.ModelSerializer):
    """
    Serializer to create new user
    """
    class Meta:
        model = User
        fields = ["name", "email", "is_active", "roles"]
        read_only_fields = ['roles']
        
    roles = serializers.MultipleChoiceField(choices=[(e.name,e.value) for e in AuthGroup])
        
    
    def validate_roles(self, value):
        
        if not value:
            raise ValidationError(detail=constants.NAMED_FIELD_REQUIRED.format(field="roles"))

My model looks like this,

class User(AbstractBaseUser, PermissionsMixin):
    class Meta:
        db_table = "auth_user"
        app_label = "users"

    USERNAME_FIELD = "email"

    REQUIRED_FIELDS = []

    name = models.CharField(max_length=255, null=True)

    email = models.EmailField("email address", unique=True, null=True)

    is_active = models.BooleanField(default=True)

    is_superuser = None

    is_admin = None

    is_verified = models.BooleanField(default=False)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    objects = UserManager()

    def __str__(self):
        return self.name or self.email

    def is_admin(self):
        return self.groups.filter(name=AuthGroup.ADMIN.name).exists()

This is the request body which is coming from user.

{
  "name": "test4",
  "email": "[email protected]",
  "is_active": true,
  "roles": [
    "ADMIN", "OPERATOR"
  ]
}

So I get this error whenever I try to call the API,

raise TypeError("%s() got an unexpected keyword argument '%s'" % (cls.name, kwarg)) TypeError: User() got an unexpected keyword argument 'roles'

So anyone have any idea how can I solve this or achieve what I have said above? Any help would be appreciated...

CodePudding user response:

This is because the validated data is getting passed to the create(...) method of QuerySet inside the create(...) method of the serializer. So, you need to pop the roles field before the creation operation.

class UserCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ["name", "email", "is_active", "roles"]

    roles = serializers.MultipleChoiceField(
        choices=[(e.name, e.value) for e in AuthGroup],
        required=True,
        write_only=True
    )

    def validate_roles(self, value):
        # do some validations
        return value

    def create(self, validated_data):
        roles = validated_data.pop("roles", None)
        return super().create(validated_data)

Notes

  • You can use required=True to set the field as mandatory so that you don't have to do any extra validations for that
  • read_only_fields will work for those fields which are not defined explicitly in the serializer.
  • Related