혼합 콘텐츠에 StreamField 사용하는 방법

StreamField는 부제목, 이미지, 인용구, 비디오와 같이 고정된 구조를 따르지 않는 페이지(예: 블로그 게시물 또는 뉴스 기사)에 적합한 콘텐츠 편집 모델을 제공합니다. 또한 지도 및 차트(또는 프로그래밍 블로그의 경우 코드 스니펫)와 같은 더 전문화된 콘텐츠 유형에도 적합합니다. 이 모델에서 이러한 다양한 콘텐츠 유형은 ‘블록’ 시퀀스로 표현되며, 원하는 순서로 반복하고 정렬할 수 있습니다.

StreamField에 대한 추가 배경 정보와 기사 본문에 리치 텍스트 필드 대신 StreamField를 사용하는 이유는 블로그 게시물 리치 텍스트 필드 및 더 빠른 말을 참조하십시오.

StreamField는 또한 간단한 하위 블록 모음(예: 이름, 성, 사진으로 구성된 ‘사람’ 블록)부터 자체 편집 인터페이스가 있는 완전히 사용자 지정된 구성 요소에 이르기까지 자체 블록 유형을 정의하기 위한 풍부한 API를 제공합니다. 데이터베이스 내에서 StreamField 콘텐츠는 JSON으로 저장되어 필드의 전체 정보 콘텐츠가 HTML 표현뿐만 아니라 보존되도록 합니다.

StreamField 사용

StreamField 는 다른 필드와 마찬가지로 페이지 모델 내에 정의할 수 있는 모델 필드입니다.

from django.db import models

from wagtail.models import Page
from wagtail.fields import StreamField
from wagtail import blocks
from wagtail.admin.panels import FieldPanel
from wagtail.images.blocks import ImageBlock

class BlogPage(Page):
    author = models.CharField(max_length=255)
    date = models.DateField("게시 날짜")
    body = StreamField([
        ('heading', blocks.CharBlock(form_classname="title")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageBlock()),
    ])

    content_panels = Page.content_panels + [
        FieldPanel('author'),
        FieldPanel('date'),
        FieldPanel('body'),
    ]

이 예시에서 BlogPage 의 본문 필드는 StreamField 로 정의되며, 작성자는 제목, 단락, 이미지의 세 가지 블록 유형에서 콘텐츠를 구성할 수 있으며, 원하는 순서로 사용하고 반복할 수 있습니다. 작성자가 사용할 수 있는 블록 유형은 (name, block_type) 튜플 목록으로 정의됩니다. ‘name’은 템플릿 내에서 블록 유형을 식별하는 데 사용되며, 변수 이름에 대한 표준 Python 규칙(소문자 및 밑줄, 공백 없음)을 따라야 합니다.

사용 가능한 블록 유형의 전체 목록은 StreamField 블록 참조에서 찾을 수 있습니다.

참고

StreamField는 RichTextField와 같은 다른 필드 유형을 직접 대체하지 않습니다. 기존 필드를 StreamField로 마이그레이션해야 하는 경우 RichTextField를 StreamField로 마이그레이션하기를 참조하십시오.

참고

블록 정의는 모델 필드와 유사하게 보이지만 동일하지는 않습니다. 블록은 StreamField 내에서만 유효합니다. 모델 필드 대신 사용하면 작동하지 않습니다.

템플릿 렌더링

StreamField는 스트림 콘텐츠 전체와 각 개별 블록에 대한 HTML 표현을 제공합니다. 이 HTML을 페이지에 포함하려면 {% include_block %} 태그를 사용하십시오.

{% load wagtailcore_tags %}

    ...

{% include_block page.body %}

기본 렌더링에서 스트림의 각 블록은 <div class="block-my_block_name"> 요소로 래핑됩니다(여기서 my_block_name 은 StreamField 정의에 지정된 블록 이름입니다). 자체 HTML 마크업을 제공하려면 필드의 값을 반복하고 각 블록에 대해 {% include_block %} 을 호출할 수 있습니다.

{% load wagtailcore_tags %}

    ...

<article>
    {% for block in page.body %}
        <section>{% include_block block %}</section>
    {% endfor %}
</article>

특정 블록 유형의 렌더링을 더 세밀하게 제어하려면 각 블록 객체는 block_typevalue 속성을 제공합니다.

{% load wagtailcore_tags %}

    ...

<article>
    {% for block in page.body %}
        {% if block.block_type == 'heading' %}
            <h1>{{ block.value }}</h1>
        {% else %}
            <section class="block-{{ block.block_type }}">
                {% include_block block %}
            </section>
        {% endif %}
    {% endfor %}
</article>

블록 결합

StreamField 내에서 내장 블록 유형을 직접 사용하는 것 외에도 다양한 방식으로 하위 블록을 결합하여 새 블록 유형을 구성할 수 있습니다. 이에 대한 예시는 다음과 같습니다.

  • 이미지 선택기와 텍스트 필드로 구성된 “캡션이 있는 이미지” 블록

  • 콘텐츠 작성자가 다른 페이지에 대한 링크를 원하는 만큼 제공할 수 있는 “관련 링크” 섹션

  • 각 슬라이드가 이미지, 텍스트 또는 비디오일 수 있고 원하는 순서로 정렬될 수 있는 슬라이드쇼 블록

새 블록 유형이 이러한 방식으로 구축되면 내장 블록 유형이 사용되는 모든 곳에서 사용할 수 있습니다. 다른 블록 유형의 구성 요소로 사용하는 것을 포함합니다. 예를 들어, 각 항목이 “캡션이 있는 이미지” 블록인 이미지 갤러리 블록을 정의할 수 있습니다.

StructBlock

StructBlock 을 사용하면 여러 ‘자식’ 블록을 그룹화하여 단일 블록으로 표시할 수 있습니다. 자식 블록은 (name, block_type) 튜플 목록으로 StructBlock 에 전달됩니다.

body = StreamField([
    ('person', blocks.StructBlock([
        ('first_name', blocks.CharBlock()),
        ('surname', blocks.CharBlock()),
        ('photo', ImageBlock(required=False)),
        ('biography', blocks.RichTextBlock()),
    ])),
    ('heading', blocks.CharBlock(form_classname="title")),
    ('paragraph', blocks.RichTextBlock()),
    ('image', ImageBlock()),
])

StreamField의 콘텐츠를 다시 읽을 때(예: 템플릿 렌더링 시) StructBlock의 값은 정의에 지정된 블록 이름에 해당하는 키가 있는 사전과 유사한 객체입니다.

<article>
    {% for block in page.body %}
        {% if block.block_type == 'person' %}
            <div class="person">
                {% image block.value.photo width-400 %}
                <h2>{{ block.value.first_name }} {{ block.value.surname }}</h2>
                {{ block.value.biography }}
            </div>
        {% else %}
            (다른 블록 유형에 대한 렌더링)
        {% endif %}
    {% endfor %}
</article>

StructBlock 서브클래싱

StructBlock의 자식 블록 목록을 StreamField 정의 내에 배치하면 읽기 어려울 수 있으며, 동일한 블록을 여러 곳에서 재사용하기 어렵게 만듭니다. 대안으로 StructBlock 을 서브클래스화하고 자식 블록을 서브클래스의 속성으로 정의할 수 있습니다. 위 예시의 ‘person’ 블록은 다음과 같이 다시 작성할 수 있습니다.

class PersonBlock(blocks.StructBlock):
    first_name = blocks.CharBlock()
    surname = blocks.CharBlock()
    photo = ImageBlock(required=False)
    biography = blocks.RichTextBlock()

PersonBlock 은 내장 블록 유형과 동일한 방식으로 StreamField 정의에서 사용할 수 있습니다.

body = StreamField([
    ('person', PersonBlock()),
    ('heading', blocks.CharBlock(form_classname="title")),
    ('paragraph', blocks.RichTextBlock()),
    ('image', ImageBlock()),
])

블록 아이콘

콘텐츠 작성자가 StreamField에 새 블록을 추가하는 데 사용하는 메뉴에서 각 블록 유형에는 연결된 아이콘이 있습니다. StructBlock 및 기타 구조 블록 유형의 경우 이러한 블록의 목적이 프로젝트에 특화되어 있으므로 플레이스홀더 아이콘이 사용됩니다. 사용자 지정 아이콘을 설정하려면 icon 옵션을 StructBlock 에 대한 키워드 인수 또는 Meta 클래스의 속성으로 전달합니다.

body = StreamField([
    ('person', blocks.StructBlock([
        ('first_name', blocks.CharBlock()),
        ('surname', blocks.CharBlock()),
        ('photo', ImageBlock(required=False)),
        ('biography', blocks.RichTextBlock()),
    ], icon='user')),
    ('heading', blocks.CharBlock(form_classname="title")),
    ('paragraph', blocks.RichTextBlock()),
    ('image', ImageBlock()),
])
class PersonBlock(blocks.StructBlock):
    first_name = blocks.CharBlock()
    surname = blocks.CharBlock()
    photo = ImageBlock(required=False)
    biography = blocks.RichTextBlock()

    class Meta:
        icon = 'user'

기본적으로 제공되는 아이콘 목록은 아이콘 개요를 참조하십시오. 프로젝트별 아이콘도 스타일 가이드에 표시됩니다.

ListBlock

ListBlock 은 반복되는 블록을 정의하여 콘텐츠 작성자가 특정 블록 유형의 인스턴스를 원하는 만큼 삽입할 수 있도록 합니다. 예를 들어, 여러 이미지로 구성된 ‘갤러리’ 블록은 다음과 같이 정의할 수 있습니다.

body = StreamField([
    ('gallery', blocks.ListBlock(ImageBlock())),
    ('heading', blocks.CharBlock(form_classname="title")),
    ('paragraph', blocks.RichTextBlock()),
    ('image', ImageBlock()),
])

StreamField의 콘텐츠를 다시 읽을 때(예: 템플릿 렌더링 시) ListBlock의 값은 자식 값 목록입니다.

<article>
    {% for block in page.body %}
        {% if block.block_type == 'gallery' %}
            <ul class="gallery">
                {% for img in block.value %}
                    <li>{% image img width-400 %}</li>
                {% endfor %}
            </ul>
        {% else %}
            (다른 블록 유형에 대한 렌더링)
        {% endif %}
    {% endfor %}
</article>

StreamBlock

StreamBlock 은 StreamField 자체와 동일한 메커니즘을 통해 혼합하고 재정렬할 수 있는 다양한 유형의 하위 블록 시퀀스를 정의합니다. 예를 들어, 이미지 및 비디오 슬라이드를 모두 지원하는 캐러셀은 다음과 같이 정의할 수 있습니다.

body = StreamField([
    ('carousel', blocks.StreamBlock([
        ('image', ImageBlock()),
        ('video', EmbedBlock()),
    ])),
    ('heading', blocks.CharBlock(form_classname="title")),
    ('paragraph', blocks.RichTextBlock()),
    ('image', ImageBlock()),
])

StreamBlockStructBlock 과 동일한 방식으로 서브클래스화할 수 있으며, 자식 블록은 클래스의 속성으로 지정됩니다.

class CarouselBlock(blocks.StreamBlock):
    image = ImageBlock()
    video = EmbedBlock()

    class Meta:
        icon = 'image'

이러한 방식으로 정의된 StreamBlock 서브클래스는 블록 유형 목록 대신 StreamField 정의에 전달될 수도 있습니다. 이를 통해 여러 페이지 유형에서 사용할 공통 블록 유형 세트를 설정할 수 있습니다.

class CommonContentBlock(blocks.StreamBlock):
    heading = blocks.CharBlock(form_classname="title")
    paragraph = blocks.RichTextBlock()
    image = ImageBlock()


class BlogPage(Page):
    body = StreamField(CommonContentBlock())

StreamField의 콘텐츠를 다시 읽을 때 StreamBlock의 값은 StreamField 자체의 최상위 값과 마찬가지로 block_typevalue 속성이 있는 블록 객체 시퀀스입니다.

<article>
    {% for block in page.body %}
        {% if block.block_type == 'carousel' %}
            <ul class="carousel">
                {% for slide in block.value %}
                    {% if slide.block_type == 'image' %}
                        <li class="image">{% image slide.value width-200 %}</li>
                    {% else %}
                        <li class="video">{% include_block slide %}</li>
                    {% endif %}
                {% endfor %}
            </ul>
        {% else %}
            (다른 블록 유형에 대한 렌더링)
        {% endif %}
    {% endfor %}
</article>

블록 수 제한

기본적으로 StreamField는 무제한의 블록을 포함할 수 있습니다. StreamField 또는 StreamBlockmin_nummax_num 옵션을 사용하면 최소 또는 최대 블록 수를 설정할 수 있습니다.

body = StreamField([
    ('heading', blocks.CharBlock(form_classname="title")),
    ('paragraph', blocks.RichTextBlock()),
    ('image', ImageBlock()),
], min_num=2, max_num=5)

또는 동일하게:

class CommonContentBlock(blocks.StreamBlock):
    heading = blocks.CharBlock(form_classname="title")
    paragraph = blocks.RichTextBlock()
    image = ImageBlock()

    class Meta:
        min_num = 2
        max_num = 5

block_counts 옵션은 특정 블록 유형에 대한 최소 또는 최대 수를 설정하는 데 사용할 수 있습니다. 이는 블록 이름을 min_nummax_num 필드(선택 사항)가 있는 사전으로 매핑하는 사전을 허용합니다. 예를 들어, 1개에서 3개 사이의 ‘heading’ 블록을 허용하려면:

body = StreamField([
    ('heading', blocks.CharBlock(form_classname="title")),
    ('paragraph', blocks.RichTextBlock()),
    ('image', ImageBlock()),
], block_counts={
    'heading': {'min_num': 1, 'max_num': 3},
})

또는 동일하게:

class CommonContentBlock(blocks.StreamBlock):
    heading = blocks.CharBlock(form_classname="title")
    paragraph = blocks.RichTextBlock()
    image = ImageBlock()

    class Meta:
        block_counts = {
            'heading': {'min_num': 1, 'max_num': 3},
        }

블록별 템플릿

기본적으로 각 블록은 간단하고 최소한의 HTML 마크업으로 렌더링되거나 전혀 마크업되지 않습니다. 예를 들어, CharBlock 값은 일반 텍스트로 렌더링되는 반면, ListBlock은 자식 블록을 <ul> 래퍼로 출력합니다. 이를 사용자 지정 HTML 렌더링으로 재정의하려면 블록에 template 인수를 전달하여 렌더링할 템플릿 파일의 파일 이름을 지정할 수 있습니다. 이는 StructBlock에서 파생된 사용자 지정 블록 유형에 특히 유용합니다.

('person', blocks.StructBlock(
    [
        ('first_name', blocks.CharBlock()),
        ('surname', blocks.CharBlock()),
        ('photo', ImageBlock(required=False)),
        ('biography', blocks.RichTextBlock()),
    ],
    template='myapp/blocks/person.html',
    icon='user'
))

또는 StructBlock의 서브클래스로 정의된 경우:

class PersonBlock(blocks.StructBlock):
    first_name = blocks.CharBlock()
    surname = blocks.CharBlock()
    photo = ImageBlock(required=False)
    biography = blocks.RichTextBlock()

    class Meta:
        template = 'myapp/blocks/person.html'
        icon = 'user'

템플릿 내에서 블록 값은 value 변수로 액세스할 수 있습니다.

{% load wagtailimages_tags %}

<div class="person">
    {% image value.photo width-400 %}
    <h2>{{ value.first_name }} {{ value.surname }}</h2>
    {{ value.biography }}
</div>

first_name, surname, photo, biography 는 자체적으로 블록으로 정의되어 있으므로 다음과 같이 작성할 수도 있습니다.

{% load wagtailcore_tags wagtailimages_tags %}

<div class="person">
    {% image value.photo width-400 %}
    <h2>{% include_block value.first_name %} {% include_block value.surname %}</h2>
    {% include_block value.biography %}
</div>

{{ my_block }} 을 작성하는 것은 {% include_block my_block %} 과 거의 동일하지만, 짧은 형식은 더 제한적입니다. request 또는 page 와 같은 호출 템플릿의 변수를 전달하지 않기 때문입니다. 이러한 이유로 자체 HTML을 렌더링하지 않는 간단한 값에만 사용하는 것이 좋습니다. 예를 들어, PersonBlock이 다음 템플릿을 사용했다면:

{% load wagtailimages_tags %}

<div class="person">
    {% image value.photo width-400 %}
    <h2>{{ value.first_name }} {{ value.surname }}</h2>

    {% if request.user.is_authenticated %}
        <a href="#">이 사람에게 연락하기</a>
    {% endif %}

    {{ value.biography }}
</div>

request.user.is_authenticated 테스트는 {{ ... }} 태그를 통해 블록을 렌더링할 때 올바르게 작동하지 않습니다.

{# 잘못됨: #}

{% for block in page.body %}
    {% if block.block_type == 'person' %}
        <div>
            {{ block }}
        </div>
    {% endif %}
{% endfor %}

{# 올바름: #}

{% for block in page.body %}
    {% if block.block_type == 'person' %}
        <div>
            {% include_block block %}
        </div>
    {% endif %}
{% endfor %}

Django의 {% include %} 태그와 마찬가지로 {% include_block %}{% include_block my_block with foo="bar" %} 구문을 통해 포함된 템플릿에 추가 변수를 전달할 수 있습니다.

{# 페이지 템플릿에서: #}

{% for block in page.body %}
    {% if block.block_type == 'person' %}
        {% include_block block with classname="important" %}
    {% endif %}
{% endfor %}

{# PersonBlock 템플릿에서: #}

<div class="{{ classname }}">
    ...
</div>

{% include_block my_block with foo="bar" only %} 구문도 지원되며, foo 를 제외한 부모 템플릿의 변수는 자식 템플릿에 전달되지 않도록 지정합니다.

부모 템플릿에서 변수를 전달하는 것 외에도 블록 서브클래스는 get_context 메서드를 재정의하여 자체 추가 템플릿 변수를 전달할 수 있습니다.

import datetime

class EventBlock(blocks.StructBlock):
    title = blocks.CharBlock()
    date = blocks.DateBlock()

    def get_context(self, value, parent_context=None):
        context = super().get_context(value, parent_context=parent_context)
        context['is_happening_today'] = (value['date'] == datetime.date.today())
        return context

    class Meta:
        template = 'myapp/blocks/event.html'

이 예시에서 is_happening_today 변수는 블록 템플릿 내에서 사용할 수 있게 됩니다. parent_context 키워드 인수는 블록이 {% include_block %} 태그를 통해 렌더링될 때 사용할 수 있으며, 호출 템플릿에서 전달된 변수 사전입니다.

마찬가지로 get_template 메서드를 정의하여 블록 값에 따라 템플릿을 동적으로 선택할 수 있습니다.

import datetime

class EventBlock(blocks.StructBlock):
    title = blocks.CharBlock()
    date = blocks.DateBlock()

    def get_template(self, value, context=None):
        if value["date"] > datetime.date.today():
            return "myapp/blocks/future_event.html"
        else:
            return "myapp/blocks/event.html"

StructBlock 뿐만 아니라 모든 블록 유형은 template 속성을 지원합니다. 그러나 CharBlockIntegerBlock 과 같은 기본 Python 데이터 유형을 처리하는 블록의 경우 템플릿이 적용될 수 있는 위치에 몇 가지 제한이 있습니다. 자세한 내용은 StreamField BoundBlocks와 값에 관하여를 참조하십시오.

블록 미리 보기 구성

StreamField 블록은 편집기에서 블록 선택기 내에 표시될 미리 보기를 가질 수 있습니다. 이 기능을 활성화하려면 미리 보기 값과 템플릿을 구성해야 합니다. 사용자가 콘텐츠에 적합한 블록을 선택하는 데 도움이 되도록 설명을 추가할 수도 있습니다.

블록을 인스턴스화할 때 preview_value, preview_template, description 키워드 인수를 전달하여 이를 수행할 수 있습니다.

("quote", blocks.StructBlock(
    [
        ("text", blocks.TextBlock()),
        ("source", blocks.CharBlock()),
    ],
    preview_value={"text": "이것은 역사상 가장 멋진 CMS입니다.", "source": "윌리 왜그테일"},
    preview_template="myapp/previews/blocks/quote.html",
    description="출처를 밝힌 인용구로, 인용 블록으로 렌더링됩니다.",
))

블록의 Meta 클래스에 preview_value, preview_template, description 을 속성으로 설정할 수도 있습니다. 예를 들어:

class QuoteBlock(blocks.StructBlock):
    text = blocks.TextBlock()
    source = blocks.CharBlock()

    class Meta:
        preview_value={"text": "이것은 역사상 가장 멋진 CMS입니다.", "source": "윌리 왜그테일"}
        preview_template="myapp/previews/blocks/quote.html"
        description="출처를 밝힌 인용구로, 인용 블록으로 렌더링됩니다."

미리 보기 옵션에 대한 자세한 내용은 해당 get_preview_value(), get_preview_template()get_description() 메서드와 get_preview_context() 메서드를 참조하십시오.

특히 get_preview_value() 메서드는 데이터베이스에서와 같이 동적 미리 보기 값을 제공하도록 재정의할 수 있습니다.

from myapp.models import Quote


class QuoteBlock(blocks.StructBlock):
    ...

    def get_preview_value(self, value):
        quote = Quote.objects.first()
        return {"text": quote.text, "source": quote.source}

전역 미리 보기 템플릿 재정의

대부분의 경우 블록별 템플릿에 설명된 대로 template 또는 get_template 을 통해 이미 구성한 블록의 실제 템플릿을 사용하려고 할 것입니다. 그러나 이러한 템플릿은 블록에 대한 HTML 조각일 뿐이며, 미리 보기에는 템플릿으로 완전한 HTML 문서가 필요합니다.

각 블록에 대해 preview_template 을 지정하는 것을 피하려면 기본 템플릿을 전역적으로 재정의할 수 있습니다. templates 디렉터리 중 하나( wagtail 앱보다 우선 순위가 높음) 내에 wagtailcore/shared/block_preview.html 템플릿을 다음 내용으로 만들어 이를 수행할 수 있습니다.

{% extends "wagtailcore/shared/block_preview.html" %}
{% load static %}

{% block css %}
    {{ block.super }}
    <link rel="stylesheet" href="{% static 'css/my-styles.css' %}">
{% endblock %}

{% block js %}
    {{ block.super }}
    <script src="{% static 'js/my-script.js' %}"></script>
{% endblock %}

템플릿 재정의에 대한 자세한 내용은 Django의 How to override templates 가이드를 참조하십시오.

전역 wagtailcore/shared/block_preview.html 재정의는 기본적으로 모든 블록에 사용됩니다. 특정 블록에 다른 템플릿을 사용하려면 preview_template 을 지정할 수 있으며, 이는 우선 순위를 가집니다.

특정 블록에 대한 미리 보기 끄기

블록에 대한 미리 보기를 끄려면 블록 클래스에서 is_previewable = False 를 설정합니다.

class ConfigBlock(blocks.StructBlock):
    ...
    is_previewable = False

사용자 지정

모든 블록 유형은 프런트엔드 및 폼 표현을 렌더링하고 데이터베이스에서 값을 저장하고 검색하기 위한 공통 API를 구현합니다. 다양한 블록 클래스를 서브클래스화하고 이러한 메서드를 재정의하여 StructBlock 폼 필드의 레이아웃을 수정하는 것부터 블록을 결합하는 완전히 새로운 방법을 구현하는 것까지 모든 종류의 사용자 지정이 가능합니다. 자세한 내용은 사용자 정의 StreamField 블록을 만드는 방법를 참조하십시오.

StreamField 데이터 수정

StreamField의 값은 목록처럼 동작하며, 인스턴스를 데이터베이스에 다시 저장하기 전에 블록을 삽입, 덮어쓰기 및 삭제할 수 있습니다. 새 항목은 (블록 유형, 값) 튜플로 목록에 작성할 수 있습니다. 다시 읽을 때 BoundBlock 객체로 반환됩니다.

# 첫 번째 블록을 'heading' 유형의 새 블록으로 바꿉니다.
my_page.body[0] = ('heading', "내 이야기")

# 마지막 블록 삭제
del my_page.body[-1]

# 스트림에 리치 텍스트 블록 추가
my_page.body.append(('paragraph', "<p>그리고 그들은 모두 행복하게 살았습니다.</p>"))

# 업데이트된 데이터를 데이터베이스에 다시 저장
my_page.save()

StructBlock을 확장하는 블록이 StreamField의 값 내에서 사용될 경우, 이 블록의 값은 Python 사전(블록의 .to_python 메서드에서 허용하는 것과 유사)으로 제공될 수 있습니다.


from wagtail import blocks

class UrlWithTextBlock(blocks.StructBlock):
   url = blocks.URLBlock()
   text = blocks.TextBlock()

# 콘텐츠 내에서 이 블록 사용

data = {
    'url': 'https://github.com/wagtail/',
    'text': '매우 흥미롭고 유용한 저장소'
}

# 정의된 이 블록 유형의 인덱스와 함께 새 블록을 스트림에 튜플로 추가
my_page.body.append(('url', data))
my_page.save()

이름으로 블록 검색

StreamField 값은 주어진 이름의 모든 블록을 검색하기 위한 blocks_by_name 메서드를 제공합니다.

my_page.body.blocks_by_name('heading')  # 'heading' 블록 목록 반환

인수 없이 blocks_by_name 을 호출하면 블록 이름을 해당 이름의 블록 목록에 매핑하는 사전과 유사한 객체가 반환됩니다. 이는 인수를 전달할 수 없는 템플릿 코드에서 특히 유용합니다.

<h2>목차</h2>
<ol>
    {% for heading_block in page.body.blocks_by_name.heading %}
        <li>{{ heading_block.value }}</li>
    {% endfor %}
</ol>

first_block_by_name 메서드는 스트림에서 주어진 이름의 첫 번째 블록을 반환하거나 일치하는 블록이 없으면 None 을 반환합니다.

hero_image = my_page.body.first_block_by_name('image')

first_block_by_name 은 인수 없이 호출하여 사전과 유사한 매핑을 반환할 수도 있습니다.

<div class="hero-image">{{ page.body.first_block_by_name.image }}</div>

사용자 지정 유효성 검사

블록에 사용자 지정 유효성 검사 논리를 추가하려면 블록의 clean 메서드를 재정의합니다. 자세한 내용은 StreamField 유효성 검사을 참조하십시오.

마이그레이션

StreamField 데이터는 단일 JSON 필드로 저장되며, 공식적인 데이터베이스 구조로 정렬되지 않으므로 StreamField의 데이터 구조를 변경하거나 다른 필드 유형으로 변환할 때 데이터 마이그레이션을 작성해야 하는 경우가 많습니다. StreamField가 Django의 마이그레이션 시스템과 상호 작용하는 방법과 리치 텍스트를 StreamField로 마이그레이션하는 방법에 대한 자세한 내용은 StreamField 마이그레이션를 참조하십시오.