badtrains
badtrains

Reputation: 91

Django REST Framework validate slug field

I have defined a serializer like this:

class ActivitySerializer(serializers.ModelSerializer):
      activity_project = serializers.SlugRelatedField(queryset=Project.objects.all(), slug_field='project_name')

activity_tags = serializers.SlugRelatedField(queryset=Tag.objects.all(), slug_field='tag_name', many=True)
class Meta:
    model = Activity
    fields = ('activity_name', 'activity_description', 'activity_status', 'activity_completion_percent', 'activity_due_date', 'activity_project', 'activity_tags',)

Now if I insert an activity_tag that does not exist in the database, I get a validation error"

{
    "activity_tags": [
        "Object with tag_name=test does not exist."
    ]
}

I would like to create a validation method that adds the tag in the database if it does not exist. I have tried using the

def validate(self, attrs): 
    ....

method, but apparently for a slug field there is a method that is called before this one.

Can someone point me to the right method I should use? Would this method be called in the corresponding view?

Upvotes: 3

Views: 2371

Answers (2)

Vitali Kaspler
Vitali Kaspler

Reputation: 1410

I managed to do this by subclassing SlugRelatedField and overriding "to_internal_value" method. In the original implementation this method tries to get an object from the queryset, and if an object doesn't exist it fails the validation. So instead of calling "get" method, I'm calling "get_or_create":

class CustomSlugRelatedField(serializers.SlugRelatedField):
    def to_internal_value(self, data):
        try:
            obj, created = self.get_queryset().get_or_create(**{self.slug_field: data})
            return obj
        except (TypeError, ValueError):
            self.fail('invalid')

Upvotes: 2

Jay Nielsen
Jay Nielsen

Reputation: 119

I think you would need to create a nested serializer for this to work. This is totally untested and off the top of my head, but maybe something like this:

class ActivityTagFieldSerializer(serializer.ModelSerializer):
    tag_name = serializers.SlugField()
    class Meta:
        model = Tag
        fields = ('tag_name')

class ActivitySerializer(serializer.ModelSerializer):
    activity_tags = ActivityTagFieldSerializer(many=True)
    class Meta:
        model = Activity
        fields = ('activity_tags', 'activity_project', ...)

    def create(self, validated_data):
        tags = validated_data.pop('activity_tags')
        activity = Activity.objects.create(**validated_data)
        for tag in tags:
            try:
                tag_to_add = Tag.objects.get(**tag)
            except:
                tag_to_add = Tag.objects.create(**tag)
            activity.activity_tags.add(tag_to_add)
        return activity

Check the API guide for writable nested serializers

Upvotes: 1

Related Questions