I'm trying to be DRY with my Django templates, and have some code that I mix with CSS for a simple hover-over popup. I'd like to reuse the code, but the contents of my popup will be HTML that may well span over multiple lines. Is it possible to stuff multi-line strings into a template variable?
I tried doing something funky with blocks and block.super but that only seems to work when extending (not include)
Here's an example of what I'd like to do. Is it possible?
index.html
<body>
<h2>My Popup</h2>
{% include "snippets/popup.html" with class="p" spantext="Hover me" popupdiv="""
<h2>This is a popup!</h2>
<ul>
<li>Something</li>
<li>Something else</li>
</ul>
"""
%}
</body>
snippets/popup.html
<div class="{{ class }}">
<span class='pointer'>{{ spantext }}</span>
<div class="popup">
{{ popupdiv }}
</div>
</div>
I know it's not possible to have multi-line template tags in Django, but is there any way round this, other than squashing all my div html onto one line, and escaping any quotes?
Cheers
It turns out "Parsing until another template tag" is what I was after. http://www.djangobook.com/en/2.0/chapter09.html
Here's my code:
tags.py (in the templatetags folder)
from django import template
from django.template.loader import get_template
from django.template.base import Node, TemplateSyntaxError
register = template.Library()
class PopupNode(Node):
def __init__(self, nodelist, class_name, spantext):
self.nodelist = nodelist
self.class_name = class_name
self.spantext = spantext
def render(self, context):
popup_html = get_template("ordersystem/snippets/popup.html")
context.update({
'class' : self.class_name,
'spantext' : self.spantext,
'popupdiv' : self.nodelist.render(context)
})
return popup_html.render(context)
#register.tag('popup')
def show_popup(parser, token):
nodelist = parser.parse(('endpopup',))
tokens = token.split_contents()
if len(tokens) != 4:
raise TemplateSyntaxError("show_popup is in the format 'popup with class=X spantext=Y")
try:
context_extras = [t.split("=")[1].strip('"') for t in tokens[2:]]
except IndexError:
raise TemplateSyntaxError("show_popup is in the format 'popup with class=X spantext=Y")
parser.delete_first_token()
return PopupNode(nodelist, *context_extras)
Then within my html file I can just do:
{% popup with class_name=management spantext=Manage %}
<h2>This is a popup!</h2>
<ul>
<li>Something</li>
<li>Something else</li>
</ul>
{% endpoup %}
The best way should be to create a templatetags in your module with an inclusion tag.
https://docs.djangoproject.com/en/dev/howto/custom-template-tags/
So imagine your are YourModule yourModule with a folder templatetags and the file popup_tag.py
yourModule/
---- views.py
---- models.py
---- templates/
---- snippet/
---- popup.html
---- templatetags/
---- __init__.py
---- popup_tag.py
Your popup_tag.py could look like the following lines :
from django import template
register = template.Library()
def show_pop_up(class, span_text, popupdiv):
return {'class': class,
'span_text': span_text,
'pop_up_div': pop_up_div}
register.inclusion_tag('snippet/popup.html')(show_popup)
Then, you just have to call your tag in your template index.html.
{% load popup_tag %}
{% show_popup "class" "span_text" "popupdiv" %}
Related
I have model LandingSnippet that contains attribute ...model = CharField()..., and it is related to context keyword (for example cars in context below)
I have next code in my view
def GeneratedLanding(request):
snippets = LandingSnippet.objects.all().filter(view_on=True).order_by('order')
context = {
'snippets':snippets,
...
'cars':Car.objects.all(), # this is cars
...
return render(request,'qlanding/generateindex.html',{'context':context})
how i can get querySet cars that is in context by keyword cars as a string
for example
{{context}}
prints
{'snippets': <QuerySet [<LandingSnippet: Snippet1Title>, <LandingSnippet: 2 - about - Лучшая служба развозки детей>]>, 'services': <QuerySet []>, 'cars': <QuerySet []>, 'faqs': <QuerySet []>}
and
{{snippet.model}}
prints
cars
QUESTION:
How can i get the {{ context.cars }} ? I think something like context[snippet.model] where snippet.model='cars'
i want push it inside another template when include
{% if snippet.module %}
{% with "qlanding/snippets/module/"|add:snippet.module|add:".html" as path %}
{% include path with model=context[snippet.model] %} # But this is incorect while rendering
{% endwith %}
{% endif %}
you can write a simple template tag like this:
first in your app directory create a directory named templatetags this directory must contains an empty file named __init__.py
create a file with any name in this directory. for example load_from_context
write these code on this file
from django import template
register = template.Library()
#register.tag(name="GetFromContext")
def get_from_context(parser, token):
bits = token.split_contents()
node_list = parser.parse(('endGetFromContext',))
variable = bits[1]
return GetFromContextNode(node_list, variable)
class GetFromContextNode(template.Node):
def __init__(self, node_list, variable):
self.node_list = node_list
self.variable = variable
def render(self, context):
variable_value = template.Variable(self.variable).resolve(context)
with context.push():
context['model'] = context.get(variable_value)
return self.node_list.render(context)
then in your template you can use it like this
{% load load_from_context %}
{# any code in your template #}
{% GetFromContext snippet.model %}
{% include path %}
{% endGetFromContext %}
#vorujack , I get the same error still. but based on your solution I got next.
from Django import template
register = template.Library()
#register.simple_tag
def get_model_from_context(context,model_name):
return context[model_name]
and how I used in view
{% get_model_from_context context=context model_name=snippet.model as model %}
{% include "qlanding/snippets/module/"|add:snippet.module|add:".html" with model=model %}
many thanks for #vorujack
I'm sure the answer is right there and I'm not seeing it. How can I render a RichTextBlock to remove the wrapping <div class="rich-text">?
{% include_block block %} and {{ block.value }} both give the wrapping div.
Unfortunately this is hard-coded and can't currently be overridden - see https://github.com/wagtail/wagtail/issues/1214.
I solved this by creating a custom template tag
In your project create a file in your templatetags directory (e.g. templatetags/wagtailcustom_tags.py) with content along the following.
from django import template
from django.utils.safestring import mark_safe
from wagtail.core.rich_text import RichText, expand_db_html
register = template.Library()
#register.filter
def richtext_withclasses(value, classes):
if isinstance(value, RichText):
html = expand_db_html(value.source)
elif isinstance(value, str):
html = expand_db_html(value)
elif value is None:
html = ""
else:
raise TypeError(
"'richtext_withclasses' template filter received an invalid value; expected string, got {}.".format(
type(value)
)
)
return mark_safe('<div class="' + classes + '">' + html + "</div>")
Then in your templates load the template tag
{% load wagtailcustom_tags %}
and render richtext fields with the custom classes (or no classes at all)
{{ myfield | richtext_withclasses:"my custom class" }}
I'm trying to create my own templatetag. How I've done this:
folder structure:
my_app/
__init__.py
models.py
views.py
my_app/
templates/
show.html
templatetags/
__init__.py
depos.py
depos.py:
# coding: utf-8
from django import template
from core.models import Depos
register = template.Library()
#register.inclusion_tag('show.html')
def show_dep():
dep = Depos.objects.all().order_by('?')[0]
return dep
show.html:
<div id="user_testimonial">
<blockquote>
<p>{{ dep.dep }}</p>
<cite>{{ dep.name }}, {{ dep.from }}</cite>
</blockquote>
</div>
in my templates:
{% load depos %}
{% show_dep %}
but I've got this error:
TypeError at /cadastro
'Depos' object does not support item assignment
You need to be passing a dictionary object from your inclusion tag to your inclusion tag template. It's mentioned in the docs:
First, define the function that takes the argument and produces a dictionary of data for the result. The important point here is we only need to return a dictionary, not anything more complex.
so try:
#register.inclusion_tag('show.html')
def show_dep():
return {
'dep' : Depos.objects.all().order_by('?')[0]
}
I need to implement a template tag that will return a string with a collection of items from an object.
I had created the following structure:
produtos/
templatetags/
__init__.py
produto_tags.py
produto_tags.py:
# -*- coding: utf-8 -*-
from django import template
from django.template import Node
from produto.models import Produto
from django.template.loader import render_to_string
register = template.Library()
#register.tag
def get_all_tags(parser, token):
args = token.split_contents()
return ProdutoTemplateNode(args[1])
class ProdutoTemplateNode(Node):
def __init__(self, produto):
self.produto = produto
def render(self, context):
list = []
produto = template.Variable(self.produto).resolve(context)
tags = produto.tags.all()
if tags:
for tag in tags:
list.append(tag.name)
return ", ".join(list)
else:
return u'Não existem tags para este produto'
Template:
{% load produto_tags %}
...
{% for produto in produtos %}
<li id="{{ produto.ordenacao }}" data-tags="{% get_all_tags produto %}">
...
</li>
{% endfor %}
</ul>
{% else %}
<p>Não existem produtos cadastrados no sistema</p>
{% endif %}
I am receiving this error:
TemplateSyntaxError at /concrete/nossos-sites.html
Invalid block tag: 'get_all_tags', expected 'empty' or 'endfor'
I read other threads where people said this error occurs if the Tag does not exist and it seems to be the case. I've been looking on the djangoproject.com documentation as well and I could not find any clue about what might be happening.
Thanks!
Template tag files need to be inside a directory called templatetags inside your app.
Follow Daniel and Ignacio's suggestions first. Also, its weird that you have {% load produto_tags %} in the top of the template but got an invalid block error: if produto_tags cannot be loaded, the error should be something like 'produto_tags is not a valid tag' blahblah. Could you please check the code and path structure you posted, again?
That was tricky, even though simple:
First, there was another 'produto_tags.py' in another folder elsewhere in the project:
project/
common/
templatetags/
produtos_tags.py
produtos/
templatetags/
produtos_tags.py
So, at first I have moved all code from produtos/templatetags/ to common/templatetags/. But when I did it Django started whining about not finding the produtos_tags from produtos. Afterwards I got the code back to produtos/templatetags/ and renamed the file to tags_produtos.py, what had worked to show the easy part that is my wrong import below:
Wrong:
from produto.models import Produto
Correct:
from produtos.models import Produto
Use {{ produto | get_all_tags }} instead.
The {% ... %} syntax is only valid for block tags such as for.
Hope that helps.
I'm trying to use WTForms with Django & a MongoEngine/MongoDB database backend. The forms are outputting properly, but I can't for the life of me get the labels to show up.
Here is my template code:
{% load wtforms %}
<form>
{% for f in form %}
{{ f.label }}: {% form_field f %}<br/>
{% endfor %}
</form>
This is what I am passing in the view:
form = StrandForm()
return render_to_response('create_strand.html', locals(), context_instance = RequestContext(request))
The StrandForm class I have tried both creating from the WTForm mongoengine extension's model_form class, and from WTForm's Form class. The label exists in the view, I can print it to the console and it shows the rendered form label, but somehow it gets lost when transferring to the template. Am I doing something wrong?
Django 1.4 has a new feature: do_not_call_in_templates attribute.
If you set it on wtforms.Field class, every child class inherits and all fields will work fine in django templates.
import wtforms
wtforms.Field.do_not_call_in_templates = True
Now following code works as expected:
{% load wtforms %}
{{ f.label }}: {% form_field f %}
I encountered the same problem today. It has to do with the way WTForms is programmed so that it will work with many different templating libraries. Django 1.3 will only see f as it's HTML string even though it has other attributes.
In order to fix this you must add a template tag to retrieve the attribute.
Add the following to your projects hierarchy:
templatetags
templatetags / init.py
templatetags / templatetags
templatetags / templatetags / init.py
templatetags / templatetags / getattribute.py
Then in your settings.py file, add the following line to INSTALLED_APPS
'templatetags',
Open up getattribute.py and paste the following code:
from django import template
from django.conf import settings
register = template.Library()
#register.tag
def getattribute(parser, token):
try:
tag_name, tag_object, tag_function = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError("%r tag requires two arguments" % token.contents.split()[0])
return getattrNode(tag_object, tag_function)
class getattrNode(template.Node):
def __init__(self, tag_object, tag_function):
self.tag_object = tag_object
self.tag_function = tag_function
def render(self, context):
return getattr(context[self.tag_object], self.tag_function)()
This will allow you to use the follow code whenever you're inside a template and need an attribute that won't show up:
{% load getattribute %}
{% getattribute OBJECT ATTRIBUTE %}
In your case:
{% getattribute f label %}
Hope that helped!