Can a template extend a template?

32 views
Skip to first unread message

Jason

unread,
Dec 13, 2011, 5:21:14 PM12/13/11
to django...@googlegroups.com
Hi there,

I love the concept of DRY, and django's enthusiasm for this concept. In light of this I have tried to have a template extend a template, but it doesn't seem to work. Is there a better way than what I'm currently doing?

The current way
index.html 
{% if current_user %}
        {% extends "_logged_out_template.html" %}
{% else %}
        {% extends "_logged_in_template.html" %}
{% endif %}
contact.html 
{% if current_user %}
        {% extends "_logged_out_template.html" %}
{% else %}
        {% extends "_logged_in_template.html" %}
{% endif %}


The way I would like it to be (but it didn't work)

index.html 
        {% extends "_base_template.html" %}

_base_script.html
{% if current_user %}
        {% extends "_logged_out_template.html" %}
{% else %}
        {% extends "_logged_in_template.html" %}
{% endif %}

Gabriel [SGT]

unread,
Dec 13, 2011, 6:00:12 PM12/13/11
to django...@googlegroups.com

I don't see why it doesn't work. Post the errors you are having.

I use templates that extends templates with no issues.

--
Kind Regards

Russell Keith-Magee

unread,
Dec 13, 2011, 6:49:47 PM12/13/11
to django...@googlegroups.com
On Wed, Dec 14, 2011 at 6:21 AM, Jason <1jason....@gmail.com> wrote:
> Hi there,
>
> I love the concept of DRY, and django's enthusiasm for this concept. In
> light of this I have tried to have a template extend a template, but it
> doesn't seem to work. Is there a better way than what I'm currently doing?

Yes :-)

> The current way
>
> index.html
>
> {% if current_user %}
>         {% extends "_logged_out_template.html" %}
>
> {% else %}
>         {% extends "_logged_in_template.html" %}
> {% endif %}
>
> contact.html
>
> {% if current_user %}
>
>         {% extends "_logged_out_template.html" %}
>
> {% else %}
>
>         {% extends "_logged_in_template.html" %}
>
> {% endif %}
>
>
>
> The way I would like it to be (but it didn't work)
>
> index.html
>         {% extends "_base_template.html" %}
>
> _base_script.html
>
> {% if current_user %}
>
>         {% extends "_logged_out_template.html" %}
>
> {% else %}
>
>         {% extends "_logged_in_template.html" %}
>
> {% endif %}

This sample suggests that you may have some misunderstandings about
how Django's template language -- and the {% extends %} tag in
particular -- are supposed work.

{% extends %} is *not* the same as {% include %}. A template can only
*extend* a single other template. The natural partner of {% extends %}
is the {% block %} tag.

Django also has an {% include %} statement, but {% extends %} and {%
block %} are generally more efficient (and, in my experience,
flexible)

{% extends %} is the only special case in Django's template parser.
The extends tag is parsed separately from the rest of the tags
(specifically so that it can interact with {% block %} tags), and as a
result, the {% block %} tag doesn't interact with {% if %} or other
logical constructs. The Django parser works out what templates are
being extended, creating a single template, and *then* sorts out the
page logic.

Working with an {% include %} tag is a bit like a "top down" design --
you have a final product in mind, which you construct by assembling
lots of smaller snippets. {% extends %} is more like a bottom up
design -- you establish a basic structure, and then work out how a
specific page modifies the basic structure.

Looking at your example, it's not entirely clear what you're trying to
achieve; hopefully the following example will give you an idea of how
{% extends %} and {% block %} work together:

base.html:

{% block header %}My Site{% endblock %}
{% block body %}{% endblock %}
{% block footer %}Thanks for coming{% endblock %}

index.html
{% extends "base.html" %}
{% block body %}
This is the index
{% endblock %}

detail.html
{% extends "base.html" %}
{% block body %}
This is the detail
{% endblock %}

This will result in two pages -- an index and a detail page. Both have
a header that reads "My Site", and a footer that says "thanks for
coming"; however, the actual content in the body changes depending on
the template.

This sort of structure can then be layered -- so, for example, if
there are many different types of detail page, you can have a "base
detail" page that defines the blocks that exist on detail pages, and
then specific detail pages that extend the base detail page.

I hope this makes the design motivation of Django's template language
a little more clear.

Yours,
Russ Magee %-)

Furbee

unread,
Dec 13, 2011, 7:07:53 PM12/13/11
to django...@googlegroups.com
Brilliant! I wasn't looking an answer to this question, and wasn't all that aware of how these interacted, but it will sure come in handy. Thanks for the extremely clear, concise explanation. I don't think the Django docs make it quite that clear.

Furbee


--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django...@googlegroups.com.
To unsubscribe from this group, send email to django-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.


Ian Clelland

unread,
Dec 14, 2011, 12:25:11 AM12/14/11
to django...@googlegroups.com

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To view this discussion on the web visit https://groups.google.com/d/msg/django-users/-/8j1xs23yjnAJ.

To post to this group, send email to django...@googlegroups.com.
To unsubscribe from this group, send email to django-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.


Honestly, I never even realized that you *could* put an {% extends %} node inside of an {% if %} node like that.

What I find works, if you really do have to dynamically change the base template that you are inheriting from, is to set a context variable in your view, and use it in the template. Nobody ever said that the argument to 'extends' has to be a literal string:

View:
    def my_view(request):
        if request.user.is_authenticated:
            base_template = "_logged_in_template.html"
        else:
            base_template = "_logged_out_template.html"

   ...
   return render("my_template.html", RequestContext({"base_template": base_template...}))


Template: (my_template.html)

    {% extends base_template %}


(In a real app, I would use a base class for the view which would set the base template for every request automatically, or I would do it in a context processor, rather than putting those four lines at the top of every single view)


--
Regards,
Ian Clelland
<clel...@gmail.com>

Jason

unread,
Dec 14, 2011, 4:09:33 AM12/14/11
to django...@googlegroups.com
That's a cool idea Ian. I was reluctant to put this code serverside (MVC considerations) but I see now that really it is somewhere in the app logic rather than display so it kinda fits in with that methodology... I'll give it a bash.

Thanks for the explanation Russell.
Reply all
Reply to author
Forward
0 new messages