使用Django rest框架实现标签 [英] Implementing Tags using Django rest framework

查看:86
本文介绍了使用Django rest框架实现标签的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

TDLR :在django-rest-framework中实现标签的最佳方法是什么。标签中的 created_by 字段是当前经过身份验证的用户。

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.

我正在尝试实现一个非常简单的方法/ common,在帖子中添加标签。但显然这不是小菜一碟。

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.

示例请求

{
    "name": "testpost1",
    "caption": "test caption",
    "tags": ["tag1", "tag2"],
},

models.py

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

serializers.py

class TagsSerializerMini(serializers.ModelSerializer):
    created_by = serializers.PrimaryKeyRelatedField(default=serializers.CurrentUserDefault(), queryset=User.objects.all())

    class Meta:
        model = Tags
        fields = ('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 tag

    def to_representation(self, instance):
        ret = super(TagsSerializerMini, self).to_representation(instance)
        data = dict()
        data['name'] = ret['name']
        return data

我尝试了两种方法。使用嵌套的序列化程序并使用slug相关字段。

I have tried two methods. Using nested serializer and using slug related field.

使用SlugRealtedfield时,它会抛出验证错误,即标记对象的主体存在。我正在计划是否可以取消此检查,我可以在create()之前创建所有标签并调用super create。但是我无法绕过该验证检查。我也想不通如何将当前用户传递给slugrelatedfield。

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.

经过一些搜索,我计划使用嵌套的序列化器。但是我必须将标签指定为dict [{ name: tag1}] 。另外,我必须定义自定义创建和更新。我可以使创建工作,而不是更新工作。

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 = Posts
        fields = ('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


推荐答案

在我看来,使用SlugRelatedField而不是嵌套的序列化器会更优雅,因为这样,您将拥有一个标签数组(和响应中的标签名称数组),而不是字典数组[{ name: tag name}]

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" }]

如前所述,如果标签不存在,则验证检查将失败。
我设法通过子类化SlugRelatedField并重写 to_internal_value方法来克服了这个问题。在原始实现中,此方法尝试从查询集中获取对象,如果不存在对象,则验证失败。因此,不是调用 get方法,而是调用 get_or_create:

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 obj
        except (TypeError, ValueError):
            self.fail('invalid')

这篇关于使用Django rest框架实现标签的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆