Search code examples
pythonflaskjinja2

Flask inheritance and adding elements by loop jinja2.exceptions.UndefinedError: 'operation' is undefined


I'm completely confused with the jinja markup, and I don't understand, am I doing everything completely wrong, or is there at least some logic in my actions? I'm need help with adding elements by loop

Flask Error - jinja2.exceptions.UndefinedError: 'operation' is undefined

base.html:

<!-- Info about operations block-->
    <div class="about-operations-block">

           <!-- Button -->
            <div class="button-about-operations">
                <button class="btn-info" onclick="toggleInfo()">?</button>
                <script src="static/js/main.js"></script>
            </div>

            <!-- Info about operations -->
            <div class="info-about-operations">

                <!-- Title -->
                <div class="title-about-operations">
                    <h3 class="example-about-operations">{% block example_about_operation1 %} {% endblock %}</h3>
                    <h3 class="example-about-operations">{% block example_about_operation2 %} {% endblock %}</h3>
                </div>

                <!-- About Operations -->
                <div class="main-info-about-operations">
                    <h3 class="operation-name">{% block operation_name %} {% endblock %}</h3>
                    <p class="operation-info">{% block operation_info %} {% endblock %}</p>
                    <p class="res-example">{% block res_example %} {% endblock %}</p>
                </div>

            </div>

Index.html:

<!-- Info about operations block-->
    <!-- Title -->
    <div class="info-about-operations">
        {% block example_about_operation1 %} {{ data.example_about_operation1 }} {% endblock %}
        {% block example_about_operation2 %} {{ data.example_about_operation2 }} {% endblock %}
        
        <!-- Operations Info-->
        {% for operation in data['operations'] %}
            {% block operation_name %} {{ operation['operation_name'] }} {% endblock %}
            {% block operation_info %} {{ operation['operation_info'] }} {% endblock %}
            {% block res_example %} {{ operation['res_example'] }} {% endblock %}
        {% endfor %}
    </div>

data.py:

data = {
    'example_about_operation1': 'A = { 1, 2, 3, 4 }',
    'example_about_operation2': 'B = { 3, 4, 5, 6 }',
    'operations': [
        {
            'operation_name': 'Merge',
            'operation_info': 'Write the elements of the set A and B in ascending order; if an element occurs >1 '
                              'time, write it once',
            'res_example': 'A ⋃ B = { 1, 2, 3, 4, 5, 6 }'
        },
        {
            'operation_name': 'Intersection',
            'operation_info': 'Write out identical elements from A and B',
            'res_example': 'A ⋂ B = { 3, 4 }'
        },
        {
            'operation_name': 'Difference',
            'operation_info': 'Rewrite A, removing elements that are in B',
            'res_example': 'A \ B = { 1, 2 }'
        },
        {
            'operation_name': 'Symmetrical Difference',
            'operation_info': 'Write out elements from A ⋃ B, removing elements from A ⋂ B',
            'res_example': 'A △ B = { 1, 2, 5, 6}'
        },
    ]
}

# It's for testing, because I'm already thinking I'm full idiot, and I'm not sure, but it's kinda "working"...
for operation in data['operations']:
    print(operation['operation_name'])
    print(operation['operation_info'])
    print(operation['res_example'])

What should happep: using a loop to add elements from the data dictionary in:

base.html:

<!-- Info about operations -->
            <div class="info-about-operations">

                <!-- Title -->
                <div class="title-about-operations">
                    <h3 class="example-about-operations">{% block example_about_operation1 %} {% endblock %}</h3>
                    <h3 class="example-about-operations">{% block example_about_operation2 %} {% endblock %}</h3>
                </div>

                <!-- About Operations -->
                <div class="main-info-about-operations">
                    <h3 class="operation-name">{% block operation_name %} {% endblock %}</h3>
                    <p class="operation-info">{% block operation_info %} {% endblock %}</p>
                    <p class="res-example">{% block res_example %} {% endblock %}</p>
                </div>

            </div>

index.html:

<!-- Info about operations block-->
    <!-- Title -->
    <div class="info-about-operations">
        {% block example_about_operation1 %} {{ data.example_about_operation1 }} {% endblock %}
        {% block example_about_operation2 %} {{ data.example_about_operation2 }} {% endblock %}

        <!-- Operations Info-->
        {% for operation in data['operations'] %}
            {% block operation_name %} {{ operation['operation_name'] }} {% endblock %}
            {% block operation_info %} {{ operation['operation_info'] }} {% endblock %}
            {% block res_example %} {{ operation['res_example'] }} {% endblock %}
        {% endfor %}
    </div>

but I have a error -- jinja2.exceptions.UndefinedError: 'operation' is undefined.

If I try do somthing like this without jinja it's kinda working(I understand that this is a little different, but still. And I think soon my brain blow up, because I don't understand why it's doesn't working):

data.py:

data = {
    'example_about_operation1': 'A = { 1, 2, 3, 4 }',
    'example_about_operation2': 'B = { 3, 4, 5, 6 }',
    'operations': [
        {
            'operation_name': 'Merge',
            'operation_info': 'Write the elements of the set A and B in ascending order; if an element occurs >1 '
                              'time, write it once',
            'res_example': 'A ⋃ B = { 1, 2, 3, 4, 5, 6 }'
        },
        {
            'operation_name': 'Intersection',
            'operation_info': 'Write out identical elements from A and B',
            'res_example': 'A ⋂ B = { 3, 4 }'
        },
        {
            'operation_name': 'Difference',
            'operation_info': 'Rewrite A, removing elements that are in B',
            'res_example': 'A \ B = { 1, 2 }'
        },
        {
            'operation_name': 'Symmetrical Difference',
            'operation_info': 'Write out elements from A ⋃ B, removing elements from A ⋂ B',
            'res_example': 'A △ B = { 1, 2, 5, 6}'
        },
    ]
}

# It's for testing, because I'm already thinking I'm full idiot, and I'm not sure, but it's kinda "working"...
for operation in data['operations']:
    print(operation['operation_name'])
    print(operation['operation_info'])
    print(operation['res_example'])

If I try to output this:

base.html:

 <div class="title-about-operations">
                    <h3 class="example-about-operations">{% block example_about_operation1 %} {% endblock %}</h3>
                    <h3 class="example-about-operations">{% block example_about_operation2 %} {% endblock %}</h3>
</div>

index.html:

 {% block example_about_operation1 %} {{ data.example_about_operation1 }} {% endblock %}
 {% block example_about_operation2 %} {{ data.example_about_operation2 }} {% endblock %}

it will output normally.

Please, tell me why this doesn't work, thank you.


Solution

  • The way you use a block does not correspond to its intended purpose.

    Template Inheritance

    A block is the definition within a parent template for use within the child template.
    The template does not know the nested context of the loop and therefore does not know the variables. This leads to the error message received.

    base.html
    <!DOCTYPE html>
    <html>
    <head>
        {% block head %}
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <title>{% block title %}{% endblock %}</title>
        {% endblock %}
    </head>
    <body>
        <main>
            {% block content %}{% endblock %}
        </main>
        {% block scripts %}{% endblock %}
    </body>
    </html>
    
    Macros

    A macro is ideal for use in conjunction with a loop. It corresponds to a predefined function that can handle the rendering for one or more items. The macro can be defined outside the template and then imported. This makes it possible to use this in multiple templates.

    index.html (Macro)
    {% extends 'base.html' %}
    
    {# This code can also be stored in another file and imported. #}
    {% macro op(operation_name, operation_info, res_example) -%}
        <h3 class="operation-name">{{ operation_name }}</h3>
        <p class="operation-info">{{ operation_info }}</p>
        <p class="res-example">{{ res_example }}</p>
    {%- endmacro %}
    
    {% block title %}Index{% endblock %}
    {% block head %}
        {{ super() }}
    {% endblock %}
    
    {% block content %}
        <div class="about-operations-block">
            <div class="button-about-operations">
                <button class="btn-info" onclick="toggleInfo()">?</button>
            </div>
    
            <!-- Info about operations -->
            <div class="info-about-operations">
    
                <!-- Title -->
                <div class="title-about-operations">
                    <h3 class="example-about-operations">{{ data.example_about_operation1 }}</h3>
                    <h3 class="example-about-operations">{{ data.example_about_operation2 }}</h3>
                </div>
    
                <!-- About Operations -->
                <div class="main-info-about-operations">
                    {% for operation in data.operations -%}
                        {{ op(**operation) }}
                    {% endfor -%}
                </div>
    
            </div>
        </div>
    {% endblock %}
    
    {% block scripts %}
        <script src="{{ url_for('static', filename='js/main.js') }}"></script>
    {% endblock %}
    
    Include

    It is also possible to use an include. Within a file is the outsourced code, which can be embedded multiple times.

    index.html (Include)
    {% extends 'base.html' %}
    
    {% block title %}Index{% endblock %}
    {% block head %}
        {{ super() }}
    {% endblock %}
    
    {% block content %}
        <div class="about-operations-block">
            <div class="button-about-operations">
                <button class="btn-info" onclick="toggleInfo()">?</button>
            </div>
    
            <!-- Info about operations -->
            <div class="info-about-operations">
    
                <!-- Title -->
                <div class="title-about-operations">
                    <h3 class="example-about-operations">{{ data.example_about_operation1 }}</h3>
                    <h3 class="example-about-operations">{{ data.example_about_operation2 }}</h3>
                </div>
    
                <!-- About Operations -->
                <div class="main-info-about-operations">
                    {% for operation in data.operations -%}
                        {% include 'operation.html' %}
                    {% endfor -%}
                </div>
    
            </div>
        </div>
    {% endblock %}
    
    {% block scripts %}
        <script src="{{ url_for('static', filename='js/main.js') }}"></script>
    {% endblock %}
    
    operation.html
    <h3 class="operation-name">{{ operation.operation_name }}</h3>
    <p class="operation-info">{{ operation.operation_info }}</p>
    <p class="res-example">{{ operation.res_example }}</p>