Customizing django object parameters value output - django

what does django do, when i do something like that in template
{{ object.parameter }}
I ask this because in case of arrayfields (postgresql array fields) it will print out either
{'value', 'value', 'value'}
(because thats how postgresql stores arrays in arrayfields)
or
['value','value','value']
if i use fields post_init method to convert postgresql array to python list.
Desired output would of course be value, value, value. I would rather not use some kind of filters for that, because then i would have to resort using IFs in templates or use some kind of template tag filter for every value i print out and that does not feel like a smart thing to do.
By the way, i know i can do something like that in template :
{% for choice in field.choices %}
{{ choice }}
{% if forloop.last %}
{% else %},
{% endif%}
{% endfor %}
and that gives me exactly what i want, but i thought there would be some other way doing it - with some modelfield method or something.
Alan

what does django do, when i do something like that in template
{{ object.parameter }}
See variables and lookups.
Desired output would of course be value, value, value. I would rather not use some kind of filters for that, because then i would have to resort using IFs in templates or use some kind of template tag filter for every value i print out and that does not feel like a smart thing to do.
You can make a really trivial filter:
#register.filter
def comma_join(values):
return u', '.join(values)
So simple:
{{ object.parameter|comma_join }}
Why would you want to avoid such a simple solution ?
and that gives me exactly what i want, but i thought there would be some other way doing it - with some modelfield method or something.
Of course you could also add such a method:
class YourModel(models.Model):
# ....
def comma_join_parameter(self):
return u', '.join(self.parameter)
And use it in your template as such:
{{ object.comma_join_parameter }}

Related

Django Template Tags and Meta-Programming: Is there any way of modifying a variable's name before calling its context?

Suppose in views.py I have a variable (i.e., changing) number of forms or types of objects in my context. (I'll just use the word 'forms' for simplicity).
context = {
'form_0': form_0,
'form_1': form_1,
'form_2': form_2,
'form_3': form_3,
# ... and so forth
}
Let's suppose that I have no way of knowing how many forms are in my context at any given time. Is it at all possible to do the following with template tags:
{% for i in (number of forms in context) %}
{{ form_i }} <-- where i is equal to the number i in the loop -->
{% endfor %}
Here the end result would translate to:
{{ form_0 }}
{{ form_1 }}
{{ form_2 }}
... and so forth
I'm doubtful that this is at all possible, but if it is, I would find it quite helpful.
Referencing by name, as in constructing a string that contains the name, is usually considered an unsafe approach: it can be harder than one thinks to make an algorithm that constructs the name correctly. Forthermore say that you do that and later remove form_3, then your algorithm will likely stop at 2, but perhaps there are still other forms there, but you forgot about the algorithm fetching the forms.
It is better here to pass a collection of objects, for example a list:
context = {
'forms': [form_0, form_1, form_2, form_3]
}
then we can render this in the template with:
{% for form_i in forms %}
{{ form_i }}
{% endfor %}
If you thus add an extra form to the list, then that form will be part of the iterations in the template, and thus Django will render that form (or whatever you put in the list).
If you need acces to some specific forms as well, you can pass then trhough another name as well, for example:
context = {
'forms': [form_0, form_1, form_2, form_3],
# if we need specific items of form_2 in the template,
# we can pass it under a specific name as well
'form_2': form_2
}
so now we can both enumerate over the forms to render these, but if form_2 has some interesting data we need to handle in the template as well, we can still use {{ form_2.non_field_errors }} for example.

Django template variable containing template tag, ex {{ {% some_tag %} }}

I have a template that receives a list context variable, tags_list. I need to iterate over this list 'inserting' the tags in the template something like this:
{% for tag in tags_list %}
{{ tag.tag }}
{% endfor %}
When this renders it returns the text value of tag.tag, "{% tagxxx %}", not the rendered tag.
How can I cause the template render to render the value of a context variable? Alternately, is there a filter, a sort of inverse verbatim, that will cause the value of a context variable to be rendered?
Updated background
tags_list is created by a fairly sophisticated process involving exec of some user provided text from a table/model field. The relevant portion of the real template looks like this:
{% for graph_row in graph_rows %}
<div class="row">
{% for graph in graph_row %}
<div class="col-md-{{ graph.width }}">
{{ graph.graph }}
</div>
{% endfor %}
</div>
{% endfor %}
The graph values look like this: {'graph':'{% piechart data1 %}', 'width':3}
Note that the order of entries in the context variable graph_rows is significant as is order of graph(s) in the row as that determines the placement of graphs on the page. Preserving this order is essential for the scheme to work correctly.
Currently, the view function simply does an {% include ... %} to get the template segment above to render in the correct order. This approach is simple and clean.
I could, as has been suggested, perform a template render within the view function but that complicates the design a bit and I'd hoped to avoid doing that if there is an easy way to trigger a render of {{ graph.graph }}. Note, as well, by moving the render into the view I loose the ability to easily take the template from arbitrary places, in particular table fields.
One of the great things about Django is the library of solution and code snippets. Sadly, they aren't a well organized and easy to find as one might wish. Nevertheless, a bit of google found a number of solutions of the general form
{% render tag.tag %}
Here are links to several:
render_as_template template tag
Allow template tags in a Flatpage's content
render_as_template.py
I'll use the general approach cleaned up a bit for error checking.
As an aside, the technique strikes me as generally useful and might be appropriate for inclusion in the standard tags.
Update 3/28/2014
After looking at the above and several others this is what I used from render_as_template template tag. There is a useful comment here.
from django import template
from django.template import Template, Variable, TemplateSyntaxError
register = template.Library()
class RenderAsTemplateNode(template.Node):
def __init__(self, item_to_be_rendered):
self.item_to_be_rendered = Variable(item_to_be_rendered)
def render(self, context):
try:
actual_item = self.item_to_be_rendered.resolve(context)
return Template(actual_item).render(context)
except template.VariableDoesNotExist:
return ''
def render_as_template(parser, token):
bits = token.split_contents()
if len(bits) !=2:
raise TemplateSyntaxError("'%s' takes only one argument"
" (a variable representing a template to render)" % bits[0])
return RenderAsTemplateNode(bits[1])
render_as_template = register.tag(render_as_template)
This gets part of the way to a solution. Unfortunately custom template tags, in my case
{% pie_chart %} are not available to render within the class RenderAsTemplateNode.
I've not tested this but it appears that this stack overflow question, Django - replacing built-in templatetag by custom tag for a whole site without {% load .. %}, points the way.
I believe I can provide a way for you to get the results you want, but there might be a better way for you to achieve the desired functionality if you can provide some context.
Anyway, you might do something like this in your view.py:
tags_list = [
Template('{% load my_tags %}{% ' + t.tag + ' %}').render(Context())
for t in tags_list
]

Django: How do I get the number of elements returned in a database call?

This seems to me like a very simple question, but I can't seem to find the answer.
All I need to do is determine the number of objects returned by a database query.
The specific circumstance is this: I have a model named Student. This model has a ManyToManyField member named courses_current, which relates to a table of Course models. When I pass my Student instance to a template, I want to be able to do something like the following (the syntax may not be exact, but you'll get the basic idea):
<div id="classes">
{% if student.classes_current.all.size == 0 %}
<h1> HEY! YOU AREN'T TAKING ANY CLASSES! REGISTER NOW!
{% else %}
Here are your courses:
<!-- ... -->
{% endif %}
</div>
Now, I'm fairly certain that X_set.all.size is not a real thing. In the manage.py shell I can just use len(student.classes_current.all()), but I don't know of any way to use built-in functions, and "dictionary-like objects" don't have .size() functions, so I'm at a loss. I'm sure there's a very simple solution (or at least I hope there is), but I can't seem to find it.
{{ student.classes_current.all.count }} but be warned that it doesn't fetch the objects so you will need to do a separate query if you want to loop over them.
If you need loop over the classes for tag has way to get what you need.
{% for cl in student.current_classes.all %}
{{ cl }}
{% empty %}
<h1>Hey! ...</h1>
{% endfor %}
Documentation https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#for-empty

Django Custom Template Tag with Context Variable Argument

I have a custom template tag which shows a calendar. I want to populate certain items on the calendar based on a dynamic value.
Here's the tag:
#register.inclusion_tag("website/_calendar.html")
def calendar_table(post):
post=int(post)
imp=IMP.objects.filter(post__pk=post)
if imp:
...do stuff
In my template, it works fine when I pass a hard coded value, such as
{% load inclusion_tags %}
{% calendar_table "6" %}
However when I try something like {% calendar_table "{{post.id}}" %} , it raises a error a ValueError for the int() attempt. How can I get around this?
You want {% calendar_table post.id %}; the extra {{ and }} are what are causing you the heartburn.
Note that, in your custom tag, you need to take the string ("post.id") that gets passed and resolve it against the context using Variable.resolve. There's more information on that in the Django docs; in particular, look here: http://docs.djangoproject.com/en/1.3/howto/custom-template-tags/#passing-template-variables-to-the-tag

Django: Add number of results

I'm displaying the number of search results, however, i do more than one search.
So to display the number of results i'd have to add them up.
So i've tried this:
<p>Found {{ products|length + categories|length + companies|length }} results.</p>
But i get an error.
How do i do this?
Django templates do not support arithmetic operators. However you can use the add filter. I think you need something like this:
<p>Found {{ products|length|add:categories|length|add:companies|length }} results.</p>
Alternatively you should calculate the total in the view and pass it pre-calculated to the template.
EDIT: Further to the comments, this version should work:
{% with categories|length as catlen %}
{% with companies|length as complen %}
<p>Found {{ products|length|add:catlen|add:complen }} results.</p>
{% endwith %}
{% endwith %}
However, this feels very hacky and it would be preferable to calculate the figure in the view.
I would do this in your view when you are creating your context dictionary:
'result_count': len(products) + len(categories) + len(companies)
Then, in your template, just use:
<p>Found {{ result_count }} results.</p>
I'd like to point that Van Gale's answer is not optimal.
From the QuerySet API documentation, you should use query.count() rather than len(query)
A count() call performs a SELECT COUNT(*) behind the scenes, so you
should always use count() rather than loading all of the record into
Python objects and calling len() on the result (unless you need to
load the objects into memory anyway, in which case len() will be
faster).
So the answer should be:
In the view:
'result_count': products.count() + categories.count() + companies.count()
The template remains unchanged