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.
Related
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 %}
I am displaying a list of images.
If the user has uploaded an image, I want to keep its opacity 0.5 and in the list of images, the images uploaded by others should have full opacity.
I have done it as follows, is there a better way to do it??
{% if request.user == obj.shared_by %}
<div class="item-image" style="opacity:0.5;filter:alpha(opacity=50);">
{% else %}
<div class="item-image">
{% endif %}
......Some code here....
</div>
Thanks!
I normally go for:
<div class="item-image{% if foo %} own-image{% endif %}">...</div>
but switching out the entire div tag may be more readable.
Either way I'd do the styling with another class, not with inline css.
I have added class on if condition by this way....
<li class="nav-item {% if app_url == '/' %} active{% endif %}">
I want to have a save button on top of django admin change list page. It seems that django don't have this functionality built-in. The save_on_top option only controls behavior on change form page. Any suggestion is welcome.
In Django 3 (and maybe earlier, not sure) in your custom admin form add save_on_top = True
class MyAdmin(admin.ModelAdmin):
save_on_top = True
First, you need a way to extend the template found at django/contrib/admin/templates/admin/change_list.html. If you don't already know how to do that, check out this answer and this answer.
Next, you need to create your own change_list.html template and put code similar to the following in it. For the sake of simplicity, I've included inline CSS. However, this is bad practice, so you should not do it. Assuming you move the CSS to an external file, you won't need to load admin_static. Lastly, the extends line that you use might not be exactly the same as what I've shown here.
{% extends "contrib/admin/templates/admin/change_list.html" %}
{% load i18n admin_static %}
{% block result_list %}
{% if cl.formset and cl.result_count %}
<div style="border-bottom: 1px solid #ccc; background: white url({% static "admin/img/nav-bg.gif" %}) 0 180% repeat-x; overflow: hidden;">
<p>
<input type="submit" name="_save" class="default" value="{% trans 'Save' %}"/>
</p>
</div>
{% endif %}
{{ block.super }}
{% endblock %}
The {% if %} tag and the <input> tag inside of it is from django/contrib/admin/templates/admin/pagination.html.
The CSS is based on the CSS for #changelist .paginator and is found in django/contrib/admin/static/admin/css/changelists.css.
If you don't mind having pagination links at the top of the page as well, you can do it with a few lines of template code, worksforme in Django 2.0.
Create my_app/templates/admin/my_app/my_model/change_list.html:
{% extends "admin/change_list.html" %}
{% load admin_list %}
{% block result_list %}
{% pagination cl %}
{{ block.super }}
{% endblock %}
This will render the pagination and the save button:
Could benefit from a line or two of CSS, though...
I am currently working on a symfony2 application and am using embedded controllers. My embedded controllers are like widgets which should encapsulate its own set of functionality and can be embedded anywhere and still be expected to function.
I have a controller called users online. The view it generates is simple, just a list of online users. But, I would like to add some javascript to that view so that I can use ajax to retrieve information for a user that's clicked on.
The controller basically returns a view:
return $this->render('AppBundle:Users:usersOnline.html.twig', array('somedata' => $data);
Here's the view for that controller:
{% extends partial.html.twig" %}
{% block content %}
<ul>
<li>User 1</li> (this would all be generated using 'somedata')
<li>User 2</li>
....
<ul>
{% endblock content %}
{% block scripts %}
..some javascript for interacting with this widget
{% endblock %}
This is the partial that is extended from:
{% block content %}
{% endblock content %}
{% block scripts %}{% endblock %}
Here's the main page that embeds the controller:
{% "base.html.twig" %}
{% block title %}Main{% endblock %}
{% block content %}
..some markup here
<div id="usersonline">
{% render "AppBundle:Users:usersOnline" with {'max': 4} %}
</div>
{% endblock %}
{% block scripts %}
..some javascript
{% endblock %}
This is the base that it extends:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{% block title %}{% endblock %} - App</title>
...Some stylesheets
</head>
<body>
{% block content %}{% endblock %}
<script src="http://yui.yahooapis.com/3.5.0pr2/build/yui/yui-min.js"></script>
{% block scripts %}{% endblock %}
</body>
</html>
The problem I am facing now is including javascripts from the embedded controller. In my case, the view extends partial and is fully rendered as 1 unit before it is inserted into the main page. In this case, I can only put my javascript in the content block, which means I will have <script> tags within <div> tags. I would also prefer to have scripts at the end of the body for user interface performance.
How should I structure my templates (or is it even possible) so that I can render the appropriate pieces from an embedded controller's view into appropriate blocks in the template that embeds the controller? In my current template the YUI library would be loaded after the embedded controller's rendered HTML markup, so accessing using the YUI library within the embedded controller would be impossible.
I have written an extension which is essentially a token parser to deal with this. The documentation for the token parser is very sparse and the code is also mostly uncommented, so it took a bit of fiddling around to get working.
I have structured the parser so that at the beginning, you would declare something (similiar to {% use %}):
{% myparser "AppBundle:Users:usersOnline" with {'max': 4} as xyz %}
Then blocks like xyzcontent would be avaliable and then you just render them in the appropriate places using {{ block('xyzcontent') }}. It works quite well. However the following does not work:
{% set max = 4 %}
{% myparser "AppBundle:Users:usersOnline" with {'max': max} as xyz %}
I could not find a way to evaluate an expression and get its value directly within the token parser. For the most part it works fine, except for this problem.
If someone has some idea as to how to get the value of a variable within the token parser, it would be great :)
After one day of looking for a satisfying solution, I finally choose for the following approach:
Render a controller for your widget's functionality (like you are already doing);
Render templates to embed your widget's assets (controllers are not needed and will waste your performance). Ignore include if the template doesn't exist.
Use a consistent naming convention to make the widget functionality more dynamic.
Example for one widget:
{% "base.html.twig" %}
{% block title %}Main{% endblock %}
{% block content %}
..some markup here
<div id="usersonline">
{{ render(controller("AppBundle:Users:usersOnline", {'max': 4})) }}
</div>
{% endblock %}
{% block scripts %}
{{ include("AppBundle:Users:usersOnline_js.html.twig" ignore missing }}
{% endblock %}
{% block stylesheets %}
{{ include("AppBundle:Users:usersOnline_css.html.twig" ignore missing }}
{% endblock %}
(As you can see, I use the Symfony 2.2 way of including and embedding)
Or, a more dynamic way of widget rendering:
{% "base.html.twig" %}
{% block title %}Main{% endblock %}
{% block widgets %}
..some markup here
{% for widget in widgets %}
{{ render(controller(widget.controller ~ ':widget', widget.options)) }}
{% endfor %}
{% endblock %}
{% block scripts %}
{% for widget in widgets %}
{{ include(widget.controller ~ ':widget_js.html.twig' ignore missing }}
{% endfor %}
{% endblock %}
{% block stylesheets %}
{% for widget in widgets %}
{{ include(widget.controller ~ ':widget_css.html.twig' ignore missing }}
{% endfor %}
{% endblock %}
My conclusion
Rendering multiple controllers in one page, while "merging" it's templates blocks, cannot be achieved out of the box in Symfony2. Symfony2 has always been focusing on one controller for each request, which explains why multiple controllers outputs are separated and hard to combine.
I think the Symfony CMF has (or will have) a proper solution for this, because this whole widget topic is more applied in dynamic content managed websites.
Anyway, I think my way of combining embedded controllers and included templates is the best approach for Symfony 2 so far.
The alternatives, in my opinion, do have to many disadvantages:
Using multiple controllers: too heavy and not needed because assets won't need any controller.
Loading the widgets assets directly in the page: hard to maintain when changing widgets / assets.
Django templates offer the builtin tag cycle for alternating between several values at different points in a template (or for loop in a template) but this tag does not reset when it is accessed in a scope outside of the cycles definition. I.e., if you have two or more lists in your template, the rows of all of which you'd like to use some css definitions odd and even, the first row of a list will pick up where the last left off, not with a fresh iteration from the choices (odd and even)
E.g., in the following code, if the first blog has an odd number of entries, then the first entry in a second blog will start as even, when I want it to start at odd.
{% for blog in blogs %}
{% for entry in blog.entries %}
<div class="{% cycle 'odd' 'even' %}" id="{{entry.id}}">
{{entry.text}}
</div>
{% endfor %}
{% endfor %}
I've tried obviating this by patching with the resetcycle tag offered here:
Django ticket: Cycle tag should reset after it steps out of scope
to no avail. (The code didn't work for me.)
I've also tried moving my inner loop into a custom tag, but this also did not work, perhaps because the compile/render cycle moves the loop back into the outer loop? (Regardless of why, it didn't work for me.)
How can I accomplish this simple task!? I'd prefer not to create a data structure in my view with this information pre-compiled; that seems unnecessary. Thanks in advance.
The easiest workaround (until the resetcycle patch gets fixed up and applied) is to use the built-in "divisibleby" filter with forloop.counter:
{% for entry in blog.entries %}
<div class="{% if forloop.counter|divisibleby:2 %}even{% else %}odd{% endif %}" id="{{ entry.id }}">
{{ entry.text }}
</div>
{% endfor %}
A little more verbose, but not hard to understand and it works great.
https://docs.djangoproject.com/en/1.8/ref/templates/builtins/#cycle
{% for o in some_list %}
<tr class="{% cycle 'row1' 'row2' %}">
...
</tr>
{% endfor %}
Give up and use Jinja2 Template System
I gave up on django template language, it's very restricted in what you can do with it.
Jinja2 uses the same syntax that the django template uses, but adds many enhancements over it.
EDIT/NOTE ( I know it sounds like a big switch for just a minor issue, but in reality I bet you always find yourself fighting the default template system in django, so it really is worthwhile and I believe it will make you more productive in the long run. )
You can read this article written by its author, although it's technical, he mentions the problem of the {% cycle %} tag in django.
Jinja doesn't have a cycle tag, it has a cycle method on the loop:
{% for user in users %}
<li class="{{ loop.cycle('odd', 'even') }}">{{ user }}</li>
{% endfor %}
A major advantage of Jinja2 is that it allows you to use logic for the presentation, so if you have a list of pictures, you can put them in a table, because you can start a new row inside a table every N elements, see, you can do for example:
{% if loop.index is divisibleby(5) %}
</tr>
{% if not loop.last %}
<tr>
{% endif %}
{% endif %}
you can also use mathematical expressions:
{% if x > 10 %}
and you can access your python functions directly (but some setup is required to specify which functions should be exposed for the template)
{% for item in normal_python_function_that_returns_a_query_or_a_list() %}
even set variables ..
{% set variable_name = function_that_returns_an_object_or_something() %}
You can use tagged cycle and resetcycle (new in Django 1.11) calls (from https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#std:templatetag-resetcycle ):
{% for blog in blogs %}
{% cycle 'odd' 'even' as rowcolors silent %}
{% resetcycle rowcolors %}
{% for entry in blog.entries %}
{% cycle rowcolors %}
<div class="{{ rowcolors }}" id="{{entry.id}}">
{{ entry.text }}
</div>
{% endfor %}
{% endfor %}
I end up doing so, with the forloop.counter0 - It works great!
{% for product in products %}
{% if forloop.counter0|divisibleby:4 %}<div class="clear"></div>{% endif %}
<div class="product {% if forloop.counter0|divisibleby:4 %}col{% else %}col20{% endif %}">
Lorem Ipsum is simply dummy text
</div>
{% endfor %}
The easiest answer might be: "give up and use jQuery." If that's acceptable it's probably easier than fighting with Django's templates over something so simple.
There's a way to do it server-side with an iterator that doesn't keep a simultaneous copy of all the entries:
import itertools
return render_to_response('template.html',
{
"flattened_entries": itertools.chain(*(blog.entries for blog in blogs)),
})