Django templates and whitespace - python

I started using custom inclusion tags within my django templates. For example I have a {% profilelink profile %} tag that inserts a link to a user profile together with a small version of the profile's picture, like so (profilelink.html):
<a href='{% url ... %}'><img src='{{ ... }}' alt='...'> {{ profile.name }}</a>
However, when I use it in the following snippet (sometemplate.html):
<p>Owned by {% profilelink owner %} (uploaded by {% profilelink uploader %})</p>
Then I get whitespace between the HTML produced by the second template tag and the closing parenthesis. This whitespace is unwanted. It comes from the final newline character in the file profilelink.html. This is a very common problem and searching Stackoverflow yields a lot of questions about whitespace in templates in general. Here's a summary of solutions found so far and why they don't work:
Some of these problems are solvable with the {% spaceless %} tag, but not all of them. This tag only removes whitespace between tags, which is not the case in the above example.
One possible solution is to not have a final EOL in profilelink.html but that's highly undesirable. Reasons: It is generally bad style; some editors (vim) silently add one back by default; that's how POSIX defines a line; it might make some SCMs unhappy; etc.
Another solution is switching to another template engine, like Jinja2, which may or may not solve this problem. It has support for constructs like {% ... -%} which eat the next EOL character. This is useful in some situations, but is also useless for my example above. But switching the templating backend for such a small annoyance seems a little overkill and adds another dependency. I'd like to stick to whatever is the standard "django" way of doing things. There are apparently plans to make Jinja2 the new Django default, though.
Some people suggested using a middleware class to remove redundant whitespace from the generated HTML before it's being sent to the browser. This is useful, but only for transforming the HTML in a way that is functionally equivalent, i.e. same semantics: it will then still be displayed the same way in the browser. That's not what I want, I want an actual change in semantics in order to have it display properly. This is impossible to implement in a generic middleware class. I need to have control over this on a case by case basis from within the template itself. I don't care about making the HTML prettier, I care about it being correct in the first place.
There's also bug #2594 which has been closed as WONTFIX with the argument (quote) "the Django template language is good enough for generating HTML, which isn't sensitive to whitespace". In my oponion this is just totally wrong. HTML is very much sensitive to whitespace, it just doesn't care how much there is of it. It cares a lot about whether there is some whitespace or none at all.
Some my question is: Is there any sane way to fix this problem in general? (One that always works, not just in some situations.)
(Any CSS-based fixes do not count. Copy/paste surprises are evil.)

I believe one solution is to use a simple_tag instead of an inclusion tag, hopefully without to much clutter.
I assume your tag is something like this:
#register.inclusion_tag('profilelink.html')
def profilelink(user):
return {"profile": user}
Would it be possible to substitute this with
from django.template.loader import render_to_string
#register.simple_tag
def profilelink(user):
t = render_to_string("profilelink.html", {"profile": user})
return t.strip()
I don't have a Django-project in front of me now, so this is untested.

This is the best I came up with so far. I still hope for a better solution, but for now it will do.
I defined a custom filter like this in base/templatetags/basetags.yp (taken from this answer):
from django import template
from django.template.defaultfilters import stringfilter
register = template.Library()
#register.filter
#stringfilter
def trim(value):
return value.strip()
And then use it as follows:
{% load basetags %}
<p>Owned by {% profilelink owner %} (uploaded by
{% filter trim %}{% profilelink uploader %}{% endfilter %})</p>

Related

I want to display only the sentences of the passed id on the screen

Template Language question. I want to pass the ID of the sentence I want to the url and display only the sentence of the passed ID on the screen.
url:
path('sentenceCard/<str:listName>/<int:listId>/', views.sentenceCard, name='sentenceCard')
views.py:
def sentenceCard(request, listName, listId):
sentence = Sentence.objects.filter(Classification=listName)
return render(request, 'english_note/sentenceCard.html', {'sentences':sentence, 'listId': listId})
html:
<a href='#'>{{sentences.0.korean_sentence}}<br>
<a href='#'>{{sentences.1.korean_sentence}}<br>
Variables in Template Variables?
like this?
{{sentences.listId.korean_sentence}}
Loop through the sentence. Something like the following if I'm understanding your setup. See the documentation on url builtin tag.
{% for sentence in sentences %}
<a href='{% url 'sentenceCard' listName=sentence.classification listId=listId %}'>{{ sentence.korean_sentence }}</a><br />
{% endfor %}
You may need to prefix the url path name with your app name:
{% url 'yourapp:sentenceCard' listName=....%}
Not asked for, but a suggestion or two which may make your Django coding easier:
lowercase your fields. Classification becomes classification
use def sentence_card(request, list_name, list_id):
when filtering, name your queryset with a plural: sentences = Sentence.objects.filter(classification=list_name)
when possible context variables mimic the object {'sentences':sentences}
None is absolutely to make the code work, but when trouble shooting later, it is nice to know by case what sort of thing you are dealing with. In Django in particular, it will help when you start doing some advanced filtering by never having to check how you named a field. Knowing the uppercased thing is the class, and the lower cased version is the instance will also come in handy.

{% translate s %} passed in {% include ... with s=s %} not in .po file

I have a basic feed I am trying to render in my Django project. I have created a feed.html file as a base template for slight variations of the same type of feed. Among those variations, is the header of the feed. Importantly, I want that header to be translated into multiple languages.
I have implemented this "variation" idea using {% include "feed.html" with variation=variation %}. However, I am having problems translating those variations.
I am trying the following in feed.html:
{% translate header %}
Then in one of the templates where I want a variation of feed.html I have:
{% include "feed.html" with header="Header" %}
The problem is, the string "Header" does not make it into any of my .po files and therefore remains untranslatable.
What am I doing wrong? Should I use different syntax?
The problem is, the string "Header" does not make it into any of my .po files and therefore remains untranslatable.
If that means that the makemessages command doesn't extract the string, then yes, it won't, because there is no string. Only a variable. That variable could take any value at all, makemessages cannot possibly trace that back to all possible locations where you might be setting the value for header. makemessages can only extract what you literally put into {% translate %} tags or _() function calls directly.
The caveat with using variables or computed values, as in the previous two examples, is that Django’s translation-string-detecting utility, django-admin makemessages, won’t be able to find these strings.
https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#standard-translation
You'll want to translate the header value before passing it:
{% include "feed.html" with header=_("Header") %}

How to protect some files from the Jinja template processor?

I am using cookiecutter to create a tornado project, using this template (it has several bugs, so you'll probably won't be able to use it out of the box). I have hit a problem which I do not know how to solve:
jinja2.exceptions.TemplateSyntaxError: unexpected char '\\' at 124272
File "./{{cookiecutter.project_slug}}/static/swagger/lib/jsoneditor.min.js", line 10
I am not sure, but I have the impression that cookiecutter is trying to Jinja-process the jsoneditor.min.js, which is not supposed to happen, since the "templating" in that file is not supposed to be processed by cookiecutter, it just happens to include the same escape characters that Jinja is using.
Is it possible to tell cookiecutter not to process files inside a certain directory? This is probably a matter of properly setting up the cookiecutter template, but not sure how this can be specified.
By default cookiecutter will try to process every file as a jinja template which produces wrong results if you have something that looks like a jinja template but is only supposed to be taken literal. Starting with cookiecutter 1.1 one can tell cookiecutter to only copy some files without interpreting them as jinja template (documentation).
To do that you have to add a _copy_without_render key in the cookiecutter config file (cookiecutter.json). It takes a list of regular expressions. If a filename matches the regular expressions it will be copied and not processed as a jinja template.
Example
{
"project_slug": "sample",
"_copy_without_render": [
"*.js",
"not_rendered_dir/*",
"rendered_dir/not_rendered_file.ini"
]
}
This will not process any javascript files (files which end with .js), any files that are in the not_rendered_dir and not the not_rendered_file.ini in the rendered_dir. They will only get copied.
Just came across this question and also this Github Issue.
It seems like a nice addition, that one can partially mark parts of a file or entire template to not be processed by using the {% raw %} tag:
{% raw %}
{% comment %}Whatever jinja code goes here....{% endcomment %}
...
{% endraw %}

Django regex - optional grouped parameters

I'm constructing the urlconf for a website but I'm facing a couple of issues;
At some point I have to present a list of tasks which can eventually be reordered. The problem is that I have no clue on how to go about configuring the regex.
So far I've got this:
url(r'^myapp/mytasks, myview.tasks),
The default behavior is to accept requests at www.mydomain.com/myapp/mytasks.
However should the order button be pressed by a user, I need Django to also accept requests of type:
www.mydomain.com/myapp/mytasks/sort_by/price_highest
www.mydomain.com/myapp/mytasks/sort_by/price_lowest
but not
www.mydomain.com/myapp/mytasks/price_lowest
Is there a regex for this scenario?
Thank you all in advance.
P.S
Ideally I would also like to know how I could possibly reverse them.
Create two url records for the same view:
url(r'^myapp/mytasks/$', myview.tasks, name='tasks'),
url(r'^myapp/mytasks/sort_by/(price_highest|price_lowest)/$', myview.tasks,
name='sorted_tasks'),
And then change the signature of the tasks() view to:
def tasks(request, sort_by=None):
...
In the template you can easily point to the both versions:
{% url 'tasks' %}
{% url 'sorted_tasks' 'price_highest' %}
UPDATE: If you really want to make the only one url record then the regex will be:
'^myapp/mytasks/(?:sort_by/(price_highest|price_lowest)/)?$'
But I am not sure that it will work with {% url %} tag.

Django `with` tag not recognizing keyword argument

I have the following code in my template:
{% include "entry_table/cell.html" with data_items = data_fields class="entry_table_title" only%}
Which gives me the following error:
"with" in 'include' tag needs at least one keyword argument.
I've tried replacing data_field (which is a variable I passed into the context) with a string, just in case that was what was causing the problem, but even if I make it:
{% include "entry_table/cell.html" with data_items = "unicorn" class="entry_table_title" only%}
I still get the same error. The only thing that fixes the issue is to get rid of data_items completely, as in:
{% include "entry_table/cell.html" with class="entry_table_title" only%}
So, what's the problem?
NOTE: I just realized that data_items is also a variable that is passed into the context of the page that is calling the other template, but when i changed the name to something else, it still didn't work. So that is not the problem.
Looks like Django is quite picky about whitespace in this instance. If I change...
{% include "entry_table/cell.html" with data_items = data_fields class="entry_table_title" only%}
...to...
{% include "entry_table/cell.html" with data_items=data_fields class="entry_table_title" only%}
...it works for me.
I had a very similar problem. Only I was using dashes, which apparently aren't permitted in variable names.
I know this is an older question and it has already been sufficiently answered, but this is closely related, and comes up with a search, so for the sake of posterity ...
{% include "partials/forum-panel-header.html" with forum-name="demo name" forum-thread-count="22" forum-post-count="30" %}
It was necessary to change the dashes to underscores ...
{% include "partials/forum-panel-header.html" with forum_name="demo name" forum_thread_count="22" forum_post_count="30" %}
It would have been nice to know this before I wrote many variable names this way, and had to go back through all the nested files to fix them.
Of course I could have found the answer, had I looked, but at the time I didn't even realize I needed an answer.
Well, then I did find the answer (when I realized there was a problem), on another post here, that came up in the search under this one (which is why I've posted on this one).
I have got the same error and the to fix it is easy you need just to get rid of whitespaces and apostrophies your code will be somethiing like this :
{% include "entry_table/cell.html" with_data_items=unicorn class="entry_table_title" only%}
and it will fix your code

Categories

Resources