Single View with Dynamic template - python

I've got modules that if enabled/disabled would require a different arrangement of tables and iframes on the frontend webpage. In order to do this I set up a function to check which modules are enabled/disabled and assigned a number to each 'set' and will be created separate templates for each "set1.html", "set2.html", "set3.html", etc...
I'd like to utilize one single view where I can pass the set number from from function I created but I can't seem to figure out how.
def homeset(request):
return render(request, 'app/set1.html', {})
Looking to figure out some way to make the "1" the return of the function I created to determine which set# to load as template and would prefer to not have to create a view for every single template needed.

Just taking a stab at this, do you have a problem with setting the template name before rendering?
def homeset(request):
# define 'my_set'
# define num
...
my_template_name = None
if num in my_set:
my_template_name = 'app/set{}.html'.format(num)
return render(request, my_template_name)

Looks like Scott Skiles answered your question, but an alternative way is to use a single base template and the "include" templatetag.
You can pass the set number to the template and use the "if" templatetag" to include the content from appropriate template(s) for each set number.
{% if set_num == 1 %}
{% include "foo/bar.html" %}
{% elif set_num == 2 %}
{% include "foo/two.html" %}
{% endif %}

Related

How can I set a (non-global) variable in a jinja context function?

I want to set variable values, but only when they are not already assigned and within a local context.
So there is a solution to this:
{% with x=( x | default(1)) %}
{{ x }}
{% endwith %}
{% with x=2 %}
{% with x=( x | default(1)) %}
{{ x }}
{% endwith %}
{% endwith %}
This works nicely but it is a lot of text. I have many situations where I don't have just one but up to 20 variables that are being set and then a macro is called, or a template is included with those values.
Writing all those default conditions is just a mess and provokes mistakes. So I would love to be able to set a value on the current context in e.g. in a context function. But if I try the following:
#contextfunction
def defaults(ctx, **vals):
for k,v in vals.iteritems():
if k not in ctx:
ctx[k] = v
I get the an exception:
TypeError: 'Context' object does not support item assignment
And trying to set a value on ctx.vars would not help either:
vars
The template local variables. This list contains environment and context functions from the parent scope as well as local
modifications and exported variables from the template. The template
will modify this dict during template evaluation but filters and
context functions are not allowed to modify it.
http://jinja.pocoo.org/docs/2.9/api/#jinja2.Context.vars
I tried with
#contextfunction
def defaults(ctx, **vals):
for k,v in vals.iteritems():
if k not in ctx.vars:
ctx.vars[k] = v
And it gives no exception but just seems to not assign the value to the context.
I know I could write to the global context but that's not what I would like to do as it would produce side effects.
Is there a possibility to get just the current context and set a value on it? I didn't find any instructions on it and how this could be done and I did not really grasp that from reading the jinja source.
I found a solution, somehow I keep solving my own problems. It is not exactly an answer to the question of "How can I set a (non-global) variable in a jinja context function?", but it solves the problem.
I have written a jinja extension, that allows for a simple "default" tag:
from jinja2 import nodes
from jinja2.ext import Extension
"""
DefaultExtension
~~~~~~~~~~~~~~~~
Very simple jinja extension that allows for the following
{% set x=(x | default(1)) %}
{% set y=(y | default(2)) %}
to be written as
{% default x=1, y=2 %}
:copyright: (c) 2017 by the Roman Seidl
:license: BSD
"""
class DefaultExtension(Extension):
# a set of names that trigger the extension.
tags = set(['default'])
def parse(self, parser):
#just dump the tag
lineno = next(parser.stream).lineno
#parse through assignments (similar to parser.parse_with)
assignments = []
while parser.stream.current.type != 'block_end':
lineno = parser.stream.current.lineno
if assignments:
parser.stream.expect('comma')
target = parser.parse_assign_target()
parser.stream.expect('assign')
expr = (parser.parse_expression())
#consruct a 'default' filter
filter = nodes.Filter(nodes.Name(target.name, 'load'), 'default', [expr], [], None, None, lineno=lineno)
#produce an assignment with this filter as value
assignment = nodes.Assign(target, filter, lineno=lineno)
assignments.append(assignment)
return assignments
I just had to add it to my app:
app.jinja_env.add_extension(DefaultExtension)
and it works quite nicely, though I must confess I haven't yet tested it very thoroughly.
Anyone think I should submit this to jinja?

how can i get all required variables and attributes in a jinja2 templates to render

I am trying to build a configuration generator for myself using jinja2. I need to know what are the expected variables and their keys before trying to render template to provide a sample file to fill before template rendering. Required parameters will change based on selected configuration type and used templates. I don't want to store all required parameters because templates will be changed time to time and also new templates will be added. There will be 50ish parameter per template
here is a sample template file
{{data.config1[0].field1}}
{{data.config2[0].field3}}
firt level change test
{% for row in data.config1 %}
{% if row.field3=='1' %}
something {{row.field1}} {{row.field2}}
{% else %}
something {{row.field1}}
{% endif %}
{% endfor %}
footer thingy
{{data.config2[0].field5}}
i tried to use find_undeclared_variables from jinja2.meta package
here is my sample code
import os
from jinja2 import Environment, FileSystemLoader,meta
template_filename = 'change.txt'
PATH = os.path.dirname(os.path.abspath(__file__))
TEMPLATE_ENVIRONMENT = Environment(
autoescape=False,
loader=FileSystemLoader(os.path.join(PATH)),
trim_blocks=False)
template_source =TEMPLATE_ENVIRONMENT.loader.get_source(TEMPLATE_ENVIRONMENT, template_filename)[0]
parsed_content = TEMPLATE_ENVIRONMENT.parse(template_source)
variables= meta.find_undeclared_variables(parsed_content)
print (variables)
this is what i can get
{'data'}
my desired output something like this.
{'config1':['field1','field2','field3'], 'config2':['field3','field5']}
please suggest.
I have used, Jinja2schema to generate expexted output.
There exists the
jinja2.meta.find_undeclared_variables function that may help you:
https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.meta.find_undeclared_variables

In a jinja2 template, how do i check if an element in a mongo database is one of three possible strings?

My template has if/else functionality to handle displaying buttons on a page, I've managed to add an elif statement that displays the buttons only if an element in a mongo database is not one of three values. I'm trying to clean this up as the elif appears four times across two jinja2 templates. My code which works now looks like this:
{% elif (
('string1' not in database.category) and ('string2' not in database.category) and
('string3' not in database.category)) %}
I've tried declaring a tuple and doing a:
{% set hidebuttons = ['string1' , 'string2' , 'string3'] %} //sets tuple
{% elif not database.category in hidebuttons %} // checks if value not in tuple
and plenty of other methods to make this work but every time I reload the web page i keep seeing the buttons when i shouldn't be. Any advice is greatly appreciated.
I believe this should be in your view, not your template. If you need to do the query in more then one place, you can create a utility function that gets the results you need, then in your views, you can call the function and pass the data as context to your templates
if you're using a view_config decorator to render template, it could be something like:
#view_config(renderer='templates/foo.pt')
def my_view(request):
show_buttons = logic_to_determine_should_buttons_be_shown()
return {'show_buttons': show_buttons }
# your template
{% if show_buttons %}
show buttons
{% endif %}

Django remove leading zero

In a Django template how do I remove the first Zero from all the this mobile numbers I'm displaying. In Python I have tried this but with no success....
{% for object in data %}
{{ object.mobile.lstrip('0') }}
{% endfor %}
views.py
def sms(request, incentive):
objectQuerySet = Recipient.objects.filter(incentiveid=incentive)
context = {'data':objectQuerySet}
return render_to_response('smssend.html', context, context_instance=RequestContext(request))
There are many ways to do that.
write custom template filter
filter user input and store phones with leading zeros already stripped
create a custom object #property returning mobile with zeroes stripped (this one a little bit dirty)
do cleanup before rendering template
I assume you will need to use similar logic in other locations of your application so why not just add a method to your model to clean the data? The code below assumes your mobile field is a string.
class Recipient(models.Model):
...
def mobile_no_zero(self):
return self.mobile[1:] if self.mobile.startswith('0') else self.mobile
And then you can call the method from your template:
{% for object in data %}
{{ object.mobile_no_zero }}
{% endfor %}
You should pass your data to your template in the correct format. Django templates are not "Python interspersed with HTML" like PHP is.
For example, when you first pass your data in your view, you should make sure that it is an iterable with the zeroes already stripped.
There is no lstrip in the Django template mini-language.
Edit: If you know that the first digit will always be a 0, you can also do this:
{{ object|slice:"1:" }}
There is one method that does work (but is more of a kludge that anything and removes 0 or more 0s) - and only if the number can be converted to a float to start with (ie, no spaces)
In [23]: t = Template('{{ value|floatformat:0 }}')
In [24]: t.render(Context({'value': '00123'}))
Out[24]: u'123'
You can use cut ( it will cut all zeros from number )
or you can use
phone2numeric
Imo its beter opption

How to pluralize a name in a template with jinja2?

If I have a template variable called num_countries, to pluralize with Django I could just write something like this:
countr{{ num_countries|pluralize:"y,ies" }}
Is there a way to do something like this with jinja2? (I do know this doesn't work in jinja2) What's the jinja2 alternative to this?
Thanks for any tip!
Guy Adini's reply is definitely the way to go, though I think (or maybe I misused it) it is not exactly the same as pluralize filter in Django.
Hence this was my implementation (using decorator to register)
#app.template_filter('pluralize')
def pluralize(number, singular = '', plural = 's'):
if number == 1:
return singular
else:
return plural
This way, it is used exactly the same way (well, with parameters being passed in a slightly different way):
countr{{ num_countries|pluralize:("y","ies") }}
Current Jinja versions have the i18n extension which adds decent translation and pluralization tags:
{% trans count=list|length %}
There is {{ count }} {{ name }} object.
{% pluralize %}
There are {{ count }} {{ name }} objects.
{% endtrans %}
You can use this even if you don't actually have multiple language versions - and if you ever add other languages you'll have a decent base which requires no changes (not all languages pluralize by adding an 's' and some even have multiple plural forms).
According to Jinja's documentation, there is no built in filter which does what you want. You can easily design a custom filter to do that, however:
def my_plural(str, end_ptr = None, rep_ptr = ""):
if end_ptr and str.endswith(end_ptr):
return str[:-1*len(end_ptr)]+rep_ptr
else:
return str+'s'
and then register it in your environment:
environment.filters['myplural'] = my_plural
You can now use my_plural as a Jinja template.
You also want to check if the word is already plural. Here is my solution:
def pluralize(text):
if text[-1:] !='s':
return text+'s'
else:
return text
Then register the tag to your environment (this can be applied to the Django templating engine too).

Categories

Resources