Implementing Tags using Django rest framework

2024/10/18 13:36:41

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
Answer

In my opinion, it is more elegant to use SlugRelatedField and not a nested serializer, because this way you will have an array of tags (and an array of tag names in the response) instead of an array of dictionaries [{ "name": "tag name" }]

As you mentioned, the validation check fails if the tag doesn't exist. I managed to overcome 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 objexcept (TypeError, ValueError):self.fail('invalid')
https://en.xdnf.cn/q/73205.html

Related Q&A

Python audiolab install, unable to install (or find) libsndfile on Mac OSX

Trying to install scikits.audiolab-0.11.0 on Mac, bit it requires libsndfile: http://www.mega-nerd.com/libsndfile/. I did install libsndfile supposedly, using libsndfile_python-1.0.0-py2.7-macosx10.5.m…

Connecting to events of another widget

This is most likely a duplicate question, but I have to ask it because other answers arent helping in my case, since I am new to pyqt (switched from tkinter few days ago).I am wondering if is it possib…

Diff multidimensional dictionaries in python

I have two dictionariesa = {home: {name: Team1, score: 0}, away: {name: Team2, score: 0}} b = {home: {name: Team1, score: 2}, away: {name: Team2, score: 0}}The keys never change but I want to get that …

Pandas DatetimeIndex vs to_datetime discrepancies

Im trying to convert a Pandas Series of epoch timestamps to human-readable times. There are at least two obvious ways to do this: pd.DatetimeIndex and pd.to_datetime(). They seem to work in quite dif…

Slicing a circle in equal segments, Python

I have a set of close of 10,000 points on the sky. They are plotted using the RA (right ascension) and DEC (declination) on the sky. When plotted, they take the shape of a circle. What I would like to …

Pyautogui screenshot. Where does it go? How to save and find later?

I am learning from Al Sweigarts you tube video for automating the boring stuff. I got to the part of taking screenshots. He didnt really explain in his video so I tested things out. I found that it tak…

How to get pip to point to newer version of Python

I have two versions of Python installed on my centOS server. [ethan@demo ~]$ python2.6 --version Python 2.6.6 [ehtan@demo ~]$ python --version Python 2.7.3The older version (2.6) is required by some es…

Connect JS client with Python server

Im relatively new to JS and Python, so this is probably a beginners question. Im trying to send a string from a JS client to Python Server (and then send the string to another Python client).This is my…

Pip does not acknowledge Cython

I just installed pip and Python via home-brew on a fresh Mac OS installation.First of all, my pip is not installing dependencies at all - which forces me to re-run pip install tables 3 times and every …

Is it me or is pygame.key.get_pressed() not working?

okay, so I am making a basic space-ship game.I cant get rotation to work because it scrambles the bitmap, but thats for another question.Should I even use a gif? any other filetype suggestions?back t…