Hi I'm trying to upload a file to server and I'm having difficulty sending the file. I'm using Django at the backend and I've already set up a put endpoint allowing users to send files and I confirmed it works as intended using postman. However, I'm having trouble uploading on the frontend. So this is how my code looks on the frontend end.
selectFileManually.addEventListener('change', function(event){
event.stopPropagation();
event.preventDefault();
axios.put('http://127.0.0.1:8000/api/v1/fileupload/', {
file: this.files[0]
}).then(resp => console.log(resp.data)).catch(err => console.log(err.response.data))
}
}
})
Here selectFileManually is an input[type='file']. However, when I send this request the server comes back with the following error: "Missing filename. Request should include a Content-Disposition header with a filename parameter and when I look at the payload it's complete empty: `{file: {}}' even though you can clearly see I provided a file to send. This is how my code looks at the backend
# views.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import FileUploadParser, MultiPartParser
import os
# Create your views here.
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
class FileUploadView(APIView):
parser_classes = (MultiPartParser, FileUploadParser, )
def put(self, request, format=None):
print(request.data)
with open(os.path.join(BASE_DIR, 'api/media', request.data['file'].name), 'wb') as f:
for chunk in request.data['file'].chunks():
f.write(chunk)
return Response(status=204)
#urls.py
from django.urls import path
from . import views
urlpatterns = [
path('fileupload/', views.FileUploadView.as_view()),
]
Could somebody please help me. I know that this.files[0] is not empty since the console logged the files and it showed me that it was indeed the right content
According to the docs, you need to specify a filename parameter for the view to handle the request. This can be done in two ways:
1. The view can be called using the filename URL keyword argument, then that argument is set as the filename.To achieve this you need to change your url pattern to
urlpatterns = [
path('fileupload/(?P<filename>[^/]+)$', views.FileUploadView.as_view()),
]
2. If the view is called without the filename URL keyword argument, then the client must set it in the Content-Disposition HTTP header. It is then handled by FileUploadParser in the views.
For eg Content-Disposition: attachment; filename=image.jpg.
Either way, you need to access filename while making a request.
In your case, since your url pattern does not contain a filename argument you need to set headers to include Content-Disposition.
Assuming this.files[0] is a fileObject, you can get filename by simply doing this.files[0].name. Now setting headers on the axios request, so your front end code should look like this.
selectFileManually.addEventListener('change', function(event){
event.stopPropagation();
event.preventDefault();
axios.put('http://127.0.0.1:8000/api/v1/fileupload/', {
file: this.files[0]
},{headers:{
'Content-Disposition': 'attachment; filename=this.files[0].name'
}
},
).then(resp => console.log(resp.data)).catch(err => console.log(err.response.data))
}
}
})
Hope this helps !
Related
What I'm trying to do is build a custom version of cache_page where I have more control over the cache key, but I'm getting stuck with even the basic caching of my response:
from django.core.cache import cache
from rest_framework import viewsets
from rest_framework.response import Response
from app import models
class BaseViewSet(viewsets.GenericViewSet):
queryset = models.Items.objects.all()
def get_queryset(self):
return models.Items.objects.all()
def list(self, request, **kwargs):
response = Response({})
cache.set('test', response, 10)
return response
Where the relevant parts of my settings.py are setup as:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
],
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning'
}
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": f"redis://127.0.0.1:6729/1",
},
}
When I try to call the endpoint I get:
django.template.response.ContentNotRenderedError: The response content must be rendered before it can be pickled.
Then if I change the line to:
cache.set('test', response.render(), 10)
I get:
AssertionError: .accepted_renderer not set on Response
(If I set the renderer it complains about the accepted media, then the context and finally fails with TypeError: 'bytes' object is not callable)
Despite the fact that the API call itself works fine without the caching.
cache_page actually works fine, so I know it's possible to cache the response, but I can't figure out what I'm missing.
I solved this issue by having the view return a django.http.JsonResponse rather than a rest_framework.response.Response object. The problem is that the rest_framework Response is mediatype agnostic, and as far as I can tell relies on having the renderer and accepted media values set further down the call chain of the view, after it has left your handler. Since JsonResponse only deals with json, it doesn't have to go through this "content negotiation".
cache_page
work because of this:
if hasattr(response, 'render') and callable(response.render):
response.add_post_render_callback(lambda r: self.cache.set(cache_key, r, timeout))
else:
self.cache.set(cache_key, response, timeout)
I followed this https://stackoverflow.com/a/31381075/10087274 because I want when url does not exist a json shows error but the problem is here that I get server error 500 when I add handler404 in my url dispatcher
here is my project url :
from django.urls import path, include
from django.conf.urls import handler404
from api.exception import custom404
handler404 = custom404
urlpatterns = [
path('api/v1/', include('acl.urls')),
]
and I have exception.py inside my project folder (near settings.py) contains this:
from django.http import JsonResponse
def custom404(request):
return JsonResponse({
'status_code': 404,
'error': 'The resource was not found'
})
I dont know how can I solve my problem
Unfortunately, you haven't provided much information regarding the error traceback. Anyway, The very first thing I noticed in your code, you've missed one optional parameter to the custom404 function. That function should take two parameters, request and exception
def custom404(request, exception=None):
response = {
'status_code': 404,
'error': 'The resource was not found'
}
return JsonResponse(response, status=404)
Reference
1. Custom Error Views
Well django rest framework is open source, so if you want to replicate certain behaviour you can read the code and pick and chose what you like. For instance, you can see that the Generic Error view (custom server and bad request error views) provided in drf docs are located inside exceptions.py inside rest_framework you can look it up here and see how it can be done.
Create a custom 404 view, like this:
def not_found(request, exception, *args, **kwargs):
""" Generic 404 error handler """
data = {
'error': 'Not Found (404)'
}
return JsonResponse(data, status=status.HTTP_404_NOT_FOUND)
I am making an api which return the JsonResponse as my text from the scrapy. When i run the scripts individually it runs perfectly. But when i try to integrate the scrapy script with python django i am not getting the output.
What i want is only return the response to the request(which in my case is POSTMAN POST request.
Here is the code which i am trying
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
import scrapy
from scrapy.crawler import CrawlerProcess
#csrf_exempt
def some_view(request, username):
process = CrawlerProcess({
'USER_AGENT': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)',
'LOG_ENABLED': 'false'
})
process_test = process.crawl(QuotesSpider)
process.start()
return JsonResponse({'return': process_test})
class QuotesSpider(scrapy.Spider):
name = "quotes"
def start_requests(self):
urls = [
'http://quotes.toscrape.com/random',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
def parse(self, response):
return response.css('.text::text').extract_first()
I am very new to python and django stuff.Any kind of help would be much appreciated.
In your code, process_test is a CrawlerProcess, not the output from the crawling.
You need additional configuration to make your spider store its output "somewhere". See this SO Q&A about writing a custom pipeline.
If you just want to synchronously retrieve and parse a single page, you may be better off using requests to retrieve the page, and parsel to parse it.
How can I include an HTTP header, such as Cache-Control or Last-Modified, in the response to a django-piston call?
You can wrap it in your urls.py following the procedure in the specifying per view cache in urlconf guide in the Django documentation. In my case I had my Piston API in a separate module and prefer to use Varnish instead of the built-in Django caching framework so I used this approach in my api/urls.py (which my main urls.py includes) to set the cache control headers I wanted:
from django.views.decorators.cache import cache_control
cached_resource = cache_control(public=True, maxage=30, s_maxage=300)
urlpatterns = patterns('',
url(r'^myresource/$', cached_resource(Resource(MyHandler))),
)
Not sure about django-piston, but in django you can just go:
from django.http import HttpResponse
response = HttpResponse('My content')
response['MyHttpHeader'] = 'MyHeaderValue'
So, do that where you can get access to the response. Middleware is often the perfect place to do this if you are using a 3rd party application. Your middleware could look something like:
def process_response(self, request, response):
response['MyHttpHeader'] = 'MyHeaderValue'
return response
I need to return css files and js files according to specific logic. Clearly, static serve does not perform what I need. I have a view, whose render method uses logic to find the proper file, but then I have to return it. Technically, I can just read the file and stuff it into a HttpResponse object with the proper mime type, but I was wondering if there was a better strategy. (like fpassthru() in php)
This is what I used:
test_file = open('/home/poop/serve/test.pdf', 'rb')
response = HttpResponse(content=test_file)
response['Content-Type'] = 'application/pdf'
response['Content-Disposition'] = 'attachment; filename="%s.pdf"' \
% 'whatever'
return response
What webserver software are you using?
At least for Apache and NginX, there is a module enabling you to use the X-SendFile HTTP header. The NginX website says Lighty can do this, too.
In your wrapper view:
...
abspath = '/most_secret_directory_on_the_whole_filesystem/protected_filename.css'
response = HttpResponse()
response['X-Sendfile'] = abspath
response['Content-Type'] = 'mimetype/submimetype'
# or let your webserver auto-inject such a header field
# after auto-recognition of mimetype based on filename extension
response['Content-Length'] = <filesize>
# can probably be left out if you don't want to hassle with getting it off disk.
# oh, and:
# if the file is stored via a models.FileField, you just need myfilefield.size
response['Content-Disposition'] = 'attachment; filename=%s.css' \
% 'whatever_public_filename_you_need_it_to_be'
return response
Then you can connect the view via http://mysite.com/url_path/to/serve_hidden_css_file/.
You can use it anytime you need to do something upon a file being requested that should not be directly accessible to users, like limiting who can access it, or counting requests to it for stats, or whatever.
For Apache: http://tn123.ath.cx/mod_xsendfile/
For NginX: http://wiki.nginx.org/NginxXSendfile
Why don't you use Django staticfiles inside your view
from django.contrib.staticfiles.views import serve
...
def view_function(request):
return serve(request, 'absolute_path_to_file_name')
Why not return an HttpResponseRedirect to the location of the correct static file?
Serving files directly from a view is very slow. If you are looking for normal file serving see this question: Having Django serve downloadable files
To very easily serve files through a view (for debug purposes, for example) keep reading.
# In your urls.py:
url(r'^test-files/(?P<name>.+)/$', views.test_files, name='test_files'),
# In your views.py:
from django.http.response import HttpResponse
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt # (Allows file download with POST requests, can be omitted)
def test_files(request, name):
if name == "myxml":
fsock = open("/djangopath/data/static/files/my.xml", "rb")
return HttpResponse(fsock)
This allows you to download the file from: http://127.0.0.1:8080/app/test-files/myxml/
Pass an iterator (such as the result of open()) to the HttpResponse constructor.
you can use below code in your view:
Note:in this function I return images but you can return every thing based your need and set your context_type
from django.http import HttpResponse,Http404
import os
def img_finder(request, img_name):
try:
with open(os.path.dirname(os.path.abspath(__file__)) + '/static/img/' + img_name, 'rb') as f:
return HttpResponse(f.read(), content_type="image/jpeg")
except IOError:
raise Http404
Here the most simple and efficient way to do this.
app/urls.py
from django.urls import re_path
from app import views
urlpatterns = [
re_path(r'^(?P<public_url>.*)$', views.public, name="public"),
]
Warning : put the URL pattern at the end
app/views.py
import os
from django.conf import settings
from django.views.static import serve
def public(request, public_url):
public_folder = os.path.join(str(settings.BASE_DIR), 'folder_path')
return serve(request, public_url, document_root=public_folder)
It should be wasteful to use django to serve static content (not to mention, several orders of magnitude slower).
I'd rather convert the view into a context processor and use the variables in templates to find what blocks to include.