【问题标题】:Django Rest Framework - Serializer not saving Model that has an ImageFieldDjango Rest Framework - 序列化程序不保存具有 ImageField 的模型
【发布时间】:2020-10-24 21:11:24
【问题描述】:

我有一个图像模型,Image,它在不同类型的文章上有外键。 我想通过 REST 接口(使用 Django-Rest-Framework 构建)公开这个模型,并通过 Angular 10 中的 AJAX 调用将图像上传到它。

到目前为止,在一般情况下进行文件上传工作,因为我能够成功关注this guide here(编辑:这是不正确的,因为我已经意识到。它确实创建了文件,但那些不是'不是实际的图像,它们实际上是不可读的)。

但它在某种程度上不适用于我的 ImageModel 和 ImageSerializer。当我此时触发我的 AJAX 调用时,目前我在前端收到 HTTP 500 响应,而在 Django 的后端收到此错误:

File "/home/isofruit/.virtualenvs/AldruneWiki-xa3nBChR/lib/python3.6/site-packages/rest_framework/serializers.py", line 207, in save
    'create() did not return an object instance.'

这是我通过 AJAX 调用发送的 FormData 对象内容的控制台日志,但我的 Image 模型失败(控制台日志是通过迭代 FormData 获取的,因为仅记录 FormData 不会显示其内容)。请注意,除了图像之外,模型中不需要这些值:

在模型、序列化程序和 DRF 中的视图下方找到我的 Type-Script POST 方法以调用该 API:

//Typescript ImageUploadService.ts post method
  postImage(imageModel: Image, imageFile: File){
    const url = `${Constants.wikiApiUrl}/image/upload/`;
    const formData: FormData = new FormData();
    for ( var key in imageModel ) {
      if (key==="image"){
        formData.append("image", imageFile, imageFile.name);
      } else if (imageModel[key]){
        formData.append(key, imageModel[key]);
      }
    }
    const options = {headers: new HttpHeaders({"Content-Disposition": `attachment; filename=${imageFile.name}`})}
    return this.http.post(url, formData, options);
  }
#serializers.py
class ImageSerializer (serializers.ModelSerializer):
    image = serializers.ImageField("image.image")

    class Meta:
        model = wiki_models.Image
        fields = [
            'pk',
            'image',
            'name',
            'character_article',
            'creature_article',
            'encounter_article',
            'item_article',
            'location_article',
            'organization_article',
        ]


#api_views.py
class ImageUploadView(APIView):
    parser_classes = (FileUploadParser,)

    def post(self, request, *args, **kwargs):
        image_serializer = ImageSerializer(data=request.data)
        if image_serializer.is_valid():
            image_serializer.save()
            return Response(image_serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(image_serializer.errors, status=status.HTTP_400_BAD_REQUEST)


# models.py
class Image(models.Model):
    """A table to hold additional images for articles"""
    image = models.ImageField(upload_to='article_images',
                              default=settings.DEFAULT_IMAGE,
                              max_length=400)
    name = models.CharField(max_length=400, null=True, blank=True, help_text='A name describing the image')
    character_article = models.ForeignKey('Character', null=True, blank=True, related_name='character_gallery_image',
                                          on_delete=models.CASCADE)
    creature_article = models.ForeignKey('Creature', null=True, blank=True, related_name='creature_gallery_image',
                                         on_delete=models.CASCADE)
    encounter_article = models.ForeignKey('Encounter', null=True, blank=True, related_name='encounter_gallery_image',
                                          on_delete=models.CASCADE)
    item_article = models.ForeignKey('Item', null=True, blank=True, related_name='item_gallery_image',
                                     on_delete=models.CASCADE)
    location_article = models.ForeignKey('Location', null=True, blank=True, related_name='location_gallery_image',
                                         on_delete=models.CASCADE)
    organization_article = models.ForeignKey('Organization', null=True, blank=True, related_name='gallery_image',
                                             on_delete=models.CASCADE)

    class Meta:
        ordering = ['creature_article', 'organization_article', 'location_article',
                    'encounter_article', 'item_article', 'character_article']

    @property
    def article(self):
        fields = [field for field in self._meta.fields if 'article' in field.name and getattr(self, field.name) is not None]
        if len(fields) > 1:
            raise AssertionError(f'{self} is linked to more than one article!')
        elif len(fields) < 1:
            raise AssertionError(f'{self} is not linked to an article!')
        else:
            target_article_fieldname = fields[0].name
            return getattr(self, target_article_fieldname)

    @property
    def article_fieldname(self):
        article_model = type(self.article).__name__.lower()
        return f'{article_model}_article'

编辑:

为了完整起见,并且因为需要这些信息来发现错误,这里是我使用的表单的 HTML 和 Typescript(我正在使用 Angular 的“Formly”生成表单):

                    <form [formGroup]="form" (ngSubmit)="cl(constants.createSignal)">
                        <formly-form [form]="form" [fields]="fields" [model]="model"></formly-form>
                        <div class="form-group">
                            <input type="file" (change)="onFileSelected($event)">
                        </div>
                        <button type="submit" class="btn btn-outline-secondary">Submit</button>
                    </form>

【问题讨论】:

    标签: python django file django-models django-rest-framework


    【解决方案1】:

    在我第三次研究了所有其他有类似问题的问题之后,这个问题有了答案。

    由于我对自己的代码不信任,我开始使用Postman 桌面客户端发送请求。再次应用几个答案后,我终于发现我的问题有多个方面:

    1. 我需要使用正文编码 multipart-formdata 发送请求,因为我的服务使用 JS FormData 对象发送请求。所以我需要将它放入我的表单中,以便将它放在我的 POST 请求的标题中。因此我需要将enctype="multipart/form-data" 添加到我的&lt;form&gt; 标签中。
    2. 我的ImageUploadView 需要一个可以处理多部分表单数据请求的解析器​​,因为我之前使用的是FileUploadParser。 Django Rest Framework 自带一个,所以我在我的ImageUploadView 这一行改了:parser_classes = (FileUploadParser,)parser_classes = (MultiPartParser,)

    问题的基本性质与this question 中的相同,其中收到“未提交文件” - 这就是当您使用 FileUploadParser 而不是 MultiPartParser 以及编码类型错误时得到的结果。

    【讨论】:

      猜你喜欢
      • 2018-09-10
      • 2019-05-25
      • 1970-01-01
      • 2021-07-30
      • 2013-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多