(streamfield_topic)= # 혼합 콘텐츠에 StreamField 사용하는 방법 StreamField는 부제목, 이미지, 인용구, 비디오와 같이 고정된 구조를 따르지 않는 페이지(예: 블로그 게시물 또는 뉴스 기사)에 적합한 콘텐츠 편집 모델을 제공합니다. 또한 지도 및 차트(또는 프로그래밍 블로그의 경우 코드 스니펫)와 같은 더 전문화된 콘텐츠 유형에도 적합합니다. 이 모델에서 이러한 다양한 콘텐츠 유형은 '블록' 시퀀스로 표현되며, 원하는 순서로 반복하고 정렬할 수 있습니다. StreamField에 대한 추가 배경 정보와 기사 본문에 리치 텍스트 필드 대신 StreamField를 사용하는 이유는 블로그 게시물 [리치 텍스트 필드 및 더 빠른 말](https://torchbox.com/blog/rich-text-fields-and-faster-horses/)을 참조하십시오. StreamField는 또한 간단한 하위 블록 모음(예: 이름, 성, 사진으로 구성된 '사람' 블록)부터 자체 편집 인터페이스가 있는 완전히 사용자 지정된 구성 요소에 이르기까지 자체 블록 유형을 정의하기 위한 풍부한 API를 제공합니다. 데이터베이스 내에서 StreamField 콘텐츠는 JSON으로 저장되어 필드의 전체 정보 콘텐츠가 HTML 표현뿐만 아니라 보존되도록 합니다. ## StreamField 사용 `StreamField` 는 다른 필드와 마찬가지로 페이지 모델 내에 정의할 수 있는 모델 필드입니다. ```python 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_block_reference)에서 찾을 수 있습니다. ```{note} StreamField는 RichTextField와 같은 다른 필드 유형을 직접 대체하지 않습니다. 기존 필드를 StreamField로 마이그레이션해야 하는 경우 [](streamfield_migrating_richtext)를 참조하십시오. ``` ```{note} 블록 정의는 모델 필드와 유사하게 보이지만 동일하지는 않습니다. 블록은 StreamField 내에서만 유효합니다. 모델 필드 대신 사용하면 작동하지 않습니다. ``` (streamfield_template_rendering)= ## 템플릿 렌더링 StreamField는 스트림 콘텐츠 전체와 각 개별 블록에 대한 HTML 표현을 제공합니다. 이 HTML을 페이지에 포함하려면 `{% include_block %}` 태그를 사용하십시오. ```html+django {% load wagtailcore_tags %} ... {% include_block page.body %} ``` 기본 렌더링에서 스트림의 각 블록은 `
` 요소로 래핑됩니다(여기서 `my_block_name` 은 StreamField 정의에 지정된 블록 이름입니다). 자체 HTML 마크업을 제공하려면 필드의 값을 반복하고 각 블록에 대해 `{% include_block %}` 을 호출할 수 있습니다. ```html+django {% load wagtailcore_tags %} ...
{% for block in page.body %}
{% include_block block %}
{% endfor %}
``` 특정 블록 유형의 렌더링을 더 세밀하게 제어하려면 각 블록 객체는 `block_type` 및 `value` 속성을 제공합니다. ```html+django {% load wagtailcore_tags %} ...
{% for block in page.body %} {% if block.block_type == 'heading' %}

{{ block.value }}

{% else %}
{% include_block block %}
{% endif %} {% endfor %}
``` ## 블록 결합 StreamField 내에서 내장 블록 유형을 직접 사용하는 것 외에도 다양한 방식으로 하위 블록을 결합하여 새 블록 유형을 구성할 수 있습니다. 이에 대한 예시는 다음과 같습니다. - 이미지 선택기와 텍스트 필드로 구성된 "캡션이 있는 이미지" 블록 - 콘텐츠 작성자가 다른 페이지에 대한 링크를 원하는 만큼 제공할 수 있는 "관련 링크" 섹션 - 각 슬라이드가 이미지, 텍스트 또는 비디오일 수 있고 원하는 순서로 정렬될 수 있는 슬라이드쇼 블록 새 블록 유형이 이러한 방식으로 구축되면 내장 블록 유형이 사용되는 모든 곳에서 사용할 수 있습니다. 다른 블록 유형의 구성 요소로 사용하는 것을 포함합니다. 예를 들어, 각 항목이 "캡션이 있는 이미지" 블록인 이미지 갤러리 블록을 정의할 수 있습니다. ### StructBlock `StructBlock` 을 사용하면 여러 '자식' 블록을 그룹화하여 단일 블록으로 표시할 수 있습니다. 자식 블록은 `(name, block_type)` 튜플 목록으로 `StructBlock` 에 전달됩니다. ```{code-block} python :emphasize-lines: 2-7 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의 값은 정의에 지정된 블록 이름에 해당하는 키가 있는 사전과 유사한 객체입니다. ```html+django
{% for block in page.body %} {% if block.block_type == 'person' %}
{% image block.value.photo width-400 %}

{{ block.value.first_name }} {{ block.value.surname }}

{{ block.value.biography }}
{% else %} (다른 블록 유형에 대한 렌더링) {% endif %} {% endfor %}
``` ### `StructBlock` 서브클래싱 StructBlock의 자식 블록 목록을 `StreamField` 정의 내에 배치하면 읽기 어려울 수 있으며, 동일한 블록을 여러 곳에서 재사용하기 어렵게 만듭니다. 대안으로 `StructBlock` 을 서브클래스화하고 자식 블록을 서브클래스의 속성으로 정의할 수 있습니다. 위 예시의 'person' 블록은 다음과 같이 다시 작성할 수 있습니다. ```python class PersonBlock(blocks.StructBlock): first_name = blocks.CharBlock() surname = blocks.CharBlock() photo = ImageBlock(required=False) biography = blocks.RichTextBlock() ``` `PersonBlock` 은 내장 블록 유형과 동일한 방식으로 `StreamField` 정의에서 사용할 수 있습니다. ```python body = StreamField([ ('person', PersonBlock()), ('heading', blocks.CharBlock(form_classname="title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageBlock()), ]) ``` (block_icons)= ### 블록 아이콘 콘텐츠 작성자가 StreamField에 새 블록을 추가하는 데 사용하는 메뉴에서 각 블록 유형에는 연결된 아이콘이 있습니다. StructBlock 및 기타 구조 블록 유형의 경우 이러한 블록의 목적이 프로젝트에 특화되어 있으므로 플레이스홀더 아이콘이 사용됩니다. 사용자 지정 아이콘을 설정하려면 `icon` 옵션을 `StructBlock` 에 대한 키워드 인수 또는 `Meta` 클래스의 속성으로 전달합니다. ```{code-block} python :emphasize-lines: 7 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()), ]) ``` ```{code-block} python :emphasize-lines: 7-8 class PersonBlock(blocks.StructBlock): first_name = blocks.CharBlock() surname = blocks.CharBlock() photo = ImageBlock(required=False) biography = blocks.RichTextBlock() class Meta: icon = 'user' ``` 기본적으로 제공되는 아이콘 목록은 [아이콘 개요](icons)를 참조하십시오. 프로젝트별 아이콘도 [스타일 가이드](styleguide)에 표시됩니다. ### ListBlock `ListBlock` 은 반복되는 블록을 정의하여 콘텐츠 작성자가 특정 블록 유형의 인스턴스를 원하는 만큼 삽입할 수 있도록 합니다. 예를 들어, 여러 이미지로 구성된 '갤러리' 블록은 다음과 같이 정의할 수 있습니다. ```{code-block} python :emphasize-lines: 2 body = StreamField([ ('gallery', blocks.ListBlock(ImageBlock())), ('heading', blocks.CharBlock(form_classname="title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageBlock()), ]) ``` StreamField의 콘텐츠를 다시 읽을 때(예: 템플릿 렌더링 시) ListBlock의 값은 자식 값 목록입니다. ```html+django
{% for block in page.body %} {% if block.block_type == 'gallery' %} {% else %} (다른 블록 유형에 대한 렌더링) {% endif %} {% endfor %}
``` ### StreamBlock `StreamBlock` 은 StreamField 자체와 동일한 메커니즘을 통해 혼합하고 재정렬할 수 있는 다양한 유형의 하위 블록 시퀀스를 정의합니다. 예를 들어, 이미지 및 비디오 슬라이드를 모두 지원하는 캐러셀은 다음과 같이 정의할 수 있습니다. ```{code-block} python :emphasize-lines: 2-5 body = StreamField([ ('carousel', blocks.StreamBlock([ ('image', ImageBlock()), ('video', EmbedBlock()), ])), ('heading', blocks.CharBlock(form_classname="title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageBlock()), ]) ``` `StreamBlock` 은 `StructBlock` 과 동일한 방식으로 서브클래스화할 수 있으며, 자식 블록은 클래스의 속성으로 지정됩니다. ```python class CarouselBlock(blocks.StreamBlock): image = ImageBlock() video = EmbedBlock() class Meta: icon = 'image' ``` 이러한 방식으로 정의된 StreamBlock 서브클래스는 블록 유형 목록 대신 `StreamField` 정의에 전달될 수도 있습니다. 이를 통해 여러 페이지 유형에서 사용할 공통 블록 유형 세트를 설정할 수 있습니다. ```python 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_type` 및 `value` 속성이 있는 블록 객체 시퀀스입니다. ```html+django
{% for block in page.body %} {% if block.block_type == 'carousel' %} {% else %} (다른 블록 유형에 대한 렌더링) {% endif %} {% endfor %}
``` ### 블록 수 제한 기본적으로 StreamField는 무제한의 블록을 포함할 수 있습니다. `StreamField` 또는 `StreamBlock` 의 `min_num` 및 `max_num` 옵션을 사용하면 최소 또는 최대 블록 수를 설정할 수 있습니다. ```python body = StreamField([ ('heading', blocks.CharBlock(form_classname="title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageBlock()), ], min_num=2, max_num=5) ``` 또는 동일하게: ```python 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_num` 및 `max_num` 필드(선택 사항)가 있는 사전으로 매핑하는 사전을 허용합니다. 예를 들어, 1개에서 3개 사이의 'heading' 블록을 허용하려면: ```python body = StreamField([ ('heading', blocks.CharBlock(form_classname="title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageBlock()), ], block_counts={ 'heading': {'min_num': 1, 'max_num': 3}, }) ``` 또는 동일하게: ```python 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}, } ``` (streamfield_per_block_templates)= ## 블록별 템플릿 기본적으로 각 블록은 간단하고 최소한의 HTML 마크업으로 렌더링되거나 전혀 마크업되지 않습니다. 예를 들어, CharBlock 값은 일반 텍스트로 렌더링되는 반면, ListBlock은 자식 블록을 `