I'm trying to upload the image file to the Media url specified in my setting.py and
store the path of an image in the database table.
However, I could not achieve this when using Ajax for uploading the image file..
template.html
<div class="profpic" style="background:url(../../../static/app/img/test.png);background-size:cover">
<input type="file" id="picpath" name="picpath" class="uploadpic" value=""/>
</div>
Ajax :
function saveprof() {
$.ajax({
type: "POST",
url: "saveprof",
enctype: 'multipart/form-data',
async: true,
data: {
'picpath_Aj': $('#picpath').val(),
'profemail_Aj': $('#profemail').val(),
'csrfmiddlewaretoken': $("input[name=csrfmiddlewaretoken]").val()
},
success: function (data, textStatus, jqXHR) {
$('#message').html(data);
}
});
}
Views.py
def saveprof(request):
if request.method == "POST":
picpathV = request.POST['picpath_Aj']
else:
profemailV = ''
response_data = 'Nothing to update!'
return HttpResponse(response_data, content_type="text/plain")
response_data = 'Empty'
try:
res=td_table.objects.filter(id=request.session.get('proid')).update(picpath=picpathV)
except:
response_data = 'Something went wrong!'
return HttpResponse(response_data, content_type="text/plain")
Above code is working fine, but I could save only the file path like ('C:\fakepath\file.jpg').. and file is not getting saved to the media
path provided in the Settings.py.
I could upload the file when I use request.FILES in the view, when used Django form.. but in my case, I need to get this done using Ajax function only.
What could be the wrong in the view code ?
Here is my models.py
class td_Student(models.Model):
firstname = models.CharField(max_length=300,blank=True)
picpath=models.FileField(upload_to=unique_filename)
def unique_filename(instance, filename):
ext = filename.split('.')[-1]
filename = "%s_%s.%s" %(uuid.uuid4(),time.strftime("%Y%m%d_%H%M%S"), ext)
return os.path.join('documents/documents/'+time.strftime("%Y%m%d"), filename)
As per above logic, file name should be like 'documents/documents/20150716/a1f81a80-ce6f-446b-9b49-716b5c67a46e_20150716_222614.jpg' - This value should be stored in my database table.
settings.py
MEDIA_ROOT = 'C:/DJ/'
MEDIA_URL = '/DJ/'
The problem is not with Django, but with your AJAX post, you are just passing the name, hence Django receives and saves the name.
Solution: Once the user selects a file, change event will be emitteed, on this change evet you will have to grab the file instance using event.target.files store it in a local variable and pass it to picpath_Aj'.
// Add events
$('input[type=file]').on('change', prepareUpload);
// Grab the files and set them to our variable
function prepareUpload(event)
{
files = event.target.files;
}
Detailed guide is here http://abandon.ie/notebook/simple-file-uploads-using-jquery-ajax
And alternative Django solution with JS and backend code is https://github.com/skoczen/django-ajax-uploader
Related
I am working on a project that generates dynamic urls, For ex. if I type 127.0.0.1:8000/newpage it generates a new model with slug newpage
Earlier the project was working fine but suddenly it started to show some bugs.
I am calling a URL using ajax like this (5th line):
$(document).on('click', '#save', function (e) {
e.preventDefault();
$.ajax({
type: 'POST',
url: '{% url "text:update" %}',
data: {
newText: $('#text-content').val(),
slug: "{{ obj.slug }}",
csrfmiddlewaretoken: "{{csrf_token}}",
action: 'post'
},
success: function (json) {
if (json['status'] === 'OK') {
document.getElementById('msg-box').innerText = 'TEXT SAVED';
window.removeEventListener("beforeunload", beforeUnloadListener, { capture: true });
}
},
error: function (xhr, errmsg, err) {
}
});
});
It should load the view which I defined in the update url patterns but because of some reason it is still loading my slug view and generating a new url with slug update, I mean it shouldn't do that if I am telling it to load a specific view in URL pattern then why it is still loading slug view below is my urls.py:
#Only patterns
path('', home, name='home'),
path('<slug:slug>/', textview, name='textview'),
path('update/', update, name='update'),
views.py
def textview(request, slug):
obj, created= Text.objects.get_or_create(slug=slug, defaults={'text':'', 'password':'123'})
return render(request, 'text/textpage.html', {'obj' : obj, 'created' : created})
def update(request):
if request.POST.get('action') == 'post':
slug = request.POST.get('slug')
text = request.POST.get('newText')
obj = Text.objects.get(slug=slug)
obj.text = text
obj.save()
response = JsonResponse({'status':'OK','text':text})
return response
Where are you using this AJAX code? If it is in a JavaScript file (.js), Jinja won't work there, so you have to write the absolute URL or you have to define a variable for the Jinja in the HTML and then use that variable in the JS file.
And try to add a slash after the URL if your APPEND_SLASH not True in settings.py.
I am trying to download a CSV file from the server to a client machine via a jQuery Ajax call, but for some reason the return response is not triggering the download. I know that there are several other questions which are quite similar, however, none of them are specifically using ajax with Django (that I've found), so I'm not sure if something needs to be done differently. The closest thing I could find was this one: download file using an ajax request which sets the window.location to a download.php file on success, but that doesn't really seem applicable. Everything I've been able to find, including the django documentation, uses some method that is very similar to what I have and yet every variation I've tried produces more or less the same result - nothing gets downloaded.
I've confirmed that each step of the process is executing correctly except for the last one in the views.py.
download_page.html:
{% extends 'base.html' %}
{% load staticfiles %}
<script>
$(function() {
$('#download').on('click', function () {
var pk = $(this).val();
$.ajax({
url: '/ajax/download/',
data: {'pk': pk}
});
});
});
</script>
{% block body %}
...
<button id="download" value="{{ view_id }}">Download</button>
...
{% endblock %}
urls.py:
urlpatterns = [
...
url(r'^ajax/download/', views.download, name='download'),
...
]
views.py:
import pathlib2
from django.http import HttpResponse
from src.apps.main.models import DataModel
def download(request):
pk = request.GET.get('pk', None)
fname, fpath = DataModel.export_to_csv(pk) # does the export and returns fullpath to file
if pathlib2.Path(fpath).exists():
file_download = open(fpath, 'r')
response = HttpResponse(file_download, content_type='application/csv')
response['Content-Disposition'] = 'attachment; filename="{}"'.format(fname)
return response
It gets all the way to the point at which it should return the response and then nothing happens. There are no warnings or errors, it just seems to silently finish without ever triggering the download on the client side. I'm guessing that there's something I need to do differently to return the file, but I've not been able to determine what that is.
maybe could do fname of you are a path
response['Content-Disposition'] = 'attachment; filename="{}"'.format(fname)
you can try:
response['Content-Type'] = 'application/force-download'
response['Content-Disposition'] = 'attachment; filename="donwload.scv"'
note: you can edit the filename accordingly.
My actual page that I want to load takes quite a bit of time because it querying an API for a lot of data (python is doing the backend querying). How can I render my loading page and then render my actual page when the data has been gathered.
What I am trying to do in my view.py
class Page(ListView):
def loading(request):
return render(request,'loading.html')
def viewProfile(request, player_name):
Page.loading(request)
context = {
'data1' : query_api(1),
'data2' : query_api(2),
'data3' : query_api(3),
}
return render(request, 'actualpage.html', context)
In your loading page, make an ajax request to the view which will query the api, and in the success callback set the html data in your template.
However, if the api takes a lot of time, I would suggest you to use celery for processing it asynchronously so that your user can navigate the website normally instead of waiting.
In your template -
$.ajax({
url: "<query_view_url>",
type: "GET",
dataType: 'json',
success: function (data){
$("#div_id").html(data.query_data);
},
error: function(e){
console.log(e);
}
});
In your views.py -
def view_of_query_url(request, <other parameters>):
<query_logic>
data = dict()
data['query_data'] = <your_queried_data> # In html format using render_to_string
return JsonResponse(data)
I am calling the image upload API (Django REST API) from my view in a separate Django project.
My View
if request.method == 'POST' and request.FILES['file']:
try:
resp = requests.post(
"http://19.******/BankImage_API",
files = {"file" :request.FILES['file']},
headers={"content-type": "multipart/form-data",
"Authorization": "Token 71117971*************"
}, verify=False)
API
class Bankimageapi(APIView):
def post(self, request):
if request.method == 'POST' and request.FILES['file']:
try:
........
When I tried to upload an image, I got an error in API where FILES is <MultiValueDict: {}>:
django.utils.datastructures.MultiValueDictKeyError: 'file'
Please guide me to solve this problem.
In your view, the received request.FILES['file'] is an UploadedFile (see docs here), which is a subclass of File.
The requests library wants you to post the binary contents of the file. You can access the contents of the file using (file = request.FILES['file']):
read(): files={"file": file.read()}
file attribute: files = {"file": file.file} although I'm not sure this will give you control over whether or not this is binary.
Furthermore, you should remove the "content-type" header, which is added automatically by the requests package. This seems to mess up the body of your request.
What is the id of the input in the html?
Update:
The request.FILES is a dict that references the objects by their id on the html.
My local development and live development server have the exact same code and database (all static/media files upload to s3 storage).
I have a Post model with an image field. My Post has an image upload field which uploads via AJAX to my s3 storage. In both my local and live server, the image successfully uploads and the url of that image in my s3 bucket is saved. So when I submit the Post in my local server, it doesn't upload again, I just point to the URL when I render the image.
However, my remote server doesn't do this. After I have uploaded the image via AJAX, when I submit the Post form, it then submits the image again (it shows the upload progression on my browser - 3%, 5%, 10% etc...).
Any idea why this happens? My code is here:
models
class Post(models.Model):
image = models.FileField(null=True, blank=True)
views
def post(request):
if request.user.is_authenticated():
form_post = PostForm(request.POST or None, request.FILES or None)
if form_post.is_valid():
instance = form_post.save(commit=False)
instance.user = request.user
...
if instance.image:
instance.imageURL = "https://s3.us-east-2.amazonaws.com/my-bucket/media/%s" % filename
instance.image = None #this prevents re-upload upon form submission
instance.save()
return HttpResponseRedirect('/')
else:
form_post = PostForm()
context = {
'form_post': form_post,
}
return render(request, 'post/post.html', context)
else:
return HttpResponseRedirect("/accounts/signup/")
ajax view
def upload_image(request):
random_filename = request.POST.get('random_filename')
if request.is_ajax():
if request.FILES:
img = request.FILES.get('image')
session = boto3.Session(
aws_access_key_id=AWS_ACCESS_KEY_ID,
aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
)
s3 = session.resource('s3')
s3.Bucket('my-bucket').put_object(Key='media/%s' % random_filename, Body=img)
return HttpResponse()
else:
return HttpResponse()
template
<input id="id_image" type="file" name="image" />
JS
$('input#id_image').on('change', function(e) {
var file = $('input[type=file]')[0].files[0];
var formData = new FormData();
$('.add_file_div h3').hide();
$('.add_file_label').css({
'border-radius': '5px',
'box-shadow': '5px',
});
var imagePath = URL.createObjectURL(e.target.files[0]);
var random_filename = random_string();
formData.append('image', file);
formData.append('csrfmiddlewaretoken', $("input[name='csrfmiddlewaretoken']").val());
formData.append('random_filename', random_filename);
$.ajax({
url: '/upload_image/',
data: formData,
type: 'POST',
contentType: false,
processData: false,
success: function(){
console.log('SUCCESSFULLY UPLOADED');
$('.add_file_label').css('opacity', '1');
$('.add_file_label').css('background', 'url(' + imagePath + ') scroll no-repeat center/cover');
$('.loading_spinner').css('visibility', 'hidden');
}
});
$('.add_file_label').css('background', 'url(' + imagePath + ') scroll no-repeat center/cover');
$('.add_file_label').css('opacity', '0.4');
$('.loading_spinner').css('visibility', 'visible');
console.log(imagePath);
$('.add_file_label').append('<input class="temp_s3_url" type="hidden" name="temp_s3_url">');
$('.temp_s3_url').val(random_filename);
$('input#id_imageURL').hide();
$('#delete_preview_image').show();
});
PS: My nginx has client_max_body_size 200m; so that's not the problem
I think the same issue may be happening locally also. But when you are local the speed is fast enough for you to not see a upload progress bar. The thing is that you should clear your file field when you have had a success in the ajax upload
how to reset <input type = "file">
so inside
success: function(){
console.log('SUCCESSFULLY UPLOADED');
$('.add_file_label').css('opacity', '1');
$('.add_file_label').css('background', 'url(' + imagePath + ') scroll no-repeat center/cover');
$('.loading_spinner').css('visibility', 'hidden');
}
You would reset the file element to make it a blank value, else it will get submitted with the form.
Another option could be to just give the file input field a id and not a name. If there is no name attached to a input element it doesn't get submitted with a form