Django template indentation guideline - django

There is PEP 8 for Python, but I haven't seen a preferred indentation guideline for django templates.
What I mean is, I normally indent blocks like this:
<span>outside</span>
{% if condition %}
<span>within condition</span>
{% endif %}
<span>outside</span>
While this looks good on the editor, but it will look crap on view source like this:
<span>outside</span>
<span>within condition</span>
<span>outside</span>
It would even look worse within the HTML indentation, see below:
<div>
<span>outside</span>
{% if condition %}
<span>within condition</span>
{% endif %}
</div>
will become:
<div>
<span>outside</span>
<span>within condition</span>
</div>
While I agree having better layout in editor is way way more important, but I also get paranoid about the generated messy HTML source code.

I am currently following my own convention in django template guideline for consistency matter. The rule is simple:
Django tag does NOT increase the indentation level
HTML tag does increase the indentation level
It does not matter how many nested Django Tag you have, you should still on the same level of indentation. I consider this as the trade off for putting logic in templates which should be minimized if possible to avoid confusion.
<html>
<body>
<ul>
{% if condition %}
{% for item in menu_item %}
<li>{{ item }}</li>
{% endfor %}
{% endif %}
</ul>
<main>
{% block content %}
<p>Hello World</p>
{% endblock content %}
</main>
</body>
</html>
Side note
I am using 2 spaces for HTML indentation, because HTML tends to have a very deep nested.
For Vim user, please note that the syntax is not html but htmldjango
Thus my ~/.vimrc looks something like:
autocmd Filetype htmldjango setlocal ts=2 sts=2 sw=2 expandtab

Depending your editor, there are ways to set a specific indent width for HTML files.
As for the Django tags, it is actually a good thing not to not add a indent level. See that example:
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
Would be rendered like so:
<ul>
<li>Bacon</li>
<li>Ninja</li>
<li>Tumbleweed</li>
</ul>
And we do not want the two levels of indent. Instead:
{% block content %}
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endblock content %}

My apologies for reviving an old question, but I think the way Emacs' web-mode.el indents Django templates deserves a mention here:
{% block content %}
<div
id="1"
class="fancy"
>
{% if link %}
<a href="{{ link }}">
Click here
</a>
{% endif %}
</div>
{% endblock %}
As you can see, it indents both Django tags and HTML tags, as well as multiline HTML tags. It supports <style> and <script> tags too. In fact, I like this behavior so much that I created DjHTML, a standalone indenter and a pre-commit hook that uses the same indentation rules as web-mode.

I think this is more of a personal preference and what is easy to read and easy to understand in the future (my time limit is "after six months") would be a better alternative than some cookbook recipes. What I use is a two type approach, not just for Django templates but for any language that accepts free formatting of the code. As such I format not critical portions as just left justified block of lines which is an admittedly eyesore but I tend to just ignore them so I can focus on important parts which is extensively indented, preferably in general single action per line and indented at each semantic group change thus all parent/child grouping, nested elements etc. are visible with just one look only.
{% if error_message %}
<p>
<strong>{{error_message}}</strong>
</p>
{% else %}
<table border>
<tr>
<td>
{{degerlendirme.adi}}
</td>
</tr>
<tr>
<td>
<table border>
<tr>
<td>
Tip
</td>
<td>
{{ tip }}
</td>
</tr>
<tr>
<td>
Açıklama
</td>
<td>
{{ degerlendirme.aciklama }}
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<form action="{% url 'perf:degerlendirme' degerlendirme.id %}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="OK" />
</form>
</td>
</tr>
</table>
{% endif %}
I usually use Eclipse and there are several good HTML editing tools that you can install into that environment. I try to use them, but prefer to depend solely on indentation for ease of reading/coding/analysis cycle. That is mainly because I regularly need to use other editors, mostly vi. In this case if I rely mainly on Eclipse or whatever IDE I was using for understanding the code vi, nano or other text editors would make my life miserable.
As one of my professors said in a classroom, lots of years ago, spaces are free and tabs are even cheaper.
One last note; Once I needed to remove all white space from a Django template that was creating enormous nested tables in order to improve run time performance. Such might be needed for certain cases. In similar situations it is better to keep one working copy and generate runtime copy from that by a script or tool. I know that, there are some HTML de-clutter tools. In my case, tools I tried corrupted the template and I needed to perform that operation by hand.

Related

How to render an arbitrary set of key->value pairs from a JSONField in a Jinja2 template?

I'm trying to add debug information to a frontend; for reasons that don't need to be gone into at the moment I'm storing the pertinent information in a JSONField.
Storing and retrieving the information works correctly, but when I try to render it via J2 to the page it's supposed to be at I'm running into some issues.
Here's the segment in question:
{% for log in connection.debug_logs.all %}
<tr>
<td></td>
<td>{{ log.log_message }}</td>
<td>
{% for key in log.log_data %}
{{ key }}: {{ log.log_data.key }} <br/>
{% endfor %}
</td>
</tr>
{% endfor %}
What I'm hoping for is for that to produce a series of lines of key: value. What I'm getting instead is:
<td>Login requested by vmx1.internal (11.22.33.44)</td>
<td>
Framed-Route: <br/>
Service-Type: <br/>
Framed-IP-Address: <br/>
Framed-IPv6-Route: <br/>
control:Auth-Type: <br/>
Framed-IPv6-Prefix: <br/>
Delegated-IPv6-Prefix: <br/>
ERX-Egress-Policy-Name: <br/>
ERX-Ingress-Policy-Name: <br/>
ERX-Virtual-Router-Name: <br/>
control:Cleartext-Password: <br/>
</td>
Using {{ log.log_data | pprint }} does yield the keys and values, but renders them as a plaintext JSON string which gets flattened by the html renderer and isn't terribly useful for debugging purposes.
Trying 'log.log_data[key]' instead yields a 'Could not parse the remainder' error.
I've tried the suggestions in this question as well as these and a few others that came up during google searches, but none of them seem to address this issue -- all of them are either working with known keys, working with an actual dict instead of a JSONField, or sometimes both.
I'm probably missing something very simple and straightforward, but I've run out of ways to phrase my question in a search engine. Any tips?
EDIT No, it is not actually a dictionary. I've also tried the solutions in this answer and that leads back to the Could not parse the remainder error.
So the data I'm looking for is absolutely there, I'm just having issues trying to get it to render properly.
Third and final edit: The problem was apparently that Django templates are not quite Jinja2 templates, and rather than {% for key, value in log.log_data.items() %} I needed to use {% for key, value in log.log_data.items %}
Do the conversion from json inside your view to have a greater range of utilities than inside the jinja2 template world.
import json
DEBUG_LOGS_JSON = "[
{"log_data": {"Framed-Route": "route1", "Service-Type": "type1"}, "log_message": "my"},
{"log_data": {"Framed-Route": "route2", "Service-Type": "type2"}, "log_message": "name"},
{"log_data": {"Framed-Route": "route3", "Service-Type": "type3"}, "log_message": "Tarquinius"},
]"
def my_view(request):
my_dict = json.loads(DEBUG_LOGS_JSON) # instead you could also restructure the data passed to the template here.
return render("my_template.html", context=my_dict)
{% for dictionary in my_dict %}
<tr>
<td>{{ dictionary.log_message }}</td>
<td>
{% for key, value in dictionary.log_data.items() %}
{{ key }}: {{ value }} <br/>
{% endfor %}
</td>
</tr>
{% endfor %}
If this does not represent the structure of your json, then please provide an example. Let me know how it goes.

Replace function inside value not working

Maybe I'm doing it wrong (tried several ways to achieve my goal) or overlooking something here.
What I'd like to achieve is this:
When I use the Live-search function, I get categories containing the search keyword (like: Paint -> Paint buckets, Paint brushes, Paint colors etc.) which works like a charm. The one thing I need is to style the searched keyword in the presented categories like:
Paint bucket,
Paint brushes,
Color paint
This is the code I have at the moment:
{% if (products.length + categories.length + pages.length) == 0 %}
<div id="Pi56dYpB" class="undefined"> No results for:
<b>{{ query }}</b>...
</div>
{% endif %}
{% if categories.length > 0 %}
<div class="categories">
{% for category in categories %}
{% if loop.index < 7 %}
<a class="p-0" href="{{ category.url }}" style="all:inherit;">
<h3 id="bvwiyjEN" class="undefined">{{ category.name|replace({{ query }}: "<strong>"{{ query }}"<strong>"})|raw }}</h3>
</a>
{% endif %}
{% endfor %}
{% endif %}
Unfortunately this isn't working. I did check if the {{ query }} value is accessible with this simple line of code:
<h3 id="bvwiyjEN" class="undefined">{{ category.name }} - {{ query }}</h3>
No problems found here.
Did I use the wrong syntax in my code maybe? Any help would be appreciated!
replace({{ query }}) is the wrong syntax - replace(query) should work, as you don't want to echo the variable query here
As Nico Haase pointed out already, {{ ... }} is used to output variables, this translate to something like <?php echo $foo; ?> and can't/shouldn't be used inside statements
Furthermore, the filter replace expects an array of keys to replace with the values.
You would need to change your code to the following:
{% for category in categories %}
{{ category|replace({ (query): '<strong>'~query~'</strong>',})|raw }}
{% endfor %}
demo
Some notes:
The filter replace uses the PHP function strtr in the background. This mean the output will be case-sensitive. If you don't want this, I'd advice you to follow through here
Wrapping the key with parantheses is mandatory. This will force twig to evaluate/interpolate the value of the key - demo

How to reduce for loops in html in Django?

I tried the following for loops and if statements in django html, but it takes so long to load one page. First, here is the html:
{% for time in TIME_CHOICES %}
<tr class="bg-white border-b border-gray-400">
<td class="border-r border-gray-400 py-1 whitespace-nowrap text-sm font-medium text-gray-900">
{{time}}
</td>
{% for each_date in dates_in_month %}
{% if each_date not in weekends %}
{% for class in classes %}
<h1>class</h1>
{% endfor %}
{% else %}
<td class="py-1 whitespace-nowrap text-sm text-gray-500">
<ul class="list-none" style="font-size: 0.70rem; line-height: 0.85rem;">
<li>-----</li>
<li>--(--)</li>
</ul>
</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
I think this is because I have too many for loops and if statements happening in my html. Is there anyway I can increase the speed? Or is there any way I can do the same thing in django views(I am using generic list view, so I need some code for the get_context_data)? Thank you, and please leave any questions you might have.
It is always better to reduce database hits. In your code you are hitting database in an iteration so if the loop run for 1000 times it will hit database 1000 times this can be reduced to just one query like this:
classes = Class.objects.filter(
teacher=teacher, date__in=[each_date for each_date in dates_in_month
if each_date not in weekends]
).order_by('date','time')
then you can iterate the classes queryset to continue with the rest of the code.
Also make your code more readable right now it is a messy.
It's hard to say where is a performance problem in your code. Loops in html shouldn't take a lot of time. Maybe you have a lot of db queries or run some heavy methods.
Try to investigate what part of your code is really slow. You can you Silk profiler for this
Silk is a live profiling and inspection tool for the Django framework. Silk intercepts and stores HTTP requests and database queries before presenting them in a user interface for further inspection:
Installation
pip install django-silk
In settings.py add the following:
MIDDLEWARE = [
...
'silk.middleware.SilkyMiddleware',
...
]
INSTALLED_APPS = (
...
'silk'
)
Try to find the method that takes the most part of time and optimize it. Also, you can add Silk's result to your question. It helps to figure out the problem

How to mark string for translation in jinja2 using trans blocks

I am using django and jinja2 and have something like this in one of my html pages
<p><strong>Q. {{ _("What products will you accept?") }}</strong></p>
<p class="style3"><strong>A: </strong>{% trans myurl=request.url('start') %}A list of qualifying devices is available once you start your trade-in estimate. <a href= {{myurl}}>Click here</a> to learn what your old device is worth.</p>{% endtrans %}
When I run django-admin.py makemessages, "What products will you accept?" is the only string that gets processed. I thought that wrapping a string with the {% trans %} block also marks that string or is this a wrong statement?
What is the best technique to mark that second string (it's tricky because of the request.url variable)
I've tried {{ _("A list of qualifying devices is available once you start your trade-in estimate. <a href= {{ request.url('start') }}>Click here</a> to learn what your old device is worth.")|safe }} but then the link doesn't work properly.
To translate a block like that you need to use the {% blocktrans %} template tag,
{% blocktrans with myurl=request.url('start') %}
A list of qualifying devices is available once you start your trade-in estimate. <a href= {{myurl}}>Click here</a> to learn what your old device is worth.</p>
{% endblocktrans %}
However, I'm not sure you can call a function like that anywhere in a template like you are attempting.
Consider instead passing myurl in as a template variable, and then using smaller chunks of text. This increases reuse of the translations - especially for small common blocks like "Click here".
{% blocktrans %}
"A list of qualifying devices is available once you start your trade-in estimate."
{% endblocktrans %}
<a href= {{myurl}}>{% trans "Click here" %}</a>
{% trans "to learn what your old device is worth." %}
Also, when using HTML and template tags, try and keep them correctly nested. For example, in your code you would have:
<p>... {% blocktrans %}... </p>{% endblocktrans %}
Instead try to nest them like so:
<p>... {% blocktrans %}... {% endblocktrans %}</p>
This is especially useful for IDEs that can auto indent and fold content and helps readability.

Can I specify a multiline context variable/parameter when {% include %}ing a template?

I know that it is possible to set context variables when including a Django template from another template using
{% include "default_table.html" with table_header=table_header1 table_data=table_data1 %}
or
{% with "My data" as table_data %}
{% include 'default_table.html' %}
{% endwith %}
My issue with this is that both approaches don't let me define multiline variables (unless they are based on a previous multiline variable of course).
My specific usecase is this
<!-- widget.html -->
<div class="box">
<div class="title">{{ title }}</div>
<div class="title">{{ body }}</div>
</div>
and I'd like to be able to set a longer text for the body context variable. This will make is possible for me to reuse common widget HTML in various places. Can this be done?
I've been searching a bit on http://djangosnippets.org for an über {% with ... %} template tag, but haven't found any so far.
This Django snippet kinda solves my issue: http://djangosnippets.org/snippets/1860/ But I'd love to be able to set context variables instead of defining {% localblock step_ready_js %}{% endlocalblock %} in my widget HTML.