Невозможно отобразить Listblock внутри StreamBlock в шаблоне Wagtail

#python #django-templates #wagtail

#python #django-шаблоны #wagtail

Вопрос:

У меня есть StructBlock внутри Listblock, все из которых находятся внутри class BannerBlock следующим образом:

 class BannerBlock(blocks.StreamBlock):
    """
    Blocks for displaying individual banners
    """
    
    banners = blocks.ListBlock(
        blocks.StructBlock(
            [
                ('image', ImageChooserBlock(required=True)),
                ('title', blocks.CharBlock(required=True, max_length=128)),
                ('description', blocks.CharBlock(required=True, max_length=1024)),
                ('link', blocks.URLBlock(required=False)),
            ]
        )
    )
    
    class Meta:
        template = "home/banner_block.html"
        icon = "placeholder"
        label = "Banners"
 

Шаблон Banner_block выглядит следующим образом:

 {% load wagtailimages_tags %}

<!-- Banner -->
<div id="banner">
    {% for banner in self.banners %}
        <article data-position="bottom right">
            <div class="inner">
                {% image banner.image original as image %}
                <img src="{{ image.url }}" alt="">
                <div class="features">
                    <a href="{{ banner.link.url }}" class="c-accent alt no-bg">
                    <h2>{{ banner.title }}</h2>
                    <p>{{ banner.description }}</p>
                    </a>
                </div>
            </div>
        </article>
    {% endfor %}
</div>
 

Наконец, я пытаюсь отобразить каждый баннер (Listblock) следующим образом:

 {% for block in page.banners_collection %}
     {% include_block block %}
{% endfor %} 
 

Однако каждый блок списка отображается в виде обычного текста, как элементы списка.
Как мне правильно отобразить каждый элемент?
Спасибо.

Ответ №1:

Я предполагаю banners_collection , что поле в вашей модели страницы определено как:

 banners_collection = StreamField(BannerBlock())
 

где вы указываете StreamBlock для использования на верхнем уровне StreamField, а не для более простой настройки

 some_stream = StreamField([
    ('first_block_type', some_block_definition),
    ('another_block_type', some_block_definition),
])
 

где вы передаете список блоков для StreamField для создания потока. Это означает, что при доступе page.banners_collection он предоставит вам один экземпляр значения BannerBlock. Когда вы перебираете это значение с for block in page.banners_collection помощью, вы фактически возвращаете значения ListBlock в свой поток и отображаете их по отдельности с include_block помощью — минуя шаблон, который вы настроили для BannerBlock . У ваших ListBlock значений нет собственного шаблона, поэтому в конечном итоге они используют рендеринг по умолчанию.

Если вы вызываете include_block значение BannerBlock в целом:

 {% include_block page.banners_collection %}
 

затем он будет использовать ваш шаблон. Однако есть вторичная проблема — в строке

 {% for banner in self.banners %}
 

self.banners недопустимо, поскольку значение потока ведет себя как последовательность, а не как словарь. Вы не можете искать banners дочерние элементы по имени — вам нужно перебирать список дочерних элементов, находя те, которые имеют тип banners . (За исключением того, что в вашем случае вы можете пропустить эту проверку, поскольку banners это единственный доступный тип блока в потоке). Правильный код шаблона будет выглядеть так:

 <div id="banner">
    {% for block in self %}
        {# the 'block' object here has properties block_type (which is always 'banners' here) and value #}
        {% for banner in block.value %}
            {# banner is a StructBlock value, with image, title, description, link properties #}
            <article data-position="bottom right">
                <div class="inner">
                    {% image banner.image original as image %}
                    ...
               </div>
           </article>
        {% endfor %}
    {% endfor %}
</div>
 

Однако вы уверены, что вам нужен ListBlock в дополнение к StreamBlock? StreamBlock уже представляет собой последовательность блоков, поэтому ListBlock внутри StreamBlock выдает вам список списков баннеров. Если вы упростили значение BannerBlock до

 class BannerBlock(blocks.StreamBlock):
    """
    Blocks for displaying individual banners
    """
    
    banner = blocks.StructBlock(
        [
            ('image', ImageChooserBlock(required=True)),
            ('title', blocks.CharBlock(required=True, max_length=128)),
            ('description', blocks.CharBlock(required=True, max_length=1024)),
            ('link', blocks.URLBlock(required=False)),
        ]
    )
    
    class Meta:
        template = "home/banner_block.html"
        icon = "placeholder"
        label = "Banners"
 

тогда BannerBlock в целом будет представлять собой список баннеров, а шаблон станет

 <div id="banner">
    {% for block in self %}
        {# block.value is now a StructBlock value #}
        <article data-position="bottom right">
            <div class="inner">
                {% image block.value.image original as image %}
                ...
           </div>
       </article>
    {% endfor %}
</div>