I have a page that uses a form with POST method that passes a file to my flask application. I have read that my app.route cannot handle both a send_file and a redirect. So my workaround is to refresh the page after the post request is successful.
Here is my HTML with my script at the bottom:
{% extends "base.html" %}
{% block head %}
<!-- {# <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles/leaf.css') }}"> #} -->
<script src="https://code.jquery.com/jquery-3.5.0.js"></script>
<!-- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles/base.css') }}"> -->
{% endblock %}
{% block content %}
<div id="vo_budget_file_settings">
{# Upload Final CRO Budget File #}
<p>Please upload the final CRO budget File</p>
<form class="" action="/generatecleanbudgetfile" method=POST enctype=multipart/form-data>
<input type="file" name="data_file" accept=".xls, .xlsx, .xlsm"/>
<input type="submit" value="Begin Format" onclick="loading();"/>
</form>
</div>
<!-- funtion to show css spinner on button click -->
<script type="text/javascript">
function loading(){
$(".loader").show();
}
</script>
<script type="text/javascript">
// Reload page 60 seconds after form submission
$("vo_budget_file_settings").onsubmit = setTimeout(function () {
window.location = "/bugetformatter";
}, 60000);
console.log(window.location);
</script>
{% endblock %}
Here is my app:
#app.route('/bugetformatter')
def data_request_portal():
return render_template('CROBudgetFormatter.html', title='CRO Budget Formatting Tool')
#app.route('/generatecleanbudgetfile', methods=['GET', 'POST'])
def clean_budget():
file = request.files.get('data_file')
app.logger.info('Conversion has started')
try:
if request.method == 'POST':
file = request.files.get('data_file')
file.seek(0)
buffer = budget_cleaner(file)
buffer.seek(0)
app.logger.info('Conversion Complete')
return send_file(
buffer,
as_attachment=True,
attachment_filename=f'stripped_budget_{dt.today().strftime("%m.%d.%Y")}.xlsx',
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
)
except:
return render_template('error_type.html', title='Unable to process uploaded budget.')
Question:
Is there a script that behaves like a callback and will only reload the window once the request is completed and my file downloads to the browser?
Right now I'm using the 60 timer to reset after the form is submitted, but I would like it to be tied to the file download just in case the job takes longer than that.
your app needs to be
#app.route('/generatecleanbudgetfile', methods=['GET', 'POST'])
def clean_budget():
file = request.files.get('data_file')
app.logger.info('Conversion has started')
try:
if request.method == 'POST':
file = request.files.get('data_file')
file.seek(0)
buffer = budget_cleaner(file)
buffer.seek(0)
app.logger.info('Conversion Complete')
return send_file(
buffer,
as_attachment=True,
attachment_filename=f'stripped_budget_{dt.today().strftime("%m.%d.%Y")}.xlsx',
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
)
return 1
except:
return render_template('error_type.html', title='Unable to process uploaded budget.')
and your html file needs to be:
{% extends "base.html" %}
{% block head %}
<!-- {# <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles/leaf.css') }}"> #} -->
<script src="https://code.jquery.com/jquery-3.5.0.js"></script>
<!-- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles/base.css') }}"> -->
{% endblock %}
{% block content %}
<div id="vo_budget_file_settings">
{# Upload Final CRO Budget File #}
<p>Please upload the final CRO budget File</p>
<form class="formdata" action="/generatecleanbudgetfile" method=POST enctype=multipart/form-data>
<input type="file" name="data_file" accept=".xls, .xlsx, .xlsm"/>
<input type="button" value="Begin Format" onclick="submit();"/>
</form>
</div>
<!-- funtion to show css spinner on button click -->
<script type="text/javascript">
$("form.formdata").submit(function(e) {
e.preventDefault();
var formData = new FormData(this);
$.ajax({
type: "POST",
url: '/generatecleanbudgetfile',
data: formData,
cache: false,
contentType: false,
processData: false,
success: function(data) {
window.location.reload();
},
error: function() {
alert('Error occured');
window.location.reload();
}
});
});
</script>
{% endblock %}
Hope this helps you.
Related
I want to redirect to the python flask route home after showing the alert window.
<input type="submit" value="Register" onclick="call(); log();"></input>
Here are both functions.
<script>
function call()
{
window.alert("Congragulations! You Registerd Sucessfully ");
}
</script>
<script>
function log()
{
<a href="/home" > </a>
}
</script>
If I understood your project correctly, you want to forward the user after he has registered. At the same time, a message should appear that the process was successfully completed.
I think your javascript approach is not sufficient for this, since the data must first be sent from the client to the server so that the process is complete. Here the inputs should also be validated and will likely be processed or stored in some way.
I have written a small example for you, which accepts form data and checks it. If the verification is successful, the user is redirected and a message appears. To forward and display the message I use redirect and flash from the flask package.
Python Flask
import re
from flask import Flask
from flask import (
flash,
redirect,
render_template,
request,
session,
url_for
)
app = Flask(__name__)
app.secret_key = b'your secret here'
#app.route('/')
def home():
return render_template('index.html')
#app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
username = request.form.get('username')
if username and re.match(r'^[a-z0-9\_\-]+$', username, re.IGNORECASE):
session['username'] = username
flash('Congragulations! You Registerd Sucessfully.')
return redirect(url_for('home'))
return render_template('register.html')
HTML (index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<nav>
<ul style="list-style: none;">
<li>Register</li>
</ul>
</nav>
{% with messages = get_flashed_messages() -%}
{% if messages -%}
<ul class="flashes">
{% for message in messages -%}
<li>{{ message }}</li>
{% endfor -%}
</ul>
{% endif -%}
{% endwith -%}
<h1>Welcome {{ session.get('username', 'Guest') }}!</h1>
</body>
</html>
HTML (register.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Register</title>
</head>
<body>
<form method="post">
<div>
<label for="username">Username</label>
<input type="text" name="username" id="username" />
</div>
<input type="submit" value="Register" />
</form>
</body>
</html>
I have a peculiar problem with Django-autocomplete-light. When I go to my browser and try searching for something I get "no results found" - but when I go to the admin panel and add a model, the results pop up as they should. When I have done this one time, I can go back to the browser and then the results show up as they should. I'm suspecting some caching issue, but not sure how to debug this. Maybe someone can take a look if there is something wrong with my set-up.
models.py
class DogBreeds(models.Model):
name = models.CharField(max_length=150)
def __str__(self):
return self.name
class Advertisement(SoftDeleteModel, TimeStampedModel):
breed = models.ForeignKey(DogBreeds, on_delete=models.CASCADE, null=True, verbose_name='Hundras')
views.py
class BreedAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
if not self.request.user.is_authenticated:
return DogBreeds.objects.none()
qs = DogBreeds.objects.all()
if self.q:
qs = qs.filter(name__icontains=self.q)
return qs
Form template
{% extends '_base.html' %}
{% load static %}
{% block content %}
<form method="post" enctype="multipart/form-data" id="adForm" data-municipalities-url="{% url 'ajax_load_municipalities' %}" data-areas-url="{% url 'ajax_load_areas' %}" novalidate>
{% csrf_token %}
<table>
{{ form.as_p }}
</table>
<button type="submit">Publish</button>
</form>
</body>
{% endblock content %}
{% block footer %}
{% comment %} Imports for managing Django Autocomplete Light in form {% endcomment %}
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
<link rel="stylesheet" type="text/css" href="{% static 'autocomplete_light/select2.css' %}" />
<script type="text/javascript" src="{% static 'autocomplete_light/select2.js' %}"></script>
<link rel="stylesheet" type="text/css" href="{% static 'autocomplete_light/vendor/select2/dist/css/select2.css' %}" />
<script type="text/javascript" src="{% static 'autocomplete_light/vendor/select2/dist/js/select2.full.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
{{ form.media }}
{% endblock footer %}
Admin.py
class AdvertisementAdmin(admin.ModelAdmin):
form = NewAdTakeMyDogForm
readonly_fields = ('id',)
admin.site.register(Advertisement, AdvertisementAdmin)
The issue was resolved by removing the following lines, as per the recommendation by the good Iain Shelvington!
if not self.request.user.is_authenticated:
return DogBreeds.objects.none()
I have a javascript file project_dashboard.js with a few things defined inside of it and an html file where I try to call such function (amont other things).
For some reason, I keep getting the error that the function is not defined at HTMLButtonElement.onclick. What drives me crazy is that the first part of the .js is loaded and works (the css is changed if I click on table). So the script is there, but I can't use the function.
project_dashboard.js
$('table').on('click', 'tr.parent .fa-chevron-down', function(){
$(this).closest('tbody').toggleClass('open');
});
function EnablePositionEdit(position_id) {
const checked_events = []
$("#edit_button_____" + position_id).toggle()
$("#save_button_____" + position_id).toggle()
$("#delete_____" + position_id).toggle()
inputs = document.querySelectorAll(".availability_____" + position_id + " input")
inputs.forEach(input => {
input.removeAttribute("disabled")
})
}
html
{% extends 'action/base.html' %}
{% block extrastylesheets %}
{% endblock extrastylesheets %}
{% block content %}
<head>
{% load static %}
<link rel="stylesheet" href="{% static 'action/css/project_dashboard.css' %}">
<link href="https://cdn.jsdelivr.net/npm/summernote#0.8.18/dist/summernote.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
</head>
...
<div id="edit_button_____{{ position.id }}" style="display: block;">
<button class="btn" style="font-size:10px; color: rgb(59, 89, 152);" onclick = EnablePositionEdit('{{ position.id }}')> <i class="fa fa-pencil"></i> </button>
</div>
...
{% endblock content %}
{% block extrascripts %}
<script src="https://cdn.jsdelivr.net/npm/summernote#0.8.18/dist/summernote.min.js"></script>
<script type="text/javascript" src="{% static 'action/js/project_dashboard.js' %}"></script>
<script>
$(document).ready(function() {
$('.summernotable').summernote({
toolbar: [],
});
});
....
</script>
{% endblock extrascripts %}
You have to wrap the function name in quotes, as you do for all attributes, and place the script that contains the function before you call the function.
<script type="text/javascript" src="{% static 'action/js/project_dashboard.js' %}"></script>
<button class="btn" style="font-size:10px; color: rgb(59, 89, 152);" onclick = "EnablePositionEdit('{{ position.id }}')"> <i class="fa fa-pencil"></i> </button>
I have a Django project with 2 apps: registration and randomization
- django-project
- registration
- randomization
- static
- randomization
- js
- script.js
- templates
- randomization
- stock.html
- templates
- layouts
- _nav.html
- base.html
- home.html
I use JQuery/ajax that work but is repeated in all my templates so I want to refactor
script.js seems to be download (browser debug network / status = 200)
but when I run my ajax request, I got the following error:
Not Found: /{% url "randomization:stock" %}
I try to manage static files like this:
settings.py
STATIC_URL = '/static/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'static'),
os.path.join(BASE_DIR,'randomization/static'),
)
randomization/static/randomization/js/script.js
$(document).ready(function() {
//CORRECTION
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
//END CORRECTION
$("#site").on("change", function(event){
console.log($(this).val());
//CORRECTION
var csrftoken = getCookie('csrftoken');
$.ajax({
type: "POST",
//url: '{% url "randomization:stock" %}',
url: $("#site").data("randomization-url"), //CORRECTION
data: {
//csrfmiddlewaretoken: '{{ csrf_token }}',//CORRECTION
csrfmiddlewaretoken: csrftoken,
'site' : $(this).val(),
},
dataType: 'html',
success: function (data) {
if (data.includes("Insufficient")) {
$("#alerte").html(data);
$("#randomize").children().remove();
}
else {
$("#alerte").children().remove();
$("#randomize").html(data);
}
}
});
});
$("body")
.on("click", '#informations', function(event) {
$('#pharmacy').modal('show');
})
.on('click','#button_ok',function(event){
$(this).modal('close')
});
});
base.html
{% load static i18n %}
{% load static %}
{% load widget_tweaks %}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="{% static 'css/styles.css' %}">
{% block extrahead %}{% endblock %}
<title>{% block title %}{% endblock %}</title>
</head>
<body>
{% include 'layouts/_nav.html' %}
{% block content %}{% endblock %}
{% include 'randomization/_alerte.html' %}
{% include 'layouts/_footer.html' %}
<!--09/11/2019 : config qui fonctionne-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<!--09/11/2019 : config qui fonctionne-->
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="{% static 'randomization/js/script.js' %}"></script>
{% block extrabody %}{% endblock %}
</body>
</html>
page.html
{% extends 'layouts/base.html' %}
{% load i18n %}
{% load static %}
{% block title %}{% trans 'Homepage' %} | Intense TBM{% endblock %}
{% block content %}
<div class='container'>
<h1>page.html</h1>
</div>
{% endblock %}
You can only use the template tags within Django templates. The static files are simply files that are served as is by typically the web server (apache, nginx) or served by a CDN.
What you can do is something like this:
$(document).ready(function() {
$("#site").on("change", function(event){
console.log($(this).val());
$.ajax({
type: "POST",
url: $("#site").data("randomization-url"),
...
Then in the template that defines the html tag with id "site", you'd do the following:
<div id="site" data-randomization-url="{% url 'randomization:stock' %}"></div>
This way your element defines the URL that should be used and you don't have to worry about any weirdness being injected into your javascript. And you don't have to resort to hardcoding url paths in your javascript.
I'm trying to delete a list of images selected via checkbox via Ajax, which have been uploaded via Django Ajax Uploader. I've successfully obtained a list of the images but I'm not sure how to pass it Django function via Ajax. Can anyone please advise on:
How can I pass the list of selected images to a Django function to delete the images?
How should I handle the CSRF for the ajax portion?
html
<!DOCTYPE html>{% load static from staticfiles %} {% load i18n %}
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Demo</title>
<script src="{% static 'js/jquery.min.js' %}"></script>
<!-- Latest compiled and minified JavaScript -->
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" />
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css">
<style>
input.chk {
margin-left: 70px;
}
</style>
</head>
<body>
<div class="wrapper">
<div id="content" class="clearfix container">
{% load i18n crispy_forms_tags %}
<form method="post" action="." enctype="multipart/form-data">
{% csrf_token %} {% crispy form %} {% crispy formset formset.form.helper %}
<div class="image-upload-widget">
<div class="preview">
</div>
<div class="file-uploader">
<noscript>
<p>{% trans "Please enable JavaScript to use file uploader." %}</p>
</noscript>
</div>
<p class="help_text" class="help-block">
{% trans "Available file formats are JPG, GIF and PNG." %}
</p>
<div class="messages"></div>
</div>
<input type="submit" name="save" class="btn btn-primary pull-right" value="Submit">
<input type="button" id="delete" class="btn btn-primary pull-left" value="Delete Selected Files">
</form>
</div>
</div>
<script src="{% static 'js/fileuploader.js' %}"></script>
<link href="{% static 'css/fileuploader.css' %}" media="screen" rel="stylesheet" type="text/css" />
<script>
$(function() {
var uploader = new qq.FileUploader({
action: "{% url 'planner:ajax_uploader' %}",
element: $('.file-uploader')[0],
multiple: true,
onComplete: function(id, fileName, responseJSON) {
if (responseJSON.success) {
url = '<label for="' + fileName + '"><img src="' + {{MEDIA_URL}} + responseJSON.url + '" alt="" /></label>';
checkbox = '<p><input type="checkbox" class="chk" id="' + fileName + '" name="' + fileName + '" value="0" /></p>';
$('.preview').append(url);
$('.preview').append(checkbox);
} else {
alert("upload failed!");
}
},
onAllComplete: function(uploads) {
// uploads is an array of maps
// the maps look like this: {file: FileObject, response: JSONServerResponse}
alert("All complete!");
alert(checkbox);
},
params: {
'csrf_token': '{{ csrf_token }}',
'csrf_name': 'csrfmiddlewaretoken',
'csrf_xname': 'X-CSRFToken',
},
});
});
</script>
<script>
$("#delete").on('click', function() {
var allVals = [];
$(":checkbox").each(function() {
var ischecked = $(this).is(":checked");
if (ischecked) {
allVals.push(this.id);
}
});
});
</script>
</body>
</html>
Once you got the list of images to delete, you pass it on to an Ajax call that will be handled by a Django view, like so:
function submitData(data) {
var csrftoken = getCookie('csrftoken'); // from https://docs.djangoproject.com/en/1.7/ref/contrib/csrf/#ajax
$.ajax({
type: 'POST',
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
},
data: JSON.stringify(data),
url: {% url 'image_handler' %}
}).done(function(data) {
console.log(data.deleted + ' images were deleted');
}).error(function(data) {
console.log('errors happened: ' + data);
});
}
Then, in your views.py:
def image_handler(request):
if request.method != 'POST':
return HttpResponseNotAllowed(['POST'])
str_body = request.body.decode('utf-8')
changes = json.loads(str_body)
for change in changes:
pass # `change` contains the id of the image to delete
data = {'status': 200,
'deleted': len(changes)}
return JsonResponse(data)
It works pretty nicely :)