Django rest framework, AttributeError: 'str' object has no attribute 'data' and unable to upload image using forms

huangapple go评论94阅读模式
英文:

Django rest framework, AttributeError: 'str' object has no attribute 'data' and unable to upload image using forms

问题

我在招聘任务中遇到了困难,我在Django方面相当新手,以前没有使用rest framework。
我尝试创建一个视图,用户可以上传图片并设置链接的有效期(在3000-30000范围内,验证已经起作用)。这个功能在Postman中工作正常,但在接收到文件链接时出现以下错误:

return renderer.render(serializer.data, None, {'style': style})
AttributeError: 'str' object has no attribute 'data'

当我尝试使用网站上传文件时,序列化程序未通过验证。

我在HTML文件中使用render_form,在QueryDict中的结果与Postman中不同,所以我猜这可能是问题,但不知道如何解决它:
使用网站:

'image_url': ['airplane.jpg'],

使用Postman:

 'image_url': [<InMemoryUploadedFile: airplane.jpg (image/jpeg)>]

models.py:

import datetime

from django.db import models
from django.contrib.auth.models import User
from images.validators import expiring_in_validator

def upload_to(instance, filename):
    unique_identify = str(int(datetime.datetime.now().timestamp()))
    return f"media/{filename}-{unique_identify}"

# Create your models here.
class Image(models.Model):
    uploaded_by = models.ForeignKey(User, on_delete=models.CASCADE)
    image_url = models.ImageField(upload_to=upload_to)
    expiring_within = models.IntegerField(validators=[expiring_in_validator])

serializers.py:

from rest_framework import serializers
from images.models import Image

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Image
        fields = ['image_url', 'expiring_within']

views.py:

from django.shortcuts import render
from django.views import View
from rest_framework import status
from rest_framework.parsers import FormParser, MultiPartParser
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.renderers import TemplateHTMLRenderer
from images.serializers import PostSerializer

# Create your views here.
class AddImage(APIView):
    renderer_classes = [TemplateHTMLRenderer]
    # permission_classes = [IsAuthenticated]
    template_name = "add_image.html"
    parser_classes = [MultiPartParser, FormParser]
    def get(self, request):
        serializer = PostSerializer()
        return Response({"serializer": serializer})

    def post(self, request):
        serializer = PostSerializer(data=request.data)
        if serializer is_valid():
            serializer.save(uploaded_by=self.request.user)
            return Response(serializer.data, status=status.HTTP_200_OK)

html:

<form method="post">
{% csrf_token %}
    {% render_form serializer %}

    <input type="submit" value="Wyślij">
</form>

表单现在可以使用,但在上传图片后,我得到以下的跟踪:

Traceback (most recent call last): File
"/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/core/handlers/exception.py",
line 56, in inner
response = get_response(request) File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/core/handlers/base.py",
line 220, in _get_response
response = response.render() File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/template/response.py",
line 114, in render
self.content = self.rendered_content File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/rest_framework/response.py",
line 70, in rendered_content
ret = renderer.render(self.data, accepted_media_type, context) File
"/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/rest_framework/renderers.py",
line 166, in render
return template.render(context, request=request) File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/template/backends/django.py",
line 61, in render
return self.template.render(context) File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/template/base.py",
line 175, in render
return self._render(context) File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/template/base.py",
line 167, in _render
return self.nodelist.render(context) File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/template/base.py",
line 1005, in render
return SafeString("".join([node.render_annotated(context) for node in self])) File
"/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/template/base.py",
line 1005, in return SafeString("".join([node.render_annotated(context) for node in self])) File
"/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/template/base.py",
line 966, in render_annotated
return self.render(context) File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/template/library.py",
line 237, in render
output = self.func(*resolved_args, **resolved_kwargs) File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/rest_framework/templatetags/rest_framework.py",
line 81, in render_form
return renderer.render(serializer.data, None, {'style': style}) AttributeError: 'str' object has no attribute 'data' [08/Mar/2023
09:59:08] "POST /uploadimage/ HTTP/1.1" 500 115861

英文:

I'm struggling with recruitment task, i'm pretty new in Django and I haven't used rest framework before.
I'm trying to built view where user can upload picture and set how long link should be valid(in range 3000-30000, validation works for that). This feature works thru postman instaed of received link to file I have following error:

    return renderer.render(serializer.data, None, {&#39;style&#39;: style})
AttributeError: &#39;str&#39; object has no attribute &#39;data&#39;

When I try to upload file using website serializer is not passing validation.

I'm using render_form in html file and result in QueryDict is different than in postman so i guess thats the issue, however no idea how to solve it:
using website:

&#39;image_url&#39;: [&#39;airplane.jpg&#39;],

using postman:

 &#39;image_url&#39;: [&lt;InMemoryUploadedFile: airplane.jpg (image/jpeg)&gt;]

models.py:

import datetime

from django.db import models
from django.contrib.auth.models import User
from images.validators import expiring_in_validator


def upload_to(instance, filename):
    unique_identify = str(int(datetime.datetime.now().timestamp()))
    return f&quot;media/{filename}-{unique_identify}&quot;

# Create your models here.
class Image(models.Model):
    uploaded_by = models.ForeignKey(User, on_delete=models.CASCADE)
    image_url = models.ImageField(upload_to=upload_to)
    expiring_within = models.IntegerField(validators=[expiring_in_validator])

serializers.py:

from rest_framework import serializers
from images.models import Image


class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Image
        fields = [&#39;image_url&#39;, &#39;expiring_within&#39;]

views.py:

from django.shortcuts import render
from django.views import View
from rest_framework import status
from rest_framework.parsers import FormParser, MultiPartParser
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.renderers import TemplateHTMLRenderer

from images.serializers import PostSerializer


# Create your views here.
class AddImage(APIView):
    renderer_classes = [TemplateHTMLRenderer]
    # permission_classes = [IsAuthenticated]
    template_name = &quot;add_image.html&quot;
    parser_classes = [MultiPartParser, FormParser]
    def get(self, request):
        serializer = PostSerializer()
        return Response({&quot;serializer&quot;: serializer})

    def post(self, request):
        serializer = PostSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save(uploaded_by=self.request.user)
            return Response(serializer.data, status=status.HTTP_200_OK)

html:

&lt;form method=&quot;post&quot;&gt;
{% csrf_token %}
        {% render_form serializer %}

    &lt;input type=&quot;submit&quot; value=&quot;Wyślij&quot;&gt;
&lt;/form&gt;

Forms works now but after uploading image instead of link I'm having following traceback:(

> Traceback (most recent call last): File
> "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/core/handlers/exception.py",
> line 56, in inner
> response = get_response(request) File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/core/handlers/base.py",
> line 220, in _get_response
> response = response.render() File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/template/response.py",
> line 114, in render
> self.content = self.rendered_content File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/rest_framework/response.py",
> line 70, in rendered_content
> ret = renderer.render(self.data, accepted_media_type, context) File
> "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/rest_framework/renderers.py",
> line 166, in render
> return template.render(context, request=request) File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/template/backends/django.py",
> line 61, in render
> return self.template.render(context) File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/template/base.py",
> line 175, in render
> return self._render(context) File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/template/base.py",
> line 167, in _render
> return self.nodelist.render(context) File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/template/base.py",
> line 1005, in render
> return SafeString("".join([node.render_annotated(context) for node in self])) File
> "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/template/base.py",
> line 1005, in <listcomp>
> return SafeString("".join([node.render_annotated(context) for node in self])) File
> "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/template/base.py",
> line 966, in render_annotated
> return self.render(context) File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/django/template/library.py",
> line 237, in render
> output = self.func(*resolved_args, **resolved_kwargs) File "/home/wojciech/PycharmProjects/thumbnail/virtual/lib/python3.10/site-packages/rest_framework/templatetags/rest_framework.py",
> line 81, in render_form
> return renderer.render(serializer.data, None, {'style': style}) AttributeError: 'str' object has no attribute 'data' [08/Mar/2023
> 09:59:08] "POST /uploadimage/ HTTP/1.1" 500 115861

答案1

得分: 1

关于表单,您需要添加enctype信息以将文件绑定到表单,就像这样:

<form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {% render_form serializer %}
    <input type="submit" value="Wyślij">
</form>

关于实际问题"str对象没有属性数据",DRF文档提到:

Django Rest Framework寻找render_form模板标签来呈现序列化器。

错误发生是因为在HTML文件中它正在寻找使用render_form标签来呈现序列化器。由于您将序列化器作为serializer上下文变量传递,您需要像这样在POST方法中发送序列化器:

if serializer.is_valid():
    serializer.save(uploaded_by=self.request.user)
    return Response({"serializer": serializer})

如果您不想再次呈现表单,我建议将其重定向到不同的URL,在那里您以JSON格式获取相同的响应,使用JSONRenderer。如果您打算在HTML中呈现响应,那么我建议添加另一个上下文变量,像这样:

视图:

if serializer.is_valid():
    serializer.save(uploaded_by=self.request.user)
    return Response({"serializer": serializer, "data": serializer.data})

模板:

{% if data %}
    {{ data }}
{% endif %}
<form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {% render_form serializer %}
    <input type="submit" value="Wyślij">
</form>
英文:

For the form, you need to add enctype information to bind the file to the form, like this:

&lt;form method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;
{% csrf_token %}
        {% render_form serializer %}

    &lt;input type=&quot;submit&quot; value=&quot;Wyślij&quot;&gt;
&lt;/form&gt;

For the actual problem regarding str object has no attribute data, DRF documentation mentions that:

>Django rest framework looks for render_form template tag, to render Serializer.

The error is happening because in the html file it is looking for rendering the serializer using render_form tag. Since you are passing the serializer as serializer context variable, you need to send the serializer in post method like this:

   if serializer.is_valid():
        serializer.save(uploaded_by=self.request.user)
        return Response({&quot;serializer&quot;:serializer})

If you do not want to render the form again, I suggest either redirect it to a different url where you get the same response in JSON format using JSONRenderer. If you intend to render in the response in HTML, then I would put another context variable, like this:

# view
if serializer.is_valid():
    serializer.save(uploaded_by=self.request.user)
    return Response({&quot;serializer&quot;:serializer, &quot;data&quot;: serializer.data})

# template
{% if data %}
    {{ data }}
{{ endif }}
&lt;form method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;
{% csrf_token %}
        {% render_form serializer %}

    &lt;input type=&quot;submit&quot; value=&quot;Wyślij&quot;&gt;
&lt;/form&gt;

huangapple
  • 本文由 发表于 2023年3月7日 17:48:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/75660321.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定