【问题标题】:Django Rest Framework - Create foreign key object on POSTDjango Rest Framework - 在 POST 上创建外键对象
【发布时间】:2018-10-28 01:40:50
【问题描述】:

我有一个简单的 DRF REST API,我想用它来创建博客文章。我希望能够为这些博客文章添加标签,以便用户可以搜索标签并查看相关文章。但是,标签可能还不存在。我创建了一个带有 ForeignKey 字段的 Article 模型到 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 将其显示为一个下拉列表,其中填充了预先存在的标签:

如何从我的Article API 端点添加或创建多个Tags?

编辑:根据要求,我添加了我的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)),
]

【问题讨论】:

  • 你的模型很好,但你可能需要对你的视图做一些工作。为什么不发布您的views.py?
  • 好主意@RedCricket。我已经添加了我的观点。如果他们是常规的 CBV,我会很清楚如何做到这一点,但我对 DRF 有点陌生。

标签: python django django-rest-framework foreign-keys


【解决方案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

【讨论】:

  • 谢谢!你的回答让我更接近了!但是,我希望创建多个标记对象(每个都以一个单词作为名称)。为了扩展您的答案,我想我需要将 ForeignKeyArticle 移动到 Tag 并更改其他一些内容。由于我认为他们可能对预期行为的含义有些困惑,因此我会将您的解决方案标记为正确,然后在我得到它的工作时也发布我的解决方案。
  • 我认为,M2M 关系对于Tags 会更好
  • 您的文章与标签的关系需要是 ManyToManyField,以便您可以添加多个标签对象。
【解决方案2】:

好的,感谢@JPG 的帮助。这就是我最终的结果。它允许用户将空格分隔的标签添加到/api/blog/article 端点上的CharField 中。当执行一个 POST 请求时,标签被分割成空格,get_or_create()d(为此我需要将Tag.name 设为主键),然后使用article.tags.set(tag_list) 添加到Article。正如@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')

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多