Home > Software design >  How to make ModelSerializer field optional in Django rest framework
How to make ModelSerializer field optional in Django rest framework

Time:04-10

I have this model Reaction with code and comment fields. I have both of them null=True, blank=True in my models.py file. I want one of them to filled and other to be empty, for this I created clean method in models and validate method in serializers. But when I try to create a reaction without code/comment it says code/comment is required.

models.py
class Reaction(models.Model):
    code = models.ForeignKey(Code, null=True, blank=True, related_name="code_reactions", on_delete=models.CASCADE)
    comment = models.ForeignKey(Comment, null=True, blank=True, related_name="comment_reactions", on_delete=models.CASCADE)
    user = models.ForeignKey(User, related_name="reations", on_delete=models.CASCADE)
    
    REACTION_CHOICES = [
        ("like", "Like"),
        ("dislike", "Dislike"),
        ("wow", "Wow"),
        ("love", "Love"),
        ("sad", "Sad"),
        ("haha", "Haha"),
        ("rocket", "Rocket"),
        ("angry", "Angry"),
    ]
    name = models.CharField(choices=REACTION_CHOICES, max_length=10)
    
    class Meta:
        unique_together = [["code", "user"], ["comment", "user"]]
    
    def clean(self):
        if self.code and self.comment:
            raise ValidationError(_('One reaction cannot be assigned to a code and a comment'))
        
        if not self.code and not self.comment:
            raise ValidationError(_("Please enter a code or a comment"))
     
serializers.py

class ReactionCreateSerializer(serializers.ModelSerializer):
    code = serializers.IntegerField(required=False)
    comment = serializers.IntegerField(required=False)
    
    class Meta:
        model = Reaction
        fields = ["id", "user", "code", "comment", "name"]
        

    def validate(self, data):
        if data["code"] and data["comment"]:
            raise serializers.ValidationError(_('One reaction cannot be assigned to a code and a comment'))
        
        if not data["code"] and not data["comment"]:
            raise serializers.ValidationError(_("Please enter a code or a comment"))
        return data
    

I have also tried

class Meta:
    extra_kwargs = {'code': {'required': False},
                        'comment': {'required': False,}
                        }
example request
{
    "code": 1,
    "name": "love",
    "user": 1
}

returned response 

{"comment":["This field is required."]}

CodePudding user response:

Required=False allows you to send None/Blank values in that field. It doesn't mean that you can skip sending the field altogether in your request params. In other words 'required' should not be confused with 'exclude' fields.

For reference:

If a Field has required=False and you pass clean() an empty value, then clean() will return a normalized empty value rather than raising ValidationError. For CharField, this will return empty_value which defaults to an empty string. For other Field classes, it might be None. (This varies from field to field.)

documentation : https://docs.djangoproject.com/en/4.0/ref/forms/fields/#required

What you are doing in your serializer is correct. Just make sure that you set None to the field what you are not sending:

{
    "code": 1,
    "name": "love",
    "user": 1,
    "comment": None
}

or

{
    "code": None,
    "name": "love",
    "user": 1,
    "comment": 2
}
  • Related