英文:
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, inreturn 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, {'style': style})
AttributeError: 'str' object has no attribute 'data'
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:
'image_url': ['airplane.jpg'],
using 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>
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:
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% render_form serializer %}
<input type="submit" value="Wyślij">
</form>
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({"serializer":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({"serializer":serializer, "data": serializer.data})
# template
{% if data %}
{{ data }}
{{ endif }}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% render_form serializer %}
<input type="submit" value="Wyślij">
</form>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论