Django way to do conditional formatting - python

What is the correct way to do conditional formatting in Django?
I have a model that contains a date field, and I would like to display a list of records, but colour the rows depending on the value of that date field. For example, records that match today's date I want to be yellow, records that is before today I want green and ones after I want red.
Somewhere in Django you will need to do that comparison, comparing the current date with the date in the record.
I can see three different places that comparison could be done:
Add a method to my model, for example, status(), that returns either 'past', 'present', 'future' and then use that in the template to colour the rows.
In the view instead of returning a queryset to the template, pre-process the list and compare each record, build a new dict containing the 'past', 'present' and 'future' values to be used in the template
Create a new template tag that does the comparison.
Which of these methods is the correct Django way of doing it? It sounds like conditional formating is something that would come up quite often, and since you can't do arbitrary comparisons in the template some other solution is needed.
The same would apply for simpler formatting rules, for example, if I wanted to display a list of student grades, and I wanted to make the ones higher than 80% green and the ones below 30% red.

I'm a big fan of putting ALL "business" logic in the view function and ALL presentation in the templates/CSS.
Option 1 is ideal. You return a list of pairs: ( date, state ), where the state is the class name ("past", "present", "future").
Your template then uses the state information as the class for a <span>. Your CSS then provides the color coding for that span.
You are now free to change the rules without breaking the template. You can change the CSS without touching HTML or Python code.
{% for date,state in the_date_list %}
<span class="{{state}}">date</span>
{% endfor %}

I had a very similar requirement; as this is pretty connected to the business logic, I have added a model method to manage this kind of information, to be used then in the template:
{% if not bug.within_due_date %}bgcolor="OrangeRed"{% endif %}
It could also be obtained through a template tag or filter; but in my case, I felt the best place for the logic was inside the model; I would suggest you analyzing it in the same way.

Since you are doing static comparison (no queries needed), you should go for the most DRY and easy to implement option. In this case, I would go for option 4, make a template filter. Then you could do value|filter to get the class you would need to set the background colour. Template filters are actually a bit simpler than template tags to implement.

you can also take a look at Django's reference for built in Formatting and Filtering tags. It sure has what you are looking for and more and it's a good link to have bookmarked.
You can take a look at it here.

Related

How to have partially pre-rendered fields using Django-haystack

I've started using a rendered field in my django-haystack indexing to avoid database hits when a search is rendered. This is working well for the most part, but I have some runtime information (such as an edit button for staff) in the results as well that I'd like to splice in. A simplified example:
{{object.name}}<br/>
{% if user.is_staff %}
Edit
{% endif %}
{{ object.description}}
The user logic obviously can't be applied at indexing time so doesn't occur. Without using javascript hacks is there a way to splice some runtime output in amongst the pre-rendered text? I'm thinking it can be done by passing the rendered text with some formatting placeholders to a custom template tag, but I wonder if there's another way.
Edit: Perhaps multiple rendered, stored fields might be possible, covering the main fragments of the search result surrounding the logic parts, and then assembled at run time in the main results template. Would this work?

Generic list view + site-wide data in django?

I have a view that perfectly fits the ListView in Django. Like this:
url(r'^$',
ListView.as_view(
queryset=Poll.objects.order_by('-pub_date')[:5],
context_object_name='latest_poll_list',
template_name='polls/views/index.html')),
However I on the page of the view I also would like to display content from another model. This model contains only one row of data. It's sitewide and contains the footer-text page-title etc.
What's the best solution for this? Ideally I'd like to keep the generic-model approach.
There are several methods that will solve this issue. The view is definitely not the correct place.
Template Tags
One way to use template tags is as mini-views. Whether you are requesting data from another model, or rendering an included template.
Because you can also pass parameters into the template tag, they can be customized by the context.
Context Processors
For simpler information,context processors work well. I wouldn't get too complicated with the data, as it is global and can slow down every request.
On an aside, I would also question your one-row model. My guess is that this is to allow user editing. If users access it, expect every possible way of screwing it up. For example, adding more rows. There are several Django applications for managing user-editable settings that might be useful. If the data isn't going to change often, I would make it an actual setting, or put it in a template snippet and {% include %} it.

How do I pass dissimilar data from view to template?

I am a relatively new to Django, and have just run into a wall, but I am sure this will be a cake walk for you veterans out there. I have a list of items I am displaying in a table in a template. That is no problem I create an object (list of values) in my view send it to the template and render the table. However, I would like to also show on my template a bunch of count()’s displayed as links, that when clicked will further filter the list of items displayed. For example, I may have items in the list that cost between $25 and $50, my link would show that there are say 20 items that match that criteria. When the link is selected in sends a request to the url.py that in turn executes a view that further filters the queryset then renders template again.
How do I get the count() info to the template? I do not think I can send two separate lists (objects) to the template (at least I have not been able to figure out how yet). I think I need to get the counts at the view and then somehow append them to my list object, but I’m not sure how to do that and also, not quite sure how to parse those values in the template. I want the counts to show separate from the table generated from my list object, and I am somewhat concerned I’m going to mess up my table that is working fine now.
I would appreciate any suggestions you have about how to tackle this, and I would really appreciate code examples because I am still somewhat Python/Django code challenged.
You can send as many lists or objects to the template as you like. Template context is just a dictionary, and you can add items to it as necessary.
context = {
'list1': my_first_list,
'list2': my_second_list,
...
}
Your requirements are vague. Code helps. Also. Periods to end sentences help.
I think I need to get the counts at the view and then somehow append them to my list object
how to parse those values in the template.
Here's an approach. It doesn't use aggregate functions. They are more efficient, but also a little more difficult to understand. https://docs.djangoproject.com/en/1.3/topics/db/aggregation/
You have something like this in your view:
object_list = Model.objects.filter( some_attribute=some_value )
You do this in your form to see a count.
{% for obj in object_list %}
<tr><td>{{obj.name}}</td> <td>{{obj.related_set.count}}</td><tr>
{% endfor %}
This evaluates obj.related_set.count() for you. It's not the most efficient (aggregates are more efficient). But it's simple.
If you want a link, you'll replace the simple {{obj.related_set.count}} with something that includes a link
{{obj.related_set.count}}

How can I have attributes that get extracted for i18n in ZPT macros?

I'm using Pyramid with ZPT templates to make a web app. I'm attempting to make a form template macro library to make life easier for things like setting current values of form fields, etc. I'm also using the lingua message extractors to automatically pick up strings for localization. I can't figure out how to make the extractors work with attributes on macro tags though. For example, say I have a macro that looks like this:
<tal:block metal:define-macro="text">
<input type="text" name="${field}" value="${request.params.get(field) or default_value}"/>
</tal:block>
And I use it in a page template like this:
<tal:block tal:define="field 'fred'; default_value _('initial value')" metal:use-macro="form.macros['text']"/>
The text "initial value" won't get picked up by the message extractor. Is there a way to make this happen? I'd like to keep the strings inside the template if possible, I know I could work around this by having all the strings for default values generated in the view callable. Maybe there is there a better way of passing values to macros for use in attributes?
You need to use [lingua] to version 2 (or later) to extract messages from templates for this to work.

Since Django discourages passing arguments to functions in templates, what is encouraged instead?

I understand that in Django, the template language is purposely neutered to prevent too much computation in display code. It means that, ideally, for every situation where users might feel compelled to do computation, there's a more proper alternative. Either a tag or filter that does the trick, or something hopefully straightforward in the view. Any annoyances that don't fit into here are hopefully rare.
But I've found a common case that is rather annoying, and either Django has a better way to do this that I haven't thought of, or they ought to see the light here and move the line a little bit on computing within a template in a near future release (as they did with if statement parameters, for instance):
I have a queryset of items. I need to display them somehow, but what I display depends not only on the state of the object, but also other independent things (usually who is logged in). So adding a function to the model won't help.
What I've been doing so far is turning the queryset into a list or tree structure (depending on the task), and adding a "view_extra" attribute to each one. view_extra is a dictionary where I generally stick in values that are dependent on things like who is logged in. Apart from being a hassle, it also destroys the laziness of the queryset. I guess I could go so far as to make a generator, but obviously this is not what the Django developers had in mind for us to do.
I should probably try queryset annotation more, but I don't know how well that would work in some more complicated cases. Plus, no good in a tree or list-within-list structure scenario (queryset of items with members that are further querysets I need to iterate over).
I could register a filter, (as suggested here django template system, calling a function inside a model) but that is an abuse of filters, right? They are meant to transform text and maybe data, not to be a specific-purpose replacement for something the developers deliberately tried to get us not to do.
Any "proper" way to do this that I don't know about? Am I off base here by suggesting that this is a deficiency of Django's templating system as it stands?
I don't see why creating a custom tag or filter is an 'abuse'. As far as I'm concerned, that's exactly what they're for, and I use them for that all the time.
Maybe Jinja templates would be a good alternative to using Django's templating system in this instance. Apart from the important fact that Jinja allows you to use some level of logic in your templates, Jinja templates and Django templates are almost identical.
This example code taken directly from the Jinja documents looks like it might be the kind of thing you're trying to achieve.
{% for comment in models.comments.latest(10) %}
...
{% endfor %}
To integrate Jinja with Django, you could look at Coffin

Categories

Resources