【问题标题】:How Do I Include ForeignKey in django-rest-framework POST如何在 django-rest-framework POST 中包含 ForeignKey
【发布时间】:2016-12-29 09:19:00
【问题描述】:

所以我尝试通过django-rest-framework (DRF) 创建一个可浏览的 API,但我在嵌套序列化程序时遇到了一些问题。到目前为止,我可以将SportCategory 字段/外键包含到我的Article 中,但是当我尝试通过API 使用POST 时,我收到如下错误消息:

拨打Article.objects.create() 时收到TypeError。这可能是因为您在序列化程序类上有一个可写字段,它不是Article.objects.create() 的有效参数。您可能需要将该字段设为只读,或重写 ArticleSerializer.create() 方法以正确处理此问题。 原始异常文本是:int() 参数必须是字符串、类似字节的对象或数字,而不是“ArticleSport”。

这是我的文件:

models.py

[...]

class ArticleSport(TimeStampedModel):
    title = models.CharField(max_length=20, blank=False)
    slug = AutoSlugField(populate_from='title', unique=True, always_update=True)
    parent = models.ForeignKey('self', blank=True, null=True, related_name='children')  # TODO: Add on_delete?

    uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)

    def __str__(self):
        return '{0}'.format(self.title)

    #class Meta:  # TODO: Migrate live
        #unique_together = ('title', 'parent')


class ArticleCategory(TimeStampedModel):
    title = models.CharField(max_length=20, blank=False)
    slug = AutoSlugField(populate_from='title', unique=True, always_update=True)
    parent = models.ForeignKey('self', blank=True, null=True, related_name='children')  # TODO: Add on_delete?

    uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)

    def __str__(self):
        return '{0}'.format(self.title)

    class Meta:
        verbose_name_plural = 'article categories'
        #unique_together = ('title', 'parent')  # TODO: Migrate live


class Article(TimeStampedModel):
    DEFAULT_FEATURED_IMAGE = settings.STATIC_URL + 'images/defaults/default-featured-image.png'

    title = models.CharField(max_length=160, blank=False)
    slug = AutoSlugField(populate_from='title', unique=True, always_update=True)
    sport = models.ForeignKey(ArticleSport, on_delete=models.CASCADE, related_name='articleAsArticleSport')
    category = models.ForeignKey(ArticleCategory, on_delete=models.CASCADE, related_name='articleAsArticleCategory')
    featured_image = models.ImageField(upload_to=PathAndUniqueFilename('featured-images/'), blank=True)
    featured_image_caption = models.CharField(max_length=100, blank=True)
    views = models.IntegerField(default=0)

    uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)

    def get_absolute_url(self):
        return reverse('main:article_specific', args=[self.slug])  # TODO: Remove if standalone pages are removed

    def get_featured_image(self):
        if self.featured_image:
            return self.featured_image.url
        else:
            return self.DEFAULT_FEATURED_IMAGE

    def get_comment_count(self):
        return ArticleComment.objects.filter(article=self).count()

    def __str__(self):
        return '{0}'.format(self.title)

[...]

urls.py

[...]

class ArticleSportSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = ArticleSport
        fields = ('id', 'title', 'parent', 'created', 'modified')


class ArticleCategorySerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = ArticleCategory
        fields = ('id', 'title', 'parent', 'created', 'modified')


class ArticleSerializer(serializers.HyperlinkedModelSerializer):
    sport = ArticleSportSerializer(read_only=True)
    sport_id = serializers.PrimaryKeyRelatedField(queryset=ArticleSport.objects.all(), write_only=True)
    category = ArticleCategorySerializer(read_only=True)
    category_id = serializers.PrimaryKeyRelatedField(queryset=ArticleCategory.objects.all(), write_only=True)
    modified = serializers.HiddenField(default=timezone.now())  #TODO: Figure out how to implement this

    class Meta:
        model = Article
        fields = ('id', 'title', 'sport', 'sport_id', 'category', 'category_id', 'featured_image', 'featured_image_caption', 'views', 'created', 'modified')

[...]

到 API 的示例 POST:

{
    "title": "This is a test Title",
    "sport_id": 1,
    "category_id": 1,
    "featured_image": null,
    "featured_image_caption": "",
    "views": null,
    "modified": null
}

【问题讨论】:

  • 为什么不发送主键而不是整个模型?
  • 我对 DRF 很陌生,我将如何处理您的建议?

标签: python django api post django-rest-framework


【解决方案1】:

您需要覆盖序列化程序的 create 方法以适应您的 POST 请求。这可能不是您要找的,但您没有包括您的样品请求,所以我们没有太多可做的。

如果我的声誉足够高,这将是一条评论。

【讨论】:

  • 感谢您的评论,我用示例 POST 请求更新了我的问题。如果您可以向我展示一个覆盖 create 方法的示例,它将对我有很大帮助。
  • 如果删除序列化器字段会发生什么?那你可以发帖吗?
  • 如果我尝试删除 sport_idcategory_id,我会收到错误提示 null value in column "category_id" violates not-null constraint(因为它是必需的)
  • 这不应该发生......我不是要你从 POST 请求中删除这些字段。我要求您将它们从序列化程序中删除 ArticleSerializer - 除非您真的想要 write_only,否则不需要它们。但我想知道的是,如果您只是从序列化程序中删除声明,POST 是否会起作用。除非您愿意不使用write_only,否则这不会解决您的问题,但它会为我们提供更多信息。
  • 澄清一下,我最后的回复实际上是从 ArticleSerializer 中删除该字段,而不仅仅是 POST 请求 - 我仍然收到错误,因为需要 category_id
【解决方案2】:

所以我能够通过使用 Carter_Smith 的一些建议来回答这个问题 - 我不是 100% 确定为什么会这样,但我将这个 create() 方法添加到我的 ArticleSerializer 中,并且它奏效了:

def create(self, validated_data):
    # Override default `.create()` method in order to properly add `sport` and `category` into the model
    sport = validated_data.pop('sport_id')
    category = validated_data.pop('category_id')
    article = Article.objects.create(sport=sport, category=category, **validated_data)
    return article

我的猜测是,PrimaryKeyRelatedField() 尝试将 sport_idcategory_id 解析为基于名称的 kwarg 字段,而它们应该只是 sportcategory,因此覆盖 .create() 允许您解决这个问题,同时仍然允许sportcategoryread_only 字段。希望这可以帮助遇到同样问题的其他人。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-27
    • 2019-07-03
    • 1970-01-01
    • 2022-08-07
    • 2022-11-25
    • 1970-01-01
    • 2022-06-14
    • 2018-09-22
    相关资源
    最近更新 更多