TDLR : what is the best way to implement tags in django-rest-framework. where the tags has a created_by
field which is the currently authenticated user.
I am trying to achieve a very simple/common thing, add tags to posts. But apparently its not a piece of cake.
So i have a posts model and a tags models (may to many relation). I want the user to be able to update and create the posts. when creating or updating posts he should be able to update the tags of the posts. When a post is tagged with a new tag, that tag should be created if it dosent exist. Also i want to user to be able to specify the tags as a list of strings in the request.
Example request
{"name": "testpost1","caption": "test caption","tags": ["tag1", "tag2"],
},
models.py
class Tags(models.Model):id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)name = models.CharField(max_length=50, unique=True)created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name="created_tags")class Posts(models.Model):id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)name = models.CharField(max_length=50)caption = models.TextField(max_length=1000)created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')tags = models.ManyToManyField('Tags', related_name='posts')
serializers.py
class TagsSerializerMini(serializers.ModelSerializer):created_by = serializers.PrimaryKeyRelatedField(default=serializers.CurrentUserDefault(), queryset=User.objects.all())class Meta:model = Tagsfields = ('name', 'created_by')extra_kwargs = {'created_by': {'write_only': True},'name': {'validators': []},}def create(self, validated_data):tag, created = Tags.objects.get_or_create(**validated_data)if not created:raise exceptions.ValidationError(validated_data['name']+" already exists.")return tagdef to_representation(self, instance):ret = super(TagsSerializerMini, self).to_representation(instance)data = dict()data['name'] = ret['name']return data
I have tried two methods. Using nested serializer and using slug related field.
When using SlugRealtedfield, it throws as validation error that the tag object dosent exists. I was planning if i could deisable this check, i could create all tags before create() and call super create. But i could'nt bypass that validation check. Also i couldnt figure out how to pass the current user to the slugrelatedfield.
After some searching, i planned to use nested serializers. But i have to specify the tags as dict [{"name":"tag1"}]
. Also i have to define custom create and update. I could get the create to work, but not the update.
class PostsSerializer(QueryFieldsMixin, WritableNestedModelSerializer):created_by = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())class Meta:model = Postsfields = ('id', 'name', 'caption', 'tags', 'created_by')def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)self.fields['tags'] = TagsSerializerMini(many=True, required=False, context=self.context)def create(self, validated_data):tags_data = validated_data.pop('tags', [])post = Posts.objects.create(**validated_data)for tag in tags_data:t, _ = Tags.objects.get_or_create(name=tag["name"])post.tags.add(t)return post