Rendering JSON objects using a Django template after an Ajax call - python

I've been trying to understand what's the optimal way to do Ajax in Django. By reading stuff here and there I gathered that the common process is:
formulate your Ajax call using some JavaScript library (e.g., jQuery), set up a URL pattern in Django that catches the call and passes it to a view function
in the Python view function retrieve the objects you are interested in and send them back to the client in JSON format or similar (by using the built in serializer module, or simplejson)
define a callback function in JavaScript that receives the JSON data and parses them, so to create whatever HTML is needed to be displayed. Finally, the JavaScript script puts the HTML wherever it should stay.
Now, what I still don't get is how are Django templates related to all of this? Apparently, we're not making use of the power of templates at all.
Ideally, I thought it'd be nice to pass back a JSON object and a template name, so that the data could be iterated over and an HTML block is created. But maybe I'm totally wrong here...
The only resource I found that goes in this direction is this snippet (769) but I haven't tried it yet.
Obviously, what's going to happen in this case is that all the resulting HTML is created on the server side, then passed to the client. The JavaScript-callback function only has to display it in the right place.
Does this cause performance problems? If not, even without using the snippet above, why not formatting the HTML directly in the backend using Python instead of the front-end?
Many thanks!
UPDATE: please use snippet 942 because it is an enhanced version of the one above! I found that the inheritance support works much better this way..

Hey thanks vikingosegundo!
I like using decorators too :-).
But in the meanwhile I've been following the approach suggested by the snippet I was mentioning above. Only thing, use instead the snippet n. 942 cause it's an improved version of the original one. Here's how it works:
Imagine you have a template (e.g., 'subtemplate.html') of whatever size that contains a useful block you can reuse:
........
<div id="results">
{% block results %}
{% for el in items %}
<li>{{el|capfirst}}</li>
{% endfor %}
{% endblock %}
</div><br />
........
By importing in your view file the snippet above you can easily reference to any block in your templates. A cool feature is that the inheritance relations among templates are taken into consideration, so if you reference to a block that includes another block and so on, everything should work just fine. So, the ajax-view looks like this:
from django.template import loader
# downloaded from djangosnippets.com[942]
from my_project.snippets.template import render_block_to_string
def ajax_view(request):
# some random context
context = Context({'items': range(100)})
# passing the template_name + block_name + context
return_str = render_block_to_string('standard/subtemplate.html', 'results', context)
return HttpResponse(return_str)

Here is how I use the same template for traditional rendering and Ajax-response rendering.
Template:
<div id="sortable">
{% include "admin/app/model/subtemplate.html" %}
</div>
Included template (aka: subtemplate):
<div id="results_listing">
{% if results %}
{% for c in results %}
.....
{% endfor %}
{% else %}
The Ajax-view:
#login_required
#render_to('admin/app/model/subtemplate.html')#annoying-decorator
def ajax_view(request):
.....
return {
"results":Model.objects.all(),
}
Of course you can use render_to_response. But I like those annoying decorators :D

There's no reason you can't return a rendered bit of HTML using Ajax, and insert that into the existing page at the point you want. Obviously you can use Django's templates to render this HTML, if you want.

When you are doing Ajax I don't think you have any use for templates.
Template is there so that you can generate dynamic HTML on the server side easily and hence it provides few programming hooks inside HTML.
In case of Ajax you are passing JSON data and you can format it as you want in Python.
and HTML/document elements will be generated on client side using the JSON by some JavaScript library e.g. jQuery on client side.
Maybe if you have a very specific case of replacing some inner HTML from server side HTML then maybe you can use templates but in that case why you would need JSON?
You can just query the HTML page via Ajax and change inner or outer or whatever HTML.

Templates are for the purpose of presentation. Responding with data in format X (JSON, JSONP, XML, YAML, *ml, etc.) is not presentation, so you don't need templates. Just serialize your data into format X and return it in an HttpResponse.

While templates are indeed just for presentation purposes, it shouldn't matter if you are doing it on the serverside or client side. It all comes down to separating the control logic that is performing an action, from the view logic that is just responsible for creating the markup. If your javascript control logic is having to handle how you are rendering or displaying the HTML, then you might be doing it wrong, but if you isolate that rendering logic to another object or function, and just passing it the data necessary for the render, then you should be fine; it mirrors how we separate our controllers, models and views on the server side.
Take a look at the github project: http://github.com/comolongo/Yz-Javascript-Django-Template-Compiler
It compiles django templates into optimized javascript functions that will generate your presentation html with data that you pass it. The compiled functions are in pure javascript, so there are no dependencies on other libraries. Since the templates are compiled instead of being parsed at runtime, the strings and variables are all already placed into javascript strings that just need to be concatenated, so you get a huge speed increase compared to techniques that require you to do dom manipulation or script parsing to get the final presentation. Right now only the basic tags and filters are there, but should be enough for most things, and more tags will be added as people start making requests for them or start contributing to the project.

You can use jquery.load() or similar, generating the HTML on the server and loading it into the DOM with JavaScript. I think someone has called this AJAH.

Unfortunately, Django templates are designed to be executed server side only. There is at least one project to render Django templates using Javascript, but I haven't used it and so I don't know how fast, well supported or up to date it is. Other than this, you have to either use the Django templates on the server or generate dynamic elements on the client without using templates.

Related

Variable URLs and passing non-input value from html to Python (Flask)

According to this question you can only send data from input forms from html to Python with POST. I'm trying to figure out how to pass a value (that's actually originally contained in a dictionary that I passed in from Python) from html to Python.
My two approaches I considered (and have not figured out how to do successfully) are:
Taking a look at the Flask quickstart, this should be quite simple. I'm just not sure what the syntax should look like on the html side to pass in this parkCode.
#app.route('/park/<parkCode>', methods =['GET', 'POST'])
def park(parkCode):
return render_template('park.html', parkCode = parkCode)
Alternatively, is there some way to simply send a string from html to Python without using an input form? I have yet to find a way to do this.
For reference, this is the line where I'm sending over the ```parks`` dictionary:
return render_template('search_results.html', parks=parks)
Then, in my search_results.html file:
{% for park in parks %}
<div method = "POST" action = "/park">{{park["fullName"]}}</div>
{% endfor %}
But I want the to send the park["fullName"] to my Python code.
.route decorator always handles only URL paths. Since form action is a static value in HTML, the only way to change it is to use JavaScript (for example, by changing the action attribute at submit time). If you're going to use JavaScript, you might as well then just use JavaScript to submit the request itself, which leads us to
Yes, you can use AJAX to send a request. Since ES6, the easiest way to do this is fetch. The choice of whether to use a form or whether to use AJAX depends on what you want to happen after the request: a submitted form results in a new page being rendered, while an AJAX request cannot change the current page, only trigger JavaScript code (although obviously you can change the page in JavaScript should you so wish).
Basically, you can't do what you want without JavaScript. The third option that does work without JavaScript is using the form as it was meant to be used. On flask side, it involves not naming the parameter inside the route, but using request.args (for GET forms) or request.form (for POST forms):
#app.route('/park', methods =['POST'])
def park():
parkCode = request.form.get('parkCode')
return render_template('park.html', parkCode = parkCode)
with the accompanying HTML:
<form action="/park" method="POST">
<input name="parkCode"/>
<input type="submit" value="Submit"/>
</form>

Using Django's urlize with CommonMark

I'd like to use Django's urlize function together with django-markwhat's CommonMark filter.
I'm imagining something like this:
{{ "http://example.com"|urlize|commonmark }}
Which outputs <p>http://example.com</p>. The issue here is that URLs already marked up in commonmark, like <http://example.com>, will get rendered with angled brackets around them.
If I apply urlize after commonmark, like this:
{{ "http://example.com"|commonmark|urlize }}
The urlize function doesn't detect the url in <p>http://example.com</p> (and it's mentioned in the urlize docs that it won't work as expected for html input).
I haven't come up with a solution yet, so let me know if you have any ideas.
For completeness, the easy answer is to switch to a Markdown parser which provides the behavior you want out of the box. Multiple extensions exist which give Python-Markdown the desired behavior.
However, if you want this behavior with CommonMark, which does not support this behavior itself, then you will need to create a custom urlize filter which operates on HTML. Fortunately, The Bleach library provides a linkify function which will handle that correctly.
Of course, you then need to create a template filter which wraps that:
from django import template
import bleach
from html5lib.tokenizer import HTMLTokenizer
register = template.Library()
#register.filter(is_safe=True)
def linkify(value):
return bleach.linkify(value, skip_pre=True, parse_email=True, tokenizer=HTMLTokenizer)
Note that I'm assuming you do want to parse email addresses as well as URLS, you do not want to have URLs in code blocks parsed, and you do not want CommonMark's output sanitized. Feel free to review Bleach's docs and adjust accordingly.
To use your custom filter, save the above code to a file named linkify.py within a templatetags directory in your app (see the Django docs for a full explanation). Then within a template, do the following:
{% load linkify %}
{{ "http://example.com"|commonmark|linkify }}

How do I find where a django template variable comes from in the python

Is there a good general way of finding the line of python code responsible for passing in variables to django templates? When newly picking up a large code base, and I see {{ x.y }} in the template, and nothing obviously related (by how things are named) to x in the {% load ... %}, what do I do? Where can I find this variable in the python so that I can change it or related code?
My current solutions tend to be tedious and overwhelming. It's a lot of searching, but I would like to be able to just know where to look.
Look at the URL of the page. Then go to urls.py and look at which view is linked to the URL. Then open views.py and search for the view which the URL linked to.
In that view, the variable 'x' should be there. If it's not, then check the template context processors and middlewares as karthikr suggested.

Smart way of preloading html email template using Django

I'm sending out emails and I'd like to use HTML which is quite lengthy and currently in a file. What do people recommend doing as a way to reload it. The issue with local storage is that it might be costly to read from time wise. On the other hand including in a string/dictionary is possible but this is really messy. What is the recommended approach for storing say 10 HTML templates. I'd like to avoid a DB if I can.
Template caching could be a possible improvement here. You can cache the whole template by using a cached.Loader or different template parts/fragments.
Also, consider using django-debug-toolbar with a template-timings panel to understand where the bottleneck is and where the time is spent:
Template-timings is a panel for Django Debug Toolbar that gives an
in-dept breakdown of the time it takes to render your Django templates
(including templates included via {% extends %} and {% include %}).

template fragment caching doesn't seem to work for some custom template tags

I've been implementing caching in my django application, and used per view caching via the cache API and template fragment caching.
On some of my pages I use a custom django template tag, this tag is provided via a third party developer, it takes some arguments in its template tags, and then make a request to a remote server, gets the response back over XML, and then renders the result in my page.
Great - I thought I could easily cache this using fragment caching, so I :
{% load cache %}
{% cache 500 request.user.username %}
{% load third party custom tags %}
{% expensive custom tag set that gets stuff from a third party server via xml %}
{{ some.stuff}}
{% endcache %}
Trouble is no matter what I do, the requests still get fired off to that remote server, it seems Django doesn't like to cache these custom template tags. I know memcached is working great, for other views and templates it all works just fine. Am I doing something that is incompatible with the fragment caching? Is there a way round it?
If the template fragment you're trying to cache can't be pickled, memcached won't be able to store it and will raise an exception. From what I can gather, exceptions generated when rendering Django templates are suppressed. Since your custom tag is doing HTTP requests, maybe socket objects (which can't be pickled) are getting stored to the template fragment somehow.
If this is the case, the only way around it I can think of would be to modify the custom tag to get rid of any leftover socket objects.
Have you tried to use a different name for the cache fragment? There could be a problem with using request.user.username for a couple of reasons:
If a user is not signed in,
request.user.username could be empty,
resulting in a non-named cache
fragment
If a user is signed in, this will
call the 3rd party template tag at
least once for each user every 3
mintues
Maybe it's worth trying to rename the cache fragment name to test:
{% cache 500 customxml %}
I'd also try loading of the 3rd party template tag outside the cache tag like so:
{% load cache third_party_custom_tags %}
{% cache 500 request.user.username %}
{% expensive custom tag set that gets stuff from a third party server via xml %}
{{ some.stuff}}
{% endcache %}
What I'm not sure of is if the cache framework caches the results of a template tag. If that doesn't work, I'd take a look at what the template tag is doing under the hood and re-implement the template tag using Django's low-level cache.
I think the problem is the custom tag, as you suggested.
I disagree that the request.user.username is a problem, as the documentation for the subject actually gives that as an example, and I have used it with internal caching (number of posts, for instance), in testing, and it worked fine.
The low level cache is potentially useful, but I would take a look at your custom tag to see what wouldn't be caching. Without the code it's hard to guess, but my guess would be something like the time, or some other variable, is being returned whic his causing it to force an update (if the XML pulls any data that changes Django may force an update, depending on other settings). I've had mixed results with Django's caching, so I would have a look at your XML feed(s) to see if it's causing anything to stop caching.
I don't think this has anything to do with the custom tag.
We endded up rewriting the Django caching tag because we needed more control than was possible with the one that was supplied. You might make a copy of it yourself and stick some debugging print statements into it. In particular, check the filename (assuming you are caching to files) and see what is being generated. It could be that it is changing when it shouldn't (for some unknown reason) and that would mean that it is always needing to re-render then enclosed block.
Look in django/templatetags/cache.py. It's only 63 lines of code.

Categories