Is there a DRY shorthand for the following Django template code?
{% if condition %}
<a href="{% url 'url_name' arg1 arg2 kwarg='some value' %}">
{# just some block of code #}
<h2>{{ value|capfirst }}</h2>
</a>
{% else %}
{# the same block of code #}
<h2>{{ value|capfirst }}</h2>
{% endif %}
Personally, I don't see something non-DRY in your code (readability trumps DRY in my opinion).
However if you must, you can create a template tag that renders your links based on the content of the condition variable. In your tag though, you'd have the same if-loop logic. It would just move the code from your template to the tag. I would caution against this though, as it is just adding complexity for vanity (in my opinion). Plus, template tags/filters are difficult to debug.
You could also decide to build the links in your view code, but again, you are going to have to write the same type of logic structure (if-statement).
Perhaps someone would suggest using javascript to modify the node based on the flag but again - simplicity and readability trumps everything else.
Remember, you'll have to maintain this code sometime down the line. As a code golf exercise this is a good one, though.
If you need to keep your HTML structure, this is probably the most readable way.
{% if condition %}
<a href="{% url 'url_name' arg1 arg2 kwarg='some value' %}">
{% endif %}
<h2>Heading</h2>
{% if condition %}
</a>
{% endif %}
Related
I want to cycle the include tags in a django template, but I am unable to nest cycle inside of includes. The cycle should be like
include cycle left_align.html right_align.html
I am trying to avoid creating a custom template just to keep it simple. I wanted to see if anyone had any suggestions of how to do this:
{% for s in sections %}
<section class="{% cycle 'lighter-section' 'cover cover-bg-section' %}">
{% if s.media.values %}
{% include 'web_builder/apple-pie/sections/left_align.html' %}
{% else %}
{% include 'web_builder/apple-pie/sections/center_align.html' %}
{% endif %}
</section>
{% endfor %}
Unfortunately there is no way to assign result of cycle templatetag to a variable with with block but you can use as keyword to dump its result to the variable and use it wherever you wish e.g. {% cycle 'left_align.html' 'right_align.html' as include_file %}. However this has a side effect.
This instruction itself produces cycle result although it is also registered in the variable. As a work-around you can place it for instance in a data- attr of some HTML element so that it isn't displayed and use the variable later in full template path concatenation inside include tag.
Full example:
{% for i in "xxxxxxxxxxxxxxxxx" %}
<section data-include-file="{% cycle 'left_align.html' 'right_align.html' as include_file %}">
{% include 'web_builder/apple-pie/sections/'|add:include_file %}
</section>
{% endfor %}
However since it looks ugly and complicated I would also consider writing custom inclusion tag for that.
In my django project I have two options that use querystrings to define a kind of list being fetched, e.g:
Shopping
Chores
On top of that I also want to check which list has been selected by the user and make it appear bold in the UI. So
{% if 'shopping' in request.GET.list or not request.GET.list %}
<b>Shopping</b>
Chores
{% elif 'chores' in request.GET.list %}
Shopping
<b>Chores</b>
{% endif %}
What's really confusing me now, is in addition to Shopping and Chores I also want to have two sub-options that define the order of the list. New and Old, for instance. To me it seems like the only way of doing this is with another duplication of all the code.
{% if 'new' in request.GET.list %}
{% if 'shopping' in request.GET.list or not request.GET.list %}
<b>Shopping</b>
Chores
<b>New</b>
Old
{% elif 'chores' in request.GET.list %}
Shopping
<b>Chores</b>
<b>New</b>
Old
{% endif %}
{% elif 'old' in request.GET.list %}
{# ... #}
{% endif %}
I think you can already see how insane this becomes to manage and you would still need to do the same for the Old if statement. I really don't know what to do here because I can't see any other way to (1) Know what should be bold and not bold. And (2) Known whether each option should begin with a ? or &.
Regarding your first problem with having bold selected value, by the use a of a HTML class you can do the following instead for example.
In your css file (or style block in your html file):
.selected {font-weight: bold;}
So your html can now become somehing like,
<a class="{% if 'shopping' in request.GET.list or not request.GET.list %} selected{% endif %}" href="{% url 'index' %}?list=shopping">Shopping</a>
<a class="{% if 'chores' in request.GET.list %}selected{% endif %}" href="{% url 'index' %}?list=chores">Chores</a>
This way you dont have to write your html twice or more for each case.
As for your second issue, you can do something like below if you are using your 'new' and 'old' in your urls or html,
{% with 'new old bla' as list %}
{% for option in list.split %}
<a class="{% if 'shopping' in request.GET.list or not request.GET.list %} selected{% endif %}" href="{% url 'index' %}?list=shopping&option={{ option }}">Shopping</a>
<a class="{% if 'chores' in request.GET.list %}selected{% endif %}" href="{% url 'index' %}?list=chores&option={{ option }}">Chores</a>
{% endfor %}
{% endwith %}
Thats just an example of how you would use it, but this will save alot of code writing.
Hope this helps!
You could use a QueryDict for your query strings. That's what Django uses internally.
https://docs.djangoproject.com/en/2.1/ref/request-response/#django.http.QueryDict
But really, I would consider refactoring your code. Put routing logic in your urls.py and business logic in your view functions. Try to keep the template files as uncomplicated as possible.
Instead of /?list=shopping you can use a regular url: /list/shopping/, for example.
I want to do this:
{% for egg in eggs %}
<p>{{ egg.spam }}</p>
{% if egg.is_cool %}
{% myvariable = egg %} // Possible in any way?
{% endif %}
{% endfor %}
Pardon the JavaScript-style comment (it shows up as a comment on SO)
I think the closest you'll get is the with tag: http://docs.djangoproject.com/en/dev/ref/templates/builtins/#with.
If you're say trying to feature an item in a template, I can imagine doing something like:
<div class="special">
{% with some_list.first as special_item %}
{{ specialitem }}
{% endwith %}
</div>
<div class="everything">
{% for item in some_list %}
{{ item }}
{% endfor %}
</div>
If you want some special logic to determine which one is the special item, I'd add a method to the object (so you end up with: {% with some_collection.my_method as special_item %} above) or determine the special item before passing it to the view. Hope that helps.
Welcome to Django Templates.
This problem is easily solved with one of the earliest snippets posted to DjangoSnippets.com: the Expr tag.
People can argue all day about the separation of logic from the templates, but that ignores that there is business logic, which belongs in the models or views, and presentation logic which belongs only in the templates. If you have a lot of presentation logic you may want to consider using Jinja2 for some or all of your templates. WARNING: although Jinja2 looks a lot like Django's template language, there are incompatibilities with things like Custom Template Tags.
Yes, you can use the with construct:
{% with myvariable as egg %}
do stuf
{% endwith %}
I think it's probably best to do this kind of test-and-set behaviour in the view, not the template. If anything, it'll give you better control over cacheing if/when you need it.
In this excellent Google Tech Talk by Jacob Kaplan-Moss, Jacob says that they added support for the include template tag despite previous dogmatic objections, and says that people shouldn't use it.
Does anyone know why? A quick search didn't show anything that would explain why. There's nothing relevant in the now-fixed ticket where support for an include tag was added. I use include tags liberally to avoid repeating myself, which seems like a good idea, but maybe I'm missing some core reason why those who know think it's bad.
I suppose he wants to encourage template reuse by inheritance (using extends) rather than by composition. Perhaps the implication is that if you can't organise your templates this way, the dogmatic opinion is that your site is organised poorly. (For example, if you are reusing a navigation menu, shouldn't it always be in the same place in the page structure? Why should each individual page decide where to put it?)
By the way, using include doesn't do much to help you stay DRY, because any context that the included template requires must be passed from all the views that use it.
By contrast, using a custom inclusion template tag allows you to execute arbitrary Python code at the point where the tag is included, rather than in the view (or by shoving it into a model just to make it easier to access in the template).
As a trivial example, I wanted to show a list of users' avatars. Using include, it looks like this:
{% for user in users %}
{% with user.gravatar_url as avatar_url %}
{% include "foo/bar/avatar.html" %}
{% endwith %}
{% endfor %}
With a custom tag:
{% for user in users %}
{% gravatar user.email %}
{% endfor %}
Using the custom inclusion tag meant that the Gravatar hashing logic no longer had to be a concern of the User model, nor of the view function.
This said, I think are are some situations where you inevitably have similar data in the context of multiple templates, you don't need to do anything fancy with it, you just want to display some of its attributes so you don't want to write a function to make it work.
For example, I wrote a blog application (who hasn't?) which had two types of archive view: a basic sequential, X-posts-per-page one, and a monthly archive view. Both templates obviously had a list of posts in their context, both used exactly the same summary template fragment to show a title and excerpt from each post, but each template presented them in a slightly different context. So I used:
{# in archive_index.html #}
{% extends "base.html" %}
{# some stuff specific to sequential archives here #}
{% for post in posts %}
{% include "post_summary.html" %}
{% endfor %}
{# probably more stuff specific to sequential archives #}
And...
{# in archive_monthly.html #}
{% extends "base.html" %}
{# some stuff specific to monthly archives here #}
{% for post in posts %}
{% include "post_summary.html" %}
{% endfor %}
{# probably more stuff specific to monthly archives #}
It really seems that in this case, composition makes more sense than inheritance. In fact it was difficult at first to imagine how inheritance would work here at all. Well, it's still possible:
{# in base_archive.html #}
{% extends "base.html" %}
{% block archive_header %}{% endblock %}
{% for post in posts %}
{% include "post_summary.html" %}
{% endfor %}
{% block archive_pagination %}{% endblock %}
Now, the two different archives extend this and just inject their unique stuff into the blocks:
{# in archive_monthly.html #}
{% extends "base_archive.html" %}
{% block archive_header %}
<h1>Archive for {{ month }}</h1>
{% endblock %}
{% block archive_pagination %}
{# previous/next month links here #}
{% endblock %}
I'll leave imagining what archive_index.html looks like as an exercise for the (no doubt bored) reader.
Phew! It feels smart to have come up with a way of doing the same thing using both composition and inheritance, but is the latter just bending over backwards to conform to the dogma that Jacob Kaplan-Moss mentioned?
Having just watched the video (yes, all 1 hour 5 minutes of it, just so I could finish answering this question), I don't think Jacob would be enormously bothered. It sounded like an off-the-cuff comment, maybe a reference to which technique you ought to consider first.
I find django's template language very limiting. Following along with django's DRY principle, I have a template that I'd like to use in many other templates. For example a patient list:
{% for physician in physicians.all %}
{% if physician.service_patients.count %}
<div id="tabs-{{ forloop.counter }}">
{% include "hospitalists/patient_list.html" %}
</div>
{% endif %}
{% endfor %}
The problem is that the patient_list template is expecting a list of patients named patients. How can I rename physician.service_patients to patients before including the template?
Thanks,
Pete
Use the with tag:
{% for physician in physicians.all %}
{% if physician.service_patients.count %}
{% with physician.service_patients as patients %}
<div id="tabs-{{ forloop.counter }}">
{% include "hospitalists/patient_list.html" %}
</div>
{% endwith %}
{% endif %}
{% endfor %}
You might also upgrade to creating a custom tag:
{% for physician in physicians.all %}
{% if physician.service_patients.count %}
{% patient-list physician.service_patients %}
{% endif %}
{% endfor %}
Although custom tags involve writing Python code, there are shortcuts that make it easy to use an existing template file as a tag: Django Inclusion Tags
When you have "functionality" (specifically an if-condition) inside a loop, you have an opportunity to move this into the view function.
First
This construct
{% for physician in physicians.all %}
{% if physician.service_patients.count %}
{% endif %}
{% endfor %}
Is so common that you have several ways to avoid it.
Change your model. Add a patients" method and use it instead of the default query set that you get with a on-to-many relationship. This method of your model has theif service_patients.count` test, removing it from your template.
This eliminates the {% if %} from your template, reducing it to {% for %} and the actual HTML, which cannot easily the eliminated.
Change your view function. Write a few lines of code to create a list of physicians with service_patients instead of a simplistic collection of physicians. This code in your view function has the if service_patients.count test, removing it from your template.
This eliminates the {% if %} from your template, reducing it to a {% for %} and the actual HTML, which cannot easily be eliminated.
The point is to get rid of the {% if %} so that you're simply cutting and pasting the {% for %} and the actual HTML. By keeping your template to just the HTML (which cannot be eliminated), the only overhead is the {% for %}
Second
It appears that you want to reuse an {% include %} construct in slightly different contexts.
It's not at all clear what the problem with this {% include %} file is. It is "expecting a list of patients named patients" seems superficially silly. Fix it, so it expects physician.patients.
Perhaps you want to use this same list twice. Once with a list called 'patients' and once with a list called 'physician.patients'. In this case, consider (a) simplifying or (b) writing a template tag.
It appears that you have a patient list that is sometimes a stand-alone page, and other times is repeated many times on a much more complex page. Repeating a list of details embedded in some longer list is not really the best page design. Django doesn't help you with this because -- frankly -- it's not easy for people to use. Hence option (a) -- consider redesigning this "patient list within a physician" list as too complex.
However, you can always write a template tags to create really complex pages.
Summary
There's a really good reason why the Django template language has limited functionality. All of your functionality should be either an essential feature of your model, or a feature of the current application that uses the model.
Presentation is simply the translation of objects (and querysets) into HTML. Nothing more
As way, you can try to use in quality templating language jinja. It is more flexible.