Combining constant and variable value for template default value - django

I'm setting up a template in which I'd like the default value to be a combination of a constant string and a variable id value. The desired HTML output would be something like:
<span id="id1234" class="foo">
Click here to view image.
</span>
In the template code for the span, I would like something like:
<span id="{{ spanid|default:'id'object.id }}" class="foo">
Similarly, the a tag would use:
<a href="/images/{{ image_file|default:'img'object.id'.jpg'"> here to view...
This doesn't work, is there a way to do this within the syntax of django templates and the default filter?

You can't. However, default is merely a shortcut. In this scenario, the shortcut doesn't work, but the longer form will allow you do what you need:
{% if image_file %}{{ image_file }}{% else %}img{{ object.id }}.jpg{% endif %}

#Chris Pratt's answer is a nice workaround, but it's not accurate that you can't achieve this with the default filter.
In my project, I just used the "add" filter to concatenate a string onto a variable value as follows:
{{ instance.image.url|default:STATIC_URL|add:"img/icons/my-image.svg" }}
UPDATE: I apologize. Upon more thorough testing, I discovered that the solution I presented here doesn't actually work. I thought that the "add" filter would apply to the default value, but it ends up being applied to whatever value the first part of the expression resolves to.
So, if there is a value at {{ instance.image.url }} then "img/icons/my-image.svg" gets concatenated that, which breaks it.

Related

Template tag as filter value in Django

I have defined one custom tag which is working fine in templates.
Like
{% get_setting "DATE_FORMAT_UI" %}
Above statement is returning correct value in template.
Now i want to use the same in a filter like this -
{{extra_info.to_date|date: '{% get_setting "DATE_FORMAT_UI" %}' }}
But this is giving error in parsing.
I tried in different ways of using quotes for the {% get_setting "DATE_FORMAT_UI" %}
But every time bad luck.
So could any body help me in solving this. I want to pass date format in filter . That date format is saved into config file. but how to pass that value dynamically in filter.
The trick is to first assign this to a variable (here myformat), and then use that variable:
{% get_setting 'DATE_FORMAT_UI' as myformat %}
{{extra_info.to_date|date:myformat }}

Django templates: optional variable in a with

This might be a pretty simple question; but I can't seem to find the 'correct' approach.
I have a template, in which I include a different template three times. I pass quite a few variables along with the include. The problem is that one of them is optional.
{% with
title="Add object"
type="function"
function_type="FUNCTION"
inputs=function_create_form
project_id=project.id
parent_function_id=root_function.id
{% if root_function.type == 'OBJECT' %}
parent_function_id=root_function.id
{% endif %}
%}
{% include "./popup-form.html" %}
{% endwith %}
(I'm aware that this should be on one line in the template, for some reason. But this is way more readable.)
The gist of it is that I only want to pass the variable in the if statement, if it is of the correct type. It will always have a value, but not always the correct one. Sadly though, Django doesn't seem to tolerate if statements in with's.
To clarify, the variable should only be passed to the template if it is of the correct type. The included template is a popup form used to add objects. These objects have different types; ROOM, OBJECT, and FUNCTION. These objects can in turn have a parent object, which is exactly what I want to pass. But this parent should only be passed to the popup form if the user is viewing the correct object type. This is determined by the if statement
I also tried to do this, same result:
{% include "./popup-form.html" with
title="Add object"
type="function"
function_type="OBJECT"
inputs=function_create_form
project_id=project.id
{% if root_function.type == 'OBJECT' %}
parent_function_id=root_function.id
{% endif %}
%}
Error: 'with' received an invalid token: '{%'
I could perhaps make an if statement outside of the with and set the value of the variable to an empty string. But there ought to be a better way, right? Doing this in the view is not an option, since the variable will always need to have a value.
The complexity of this statement should be a clue that this is the wrong approach.
If you want to pass multiple variables to an included template, you should probably be looking at an inclusion tag instead. You can then use any combination of positional and keyword arguments to render the template.

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
]

Link to Current Page in Django, with Additional GET Params?

How do I place an HTML link what refers to the current page, and which adds additional GET parameters (or overwriting existing ones, if they already exist)?
Right now I have something like:
My Link
Currently, request is passed to the page. If request.path is https://stackoverflow.com/, then the resulting link becomes https://stackoverflow.com/?Key=Value
The problem?
But of course, if the current URL is https://stackoverflow.com/?PrevKey=PrevValue then it becomes:
https://stackoverflow.com/?PrevKey=PrevValue?Key=Value
Notice the incorrect second question mark -- it should in fact be:
https://stackoverflow.com/?PrevKey=PrevValue&Key=Value
Furthermore, if there is already a key with the same name, then instead of overwriting it, my current solution ignores it -- which is wrong.
How do I solve these two problems?
You'll need a custom tag. There are a couple on djangosnippets - this one looks pretty comprehensive.
For anyone seeing this in future: https://bitbucket.org/monwara/django-url-tools
You can use: {{ request.get_full_path }} to get the current path with additional parameters.
I'm just a beginner with Django, so you'll have to take my response with a grain of salt.
Firstly, request.path should return the path to the page without the domain, according to the documentation. So if the request is for http://example.com/, request.path should be just "/". Correct me if I'm wrong.
But that's not relevant. More importantly, request.path won't have any query parameters, so if the page requested is http://example.com/?PrevKey=PrevValue, then request.path will still be "/". If you want to get the query parameters, you have to make use of the dictionary-like access of the GET and POST properties (in this case GET) of the request object. Or better yet, access them through the QueryDict methods.
What I would do here, and this is by no means the best method nor code, is to prepare a custom template filter in which you pass the current request object and the key-value pair to test against.
This is how your template code would look. Note that you can still hard code the key value pair, although here it is formatted as a string with "key colon value". The filter function can handle (if you need to) more than just one set of key-value pairs.
My Link
The filter function:
from urllib import urlencode
def addQueryParameter(request, parameter):
# parse the key-value pair(s) in parameter (which looks like a JSON string)
# then add to dictionary d
# note i am not taking into account white-space.
for param in string.split(','):
key, value = param.split(':', 1)
d[key] = value
# check if any keys in d are already in the request query parameters
# if so, delete from d. If I misunderstood your question, then refactor
# to delete from request.GET instead:
for key in d.keys():
if key in request.GET.keys():
del d[key]
# add the keys in request.GET into d:
d.extend(request.GET.items())
# convert dictionary of key value pairs into a query string for urls,
# prepend a ?
queryString = "?%s" % urlencode(d)
return "%s%s" % (request.path, queryString if queryString else "")
I should point out that if the request is for the page http://example.com/login/?prev=news and your template looks like
My Link
Then the output would be (hopefully, if it works):
<a href="/login/?prev=news&goto=dashboard&feature=products">
My Link</a>
<!-- the order of the above parameters may differ -->
Note that there is no domain (i.e. the http://example.com/ part) in this link. That is because the filter doesn't add it. But you can modify it to do so.
I'll leave it to you to register this template filter. More here. Hope this helps.
A JavaScript solution is much suitable in this case.
Example :
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a page_number="1">First Page</a>
<a page_number="{{ page_obj.previous_page_number }}">Last Page</a>
{% endif %}
{% if page_obj.has_previous or page_obj.has_next %}
<span class="current">{{ page_obj.number }}/{{ page_obj.paginator.num_pages }}</span>
{% endif %}
{% if page_obj.has_next %}
<a page_number="{{ page_obj.next_page_number }}">Next Page</a>
<a page_number="{{ page_obj.paginator.num_pages }}">End Page</a>
{% endif %}
</span>
<script type="text/javascript">
document.querySelectorAll('.pagination a').forEach(link => {
let page_number = link.getAttribute('page_number');
const gotopageUrl = new URL(location.href);
gotopageUrl.searchParams.set("page", page_number);
link.href = gotopageUrl.href;
});
</script>
</div>

need help figuring out dynamic menu generation in django

I need to dynamically generate a code like this in the resulting html:
<p>>> gallery one</p>
<p>gallery two</p>
<p>about the author</p>
<p>our news</p>
I do have menu_code string variable created in views.py (it is generated depending on an item number of the current page passed — 1 in the case above), which contains that long string with the code shown above. It is (well, supposed to) passed by locals() into the html template (all other variables are passed that way successfully):
return render_to_response('gallery_page.html', locals())
I have this:
{% include menu_code %}
inside the template html. But instead of being interpreted as code it is just shown as text in the browser.
What am I doing wrong? How to make it work as a dynamically generated menu?
Turned out it's necessary to surround the line in a template with autoescape like that:
{% autoescape off %}
{{ menu_code }}
{% endautoescape %}