How to reduce for loops in html in Django? - 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

Related

Django Templates - Best Practice in Avoiding Repeating Code that Require Different For Loops?

I'm creating a blog and have numerous pages that will display a list of articles. So, to avoid repeating that code, I'm trying to place it within a parent template that I can extend where needed.
The problem is I'll need a different for loop to display the article lists on each page/view. I figure the easiest approach would be to simply create blocks where I want the loop to start and close within the parent, then alter accordingly within each child.
However, Django doesn't allow you to close blocks that have an open for loop, despite closing the loop later in a different block.
My initial approach, in the parent, article_list.html:
<div class="row">
{% block loop_start %}
{% endblock loop_start %}
<div class="col-xs-12 col-sm-4">
<div class="card">
<a class="img-card">
<img class="img-fluid"src="../../static/{{ post.featured_image }}" />
</a>.... etc
I know I have to fix my src code.
Extends to child as:
{% block loop_start %}
{% for post in recent_articles %}
{% endblock loop_start %}
However, that doesn't work as noted above.
I've also tried wrapping the entire code for the article list in a block, extending it and performing the following within the child:
{% for post in recent_articles %}
{% block.super article_list %}
{% endblock article_list %}
{% endfor %}
That doesn't work either. Again, producing the same error as a block is closing before the loop ends. I've also tried closing the loop in the parent which doesn't work either.
Is there an easier way of going about this that I'm missing? I could pass the same variable to each view then implement the loop in the parent, but that seems janky and limiting.
What's best practice here?
You should take a look at a 'base.html' file. Take a look at this web-page: https://ultimatedjango.com/learn-django/lessons/create-the-project-base-template/
This will allow you to do {% extends 'base.html' %} all of which Django will handle.

Template NoReverseMatch exceptions, outside of Models

I am generating some Django template code on the fly, in order to display rows in tables that are not stored in
a Django database and do not have models. I know the database and I can introspect them if needed, but I don't want
to write code by hand.
For example, field PSOPRDEFN.OPRCLASS stores an optional reference to a particular row where PSCLASSDEFN.OPRID=PSOPRDEFN.OPRCLASS, essentially a foreign key relationship. If there is no relationship PSOPRDEFN.OPRCLASS has one ' ' (space character) in it.
I also have a page for a given PSCLASSDEFN row, where the url is:
url(r'^(?i)permissions/(?P<CLASSID>[A-Z0-9_&]{1,50})/$',
'pssecurity.views.psclassdefn_detail',
name="psclassdefn_detail"),
Note that the ?P CLASSID regular expression does not allow for blanks which corresponds to gets stored in the PSCLASSDEFN table - I figure it's safer to limit what the user can put in the url request.
Back to my generated template: I want to hyperlink to the relation, if it exists. I feed my home-grown template generator a json "directive" indicating what I want put into the template (thanks for the inspiration, django-tables2):
....
{
"colname": "LANGUAGE_CD"
},
{
"urlname": "security:psclassdefn_detail",
"colname": "OPRCLASS",
"kwargs": [
{
"colname": "dbr",
"accessor": "dbr"
},
{
"colname": "CLASSID",
"accessor": "inst.OPRCLASS"
}
]
},
...
Some fairly trivial code generation then results in:
<div class="row">
<div class="col-xs-6 fieldlabel" title="LANGUAGE_CD" >Language Code</div>
<div class="col-xs-6 fieldvalue text-left _fv_LANGUAGE_CD">{{inst.LANGUAGE_CD}}</div>
</div>
<div class="row">
<div class="col-xs-6 fieldlabel" title="OPRCLASS" >Primary Permission List</div>
<div class="col-xs-6 fieldvalue _fv_OPRCLASS">
{% if inst.OPRCLASS|slugify %}
{{inst.OPRCLASS}}
{% endif %}
</div>
</div>
My problem is that started getting random Template url resolution errors when displaying some of the PSOPRDEFN data. I eventually tracked it down to the blank OPRCLASS fields in some rows.
In order to avoid this I first added
{% if inst.OPRCLASS %}
<a ...></a>
{% endif %}
That didn't work because the field is not empty, it is blank (and therefore doesn't match the CLASSID regex). So, this is where I read the filter docs again and found that slugify strips out blanks and non-alpha.
{% if inst.OPRCLASS | slugify %}
<a ...></a>
{% endif %}
Works, as a workaround. The problem is that CLASSID only stores alphanum, but that's not always true for other fields. I wouldn't mind introspecting the table column definition at template generation runtime to see what to do, but I need to find an appropriate way to disable url reversal, for only some rows.
Questions. Is there a better filter, such as a |strip? I suppose I could always build my own filter.
Even better, is there a tag to selectively catch NoReverseMatch' exceptions at template generation time?
{% try NoReverseMatch %}
{{inst.OPRCLASS}}
{% endtry %}
The reason I was so verbose in my description is because this is not something that can be worked around using Models. And neither can I custom-tune the template by hand. I find Django works quite well without models in most cases, but url reversing in templates can be quite brittle when a few rows of data do not match expectations. Hardening it would be very beneficial.
You can assign the result of the url tag to a variable.
{% url 'path.to.view' arg arg2 as the_url %}
{% if the_url %}
link
{% else %}
No link
{% endif %}
This syntax does not raise an exception if reversing the view fails.

Detecting a URL in a Django wizard_form.html template

I have three SurveyWizardViews all of which use the same standard wizard_form.html which is located at templates/formtools/wizard/wizard_form.html as per the documentation
I have added some basic logic to this template which is designed to detect which page of the form the user is on so that I can include a non standard page/step, this is an image with a JS slider bar underneath. This all works perfectly.
{% if wizard.steps.current == '6' %}
<img src="{% static "survey/images/pathtwo/" %}{{display_image}}"/>
<section>
<span class="tooltip"></span>
<div id="slider"></div>
<span class="volume"></span>
</section>
{% endif %}
However I now want to have a slightly different experience for the user depending on which View/URL they are coming from.
Question Is it possible to detect which URL the view is currently using to look at the page? e.g.
{% if URL.current == 'www.mywebsite.com/experiment/surveyone/' %}
do X
{% if URL.current == 'www.mywebsite.com/experiment/surveytwo/' %}
do y
I have done some searching but Im not even sure what I'm searching for to be honest. Any help would be much appreciated.
You can use the request context variable. Something like:
{% if 'experiment/surveyone' in request.path %}
do this
{% endif %}
I prefer using in instead of == to ignore trailing and leading slashes. If you want the whole thing try the build_absolute_uri method. Also check what options does request offer to you (https://docs.djangoproject.com/en/dev/ref/request-response/#httprequest-objects).
Finally, don't forget to add django.core.context_processors.request to your TEMPLATE_CONTEXT_PROCESSORS (I think it is added by default).

Django template indentation guideline

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.

Rendering Foreign Keys in templates grouped by origin

I have an Area model, and I have Service model. Area is a Foreignkey of Service.
I want a template which shows each Service grouped under its respective Area, i.e.
Area 1
- service a
- service b
Area 2
- etc.
I've passed in an object list of all services to service_list.html. I have a custom tag get_areas which returns the areas, on which I can create the Area divisions, and from which I can potentially pass an area name to a service filter. But since I can't filter (can I?) in {% for service in object_list %}, how can I filter the service list in each Area's section of the HTML?
Thanks so much in advance.
If you post your models I can give you the exact code, but in general something like this should work:
# Pass in 'areas' variable from view with all required areas
{% for area in areas %}
{{ area.name }}
{% for service in area.service_set.all %} #Gets all the services associated with an area
{{ service.name }}
{% endfor %}
{% endofor %}
Not sure you even need a custom tag, but maybe I just don't understand that part.
Take a look at the regroup template tag. It was built for exactly the same purpose
https://docs.djangoproject.com/en/1.3/ref/templates/builtins/#regroup
One thing that I have realized is that once you get the point of displaying complex interrelated data in your templates, that it makes sense to transform the data into an appropriate object (generally a list of dictionaries) before passing them onto the template.
That way, you can test your information easier, and have a much easier time displaying it. (You'll have much powerful tools at your disposition in Python based Views than in Django Templating Language).
#Maz - thank you for that. I'm learning at the moment and need to look at service_set.
#arustgi - that worked perfectly. For the benefit of fellow novices stumbling over this, I pass in 'queryset': Service.objects.all() and use:
{% regroup object_list by area as area_list %}
{% for area in area_list %}
<h2 class="separate">{{ area.grouper }}</h2>
{% for service in area.list %}
<div class="column">
<h3>{{ service.title }}</h3>
{{ service.body }}
</div>
{% endfor %}
{% endfor %}
Concise, descriptive code. Many thanks, both of you