StreamField 내부에 또 StreamField를 적용하는 방법에 대해서 기술해보려고 한다. 

페이지 하나를 여러가지 용도로 사용하여 용도에 따라 양식이 달라야하고 양식에 다중 양식을 추가하려고 해서 이렇게 작성을 해보았다. 

 

일단 중첩 StreamField를 위해 작성을 했기 때문에 완성된 모델은 아니다. 참고용으로 사용하는 것이 제일 적당하다고 생각한다. 

 

from wagtail.images.blocks import ImageChooserBlock
from wagtail.core.blocks import (CharBlock, StreamBlock, StructBlock,)
from wagtail.core.fields import StreamField


# StudyCardBlock 요소
class QuizBlock(StructBlock):
    title = CharBlock(required=True)
    option_1 = CharBlock(required=True)
    option_2 = CharBlock(required=True)
    option_3 = CharBlock(required=True)
    option_4 = CharBlock(required=True)


class CodeBlock(StructBlock):
    title = title = CharBlock(classname="title", required=False)
    content = CharBlock(required=False)


class StudyContentBlock(StreamBlock):
    quiz = QuizBlock()
    code = CodeBlock()


# 카드 종류 양식
class NoticeCardBlock(StructBlock):
    title = CharBlock(classname="title", required=False)
    content = CharBlock(required=False)


class StudyCardBlock(StructBlock):
    title = CharBlock(classname="title", required=False)
    image = ImageChooserBlock(required=False)
    body = StudyContentBlock()


# CardBlock 
class CardBlock(StreamBlock):
    notice_block = NoticeCardBlock()
    study_block = StudyCardBlock()

 일단 StreamField는 Field 대신 Block을 사용한다. CardBlock이라는 곳에 notice card 양식과 study card 양식을 분리해 놓았다. StreamBlock과 StructBlock의 차이점은 StreamBlock은 안에 정의 된 2개의 모델중 어떤 것을 선택 할지 정의해 놓은 것이다. Struct 블록은 말 그대로 구조 모델을 뜻한다. 즉 Struct 블록에서 정의 된 모델들을 StreamBlock에 정의 해놓으면 선택을 할 수 있다는 것이다. (사람들이 이해가 되지 않을 수도 있을 것 같아 사진으로 남겨놓는다.)

 

StreamBlock

StructBlock

notice card양식은 추가 필드들이 필요 없으므로 StructBlock으로 작성해 놓았다. -> NoticeCardBlock

study card 양식 같은 경우에는 내부에 code와 quiz를 여러개 작성해야 하므로 StreamBlock으로 정의 해 놓았다. -> StudyCardBlock

 

다시 quiz와 code의 StructBlock을 선택할 수 있게 StreamBlock을 만들었다. 그것이 바로 StudyContentBlock이다. 

그리고 여기에는 code와 quiz의 StructBlock을 정의 해놓는다. 

 

그리고 models.py에 가서 CardBlock만 정의 해 놓으면 된다. 

 

    from wagtail.core.fields import StreamField
    from wagtail.admin.edit_handlers import StreamFieldPanel
    from card.blocks import CardBlock    
    
    
    body = StreamField(
              CardBlock(), verbose_name="카드 양식 선택", blank=True
          )
    
    content_panels = Page.content_panels + [
        StreamFieldPanel('body'),
    ]

body라는 이름의 변수로 StreamField CardBlock을 가져오고 정의해 놓았다. 여기까지가 모델 정의이고 이제 이것을 html에서 표현 하는 방법에 대해서 작성해보려고 한다. 

 

templates/card/card_page.html

{% for card_type in page.body %}
                {% if card_type.block_type == 'study_block' %}
                    {% with study_card=card_type.value %}
                    <!--Tab-->
                    <div class="tabs is-fullwidth">
                        <ul>
                            <li class="is-active" data-target="study_card_image">
                                <a><span>강의</span></a>
                            </li>
                            <li data-target="study_card_code">
                                <a><span>실습</span></a>
                            </li>
                            <li data-target="study_card_quiz">
                                <a><span>퀴즈</span></a>
                            </li>
                            <li data-target="study_card_question">
                                <a><span>질문</span></a>
                            </li>
                        </ul>
                    </div>

                    <!--Tab Content-->
                    <div class="px-2" id="tab-content"> 
                        <div id="study_card_image">
                            <h1 class="title">{{ study_card.title }}</h1>
                            {% image study_card.image fill-1280x960 as photo %}
                            <img src="{{ photo.url }}" alt="">
                        </div>
                        {% for content in study_card.body %}
                            {% if content.block_type == 'code' %}
                                {% with code=content.value %}
                                    <div id="study_card_code" class="is-hidden">
                                        {{ code.title }}
                                        {{ code.content }}
                                    </div>
                                {% endwith %}
                            {% endif %}
                            {% if content.block_type == 'quiz' %}
                                {% with quiz=content.value %}
                                    <div id="study_card_quiz" class="is-hidden">
                                        {{ quiz.title }}
                                        {{ quiz.option_1 }}
                                        {{ quiz.option_2 }}
                                        {{ quiz.option_3 }}
                                        {{ quiz.option_4 }}
                                    </div>
                                {% endwith %}
                            {% endif %}
                        {% endfor %}
                        <div id="study_card_question" class="is-hidden">
                            <p>강의 질문</p>
                        </div>
                    </div>
                    {% endwith %}
                {% endif %}
            {% endfor %}

파일이름은 본인의 Project에 맞게 수정 하면 될 것이다. 일단 block_type을 알아야 하는데 block_type은 StreamBlock에 정의되어 있는 Block 이름이다. 여기서는 중첩 StreamField를 설명하기 위해 study_block을 사용했다.

{% for card_type in page.body %}
      {% if card_type.block_type == 'study_block' %}
             {% with study_card=card_type.value %}

여기는 page.body변수의 값을 card_type이라는 이름으로 가져오는데 card_type의 block_type이 study_block이면 다음을 실행하라는 뜻이다. 그리고 study_block의 value를 study_card라고 부르려고 with로 정의 해 놓았다. else를 사용하면 notice_block이 나올 것이다. 

 

{% for content in study_card.body %}
	{% if content.block_type == 'code' %}
		{% with code=content.value %}
			<div id="study_card_code" class="is-hidden">
				{{ code.title }}
				{{ code.content }}
			</div>
		{% endwith %}
	{% endif %}
	{% if content.block_type == 'quiz' %}
		{% with quiz=content.value %}
        	<div id="study_card_quiz" class="is-hidden">
				{{ quiz.title }}
				{{ quiz.option_1 }}
				{{ quiz.option_2 }}
				{{ quiz.option_3 }}
				{{ quiz.option_4 }}
			</div>
		{% endwith %}
	{% endif %}
{% endfor %}

이제 StreamBlock 내부의 StreamBlock의 값을 가져오는 방법이다. study_card는 위에서 모델에서 정의한 StudyCardBlock을 가르키므로 그곳의 body변수를 content라는 이름으로 불러온다. content에서는 quiz와 code Block이 있으므로 이것을 다시 block_type을 통해 구별해준다. 그리고 이제 content.value를 통해 값 전부를 가져오는데 예를 들자면 with code=content.value면 content.value의 모든 값들을 code라는 이름으로 저장하는 것이다. code에는 모델 변수명과 값들이 전부 들어 있다. 값만 출력하고 싶다면 code.변수명으로 작성해주면 된다. 

 

다음에 다시 한번 읽어보고 설명을 수정해야겠다. 의식의 흐름대로 작성하니 나빼고 이해가 안될 수도 있을 것이라는 생각이 든다. 

+ Recent posts