Search code examples
djangoinheritanceblockextends

django template inheritance and assingment_tag


I want to write an assignment tag and add in app/templatetags/app_extras.py

@register.assignment_tag
def get_obj (objid) :
    return get_object_or_404(MyModel, id=objid)

and in a template

{% extends "index.html" %}

{% load app_extras %}
{% get_obj 1 as obj %}

{% block title %}{{ obj.name }}{% endblock %}
{% block content %}{{ obj.description }}{% endblock %}

Variable obj isn't visible in the blocks.

If I write

...
{% load app_extras %}

{% block title %}{% get_obj 1 as obj %}{{ obj.name }}{% endblock %}
...

obj is defined in the block but undefined in "content" block.

How to make the variable available in the blocks?


Solution

  • There are two problems here. First - when you are in a second-level template(which 'extends' a base one), the content outside the {% block %} is not being processed. So that's the reason why you don't have the assigned value in the blocks. It has never been assigned indeed.

    The second problem is that the context in a block is specific and accessible in the block only, and not between blocks. To make it available in multiple blocks - you have to share it. This is how context can be used in assignment_tag:

    @register.assignment_tag(takes_context=True)
    def get_obj (context, objid) :
        context.obj = get_object_or_404(MyModel, id=objid)
        return context.obj
    

    Using this tag, you can assign the variable in your first block:

    {% block title %}{% get_obj 1 as obj %}{{ obj.name }}{% endblock %}
    

    and then to use it from context in the next block:

    {% block content %}{{ obj.name }}{% endblock %}
    

    IMHO, this is not the best way to do things, although it works. The problem here is that the template modifies/overwrites the context, and may lead to undesired behavior in some cases.