DRF File Upload, How can I solve File missing Error? - django

400 Error
{"detail":"Missing filename. Request should include a Content-Disposition header with a filename parameter."}
I want to upload file via DRF FileUploadParser
But Error occurs below
Bad Request: /api/activities/40/chapter/1/upload
[09/Nov/2021 16:44:33] "POST /api/activities/40/chapter/1/upload HTTP/1.1" 400 109
And my codes in views.py about that error are this.
class FileView(APIView):
parser_classes = (FileUploadParser,)
def post(self, request, format=None, *args, **kwargs):
if 'file' not in request.FILES:
raise ParseError("Empty content")
f = request.FILES.get('file')
print(f)
print(dir(request))
print(request.__dict__)
addAttr = request.data.dict()
file_name = request.data['filename']
new_file_full_name = file_upload_path(file_name.name)
file_path = '/'.join(new_file_full_name.split('/')[0:-1])
#model Attr
addAttr['activityid'] = request.parser_context['kwargs']['pk']
addAttr['chapterid'] = request.parser_context['kwargs']['chapterid']
addAttr['filepath'] = file_path
addAttr['filename'] = file_name
addAttr['fileext'] = os.path.splitext(file_name.name)[1]
addAttr['create_date'] = datetime.datetime.now()
addAttrDict = QueryDict('', mutable=True)
addAttrDict.update(addAttr)
fileSerializer = ChapterfileSerializer(data = addAttrDict, files=request.FILES)
if fileSerializer.is_valid():
fileSerializer.save()
print(fileSerializer.data)
return Response(status=status.HTTP_201_CREATED)
else:
print(fileSerializer.errors)
return Response(fileSerializer.errors, status=status.HTTP_400_BAD_REQUEST)
If I add a parameter "filename", 500 error occured.
TypeError: post() missing 1 required positional argument: 'parameter'
[09/Nov/2021 16:48:35] "POST /api/activities/40/chapter/1/upload HTTP/1.1" 500 86716
ReactJS page sends File to my Django API Server.
activityid and chapterid are Board and Post ID.
SO, I need these insert to DB.
How can I solve this?

Use the FileUploadParser, it's all in the request. Use a put method instead, you'll find an example in the docs :)
class FileUploadAPIView(views.APIView):
parser_classes = (FileUploadParser,)
def put(self, request, filename, format=None):
file_obj = request.FILES['file']
# Do some stuff with the uploaded file
return Response({'details': 'Your file uploaded successfully.'}, status=204)

Related

Send file to a different server

I have an upload url in my backend and i want to upload a file in another server.
My API view:
class AssessmentFileUpload(APIView):
parser_classes = (MultiPartParser, )
def post(self, request, format=None):
tenant = request.user.tenant.id
response = AssessmentFileUploadHelper(tenant).upload_file(request.FILES)
response_text = json.loads(response.text)
print(response_text)
if response.status_code == status.HTTP_201_CREATED:
return Response({"message": "success", "id": response_text.get('id')}, status=status.HTTP_201_CREATED)
return Response({"message": "failed"}, status=status.HTTP_400_BAD_REQUEST)
My class which sends request data to the other serve's url:
class AssessmentFileUploadHelper:
def __init__(self, tenant_id):
self.tenant_id = tenant_id
def upload_file(self, file):
print("FILE IS", file)
url = settings.ASSESSMENT_CONNECTION_SETTINGS["api_endpoint"] + "tenant/" + \
str(self.tenant_id) + "/fileupload/"
return RequestSender().send_request(url,None, file)
class RequestSender:
def __init__(self):
super().__init__()
def __get_authorized_header(self):
usernamepassword = settings.ASSESSMENT_CONNECTION_SETTINGS["userid"] + ":" + settings.ASSESSMENT_CONNECTION_SETTINGS["password"]
userAndPass = b64encode(usernamepassword.encode("utf-8")).decode("ascii")
authorization = "Basic " + userAndPass
headers = {'Authorization': authorization, "Content-Type": "application/json"}
return headers
def send_request(self, url, data, files=None):
json_data = json.dumps(data)
response = requests.post(url,
data=json_data,
headers=self.__get_authorized_header(),
files=files
)
return response
Now, the errors im getting is InMemoryUploadedFile is not json serilizaable . How to send request.FILES to that server ?
You neet to convert 'InMemoryUploadedFile' type to string:
str = request.FILES['file'].read().decode()

How to pass data saved from a POST method to the GET method using REST API Django (without a model)?

I have created an API that allows me to upload an image using the POST method in POSTMAN. After submission, I want to display that image name after making a GET request. I am not using any model and I don't intend to grab the image from the directory it is stored in; since I will be uploading images in a server later.
I have looked at multiple sources. A few examples are this, and this.
This is my current code so far but not successful:
views.py:
class API(APIView):
parser_classes = (MultiPartParser,)
def get(self, request, *args, **kwargs):
name = self.request.GET.get('image')
if name:
return Response({"img_name": name}, status=200)
return Response({"img_name" : None}, status = 400)
def post(self, request):
file = self.request.data
img_file = file['image'] #store the image data in this variable
if img_file:
uploaded_file = img_file
img = [{"image_name": uploaded_file}]
serializer = ImgSerializer(img, many = True).data
return Response(serializer, status = 200)
else:
return Response("Please upload", status = 400)
serializers.py:
from rest_framework import serializers
class ImgSerializer(serializers.Serializer):
image_name = serializers.CharField()
My expected result within GET request should be like this:
{'image_name' : 'image_name_from_POST_Request'}
But I am getting this result instead:
None
How can I pass data from the POST request to the GET request using Django's rest framework? Is there an efficient way to deploy this requirement without using a model?
I figured it out. I just created a JSON file in the POST method and stored the necessary data in it. Finally, in order to view the data within the GET method, I opened the file and returned it as a Response.
views.py:
class API(APIView):
parser_classes = (MultiPartParser,)
def get(self, request):
with open('data.txt') as json_file:
data = json.load(json_file)
if data:
return Response(data, status=200)
return Response({"name" : None}, status = 400)
def post(self, request):
posted_file = self.request.data
img_file = posted_file['image']
if img_file:
uploaded_file = img_file
data = [{"image_name": uploaded_file}]
json_data = {"image_name": uploaded_file}
data = {}
data['key'] = []
data['key'].append(json_data)
with open('data.txt', 'w') as outfile:
json.dump(image, outfile)
serializer = ImgSerializer(image, many = True).data
return Response(serializer, status = 200)
else:
return Response(serializer.errors, status = 400)

django test client.put doesn't have data

I am trying to test django view with REST framework.
It starts like this.
class MoveViewSet(viewsets.ModelViewSet):
serializer_class = FamilySerializer
queryset = Family.objects.all()
http_method_names = ['put']
def update(self, request, pk=None):
user = request.mobile_user
...
family_id = request.POST.get('family_id', None)
...
in test.py I make a request like this.
data = dict(
join_type='join',
family_id=target_family.id,
)
# also tried data = { ... }
header = {
'Authorization': user.api_key,
...
}
client = Client()
response = client.put(url, data=data, content_type='x-www-form-urlencoded', **header)
### What I've tried ###
# response = client.put(url, data=data, **header)
# response = self.client.put(url, data=data, **header)
# response = client.put(url, data=data, content_type='application/json', **header)
but in view, request.POST.get('paramname', 'default') makes error. request.POST has empty parameter set and of course, request.PUT is None.
I checked middleware but neither could I find params there.
Also I've tried this in middleware's process_request.
def process_request(self, request):
if request.method == "PUT" and request.content_type != "application/json":
if hasattr(request, '_post'):
del request._post
del request._files
try:
request.method = "POST"
request._load_post_and_files()
request.method = "PUT"
except AttributeError as e:
request.META['REQUEST_METHOD'] = 'POST'
request._load_post_and_files()
request.META['REQUEST_METHOD'] = 'PUT'
request.PUT = request.POST
It gives me this error.
AttributeError: 'WSGIRequest' object has no attribute 'content_type'
If I send client.post() with data, request.POST has data, but put doesn't.
How could I test client.put() with parameters?
request's content_type attribute has been added in Django 1.10. Since you are using Django 1.9 you cannot use this attribute. You can chec request.META.get('HTTP_ACCEPT') solution as mentioned here or update Django.

Django test client post data

The problem has been solved thanks to Thaian, by adding a login to the beginning of the 'test_create' function, as you need to be logged in on this site to use the createview
I am currently writing a test for a createview and I am unable to post data to it.
The object being tested has the following model
class Role(models.Model):
name = models.CharField(max_length=255)
linked_tenant = models.ForeignKey(Tenant, blank=True, null=True)
And is used in the following (generic) view
class RolCreate(TenantRootedMixin, CreateView):
model = RolTemplate
form_class = RoleForm
def get_form_kwargs(self):
kwargs = super(RolCreate, self).get_form_kwargs()
kwargs['linked_tenant'] = self.request.tenant
return kwargs
def form_valid(self, form):
form.instance.linked_tenant = self.kwargs.get('tenant')
return super(RolCreate, self).form_valid(form)
def get_success_url(self, **kwargs):
return reverse('rol_list', args=[self.request.tenant.slug])
And this is the test that I am using.
class RolCreate_tests(TestCase):
def setUp(self):
self.tenant = get_tenant()
self.role = get_role(self.tenant)
self.client = Client(HTTP_HOST='tc.tc:8000')
self.user = get_user(self.tenant)
def test_create(self):
response = self.client.post(reverse('rolcreate'), {'name' : 'new_object'})
self.assertEqual(response.status_code, 302)
test_against = Role.objects.get(name='new_object')
self.assertEqual(test_against, self.tenant)
The assertion that throws the error is the 'get' request at the end.
DoesNotExist: Role matching query does not exist.
So the object is not created, yet the test does validate the 302 view, meaning a post is being made. I do not understand why this test is failing to do what it should. Could someone here help me?
=====
After Thaians suggestions I got the following values:
(Pdb) print(self.client.post)
<bound method Client.post of <django.test.client.Client object at 0x10f20da50>>
Pdb) response
<HttpResponseRedirect status_code=302, "text/html; charset=utf-8", url="/accounts/login/?next=/my/role/create/">
(Pdb) print(response)
Vary: Cookie
Content-Length: 0
Content-Type: text/html; charset=utf-8
Location: /accounts/login/?next=/my/role/create/
Did you print response and check what return maybe?
Good idea is to run tests with PDB.
def test_create(self):
response = self.client.post(reverse('rolcreate'), {'name': 'new_object'})
import pdb; pdb.set_trace()
self.assertEqual(response.status_code, 302)
test_against = Role.objects.get(name='new_object')
self.assertEqual(test_against, self.tenant)
add import pdb;pdb.set_trace() in your test and then check self.client.post().
So please paste what response contain.

Using django rest framework to send an image

I want to use DRF to send an image to response to a certain url, for this purpose I have written these codes:
#Renderer class
class ImageRenderer(renderers.BaseRenderer):
media_type = 'image/png'
format = 'image'
def render(self, data, media_type=None, renderer_context=None):
return data
#view class
class ShowImage(APIView):
renderer_classes = (ImageRenderer,)
def get(self, request, format=None):
print ('format', format)
if format == 'image':
image_file = open('path_to_image', 'rb')
response = HttpResponse(image_file, content_type='image/png')
response['Content-Disposition'] = 'attachment; filename={}'.format('image_filename')
#urls.py
urlpatterns = format_suffix_patterns([
url(r'image/?$', views.ShowImage.as_view())
])
But my problem is that always the input format is None although the request.accepted_media_type shows me image/png
I tried these requests using httpie:
http -vj 127.0.0.1:8000/api/image Accept:image/png
http -vj 127.0.0.1:8000/api/image.image Accept:image/png
But I could not get the format I expected. What is my mistake in using DRF?