Skip to main content
 首页 » 编程设计

python之如何在 django-rest-framework POST 中包含 ForeignKey

2024年10月17日23telwanggs

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

Got a TypeError when calling Article.objects.create(). This may be because you have a writable field on the serializer class that is not a valid argument to Article.objects.create(). You may need to make the field read-only, or override the ArticleSerializer.create() method to handle this correctly. Original exception text was: int() argument must be a string, a bytes-like object or a number, not 'ArticleSport'.

这是我的文件:

模型.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') 
 
[...] 

示例 POST 到 API:

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

请您参考如下方法:

所以我能够使用一些 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_id和 category_id作为基于名称的 kwarg 字段,当它们应该只是 sport 时和 category ,所以压倒一切.create()允许您修复该问题,同时仍允许 read_only sport 的字段和 category .希望这对遇到同样问题的其他人有所帮助。