How can I write a tag "copyblock" for Django templates?
For such a function:
<title> {% block title %} some title... {% endblock %} </title>
<h1>{% copyblock title %}</h1>
Take a look at the solutions mentioned in this question:
How to repeat a "block" in a django template
Django's template parser doesn't expose blocks by name. Instead, they are organized into a tree structure in the Django Template's nodelist, with rendering pushing and popping on the stack of template nodes. You'll have a nearly impossible time accessing them in the way your example indicates.
The SO link that ars references provides suggestions on the best solutions. Of those solutions, defining a variable in the context (ie: {{ title }} in the spirit of your example) that can be reused is probably the most straightforward and maintainable approach. If the piece you want to duplicate goes beyond a simple variable, a custom template tag is probably the most appealing option.
Related
I have been working a lot with Django's template language lately, and it works well in most cases, but recently I've come across a small problem it hasn't been able to solve.
I'm iterating over a bunch of database objects, and building a button in the template from each of them. Something like this:
{% for item in items %}
<button id="{{ item.action.id }}">
{{ item.action.text }}
</button>
{% endfor %}
This works fine, but my Action class is inheritable, and may have a different button structure. Say I wanted to sometimes have a javascript function attached as well. My first though was well, let's just create a render method in my class, and then call that in my template.
class ScriptAction(Action):
def render(self):
return '''
<button id="{}" onclick={}>
{}
</button>'''.format(self.id, self.func, self.text)
Because no the template is much more elegant, and doesn't rely on a fixed button structure:
{% for item in items %}
{{ item.action.render|safe }}
{% endfor %}
And now for my actual problem: what if this rendered string needs further processing by django?
Maybe render method returns a string like
<button id="action-button-id" onclick="console.log('clicked')">
{{ item.name }}
</button>
Currently, {{ item.name }} will not be rendered in the template loop. Is there a way of getting around this?
Perhaps I making this more complicated than it should be, but I've looked through the Django built-in tags and filters, even looked at writing you own parser. But at this point something inside me told me that I was being obsessive. This is actually just a small problem, and would require a fair amount of time, so -
is this more effort than it is worth?
is writing complex parsers like this secure?
is the Django template language even able to do something like this?
If you could hone in on some of these points, I would really appreciate it. This problem is really bugging me.
Thanks in advance!
I'd look into the simple_tag option for building a custom template tag, and have the tag:
Take the model instance as its argument (plus context!)
Ask the model instance for a snippet of template code to use in rendering it
Use the raw template API to render that template snippet
Return the result, optionally marked safe if you know it is safe.
I'm creating a FAQ on my website. It's a page with lots of Q and A. Each Q/A block can be complex HTML (not simple text).
I'd like to avoid to repeat the same HTML code for Q and A structure.
That's why I was thinking of creating a q-and-a.html and including the file as many time as questions I have, doing somethink like:
<div class="my-faq">
{% include q-and-a.html with question="What is life?" answer="It's complicated, son." %}
{% include q-and-a.html with question="Ping?" answer="Pong!" %}
<!-- Lots of other question/answer here -->
</div>
But in that case, I'll need to pass HTML, not texte. AFAIK, the {% include ... with %} tag does not allow me to transfer HTML to the included file. Even if it was possible, it would be hard to maintain.
Then, I was thinking the {% extends %} tag is probably a best choice regarding my need. But actually, this would work if I have 1 question only since the extends tags can be put one time per template.
My case is pretty simple so I guess there is a simple solution, but I'm a bit lost with the include/extends logic to follow here.
Thanks for your help.
When incorporating a third-party django app, I typically want it to be integrated aesthetically with the rest of my django project. While this is typically a matter of overriding the apps 'base.html' (if that), we all structure our templates a little differently, so incompatibilities often arise. For instance, suppose an app defines {% block footer %} and and makes use of it for a variety of things throughout its templates. If I am already using {% block footer %} for, say, a nav bar or copyright info, I don't want the app's templates to override my block.
A simpler, related case would be using different block names for the same thing. For instance, {% block extra-head %} versus {% block extrahead %}.
What's the best way of resolving these sorts of situations? Ideally, it would be nice to remap blocks, so you could do things like "put the child's {% block footer %} in the parent's {% block content-footer %}". Is there any way to get close to that? Or is the only solution to simply override every conflicting template?
First, the html inheritance should go:
my-sitebase.html
|-- app-base.html
|-- app-foo-template.html
I think this is what you meant, but the wording was a bit ambiguous. You might be able to just edit the app-base.html.
Second, a reusable app that overrides something like {% block footer %} is almost willfully causing trouble for anyone who uses it -- you should flag that in the provider's issue tracker.
If the app does do anything with {% block footer %} that should be done in the app-base.html area so you only have to change it once when integrating it with your site.
Finally, a recursive find-replace is actually very little effort. If you don't use an IDE that allows this, Text-Crawler is free, lightning fast, and a good non-IDE solution.
There have been a couple of attempts to create a standard inheritance pattern, I put together one that I like for the templates at djangoslingshot.com and I've seen one other -- but so far there hasn't been any coalescence around a standard. Likely because find-replace is actually a very low cost for the benefit of being able to do exactly what you want without someone else's rules getting in your way.
I've been getting lots of answers from stackoverflow now that I'm in Django just by searching. Now I hope my question will also create some value for everybody.
In choosing Django, I was hoping there was some similar mechanism to the way you can do partials in ROR. This was going to help me in two ways. One was in generating repeating indexed forms or form elements, and also in rendering only a piece of the page on the round trip.
I've done a little bit of that by using taconite with a simple URL click but now I'm trying to get more advanced. This will focus on the form issue which boils down to how to iterate over a secondary object.
If I have a list of photo instances, each of which has a couple of parameters, let's say a size and a quantity. I want to generate form elements for each photo instance separately. But then I have two lists I want to iterate on at the same time.
Context:
photos : Photo.objects.all()
and
forms = {}
for photo in photos:
forms[photo.id] = PhotoForm()
In other words we've got a list of photo objects and a dict of forms based on the photo.id.
Here's an abstraction of the template:
{% for photo in photos %}
{% include "photoview.html" %}
{% comment %}
So here I want to use the photo.id as an index to get the correct form. So that each photo has its own form. I would want to have a different action and each form field would be unique. Is that possible? How can I iterate on that? Thanks!
Would it be a with? In Python of course it's form = forms[photo.id] but here?
{% endcomment %}
Quantity: {{ oi.quantity }} {{ form.quantity }}
Dimensions: {{ oi.size }} {{ form.size }}
{% endfor %}
What can I do about this simple case. And how can I make it where every control is automatically updating the server instead of using a form at all?
Thanks!
James
I'm not sure I understand your question, but here's what I think you want, expressed in pseudo-code:
for photo in photos:
form = forms[photo.id]
generate_some_html(photo, form)
Probably the best way to achieve this is with inclusion tags: http://docs.djangoproject.com/en/dev/howto/custom-template-tags/#inclusion-tags . Basically, it's a custom tag that relies on another template, similar to an RoR partial. The details can be found in the link I provided, but what you basically need to do is
create a package names "templatetags" in one of your app directories
create a module in that package. Let's take foo_tags.py for example.
Add the following boilerplate to foo_tags.py:
from django import template
register = template.Library()
Write your custom tag, which implements generate_some_html. Make it take the caller's context:
#register.inclusion_tag('relative/path/to/some/template.html', takes_context=True)
def foo_tag(context):
photo = context['photo'] # loop variable in parent template
form = context['forms'][photo.id]
# further computation that you need in order to setup the
# context for relative/path/to/some/template.html can be
# done here...
return locals()
Use the context returned by foo_tag in relative/path/to/some/template.html.
Add {% load foo_tags %} to the parent template. You should probably put this near the top, the same way you put imports near the top of a .py file.
Use your new custom tag in the parent template:
{% for photo in photos %}
{% foo_tag %}
{% endfor %}
Django doesn't comme with backed AJAX like RAIL, but it let you choose any lib you want to use from the start.
You can do what you want by creating custom widgets and using form media.
I would like to provide the same content inside 2 different base files.
So I'm trying to do this:
page1.html:
{% extends "base1.html" %}
{% include "commondata.html" %}
page2.html:
{% extends "base2.html" %}
{% include "commondata.html" %}
The problem is that I can't seem to use both extends and include. Is there some way to do that? And if not, how can I accomplish the above?
commondata.html overrides a block that is specified in both base1.html and base2.html
The purpose of this is to provide the same page in both pdf and html format, where the formatting is slightly different. The above question though simplifies what I'm trying to do so if I can get an answer to that it will solve my problem.
When you use the extends template tag, you're saying that the current template extends another -- that it is a child template, dependent on a parent template. Django will look at your child template and use its content to populate the parent.
Everything that you want to use in a child template should be within blocks, which Django uses to populate the parent. If you want use an include statement in that child template, you have to put it within a block, for Django to make sense of it. Otherwise it just doesn't make sense and Django doesn't know what to do with it.
The Django documentation has a few really good examples of using blocks to replace blocks in the parent template.
https://docs.djangoproject.com/en/dev/ref/templates/language/#template-inheritance
From Django docs:
The include tag should be considered as an implementation of "render this subtemplate and include the HTML", not as "parse this subtemplate and include its contents as if it were part of the parent". This means that there is no shared state between included templates -- each include is a completely independent rendering process.
So Django doesn't grab any blocks from your commondata.html and it doesn't know what to do with rendered html outside blocks.
This should do the trick for you: put include tag inside of a block section.
page1.html:
{% extends "base1.html" %}
{% block foo %}
{% include "commondata.html" %}
{% endblock %}
page2.html:
{% extends "base2.html" %}
{% block bar %}
{% include "commondata.html" %}
{% endblock %}
More info about why it wasn't working for me in case it helps future people:
The reason why it wasn't working is that {% include %} in django doesn't like special characters like fancy apostrophe. The template data I was trying to include was pasted from word. I had to manually remove all of these special characters and then it included successfully.
You can't pull in blocks from an included file into a child template to override the parent template's blocks. However, you can specify a parent in a variable and have the base template specified in the context.
From the documentation:
{% extends variable %} uses the value of variable. If the variable evaluates to a string, Django will use that string as the name of the parent template. If the variable evaluates to a Template object, Django will use that object as the parent template.
Instead of separate "page1.html" and "page2.html", put {% extends base_template %} at the top of "commondata.html". And then in your view, define base_template to be either "base1.html" or "base2.html".
Added for reference to future people who find this via google: You might want to look at the {% overextend %} tag provided by the mezzanine library for cases like this.
Edit 10th Dec 2015: As pointed out in the comments, ssi is deprecated since version 1.8. According to the documentation:
This tag has been deprecated and will be removed in Django 1.10. Use the include tag instead.
In my opinion, the right (best) answer to this question is the one from podshumok, as it explains why the behaviour of include when used along with inheritance.
However, I was somewhat surprised that nobody mentioned the ssi tag provided by the Django templating system, which is specifically designed for inline including an external piece of text. Here, inline means the external text will not be interpreted, parsed or interpolated, but simply "copied" inside the calling template.
Please, refer to the documentation for further details (be sure to check your appropriate version of Django in the selector at the lower right part of the page).
https://docs.djangoproject.com/en/dev/ref/templates/builtins/#ssi
From the documentation:
ssi
Outputs the contents of a given file into the page.
Like a simple include tag, {% ssi %} includes the contents of another file
– which must be specified using an absolute path – in the current page
Beware also of the security implications of this technique and also of the required ALLOWED_INCLUDE_ROOTS define, which must be added to your settings files.