TDLR:在django-rest框架中实现标记的最佳方法是什么。其中标记有一个created_by
字段,该字段是当前经过身份验证的用户。在
我试图实现一个非常简单/普通的事情,添加标签到帖子。但显然这不是小菜一碟。在
所以我有一个posts模型和一个tags模型(may to many relationship)。我希望用户能够更新和创建帖子。当创建或更新帖子时,他应该能够更新帖子的标签。当一篇文章用一个新的标签来标记时,如果这个标签已经存在,就应该创建它。我还希望用户能够在请求中将标记指定为字符串列表。在
示例请求
{
"name": "testpost1",
"caption": "test caption",
"tags": ["tag1", "tag2"],
},
在模型.py在
^{pr2}$在序列化程序.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相关字段。在
当使用slugreatedfield时,它会抛出一个验证错误,即标记对象dosent存在。我在计划如果我可以取消这个检查,我可以在create()之前创建所有标记并调用super create。但我无法绕过验证检查。我也不知道如何将当前用户传递给slugrelatedfield。在
经过一番搜索,我计划使用嵌套序列化程序。但是我必须将标记指定为dict[{"name":"tag1"}]
。我还必须定义自定义创建和更新。我可以让创建工作,但不能更新。在
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”}]
正如您所提到的,如果标记不存在,验证检查将失败。 我通过将SlugRelatedField子类化并重写“to_internal_value”方法来克服这个问题。在最初的实现中,这个方法尝试从queryset中获取一个对象,如果对象不存在,则验证失败。因此,我没有调用“get”方法,而是调用“get”或“u create”:
如果您可以接受使用两个字段,下面是我的解决方案:
将SlugRelatedField用于只读,将列表字段用于只写,这样就可以拥有字符串列表而不是字典。在
要获取当前用户,可以使用自我语境['request'].user在序列化程序函数中。在
下面是示例代码(未测试):
注:我使用instance.tags.set而不是instance.tags.add,以便可以删除标记关系。尽管你总是需要发送标签。在
相关问题 更多 >
编程相关推荐