Django templates: optional variable in a with - django

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.

Related

How to assign value to a variable on Django template through jQuery

I have a Django template file working with a passed value like the below.
{% include 'boutique/rating.html' with score=[I want to put value here] %}
When I usually put value into the template, I could easily do it by doing like the below.
{% for store in stores %}
{% include 'boutique/rating.html' with score=store.review_score %}
{% endfor %}
However, as I get into more complex templates, I need to assign the value to the score parameter in the include section through jQuery. Is there a way that I can acheive this through jQuery?
What you need to understand is this: The part of the template between {% and %} is interpreted by Django. It is processed entirely on the server. In other words, it never shows up in the browser, but gets replaced by some sort of standard HTML.
On the other hand, jQuery is a Javascript library and operates entirely in the browser -- it doesn't know anything about the server or Django.
So, to modify the included template with jQuery, you have to find out what HTML it renders to. You can probably do that by looking at the included template file. Then, treat that HTML the way you would any other part of the page for manipulation with jQuery.

How django's template tag "include" work with None

I'm using an include tag in my templates like this:
{% include fragment_variable %}
where fragment_variable is a context variable that might not exist. I wonder if it will blow up when fragment_variable is not in context variables or is None.
NOTE: actually I tested this code in two different environments (both using Django 1.7) and got two different results (one blew up with some stack trace for templates lookups and the other just failed silently). So I'm curious if there's a setting in django that controls how template rendering behaves when "include" tag can't find a valid template.
{% if fragment_variable %}
{% include fragment_variable %}
{% else %}
<!-- something else -->
{% endif %}
Edit:
Since you are using django version prior to 1.8, take a look at the setting TEMPLATE_STRING_IF_INVALID, it sets the default value for invalid variables.
Also take a look at How invalid variables are handled:
Generally, if a variable doesn’t exist, the template system inserts
the value of the engine’s string_if_invalid configuration option,
which is set to '' (the empty string) by default.
This behavior is slightly different for the if, for and regroup
template tags. If an invalid variable is provided to one of these
template tags, the variable will be interpreted as None. Filters are
always applied to invalid variables within these template tags.
For that matter, I still consider using if is the best exercise.

django how to store an object in session to use in multiple templates

Im working with django and i would like to store a object (in session?) so i can use this in multiple templates. So similar to the "user" that is always accessible, id like to add one of my own. So i dont have to add it every time in render(request,
What i try so far:
def login_character(request, character_name):
request.session['character'] = Character.objects.get(name=character_name)
return HttpResponseRedirect(reverse('index'))
Template:
{% if 'character' in request.session %}
<p>Jeej there is some character</p>
{{ request.session.character.name }}
{% else %}
<p>Nope, nothing here</p>
{% endif %}
But that doesn't seem to work,
Can someone help me out or point in the right direction?
With kind regards,
Hans
I think you meant
{% if 'character' in request.session %}
instead of
{% if 'character' in request.session['character'] %}
Also, you wrote that you need sessions, because you want an object to be always accessible, without a need to explicitly add it in every view. In this case I think a template context processor would probably be a better choice.
Update: You also need to make sure that django.core.context_processors.request is among template context processors in your settings file. See also this answer.

Django tag better way to resolve string with variable

I have a tag that has a variable that is a string with an embeded variable. Is there a better way to evaluate it than:
{% custom_extends "this_{{is.a.test}}" %}
-- backend
string_with_variable = "this_{{is.a.test}}"
result = Template(string_with_variable).render(context)
This works, but i wasn't sure if there was any lower level function that does the same.
I guess I'm a little dense this morning. Is the problem getting some_custom to accept a param that has an embedded variable reference in it? Or is it that you want the param to some_custom to be resolved before it is passed (which is a more general situation)?
If it's the latter case, checkout the Expr templatetag. With that you can do something like:
{% expr "this_"+is.a.test as some_string %}
{% some_custom some_string param %}
Update for comment:
Since this is for {% extends %}, which, as you correctly point out, needs to be the very first line in the file, simply set a variable in your view, e.g. my_custom_template = "this_"+is.a.test+".html" and pass it in with the context. Then in your template you can say {% custom_extends my_custom_template %} and you're good to go.
Of course, this assumes that your custom_extends tag does variable resolution like the normal extends tag does, but that's an easy thing to add to your code. See django/template/loader_tags.py, ExtendsNode.get_parent() for how the core code does it.
The answer is no. As of right now if you want to resolve/render a string you need to instantiate a Template and call render with your context.
result = Template("this_{{is.a.test}}").render(context)

Is there any reason why you can't compare a template filter to a string in Django template syntax?

I'm trying to do this, and failing. Is there any reason why it wouldn't work in Django template syntax? I'm using the latest version of Django.
{% ifequal entry.created_at|timesince "0 minutes" %}
It doesn't work because it's not supposed to work. What you're asking for is not part of the template language.
You can't apply a filter in the middle of tag like {% ifequal. When a template tag uses a variable, it doesn't expect an expression, it expects a variable, nothing more.
That kind of logic -- extracting time since, comparing, etc. -- is what you're supposed to do in your view function.
Your view function then puts a "zerominutes" item in the context for the template to use. Templates just can't do much processing.
They're designed to do the minimum required to render HTML. Everything else needs to be in your view function.
{% ifequal %} tag doesn't support filter expressions as arguments. It treats whole entry.created_at|timesince as identifier of the variable.
Quik workaround: introduce intermediate variable with expresion result using {% with %} like this:
{% with entry.created_at|timesince as delta %}
{% ifequal delta "0 minutes" %}
....
{% endifequal %}
{% endwith %}
See ticket #5756 and links in its comments for more information. A patch for Django in ticket #7295 implements this functionality. A broader refactoring of the template system based on #7295 is proposed in ticket #7806, and it would fix this issue among others.
I don't think making such comparisons to work would be against the design philosophy of Django templates.
I think you can, though I don't see any uses of it my code base. Maybe entry.created_at|timesince isn't producing the value you expect. Try putting
X{{entry.created_at|timesince}}X
into your template and seeing what value it produces. The X's are so you can see leading or trailing space, in case that is the problem.
I finally gave up on using the Django template language for anything other than the simplest pages. Check out Jinja2 for an almost-syntax-compatible alternative. And yes, you can choose which to use on a page-by-page basis.