Pass JavaScript variable to Flask url_for - javascript

I have an endpoint that takes a value in the url and produces some content that will be inserted into a div. I want to build the url with url_for using a JavaScript variable. However, $variable1 is passed as a string, rather than the value of variable1. How can I pass the value of a JavaScript variable to url_for?
function myFunction() {
var variable1 = "someString"
$('#demo').load(
"{{ url_for('addshare2', share = '$variable1') }}"
);
}

Sometimes I use the following workaround with a temporary placeholder string:
var variable1 = "someString";
$('#demo').load(
"{{ url_for('addshare2', share='ADDSHARE2') }}".replace("ADDSHARE2", variable1)
);
It doesn't feel quite right and I'm still looking for a better solution. But it does the job.

You can't evaluate JavaScript in Jinja. You're trying to generate a url on the server side while Jinja is rendering, but you're referencing a variable that is only available in the JavaScript running on the client browser.
Building the url on the client side is the most straightforward fix. (I don't know what your route looks like, so here's an example.)
$('#demo').load('/url/for/addshare2/' + variable1);
However, this isn't very useful because you can't use url_for, so you have to hard-code the urls. This is a good sign that what you want is an AJAX endpoint that you pass parameters to, rather than an endpoint that contains values.
#app.route('/addshare2', methods=['POST'])
def addshare2():
share = request.json['share']
...
return jsonify(result=...)
Now you can generate the url with url_for, and pass the parameters as form data.
$.post(
'{{ url_for('addshare2') }}',
{share: variable1},
function (data) {
// do something with data on successful response
}
);

It's possible to send a variable in Jinja by making a template filter to unquote the text returned by url_for()
add this to your app.py:
from urllib.parse import unquote as urllib_unquote
#app.template_filter('unquote')
def unquote(url):
safe = app.jinja_env.filters['safe']
return safe(urllib_unquote(url))
then on template do:
function myFunction() {
var variable1 = "someString"
$('#demo').load(
`{{ url_for('addshare2', share = '${variable1}')|unquote }}`
);
}
this will do the trick.
More on custom template filters: https://flask.palletsprojects.com/en/2.1.x/templating/#registering-filters

Related

How to pass javascript variable to flask url_for which inside innerhtml?

I try to pass JavaScript to url_for which is inside innerhtml using the below code:
var data = 'abc';
mydiv.innerHTML =
"<button onclick=window.location.href='{{ url_for('flask_function', filepath="+data+") }}'></button>
But the result when I print out the data in Python is like this:
+data+
So how can I pass my data to Flask using this code?
Would like to put this in comment.
You missed an " at the end.
Also otherwise this will not work because this is rendered in Jinja template python while generating code that has to be sent of the frontend. So it does not recognize js variables.
You will have to use a python variable with jinja template instead
{% set data = 'abc' %}
Or send the data in template and use
{{data}}
you can use like this worked for me
var actualUrl = "/flask_function/" + id + ""
post.innerHTML =''

How to inject JavaScript variable in a Django template filter

I have a Django template filter to retrieve dictionary items based on the key passed.
{% with data=dict_data|get_data:key %}
I have separately made a template_tag.py file which returns those items.
def get_domain_data(dictionary, key):
p = ast.literal_eval(dictionary)
return p[key]
# data being returned successfully
The issue is in passing the dynamic value of the key in the filter function.
<script>
var key_val = $('#input_id').val();
'{% with data=dict_data|get_domain_data:"'+key_val+'" %}'; //encountering error here
// rest of the code
'{% endwith %}';
</script>
If I hardcode a string value the entire operation works, but I am unable to use the JavaScript variable within the Django {% filter %} function.
As mentionned by Matt Ellen in a comment, the template code is executed on the server, then the generated result is sent to the browser which interprets the javascript parts - so this just can not work this way.
If your data dict is small enough and doesn't depend on javascipt user interactions (ie the javascript only reads it), then the solution is to serialize it to json (in the view itself or using a template filter - one might already exists FWIW), bind it to a javascript variable (in the template) and then let the javascript code use it as just any js object, ie (assuming a "jsonify" template filter):
<script>
var data_dict = {% data_dict|jsonify %};
function do_something() {
var key_val = $('#input_id').val();
var data = data_dict[key_val];
// rest of the code
}
// and you'll probably want to bind do_something to
// some js event handler
</script>
There is a similar issue at Get javascript variable's value in Django url template tag
Providing arg1 can be numeric and the reversed url doesn't contain other instances of the string /12345/ then you can use,
var url_mask = "{% url 'someview' arg1=12345 %}".replace(/12345/, tmp.toString());

set twig variable from json array

As twig renders prior to any javascript, I'm running into what feels like a minor problem.
I need to set a variable in twig that I receive from JSON array, but I'm running into some problems, and I feel like this should be simple.
The data is fed to twig through symfony through a json array, and renders different messages depending on one element in the array; this part works without trouble.
I am able to print the output to the twig file; that works fine. The problem is that I'm having a hard time setting this to a twig variable so that I can use it in a few places.
This works fine:
$('.id').html(items[0].id);
and prints out to the twig here correctly:
<div class="id"></div>
I tried to do do something like this:
{% set requestid = '<div class="id"></div>' %}
{{ requestid }}
But as expected this simply rendered the HTML without the value.
I've been attempting to do something like this:
In the twig I have this:
{% set requestid = "request_holder" %}
{{ requestid }}
And in the jquery I have something like this:
var reqid = items[0].id;
reqid.replace("request_holder",reqid);
I also attempted something like this
var request_id = items[0].id;
window.location = request_id.replace("request_holder",request_id)
I feel like I'm missing a small piece.
**Edit for clarity **
The JSON array is being parsed by jquery.
I have the value of items[0].id
Additional edit here - to make it clear that I was confused: cleaning up a little so as not to send future readers down the wrong path
I believe[d] that the variable needs to be assigned in the javascript because the twig, which is php, is generated prior to the javascript.
I have been attempting to generate the twig in the javascript to no avail.
Here's what I have been attempting:
var requestitem = items[0].id;
$('.id').html("{% set requestId = " + requestitem + " %} <br/> {{ requestId }}");
This defines requestId as a string and is only returning + requestitem + onto the page.
When I attempt this (without the quotations)
var requestitem = items[0].id;
$('.id').html("{% set requestId = requestitem %} <br/> {{ requestId }}");
The twig does not recognize requestitem at all
I have attempted quoting out the twig brackets (e.g. "{" + "%" etc) but this of course only prints them onto the page and does not interpret them.
Twig processes on the server side. It takes variables and renders them as HTML and text. What gets displayed in the browser is just HTML / text / and Javascript. So your set requestid = "request_holder" and {{ requestid}} are just turned to text before they get to the browser.
After that, you have HTML and text on the front end which Javascript can interact with. If you need this id to change on the front end, it needs to be done in Javascript.
What are you using the id to do?
Thanks to the hint from ASOlivieri, I was able to realize what I was doing wrong. I'm putting this here in case anyone comes across this. I was simply looking for a way to create a variable and make it reusable (I didn't go into details as that seemed extraneous).
The data was only available in the JSON array, so any attempt to write it to a twig file would fail, quite simply because it had already been converted to HTML, so I was forced to find another solution,
I was able to keep the variable in a javascript as I had it before
var request_item = items[0].id;
As my original goal was to get the value to update the application through php, I simply needed to use this variable in an AJAX call, and pass it through the path I had wanted to use in twig. Here's a brief summary:
$('#mark-received').click(function()
{
var requestURL = "{{ path('my_path') }}";
jQuery.ajax({
url: requestURL,
type: 'GET',
data: {'id' : request_item},
success: function success(data, text, xhr){
$('#mark-received').addClass('hidden');
$('#received-canceled').removeClass('hidden');
$('header > .alerts').append( $('<div>Success Message</div>').addClass('alert alert-success'));
},
error: function error( xhr, status, err){
$('header > .alerts').append( $('<div>There is a problem. <div class="close">x</div></div>', err).addClass('alert alert-danger'));
}
})
});

Passing objects through Django template to javascript

I am making a website using Django and I want to pass a python object from my view (where it is created) through the Django template and to a Dajax call. The problem is that by the time it gets to dajax it has been turned into type unicode.
In my Template
<script>
var emailer = "{{emailer|safe}}"; <---If I omit the quotes here then I get a javascript error.
sessionStorage.setItem('emailer',emailer);
$(document).ready(function(){
$('.send').on('click', function(e){
var emailer = sessionStorage.getItem('emailer');
Dajaxice.InterfaceApp.sendEmail(submitverify,{'emailer':emailer});
});
});
</script>
The dajax function
#dajaxice_register(method='GET')
def sendEmail(emailer):
logger.warning("type: %s, %s" % (type(emailer),emailer))
email_body = "message"
emailer.addToMessage(email_body)
emailer.send()
message = "Email Sent"
return json.dumps({'message':message})
Here the logger statement returns: type: <type 'unicode'>, <Utils.SIMPL_Emailer instance at 0x103142ab8>. Is there any way to fix this so that I get my emailer object instead of a unicode string?
First try to understand what is happening:
On your template you're trying to save a Python object to a Javascript var:
var emailer = "{{emailer|safe}}";`
But it's not possible. When your template is rendered by Django what you really get is a call to object __str__() method and your Javascript will store the <Utils.SIMPL_Emailer instance at 0x103142ab8> value on your emailer var. And remember: this code run in the client browser. That's why you get an error when you remove the quotes.
To solve it you need to first serialize your emailer object (Turn it into something that could be represented as a String, for example, and then turned back to Python Object). But as pointed by Peter DeGlopper it is a very insecure approach. Never, ever deserialize an whole object that was public accessible. Instead send only the email data to your template. You can create a dictionary with this data, turn it into JSON (it's a serialization too, but this time you are serializating only data) and then pass it to your template.
So do not put your emailer on the template context. Instead create a dictonary and pass it to the template.
Then in your Python sendEmail(emailer) method you'll need to instanciate a new Emailer object and feed it with the data, like:
#dajaxice_register(method='GET')
def sendEmail(email_json):
email = json.loads(email_json) # email_json is a json with your email data only
logger.warning("type: %s, %s" % (type(email_json),email_json))
emailer = Emailer("<with all your params...>")
emailer.addToMessage(email.get('body'))
emailer.send()
message = "Email Sent"
return json.dumps({'message':message})

Django: reverse parametrized url in JavaScript

let's say one of my urlpatterns looks like this.
url('^objects/update/(?P<pk>\d+)$', views.UpdateView.as_view(), name = 'update-object'),
I need to redirect user to the update page depending on the selected object (the list of objects is populated using Ajax). So I'd like to pass that named url pattern to the JavaScript, in order to build the actual url on the client side.
Example of what I want to achieve:
pass the name 'update-objects' to the function
get the actual url pattern, replace (?P<pk>..) with {pk}
pass the result to the javascript, resulting in : objects/update/{pk}
any tips?
thanks
to make it more clear: at the moment of rendering, I can't do url reverse because the PK is not known yet. I need to make kind of javascript-urlpattern which will later be converted to the real url (i.e. my JS code will replace {pk} part with the actual pk value)
The actual URL reversing must happen on the server side. There are several ways to do this, and the most elegant of these probably depends on how exactly your script and markup are set up for this. One thing I've done recently is to attach the URL to a logical element using HTML5 data attributes, which are easy to retrieve using jQuery. If you're not using jQuery, I'll leave it up to you to translate to pure JS. You haven't provided any code or specifics for your client-side, so I'm kind of shooting in the dark here, but maybe this will give you the idea:
Django HTML template:
<ul class="object-list">
{% for object in objectList %}
<li data-update-url="{% url update-objects object.pk %}">object.name</li>
{% endfor %}
</ul>
JS:
$('.object-list').on('click', 'li' function () {
var updateUrl = $(this).data('update-url')
...
});
It sounds like you need to make an additional ajax call once the object has actually been selected. Don't try and second guess your url.conf by trying to work out the url on the client side - you'd just be making trouble for yourself later. Wait till you can get a pk, then use django's reverse function to give you your url (doing anything else violates DRY).
How about creating a simple view that returns the url -
from django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseBadRequest
def get_url(request):
if request.is_ajax() and request.method == 'POST':
obj_id = request.POST['obj_id']
url = reverse('object-update', kwargs{'pk': obj_id})
return HttpResponse(obj_id)
return HttpResponseBadRequest()
Then write a javascript function that gets the url using an ajax call to your new view and then redirects. You'd call this function as soon as the object's been selected. I would suggest using JQuery to do this, pure javascript will require you to write more code, and probably write browser specific code (depending on your target). Also it supports dealing with django's csrf protection (you'll need to implement this for ajax calls if you haven't already).
var redirect = function(obj) {
$.ajax({
url: '/your-get-url-view/',
method: 'post',
data: {'obj_id': obj},
success: function(url){
window.location = url;
}
});
}
I'm afraid I don't know how you're getting from the selected object to the pk (For simplicity I've assumed it's available to the redirect function) - you may have to do some processing in the view to get there.
I haven't tested the above code, but it should give you an idea of what I'm suggesting.
Try this one:
Reverse method for generating Django urls
https://github.com/mlouro/django-js-utils
One more
https://github.com/Dimitri-Gnidash/django-js-utils
If you have a URL that only has one PK field in it, you could resolve it with any number (e.g. 0), then substitute the number as required.
In my scenario my URL had a pk then an upload_id, so I had to replace on the right most instance of a 0, with <upload_id>, which the JS would replace this string occurance as required:
detele_url_upload_id_0 = reverse(f'{APP_NAME}:api_upload_delete', args=[pk, 0])
prefix, suffix = detele_url_upload_id_0.rsplit('0', 1)
context['generic_delete_url'] = prefix + '<upload_id>' + suffix
Then in the JS:
const deleteUrl = genericDeleteUrl.replace('<upload_id>', uploadId)

Categories