I would like to show an upload progress bar for S3 uploads from a Django site. Currently without the progress bar being manipulated by JQuery, the uploads are working direct to S3. Once I try to implement the progress bar using JQuery, the upload form still functions as far accepting input and the file, but will not communicate the file to S3.
Here is the upload view:
from .forms import UploadForm
def upload(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('upload')
else:
form = UploadForm()
context = {
'form': form
}
return render(request, 'content/upload.html', context)
Here is the HTML:
{% extends "about/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
{% block navbar %}{% endblock %}
<div class="site-section mb-5">
<div class="container">
<div class="form-register">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<legend>Upload Content</legend>
<div class="form-group">
{{ form | crispy }}
</div>
<button class="btn btn-outline-info" type="submit">Upload</button>
</form>
<div class="progress">
<div id="progressBar" class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;">
0%
</div>
</div>
</div>
</div>
{% endblock %}
Here is the JQuery:
$(document).ready(function() {
$('form').on('submit', function(event) {
event.preventDefault();
var formData = new FormData($('form')[0]);
$.ajax({
xhr : function() {
var xhr = new window.XMLHttpRequest();
xhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
console.log('Bytes Loaded: ' + e.loaded);
console.log('Total Size: ' + e.total);
console.log('Percentage Uploaded: ' + (e.loaded / e.total))
var percent = Math.round((e.loaded / e.total) * 100);
$('#progressBar').attr('aria-valuenow', percent).css('width', percent + '%').text(percent + '%');
}
});
return xhr;
},
type : 'POST',
url : '/upload',
data : formData,
processData : false,
contentType : false,
success : function() {
alert('File uploaded!');
}
});
});
});
I assume there is something I need to change in the JQuery since I am overriding the default submit button action, but I am not sure what exactly to change in order to make sure the file goes to S3 via django-storages and boto3, the packages being used to communicate with S3.
Related
So here is my issue.
The user can create a Client using a form.
Once created, the created client appears in a DetaiLView
In this DetailView, I put a form to add specific events related to the client, thanks to an ajaxified form so that new events appear without refreshing the page. So far everything is okay.
Now I would like to allow the user to delete one event if he wants to.
So I have done the HTML/AJAX parts. However, since it is a DetaiLView, I am having troubles to delete a specific event.
Here is my Views.py :
class CollectionDetail(LoginRequiredMixin, FormMixin, DetailView):
model = Collection
form_class = ImportantFactsForm
template_name = 'taskflow/collection_detail.html'
success_url = None
def get_context_data(self, **kwargs):
context = super(CollectionDetail, self).get_context_data(**kwargs)
context['important_facts'] = ImportantFactsForm()
return context
def post(self, request, *args, **kwargs):
form = ImportantFactsForm(request.POST)
tgt = self.get_object()
if form.is_valid():
new_fact = form.save(commit=False)
new_fact.collection_important_facts = tgt
new_fact.save()
return JsonResponse({'new_fact': model_to_dict(new_fact)}, status=200)
else:
return redirect('taskflow:collection_all')
#here I need to implement the delete function.
Here is my collection_detail.html
<div class="card-body">
<div class="tab-content">
<div class="tab-pane active" id="Canal1">
<form class="justify-content-center mx-3" id="createFactForm" method="post" data-url="{% url 'taskflow:collection_detail' pk=object.pk %}">
{% csrf_token %}
<div class="form-group">
{{important_facts.doc_ref|as_crispy_field}}
</div>
<div class="form-group">
{{important_facts.note|as_crispy_field}}
</div>
<button type="submit" class="btn btn-outline-success" id="createButton">Enregistrer</button>
</form>
{% if object.has_important_facts %}
{% for fact in object.has_important_facts.all %}
<div class="card mb-1" id="factCard" data-id="{{fact.id}}" data-url="{% url 'taskflow:collection_detail' pk=object.pk %}">
<div class="card-body">
{{fact.note}}
<form method="post">
{% csrf_token %}
<button type="submit" formmethod="post" name="DeleteFactButton" id="DeleteButtonFact" class="btn btn-danger float-right" data-id="{{fact.id}}">Delete</button>
</form>
</div>
</div>
{% endfor %}
{% endif %}
</div>
Here is my facts.js file
var csrfToken = $("input[name=csrfmiddlewaretoken]").val();
$(document).ready(function(){
$("#createFactForm").on('submit', function(evt) {
evt.preventDefault();
var CreateFactForm = $("#createFactForm");
$.ajax({
url: CreateFactForm.data('url'),
data: CreateFactForm.serialize(),
method: 'POST',
success: function(response) {
console.log(response)
$("#Canal1").append(
'<div class="card mb-1" id="taskCard" data-id="' + response.new_fact.id + '">'+
' <div class="card-body">'+
response.new_fact.note+
' <button type="button" class="btn btn-danger float-right" name="DeleteFactButton" id="DeleteButtonFact" data-id="' + response.new_fact.id + '">'+
' Supprimer'+
' </button>'+
' </div>'+
'</div>');
}
})
$("#createFactForm")[0].reset();
});
$("#DeleteButtonFact").on('click', function(response) {
console.log(response)
var dataID = $(this).data('id');
$.ajax({
url: $("#DeleteButtonFact").data('url'),
data:{
csrfmiddlewaretoken: csrfToken,
id: dataID
},
method: 'POST',
dataType: 'json',
success: function() {
$('#factCard[data-id="' + dataID + '"]').remove();
}
})
});
});
Thanks for your help !
You can use deleteView, add another url path and link it to class which inherits deleteView
also for you can check the response in ajax if an error occurred and do action accordingly
In my view, I return html if a form is valid:
if form_valid():
return render(request, 'home.html', context=context)
else:
return HttpResponse(status=204)
I'm submitting multiple forms via ajax and want to render the response, if the status code is not 204:
$.ajax({
data: $(this).serialize(),
type: $(this).attr('method'),
url: $(this).attr('action'),
success: function (response, status, jqXHR) {
if (jqXHR.status !== 204) {
document.write(response); // this works, but I lose some functionality. Some buttons stop working.
// How can I render the response correctly?
}
}
});
EDIT: The buttons that don't work anymore. It's a form using bootstrap collapse with some workarounds
main.html
<form action="." method="post">
{% csrf_token %}
{% include 'collapse_form.html' with form=mysql_form %}
{% include 'collapse_form.html' with form=postgres_form %}
{% include 'collapse_form.html' with form=sqlite_form %}
<input type="hidden" name="databases-section"/>
<a id="download-btn" class="btn btn-success">Download</a>
<a id="hidden-download-link" class="js-scroll-trigger" href="#download"></a>
</form>
collapse_form.html
{% load crispy_forms_filters %}
<div class="collapseForm">
<div class="collapseFormButton">
<button id="collapseBtn" class="d-none btn btn-check p-0 m-0" type="button" data-toggle="collapse"
data-target="#{{ form.prefix }}-collapseTarget"
aria-expanded="false">
</button>
{{ form.active|as_crispy_field }}
</div>
<div class="collapse" id="{{ form.prefix }}-collapseTarget">
<div class="card card-body">
{{ form|as_crispy_errors }}
{% for field in form.visible_fields|slice:"1:" %}
{{ field|as_crispy_field }}
{% endfor %}
</div>
</div>
</div>
js
$('.collapseFormButton').find("input").change(function () {
toggleCollapseForm()
});
function toggleCollapseForm() {
let collapseForm = $(".collapseForm");
collapseForm.each(function () {
let collapseCheckbox = $(this).find("input[id*='active']");
let collapseTarget = $(this).find("div[id*='collapseTarget']");
if (collapseCheckbox.is(':checked')) {
collapseTarget.collapse('show');
} else {
collapseTarget.collapse('hide');
}
});
}
instead of document.write there is more functions like append, you could use that to append the rendered htmls cooming to ajax as a response to a specific element selected by id to avoid any problem like lossing the documents html tags(html, body, header) .
and there is big point!
javascript has different behavior regarding dynamically added elements!
to be more clear, I think js and/or jQuery has different behavior for keword this which doesn't work on dynamically added elements. so you sould use another query-selector like $(document).on('click', element, function (){....} and this would work as expected.
I want to create a comment system with using Ajax. My main purpose is getting new comments in my page without page refreshing. I add some js code to my HTML file but it didn't work. Where are my mistakes and what should I do?
views.py
...
def post_detail(request, pk, ):
post = get_object_or_404(Post, pk=pk)
form = CommentForm(request.POST or None)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('post_detail', pk=post.pk)
context = {
'post': post,
'form': form,
}
return render(request, 'blog/post_detail.html', context)
comments.html
{% load crispy_forms_tags %}
{% load static %}
<hr>
<form method="POST" style="width: 50%; margin-left: 20px" id="comment_form">
{% csrf_token %}
{{ form|crispy }}
<input type="submit" class="btn btn-info" value="Yorum Ekle" style="margin-left: 20px">
</form>
<script type="text/javascript" src="{% static 'js/jquery-1.11.1.min.js' %}"></script>
<script type="text/javascript">
$(document).on('submit', '#comment_form', function (e) {
e.preventDefault();
$.ajax({
type: 'POST',
url: 'http://127.0.0.1:8000/post/12/',
data: {
name: $('#name').val(),
content: $('#content').val(),
created_date: $('#created_date').val(),
post: $('#post').val(),
csrfToken: $('input[name=csrfmiddlewaretoken]').val()
},
success: function () {
alert("YEAH! It works!");
}
}
)
})
</script>
post_detail.html
...
{% include 'blog/comment.html' %}
<hr>
{% for comment in post.comments.all %}
<h4>{{ comment.name }} | <small>{{ comment.created_date|timesince }} önce</small></h4>
<p>{{ comment.content|linebreaks }}</p>
{% endfor %}
urls.py
...
url(r'^post/(?P<pk>\d+)/$', views.post_detail, name='post_detail'),
When I click comment button there is no action. But when I look inspect elements, when I click button it shows Status code:403
Note: I get "YEAH! It works!" alert
You need to return a JsonResponse or a partial template in post_detail method so that you can use these data in ajax success function. I have created a django app for commenting system using ajax response for preventing page refresh. If you are interested Here the package repo
The success function takes an argument, which is the data sent back by the server.
Update your code like this:
success: function (result) {
alert("YEAH! It works!");
}
I am doing a project management tool web application. I would like user can choose 'user' from dropdown selection, click 'Add member', then the member will show somewhere under the member?. Here is what I am doing.
Please do not worry about the styling. I have not done UI yet.
view.py
def member_select(request):
if request.method == 'GET':
selection = request.GET.get('id',None)
if selection:
selected_member = User.objects.filter(pk=selection)
return selected_member
project_index.html
<form action="" method="GET" id="selection-form">
{% csrf_token %}
<select id="member_list">
{% for user in user %}
<option>
{{ user.username }}
</option>
{% endfor %}
</select>
<input type="button" value="Add member" id="selection-button">
</form>
base.html
<script>
var url = $( '#selection-form' ).attr( 'action' );
$("selection-button").onclick(function (e) {
e.preventDefault();
$.ajax({
type:'GET',
url:url,
data:{
id:$('#member_list').val()
},
success:function (result) {
alert('okay');
},
error:function (result) {
alert('error');
}
});
});
</script>
Maybe use an empty <p class='member-name'><p> and
after choosing in the jquery success function
change to
success:function (result) {
$('.member-name').text(result);
},
try this (not fully tested)
from django.core import serializers
from django.http import HttpResponse
def member_select(request):
selection = request.GET.get('id',None)
if selection:
data = serializers.serialize('json',User.objects.filter(pk=selection))
else:
data = {}
return HttpResponse(data, content_type='application/json')
from the docs https://docs.djangoproject.com/en/2.0/topics/serialization/
In javascript ajax funtion the response should be a user object if exists or empty json string
<form action="" method="GET" id="selection-form">
{% csrf_token %}
<select id="member_list">
{% for user in user %}
<option value="{{user.pk}}">{{ user.username }}</option>
{% endfor %}
</select>
<input type="button" value="Add member" id="selection-button">
</form>
<di id='res'></div>
$("#selection-button").on('click', function(e) {
e.preventDefault();
var value =$('#member_list').val();
$.ajax({
type:'GET',
url:'.',
data:{
id:value
},
success:function (result) {
alert('okay');
$('#res').append(result);
console.info(result);
},
error:function (result) {
alert('error');
}
});
});
This is a HTML template that displays all of the proposals in a database (passed through views.py as a list in the dictionary parameter). I then use a jinja for-loop to go through all the proposals in the database and display their attributes.
How can I Post-request the {{ proposal.id }} back to my python code when the "Learn more" button is clicked? I need this to allow me to display the corresponding values in my other html template.
Sorry if this is a basic question, i'm a high school student and extremely new to django! Thanks alot in advance!
{% block body %}
{% for proposal in proposals %}
<div class="jumbotron">
<h2> Proposal : {{ proposal.title }} </h2>
<h4> Status : {{ proposal.status }} </h4>
<h4> Out of --- Votes: </h4>
<div class="progress">
<div class="progress-bar progress-bar-success" style="width: {{ proposal.votes_for }}%">
<span class="sr-only">35% Complete (success)</span>
{{ proposal.votes_for }}% For
</div>
<div class="progress-bar progress-bar-danger" style="width: {{ proposal.votes_against }}%">
<span class="sr-only">10% Complete (danger)</span>
{{ proposal.votes_against }}% Against
</div>
</div>
<p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
</div>
If you just want to go to the Proposal details you should definitely look to a class-based DetailView.
You can make it with AJAX request or you can make it with form. For the both of types you should have a View to catch it.
HTML Form:
In your template you should have:
<form id="formId" method="post" action="{% url 'catch-proposal' %}">
{% csrf_token %}
<input type="hidden" name="proposal_id" value="{{ proposal.id }}"/>
<p><button type="submit" class="btn btn-primary btn-lg">Learn more</a></p>
<!-- <input type="submit" class="btn btn-primary btn-lg" value="Learn more"/> -->
</form>
It will go to your View from urls.py:
url(r'^post/for/proposal/$', catch_proposal, name='catch-proposal'),
# if your view class-based
# url(r'^post/for/proposal/$', CatchProposal.as_view(), name='catch-proposal')
Then in your view you will catch POST data:
def catch_proposal(request):
if request.method == "POST":
print(request.POST) # have a look for your post params
return reverse_lazy('index') # your response, you can make it on your own
AJAX:
Check it! AJAX and Django
Page uses AJAX without any HTML form
A page makes a POST request via AJAX, and the page does not have an HTML form with a csrf_token that would cause the required CSRF cookie to be sent.
Solution: use ensure_csrf_cookie() on the view that sends the page.
In your scripts define:
function sendPost(proposalId) {
$.ajax({
url: '{% url 'catch-proposal' %}', // or just /ajax/catch/proposal/
method : "POST",
data: {
// your data to send key => val
'id': proposalId
},
dataType: 'json', // it can be xml, json, script, html
success: function (result) {
// Do something if your request was successful (code=200)
// All response data stored in result
console.log(result)
},
error : function(xhr,errmsg,err) {
// Error case
console.log(xhr.status + ": " + xhr.responseText);
}
});
}
For your Learn More button:
<p><button class="btn btn-primary btn-lg" role="button" onclick="sendPost({{ proposal.id }})">Learn more</button></p>
And you will catch it in your View:
#ensure_csrf_cookie # Since you sending POST request without form tag
def catch_proposal(request):
response_data = {} # your response
if request.method == 'POST':
# your post request
if 'id' not in request.POST: # check the param from POST
# Provide error message
response_data['error_message'] = "Can't find ID in POST params..."
else:
# Do whatever
proposal_id = int(request.POST.get('id'))
try:
proposal = Proposal.objects.get(id=transport_id)
response_data['success'] = True
except Proposal.DoesNotExist:
response_data['success'] = False
return JsonResponse(response_data)
else:
response_data = {
'error_message': 'Something is going very strange and wrong...'
}
return JsonResponse(response_data)
Adding created View to urls.py:
from .views import catch_proposal # or yourapp.views
....
url(r'^ajax/catch/proposal/$', catch_proposal, name='catch_proposal'),
....