2

I have 4 models that need to be presented by chain inheritance each other. Like that:

├───GRANDPARENT1
│   ├───PARENT11
│   │   ├───CHILD111
│   │   └───CHILD112
│   └───PARENT12
│       ├───CHILD121
│       └───CHILD122
└───GRANDPARENT2
    ├───PARENT21
    │   ├───CHILD211
    │   └───CHILD212
    └───PARENT22
        ├───CHILD221
        └───CHILD222

I could do it by multiple {% for %} loops in a single template like it described here :

{% for grandparent in grandparents %}
    <p>{{ grandparent.title }}'s childs is:</p>
    {% for parent in grandparent.parent_set.all %}
        <p>{{ parent.title }}'s childs is:</p>
        {% for child in parent.child_set.all %}
             <p>{{ parent.title }}</p>
        {% endfor %}
    {% endfor %}
{% endfor %}

but i'd like to divide each class onto it's own template and bring them to the same form for clarity and ease of adding subsequently additional levels of nesting.

I'll show two models to show what problem is - Parent and Child:

I've tried to extend a parent template from base.html - it's works. But next, when i extend a child.html template from parent.html it outputs nothing.

models.py:

class Parent(models.Model):
    title = models.CharField(
        max_length=150,
    )
class Child(models.Model):
    title = models.CharField(
        max_length=150,
    )
    parents = models.ManyToManyField(
        Parent,
        blank=True
    )

update 2

In the database i have two objects of Parent class (PARENT1, PARENT2) and four objects of Child class (CHILD11, CHILD12,CHILD21,CHILD22). CHILD11 and CHILD 12 have PARENT1 in parents many-to-many relation; CHILD21 and CHILD22 have same on PARENT2, so it's has that structure:

├───PARENT1
│   ├───CHILD11
│   └───CHILD12
└───PARENT2
    ├───CHILD21
    └───CHILD22

views.py:

class ParentListView(ListView):
    model = Parent
    template_name = 'parent_list.html'
    context_object_name = 'parent_objects_list'


class ChildListView(ListView):
    model = Child
    template_name = 'child_list.html'
    context_object_name = 'child_objects_list'

urls.py

from django.urls import path

from .views import (
    ParentListView,
    ChildListView
)

urlpatterns = [
    path('parents/', ParentListView.as_view(), name='parents'),
    path('childs/', ChildListView.as_view(), name='childs'),
]

parent_list.html (updated) :

{% extends '_base.html' %}

{% block title %}Parents_list{% endblock title %}

{% block content %}
    {% for parent in parent_objects_list %}
        <div class="card">
            <div class="card-header">
                <span class="font-weight-bold">{{ parent.title }}</span>
            </div>
            <div class="card-body">
                {% block childs_block %}
                {% endblock childs_block %}
            </div>
        </div>
    {% endfor %}
{% endblock content %}
  • shows parent list right.

child_list.html

{% extends 'parent_list.html' %}

{% block title %}Childs_list{% endblock title %}

{% block childs_block %}
    {% for child in child_objects_list %}
        <div class="card">
            <div class="card-header">
                <span class="font-weight-bold">{{ child.title }}</span>
            </div>
        </div>
    {% endfor %}
{% endblock childs_block %}

-- that returns empty. I think i need to pass an argument with key into childs block to filter childs of certain parent but cannot find a way how to do that.

gek
  • 524
  • 6
  • 18
  • Shouldn't this be `{% block content %}` instead of `{% block childs_block %}`? – Willem Van Onsem Aug 01 '19 at 20:35
  • Oh, i've missed something important, sorry. parrent template is updated. – gek Aug 02 '19 at 09:27
  • Since there are not `parent_objects_list` objects, the `{% for ... %}` will not run, hence it indeed will not display anything. – Willem Van Onsem Aug 02 '19 at 09:28
  • where do you instruct it to send? Note that template inheritance has nothing to do with views. Since a template can be used by multiple views, and a view can use multiple templates. – Willem Van Onsem Aug 02 '19 at 11:05
  • Sorry there is no comment preview, i need to edit my previous comment: But i have an objects in the database (update2). There is no problem with parent list displaying - it's done. How can i form child_objects_list here? I thought it will be done by ChildListView, even if ChildListView doesn't know what exactly objects related to particular parent need to be presented - why it doesn't send all of them? But firstly i'd like to know how to form child_objects_list in {% for %} dynamically depending on the parent, passing parent parameter to ChildListView ? – gek Aug 02 '19 at 11:13
  • how would a template know what `parent_object_list` would mean? – Willem Van Onsem Aug 02 '19 at 11:22
  • @WillemVanOnsem parent_object_list (object_list renamed via context_object_name) formed by ParentListView from a Parent model - doesn't it? – gek Aug 02 '19 at 13:00
  • no, that's the entire point a template is *not* bound by a model/view, it simply renders with the ingredients (the context) it gets. – Willem Van Onsem Aug 02 '19 at 13:05

1 Answers1

1

Your ChildListView only sends a query of child_objects_list. This thus means that there is no parent_objects_list when you render the template, and hence the {% for ... %} loop will not loop.

You can thus pass a list of Parent object in your ChildListView, probably best with a prefetch_related on the child model:

class ChildListView(ListView):
    model = Parent
    template_name = 'child_list.html'
    context_object_name = 'parent_objects_list'
    queryset = Parent.objects.prefetch_related('child')

Then you can thus iterate over the child_set:

{% extends 'parent_list.html' %}

{% block title %}Childs_list{% endblock title %}

{% block childs_block %}
    {% for child in parent.child_set.all %}
        <div class="card">
            <div class="card-header">
                <span class="font-weight-bold">{{ child.title }}</span>
            </div>
        </div>
    {% endfor %}
{% endblock childs_block %}
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • is **parent** might be passed through from outside of {% extends %}? I've tried above but still nothing in childs_block {% for %}. I put it here: https://github.com/gekmcfh/test01.git – gek Aug 02 '19 at 13:36
  • Please, can you explain, where did the **parent** come from inside the {% childs_block %} ? If i understood correctly, in that example {% childs_block %} can operate only data that it recieved from ChildListView because in urls.py it's clearly declared. Hence {% childs_block %} doesn't know anything about **parent**. To know about it there is needed second for loop. So it'll be {% for parent in parent_objects_list %} {% for child in parent.child_set.all %} etc? – gek Aug 04 '19 at 20:11
  • 1
    @gek: you defined `parent` yourself in the parent template with `{% for parent in parent_objects_list %}`. `parent` is thus an element from the `object_list`. – Willem Van Onsem Aug 04 '19 at 20:16
  • yes, but it works only in parent_list.html and doesn't extends into child_list.html... should it really be passed ??? – gek Aug 04 '19 at 20:41
  • 1
    @gek: The template engine has a "global variable space". So variables you set in the parent persist in the child(ren). – Willem Van Onsem Aug 04 '19 at 20:42
  • ...but finally i've got a wrong result :( i pointed template parameter `child_list.html` into both of `ParentListView` and `ChildListView`. I've got a list of `-Parent1 --Child11 --Child12 -Parent2 --Child21 --Child22` but only on `'parents/'` url, while `'childs/'` still doesn't display anything. so when i change `template_name` parameter in `ParentListView` from `child_list.html` to `parent_list.html` **parent** does not persist in `parent_list.html` anymore. Could you please [help](https://github.com/gekmcfh/test01/blob/master/family/views.py)? – gek Aug 05 '19 at 08:23
  • 1
    @gek: both should have a `parent_objects_list` as `context_object_name`. – Willem Van Onsem Aug 05 '19 at 08:25