I would like to pass some data from my Python view function to a JS script using HTML. That's my view function
def home(request):
if request.method == 'POST':
params = GameOfLifeForm(request.POST)
if params.is_valid():
starting_grid = get_starting_grid(params.cleaned_data)
to_html = {
'animation': True,
'gameoflifeform': params,
'start_grid': starting_grid,
}
else:
to_html = {
'animation': False,
'warning_msg': 'Something went wrong. Try once again.'
}
return render(request, 'get_animations/home.html', to_html)
else:
form = GameOfLifeForm()
return render(request, 'get_animations/home.html', {'gameoflifeform': form})
My form contains four parameters, one of them is called iterations and that is the one I would like to pass to JS script. Moreover I would like to pass start_grid.
I tried to do it in the following way in my HTML file
{{ start_grid | json_script:"start-grid" }}
<script
type="text/javascript"
src="{% static 'js/runGameOfLife.js' %}"
></script>
Then in my JS script I wrote
var startGrid = JSON.parse(document.getElementById("start-grid").textContent);
console.log(startGrid);
Worked perfectly, I got the grid printed out in my console. Similar I could grab iterations from HTML
{{ gameoflifeform.iterations.value | json_script:"iterations"}}
<script
type="text/javascript"
src="{% static 'js/runGameOfLife.js' %}"
></script>
When I tried to add both variables into my JS script it didn't work.
{{ gameoflifeform.iterations.value | json_script:"iterations"}}
{{ start_grid | json_script:"start-grid" }}
<script
type="text/javascript"
src="{% static 'js/runGameOfLife.js' %}"
></script>
How can I pass several variables into my JS script? What would be the best way of doing it?
Best to use some combination of ajax and view functions, if you learn this pattern you can accomplish a lot:
views.py
def my_view_function(request):
''' this method accepts data from the front end,
processes it, and returns a response '''
# unpack post request:
first_value = request.POST.get('first_key')
# do some logic:
...
# pack response
response = {
"second_key" : "second_value"
}
# return a json response:
return JsonResponse(response)
scripts.js
function post_request() {
/* set an event to trigger this method
this method will then send data to the backend
and process the response */
// send an ajax post request:
$.ajax({
type : 'POST',
url : 'the_url',
data : {
first_key : 'first_value'
},
success : function(response) {
// unpack response
second_value = response.second_key
// do some logic
...
}
});
}
Once you understand this, you will be able to seamlessly pass data back and forth between the frontend and backend. Let me know if you need more details.
Related
this is my code:
$("select").change(function(){
$.post("/sort", {sort:$(this).val()}, function(table_data)
{
for (let i in table_data)
{
var tr = $("<tr/>");
var filename = table_data[i].filename;
var size = table_data[i].size;
var uploaded = table_data[i].upload_time;
tr.append("<td>"+filename+"</td>");
tr.append("<td>"+size+"</td>");
tr.append("<td>"+uploaded+"</td>");
**tr.append("<td>"+"<a href='{{url_for('.download', filename=***filename***)}}'>"+'Download'+"</a>"+"</td>")**;
tr.appendTo(table);
}
Interestingly the jinja statement inside the js statement works, the browser directs to that path, but the filename remains None, because the server, which is using python flask, cannot resolve the value sent from here which is a js variable. Table_data is a json that was returned from the server using jsonify in response to an ajax call. My question is if there's any way to use that 'filename' js variable inside that jinja statement, or perhaps convert it to a jinja variable. Thanks.
As I wrote in my comment, it is only possible to pass variables from Jinja to JavaScript when the page is served. This time has already passed during the AJAX request.
I think the best solution is to construct the url with url_for on the server and transfer it via JSON with the record. So your requirements should be met.
I wrote you a small example, which is based on your question.
All folders in the application's static folder are listed and made available to the user for selection. If he selects a directory, file information and the download URL are queried via Ajax. The information is then displayed in a table.
Flask (app.py)
import os
from datetime import datetime
from flask import Flask
from flask import (
jsonify,
render_template,
request,
send_from_directory,
url_for
)
app = Flask(__name__)
#app.route('/')
def index():
# List all folders in the static folder.
folders = [file \
for file in os.listdir(app.static_folder) \
if os.path.isdir(os.path.join(app.static_folder, file))
]
return render_template('index.html', **locals())
#app.route('/stats', methods=['POST'])
def stats():
# Return stats of all files in the selected folder.
if 'target' in request.form:
data = []
target = request.form['target']
try:
for filename in os.listdir(os.path.join(app.static_folder, target)):
filepath = os.path.join(app.static_folder, target, filename)
data.append(
{
'filename': filename,
'filesize': os.path.getsize(filepath),
'filetime': datetime.fromtimestamp(os.path.getmtime(filepath)).isoformat(),
'filedest': url_for('.download', filename=os.path.join(target, filename))
}
)
return jsonify(data)
except OSError: pass
return '', 400
#app.route('/download/<path:filename>')
def download(filename):
return send_from_directory(app.static_folder, filename)
HTML (templates/index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Index</title>
</head>
<body>
<select name="target">
<option disabled selected value>-- select an option --</option>
{% for folder in folders -%}
<option value="{{folder}}">{{folder}}</option>
{% endfor -%}
</select>
<table id="my-table" style="width: 100%"></table>
<script
src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
crossorigin="anonymous"></script>
<script type="text/javascript">
((uri) => {
$(document).ready(function() {
$('select[name="target"]').change(function(evt) {
$('table#my-table').empty();
$.post(uri, { target: $(this).val() })
.done(function(data) {
data.forEach(info => {
const { filename, filesize, filetime, filedest } = info;
const row = $('<tr/>');
row.html(`
<td>${filename}</td>
<td>${filesize}</td>
<td>${filetime}</td>
<td>Download</td>
`)
row.appendTo($('table#my-table'));
});
});
})
});
})({{ url_for('.stats') | tojson }});
</script>
</body>
</html>
From my controller I send several parameters in my html.twig, then I want to make a AJAX request like this one :
{% if text is defined %}
var text = {{ text }};
var sender = {{ sender }};
var date_send = {{ date_send|date('d/m/Y') }};
var mess_type = {{ mess_type }};
var to = {{ to }};
$.post('send.php', {text:text, sender:sender, date_send:date_send, mess_type:mess_type, to:to}, function(data)
{
$('#Map').text(data);
});
{% endif %}
SO when I do that, I have this error :
POST http://localhost:8000/send.php 500 (Internal Server Error)
My send.php is where my html.twig is. I just have an echo "hi" in my send.php for testing, but it does not display anything.
I also tried to put a send function in my controller :
/**
* #Route("/send.php", name="send")
*/
public function send(Request $request) {
dump("hi");
echo "send";
return null;
}
And I replace 'send.php' by {{path('send.php')}} inside my html.twig (inside $.post()).
I know I can't return null, but It does not display my dump. And I have this error :
An exception has been thrown during the rendering of a template ("Unable to generate a URL for the named route "/send" as such route does not exist.")
And it does not get me to /send.php. I don't know what can I miss, I didn't find any solution on google. Thank for your help.
A controller's method should return a Response object so try this:
/**
* #Route("/send.php", name="send")
*/
public function send(Request $request)
{
return new Response('send');
}
The twig path function need the route name instead of the path, so try the following:
{{path('send')}}
Hope this help
NB: I order to identify the real error message, try put the url in the browser and see what happen
I'm trying to use flask with url_for. The problem is that when I try to launch an alert with the value of the javascript variable everything seems ok, but when I try to launch a alert with the url_for the content of the variable is not printed. What I'm doing wrong? or What is missing in my code?
How can I pass a JavaScript variable into the url_for function?
html code:
<a class="dissable_user_btn" data-user_id="{{user.id}}" href="#" title="Change Status"><i class="fa fa-plug"></i>
</a>
JS Code:
<script type="text/javascript">
$(document).ready(function(){
$('.dissable_user_btn').click(function( event ) {
var user_id = $(this).data("user_id")
alert(user_id) //everything ok
alert ('{{url_for('.dissable', _id=user_id)}}'); //dont print the valur of user_id
</script>
Short answer: you can't. Flask & Jinja2 render the template on the server side (e.g. Flask is translating all of the {{ }} stuff before it sends the HTML to the web browser).
For a URL like this where you're including a variable as part of the path you'd need to build this manually in javascript. If this is an XHR endpoint I'd recommend using GET/POST to transfer the values to the server as a better best practice than constructing the URL this way. This way you can use Jinja:
$(document).ready(function(){
var baseUrl = "{{ url_for('disable') }}";
$('.dissable_user_btn').click(function(event) {
var user_id = $(this).data("user_id");
// first part = url to send data
// second part = info to send as query string (url?user=user_id)
// third parameter = function to handle response from server
$.getJSON(baseUrl, {user: user_id}, function(response) {
console.log(response);
});
});
});
I found another solution for this. My problem started when I needed to pass a variable with space.
First I created a function to remove trailing and leading spaces
function strip(str) {
return str.replace(/^\s+|\s+$/g, '');}
After that, I used the function and encoded the URL
<script type="text/javascript">
$(document).ready(function(){
$('.dissable_user_btn').click(function( event ) {
var user_id = $(this).data("user_id")
alert(user_id)
user_id = strip(user_id).replace(" ","%20");
alert ('{{url_for('.dissable', _id='user_id')}}.replace('user_id',user_id);
</script>
It worked pretty nice for me!
This is how I applied to my problem
<script>
function strip(str) {
return str.replace(/^\s+|\s+$/g, '');}
$(document).ready(function() {
$('#exportcountry').click(function() {
var elemento = document.getElementById("countryexportbtn");
var country = strip(elemento.textContent).replace(" ","%20");
$('#exportevent').load("{{ url_for('get_events',country = 'pais') }}".replace('pais',country));
});
});
</script>
I am getting an error saying unexpected token while trying for passing id from django template to reatjs for uploading multiple images to its associated foreign key object. The error is shown as unexpected token }. In depth it is shown as
in console
var uploadUrl = {
url:
};
What i am trying to do is , I have created a listing page with multiple form and it is entirely developed using reactjs. I want user to fill the data about room and upload multiple images related to their room. There are two models one with room info and another gallery(multiple image is associated with one rent). I wanted the uploaded images be associated with its rent so i coded it as below
urls.py
urlpatterns = [
url(r'^add/$', AddView.as_view(), name="add"),
url(r'^add/space/$', AddSpaceView.as_view(), name="addSpace"),
url(r'^upload/image/(?P<pk>\d+)/$', ImageUpload, name="ImageUpload"),
]
views.py
def ImageUpload(request,pk=None): // for saving images only to its asscoiated rent
if request.POST or request.FILES:
rental = Rental.objects.get(id=pk)
for file in request.FILES.getlist('image'):
image = GalleryImage.objects.create(image=file,rental=rental)
image.save()
return render(request,'rentals/add.html')
class AddView(TemplateView): // for listing page
template_name = 'rentals/add.html'
class AddSpaceView(View): // for saving data to database except image
def post(self,request,*args,**kwargs):
if request.POST:
rental = Rental()
rental.ownerName = request.POST.get('ownerName')
rental.email = request.POST.get('email')
rental.phoneNumber = request.POST.get('phoneNumber')
rental.room = request.POST.get('room')
rental.price = request.POST.get('price')
rental.city = request.POST.get('city')
rental.place = request.POST.get('place')
rental.water = request.POST.get('water')
rental.amenities = request.POST.get('amenities')
rental.save()
return HttpResponseRedirect('/')
listing.js(ajax code for uploading multiple image)
var image = [];
image = new FormData(files);
$.each(files,function(i,file){
image.append('image',file);
});
$.ajax({
url:"/upload/image/", // want to used id over here that is passed from add.html script tag so that image will be uploaded to its associated foriegn key object
data:image,
contentType:false,
processData:false,
type:'POST',
mimeType: "multipart/form-data",
success: function(data) {
console.log('success');
}
});
}
add.html page
<div id="listing">
</div>
{% include 'includes/script.html'%}
<script type="text/javascript">
var uploadUrl = {
url: {% for rental in object_list %} { "id": {{ rental.id }} } {% endfor %} // here is an error
};
console.log('url is', url);
$(function() {
app.showListingSpaceForm("listing");
});
</script>
The code might explained what i was trying to achieve. If models.py is also required for more scrutiny then i will update it.
You're missing a fundamental piece: TemplateView has no concept of object_list, you have to populate it yourself. If your view is simple enough use ListView and set your model property. If not, you have to manually populate the object list, something like this:
def get_context_data(self, **kwargs):
context['object_list'] = MyModel.objects.all()
That was just an example to set you on the right path.
I'm a new Django user, stuck on a problem. I'd like to obtain an auto-refresh of my jQuery Datatables datas, showing my new DB content without reload entire html page (using ajax request). I browsed a lot of posts, but did not find my graal...
Here is my Django template.html :
{% block js %}
<script type="text/javascript" language="javascript" class="init">
$(document).ready(function(){
var oTable = $('#db_name').DataTable();
} );
setInterval(function () {
oTable.ajax.reload();
}, 2000 );
} );
</script>
{% endblock %}
My views.py :
def db_update(request):
watch_files()
all_ = db.objects.all()
return render_to_response('supervision.html', {'all_fields': all_})
The problem is an error message "Datatables warning: table id=db_name - Invalid JSON response" displayed each 2sec. I think this is normal because no JSON. Despite this error, the reload is effective : if I do a manual refresh (F5), all new datas added in my DB (the function watch_files create entries in it) appear well in my page.
The ideal, for me, would be to obtain a transparent datas auto-refresh, keeping current sort/page options of the Datatables array.
I tried also this, trying to pass JSON, but without success :
$(document).ready(function() {
var oTable = $('#db_name').DataTable( {
ajax: {{obj_as_json}}
} );
setInterval(function () {
/* oTable.ajax.reload(); */
oTable.fnReloadAjax();
/* oTable.fnDraw(); */
}, 2000 );
} );
def db_update(request):
watch_files()
all_ = db.objects.all()
jsondata = serializers.serialize('json', all_)
return render_to_response('supervision.html', {
'obj_as_json':json.dumps(jsondata),
'all_fields': all_})
If anybody could help me, it would be great.
Thks, Christophe
render_to_response('supervision.html') always return an html content that produced by supervision.html template.
You need to return JsonResponse object, that would contain json data only.
from django.http import JsonResponse
def db_update(request):
watch_files()
all_ = db.objects.all()
if request.is_ajax():
jsondata = serializers.serialize('json', all_)
return JsonResponse(jsondata)
else:
return render_to_response('supervision.html', {
'all_fields': all_})
In addition, you need to transform your all_ variable to looks like datatables expects
{
"data": [
[ "column1_val",
"column2_val",
...
],
]
}
I suggest you take a look to https://github.com/pivotal-energy-solutions/django-datatable-view implementation