我有一个简单的DRF REST API,可用于创建博客文章。我希望能够向这些博客文章添加标签,以便用户可以搜索标签并查看相关文章。但是,标签可能还不存在。我已经创建了一个Article
模型,其中一个ForeignKey字段是这样的Tag
模型:
class Tag(models.Model):
name = models.CharField(max_length=32)
def _str__(self):
return self.name
class Meta:
ordering = ('name',)
class Article(models.Model):
title = models.CharField(max_length=256)
author = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
date = models.DateTimeField(auto_now_add=True)
tags = models.ForeignKey(Tag, on_delete=models.CASCADE, blank=True, default=None)
def __str__(self):
return self.title
class Meta:
ordering = ('date', 'id')
理想情况下,我想要的是能够使用一组标签发布新的Article
,并且如果不存在任何标签,请在数据库中创建它们。但是,就目前而言,标记必须已经存在才能添加到Article
中。在视觉上,DRF将其显示为一个下拉列表,其中填充了预先存在的标签:
如何从我的Tag
API端点添加或创建多个Article
?
编辑:根据要求,我添加了views.py
views.py:
from api.blog.serializers import ArticleSerializer, TagSerializer
from rest_framework import viewsets
# /api/blog/articles
class ArticleView(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
# /api/blog/tags
class TagView(viewsets.ModelViewSet):
queryset = Tag.objects.all()
serializer_class = TagSerializer
为完整起见,这是我的REST API的serializers.py中的序列化器。
serializers.py:
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = '__all__'
urls.py:
from rest_framework import routers
router = routers.DefaultRouter()
router.register('articles', views.ArticleView)
router.register('tags', views.TagView)
urlpatterns = [
path('', include(router.urls)),
]
答案 0 :(得分:1)
将序列化程序的 create()
方法覆盖为
class ArticleSerializer(serializers.ModelSerializer):
tags = serializers.CharField()
class Meta:
model = Article
fields = '__all__'
def create(self, validated_data):
tag = validated_data.pop('tags')
tag_instance, created = Tag.objects.get_or_create(name=tag)
article_instance = Article.objects.create(**validated_data, tags=tag_instance)
return article_instance
答案 1 :(得分:0)
好的,感谢@JPG的帮助。这就是我最终得到的。它允许用户在CharField
端点上的/api/blog/article
中添加以空格分隔的标签。执行POST请求时,标记将在空格上进行分割,get_or_create()
d(为此,我需要将Tag.name
用作主键),然后使用以下命令将其添加到Article
中article.tags.set(tag_list)
。正如@JPG和@Martins所建议的,ManyToManyField()
是执行此操作的最佳方法。
这是我的完整代码:
serializers.py:
class ArticleSerializer(serializers.ModelSerializer):
class TagsField(serializers.CharField):
def to_representation(self, tags):
tags = tags.all()
return "".join([(tag.name + " ") for tag in tags]).rstrip(' ')
tags = TagsField()
class Meta:
model = Article
fields = '__all__'
def create(self, validated_data):
tags = validated_data.pop('tags') # Removes the 'tags' entry
tag_list = []
for tag in tags.split(' '):
tag_instance, created = Tag.objects.get_or_create(name=tag)
tag_list += [tag_instance]
article = Article.objects.create(**validated_data)
print(tag_list)
article.tags.set(tag_list)
article.save()
return article
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = '__all__'
请注意,我必须创建一个自定义TagField()
并覆盖to_representation()
。这是因为如果我使用常规的serializer.CharField()
标签,则显示为:“ Blog.tag.None”而不是标签值,如下所示:
models.py:
class Tag(models.Model):
name = models.CharField(max_length=32, primary_key=True)
def __str__(self):
return self.name
class Meta:
ordering = ('name',)
class Article(models.Model):
title = models.CharField(max_length=256)
author = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
date = models.DateTimeField(auto_now_add=True)
tags = models.ManyToManyField(Tag)
def __str__(self):
return self.title
class Meta:
ordering = ('date', 'id')