Proposal: Multiple-Inheritance in Templates

357 views
Skip to first unread message

Yo-Yo Ma

unread,
Feb 16, 2020, 11:02:06 AM2/16/20
to Django developers (Contributions to Django itself)
Please read this in full - you will not be disappointed by the end.

We often talk about "multiple-inheritance" when referring to multiple levels of {% extends ... %} and defining {% block ... %} many layers up in base templates.

But, something that comes up as a useful idea in my mind on a regular basis when working in Django templates is the ability to actually inherit from multiple base templates.

The immediate reaction to this idea might be repulsion until you think through the implications and weigh them against the benefits.

An example (simplified) use case speaks louder than explanation:

base.html

<a>Some top-level menu</a>
{% block submenu %}{% endblock %}
{% block content %}{% endblock %}

--

orders/base.html

{% extends 'base.html' %}

{% block submenu %}
<a>View all Orders</a>
<a>Enter an Order</a>
{% endblock %}

--

products/base.html

{% extends 'base.html' %}

{% block submenu %}
<a>View all Products</a>
<a>Create a Product</a>
{% endblock %}

--

So, we have a relatively common template setup, with some apps defining their own base templates that extend the main base template, and then defining their own submenus, etc.

At this point, we know there will be a product list page and an order list page, but we also know that the product list template will extend the product base template and the order list template will extend the order base template; same goes for the product create and order create, both also extending their respective apps' base templates.

This means duplication. The product list template will contain most of the same list-related HTML, etc. as the order list, but the only way to avoid that duplication will be to make a base list template and extend that template from both the product list and order list.

generic-list.html

{% extends 'base.html' %}

{% block content %}
    <ul>
    {% for object in page.object_list %}
        {% block list-item %}
            <li>{{ object }}</li>
        {% endblock %}
    {% endfor %}
    </ul>
{% endblock %}

--

But, in this case we lose the benefit of the product base and order base (the submenus they define). Submenus are a super simplified example, but in reality there may be *many* different things about a given app's base template, making it a dilemma, or simply removing the practical ability to subclass a "generic list" template altogether.

Introducing multiple-inheritance:

orders/list.html

{% extends 'generic-list.html 'orders/base.html' %}

{% block list-item %}
    <li>Order # {{ order.id }} (total: {{ order.total }})</li>
{% endblock %}

--


products/list.html

{% extends 'generic-list.html 'products/base.html' %}

{% block list-item %}
    <li>Product # {{ product.id }} (price: {{ product.price }})</li>
{% endblock %}

--

The order of the templates would define the order of block definition and override precedence, from left to right (similar to Python class MRO).

You can also see, by the overriding of the "list-item" block, that there are other benefits of using extension like this, compared to e.g., defining a list template as an include, as a workaround.

Each template in the list would need to extend from template at the very right, meaning that each template left of the main template would essentially be a mixin. This would solve the very real problem of making generic bases for types of templates (list, detail, form, etc.) in your app, while still preserving the benefits of extension (block overrides) to all other facets of your final templates (vs having to use includes).

Matthew Pava

unread,
Feb 17, 2020, 10:55:44 AM2/17/20
to django-d...@googlegroups.com

In your example, you could just inherit from generic-list.html since it already extends base.html. You would be repeating yourself in your current example.

 

orders/list.html

 

{% extends 'generic-list.html' %}

--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/cc068a12-04ee-481a-81e1-919ed958c688%40googlegroups.com.

Yo-Yo Ma

unread,
Feb 18, 2020, 3:25:12 PM2/18/20
to Django developers (Contributions to Django itself)
@matthew.pava please read the entire OP again.

You can't have actually read the post and come to the conclusion that inheriting from generic-list.html would solve the problem.

These kinds of "I didn't read it but it looks like you could just X" replies are a big problem in mailing lists; they have the unintended consequence of causing serious posts to like noobie "help me" questions by later passers by.

I'm a 12-13 year Python veteran (11 years full-time Django), not a noob, and I think this feature could be a tremendous benefit to Django templates, and I'd like some real thoughts on the design concept.

To unsubscribe from this group and stop receiving emails from it, send an email to django-d...@googlegroups.com.

Matthew Pava

unread,
Feb 18, 2020, 3:32:12 PM2/18/20
to django-d...@googlegroups.com

I did read your post, and I did come to that conclusion. I may have ultimately misunderstood, and I’ll certainly take the blame for my own misunderstanding. The example you provided can be done without multiple template inheritance. So far, my thoughts on the design concept is that multiple template inheritance is unnecessary.

To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/fa0af20a-ab93-4209-aa7b-564b3aac3893%40googlegroups.com.

Yo-Yo Ma

unread,
Feb 18, 2020, 3:44:22 PM2/18/20
to Django developers (Contributions to Django itself)
Please help us all understand, then:

How is it possible to inherit the blocks defined in both the orders/base.html template AND the generic-list.html template while inheriting only from the generic-list.html template?

Matthias Kestenholz

unread,
Feb 18, 2020, 3:58:34 PM2/18/20
to django-d...@googlegroups.com
Hi,

You could make the generic-list.html template extend a configurable base template, e.g. "{% extends base_template|default:'base.html' %}", and add context["base_template"] = "orders/base.html" in views belonging to the orders app, and "products/base.html" in the products app.

I often use this pattern exactly for the case when some app needs to add its own submenu.

Best,
Matthias


Aymeric Augustin

unread,
Feb 18, 2020, 3:59:09 PM2/18/20
to django-d...@googlegroups.com
Hello Yo-Yo Ma,

Please assume good faith. You've been around for 11 years, so you know the way you address Matthew isn't how we behave on this mailing-list.

I believe that the most common way to achieve what you want is to include the "generic list" HTML with the {% include %} tag. This allows you to share generic components across multiple templates.

In theory, it's less powerful than the multiple inheritance mechanism you've been proposing (because it does a separate render and includes the result verbatim). In practice, I think it does the job.

Best regards,

-- 
Aymeric.



To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/58e9a945-2229-4935-b31b-7e49c6305834%40googlegroups.com.

Aymeric Augustin

unread,
Feb 18, 2020, 4:01:42 PM2/18/20
to django-d...@googlegroups.com
This suggestion is better than mine for the use case discussed here :-)

-- 
Aymeric.



-- 
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/CANvPqgAB4n-jPVzW-Fv5NnN-4Lpx1vm7ObA8pTD2aP6U5y%2B-OA%40mail.gmail.com.

Yo-Yo Ma

unread,
Feb 18, 2020, 4:31:23 PM2/18/20
to Django developers (Contributions to Django itself)
Hi Aymeric,

Thanks for the reply. I have to restate a point that argues against the assumption that the existing solution is "good enough": includes cannot override blocks, giving them only limited use. Notice in the products/list.html template I'm overriding the {% block list-item %} block; that can't be done if the list code is included. Conversely, if the menus are included, then they can't make use of block overrides. Both of these are actual cases I've experienced many times over the years. The former is illustrated in the example, and the latter exists when I have a subsection with a submenu that also includes a menu item for a detail page (making use of a block override that uses {{ block.super }} and then adds a new menu item).

The latter example above can be worked around by adding all sorts of little {% block before-submenu-close %}, { % block after-submenu %} type blocks in the base.html template, but it leads to messier and less maintainable templates with lots of empty blocks in base templates (very common pattern I've seen over the years).

One of the other reasons a feature like this is worth looking at is that it's a very take-it-or-leave it feature that wouldn't affect any existing code and, with the rules I stated toward the bottom of the OP, it would be very intuitive and leave little room for mistakes on the part of template coders.

P.S. (re: my reply to matthew.pava) following the rule you've laid out in your first sentence ("Please assume good faith."), my usage of elenchus, especially in the context of what appeared to be a deliberately obtuse response (the second response), should be assumed to be a perfectly valid response. Being direct is super necessary in asynchronous communication.

-- 
Aymeric.



Yo-Yo Ma

unread,
Feb 18, 2020, 4:39:34 PM2/18/20
to Django developers (Contributions to Django itself)
Hi Matthias,

Thanks for the reply. My proposal is not about making it easier to use the most relevant base template. It's about making it easier to combine parts of different base templates to allow for generic type templates (create, list, etc.) to coexist with section templates (different apps that define menus, different models, etc.).

So, for example:

base.html defines basic structure and provides blocks

product-base.html overrides some of base.html's blocks, pertaining to product-specific menus, etc.

generic-list.html overrids some of base.html's blocks, pertaining to the content section, adding a list and pagination, etc.

generic-edit.html overrids some of base.html's blocks, pertaining to the content section, adding a form for editing an object, etc.

product-list.html extends BOTH product-base.html and generic-list.html, giving it both the block overrides for lists and for product menus, etc. It also may override some blocks that were defined in product-base.html and some that were defined in generic-list.html (such as the {% block list-item %} example in my OP).

product-edit.html extends BOTH product-base.html and generic-edit.html, giving it both the block overrides for lists and for product menus, etc. It also may override some blocks that were defined in product-base.html and some that were defined in generic-edit.html

izaias de oliveira elias

unread,
Feb 18, 2020, 5:51:22 PM2/18/20
to django-d...@googlegroups.com
I need help with this project, I can't configure the template pages to display the information

--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/e5fd407c-5450-44a3-b647-4303bda489bb%40googlegroups.com.

Yo-Yo Ma

unread,
Feb 19, 2020, 10:54:20 AM2/19/20
to Django developers (Contributions to Django itself)
Hi izaias de oliveira elias, this is an existing thread in the Django Developers group.

What you want is to create a new thread in the Django Users group: https://groups.google.com/forum/#!forum/django-users
To unsubscribe from this group and stop receiving emails from it, send an email to django-d...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages