Custom Widget을 작성하는 방법에 대해서 기술하려고 한다.

아무리 찾아봐도 나오지 않아 그냥 생각대로 해봤더니 성공했다. 

Wagtail에서 Codemirror Widget을 만들고 적용하는 방법으로 해결 했다. 

 

1. Widget 만들기

myapp/widgets.py

from django.forms import widgets
from wagtail.utils.widgets import WidgetWithScript


#custom widget 적용
class MyCustomWidget(WidgetWithScript, widgets.Textarea):
    template_name = 'widgets/codingwidget.html'

    def __init__(self, attrs=None):
        default_attrs = {}
        if attrs:
            default_attrs.update(attrs)

        super().__init__(default_attrs)
        
    class Media:
        js = ('test.js',)

Codemirror를 적용하기 위해서는 Textarea가 필요하므로 django widget에서 textarea.html을 가져와야 한다. 

(widget만드는 방법은 wagtail 자체에서 widget 만드는 부분을 참고하여 작성해보았다.)

 

default_attrs는 정의 할 attrs값을 적어주면 되는데 id값은 정의가 불가능 하다. (wagtail에서 widget을 구별할 때 id값으로 구별하기 때문에 그렇다.)

ex) default_attrs = {'class':'test'} 이런식으로 태그의 속성 값을 정의 해줄 수가 있다.

 

template_name에서는 사용할 widget template을 적어주면된다. 

 

2. 사용할 Widget 가져오기

django/forms/widgets/textarea.html

<textarea name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>
{% if widget.value %}{{ widget.value }}{% endif %}</textarea>

code mirror에서는 textarea가 필요하기 때문에 textarea widget을 가져왔다.

(다른 widget이 필요하다면 django widget공식문서를 확인하거나 wagtail이나 django widget에서 찾아보자!)

 

textarea.html을 그대로 복사해서 우리가 새로 만들 widget html에 붙여넣는다. 

 

templates/widgets/codingwidget.html

<textarea name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>
{% if widget.value %}{{ widget.value }}{% endif %}</textarea>

<script>
    var editor = CodeMirror.fromTextArea(document.getElementById('{{ widget.attrs.id }}'), {
        mode: "python",
        theme: "dracula",
        lineNumbers: true,
    });
    editor.setSize("100%","100%");
</script>

그리고 필요한 css나 js를 작성해 주면 된다. 

Codemirror javascript를 작성하였다. textarea의 id값이 필요한데 widget의 정보는 {{ widget }}을 통해 확인할 수 있다. 

{'name': 'mtf-0-testfield', 'is_hidden': False, 'required': False, 'value': 'ddd', 'attrs': {'cols': '40', 'rows': '10', 'id': 'id_mtf-0-testfield'}, 'template_name': 'widgets/codingwidget.html'}

내가 작성한 widget에서는 위와 같은 정보가 있었다. 접근 방법은 {{ widget.name }} 이런방법으로 접근하면 되는데

attrs는 {{ widget.attrs.id }}와 같이 한번 더 접근해주면 된다. 이걸로 우리는 codemirror 적용에 필요한 id값을 얻을 수가 있다. 

 

JS, CSS 추가방법

Widget에 JS파일이나 CSS 추가하고 싶다면 Custom Widget에 Midea를 설정해주면 된다. 

    class Media:
        js = ('test.js',)

주의)

'test.js'하고 뒤에 , 를 꼭 붙여야한다. 그래야지 'test.js'를 하나의 문자열로 인식하고 가져올 수 있다. 

 

3. Widget 적용 하기

myapp/models.py

class MytestRelated(Orderable):
    page = ParentalKey('CardPage', on_delete=models.CASCADE, related_name='mtf')

    testfield = models.TextField(null=True, blank=True)

    panels = [
        FieldPanel('testfield', widget=MyCustomWidget())
    ]

나는 Inlinepanel을 통해 여러개의 codemirror 필드를 사용하려고 ParentalKey를 사용하였다.

(필드 하나만 필요하다면 그냥 Page 모델에 필드를 정의하고 FieldPanel에 widget을 우리가 만든 widget을 적어주면된다.) 

CardPage라는 Page모델에 추가시켜보겠다. 

class CardPage(Page):

    content_panels = Page.content_panels + [
        InlinePanel('mtf', label="test"),
    ]

4. 결과

정상적으로 적용되어 있는 codemirror widget을 확인할 수 있다.

 

추가) Inline으로 작성된 필드들을 html에서 표현해보자. 

 

ParentalKey로 정의되어 있으므로 따로 return 값을 정의해주어야 한다. CardPage 맨 아래에 추가시켜보자.

@property
def codes(self):
  codes = [n.testfield for n in self.mtf.all()]
  return codes

이렇게 되면 html에서 page.codes로 가져올 수 있다. 

 

card_page.html

{% for code in page.codes %}
    {{ code }}
{% endfor  %}

 

번외) custom model 작성방법

모델을 custom 해서 새로 작성할 경우가 생긴다. 이럴경우 다음과 같이 model을 custom 하면 된다.

class MytestField(models.Model):
    testfield = models.TextField(null=True, blank=True)

    panels = [
        FieldPanel('testfield', widget=MyCustomWidget())
    ]

    class Meta:
        abstract = True

 

Wagtail Custom Field와 Panel을 작성하는 방법에 대해서 기술해보려고 한다. 

https://www.accordbox.com/blog/wagtail-tutorials-8-add-markdown-support/

 

Wagtail Tutorials #8: Add Markdown Support In Wagtail Blog

In this wagtail tutorial, I will show you how to add Markdown support to our wagtail blog.

www.accordbox.com

(참고 사이트)

 

사실 일반 목적의 필드를 커스텀 하고 싶을 때는 이후에 사용할 Widget을 커스텀 하는 방법을 사용하면 되지만 Field하고 Panel도 언젠가는 사용할 일이 올 것으로 생각하고 정리해본다. 

 

from django.db.models import TextField
from django.utils.translation import ugettext_lazy as _

from wagtail.admin.edit_handlers import FieldPanel


class MarkdownField(TextField):
    def __init__(self, **kwargs):
        super(MarkdownField, self).__init__(**kwargs)


class MarkdownPanel(FieldPanel):
    def __init__(self, field_name, classname="", widget=None, **kwargs):
        super(MarkdownPanel, self).__init__(
            field_name,
            classname=classname,
            widget=widget,
            **kwargs
        )

        if self.classname:
            if 'markdown' not in self.classname:
                self.classname += "markdown"
        else:
            self.classname = "markdown"

방법은 간단하다 위에 코드는 사이트에 나온 그대로 가져온 것이다. Django의 TextField를 상속받아서 재정의 해준 것이다. Panel로 마찬가지이다. 다만 panel에서 widget이나 classname을 정의해서 custom 할 수 있다. 이후에 소개할 custom widget을 custom panel에 적용해서 사용할 수도 있겠다. 

'Back-End > Wagtail, Django' 카테고리의 다른 글

Wagtail Video  (0) 2021.10.01
Wagtail Custom Widget (+Model)  (0) 2021.09.29
Wagtail hooks로 RichTextField에 구글 폰트 적용  (0) 2021.09.19
Wagtail Custom Block Field 만들기  (0) 2021.09.19
Wagtail 중첩 StreamField  (0) 2021.09.01

Wagtail hooks를 사용하면 Admin Page의 세부요소들을 편리하게 관리를 할 수 있는 것 같다. 한번 시간을 내서 wagtail hooks에 대해서 공부해보려고 한다. 일단 RichTextField에 원하는 폰트를 적용하는 방법에 대해서 기술 하려고 한다. 

 

https://docs.wagtail.io/en/stable/advanced_topics/customisation/extending_draftail.html

 

Extending the Draftail Editor — Wagtail Documentation 2.14.1 documentation

 

docs.wagtail.io

wagtail 공식문서 draftail을 창고하였다.

 

wagtail_hooks.py

import wagtail.admin.rich_text.editors.draftail.features as draftail_features
from wagtail.admin.rich_text.converters.html_to_contentstate import (
    InlineStyleElementHandler
)
from wagtail.core import hooks
from django.templatetags.static import static
from django.utils.html import format_html, format_html_join


@hooks.register("register_rich_text_features")
def register_font_styling(features):

    # step 1
    feature_name = 'font-type'
    type_ = 'FONT-TYPE'
    tag = 'div'

    # step 2
    control = {
        "type": type_,
        "label": "F",
        "description": "Font-type",
        "style": {
            'font-family': "'Nanum Pen Script', cursive",
            'src': "url('https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&display=swap')",
        },
    }

    # step3
    features.register_editor_plugin(
        "draftail", feature_name, draftail_features.InlineStyleFeature(control)
    )

    db_conversion = {
        "from_database_format": {tag: InlineStyleElementHandler(type_)},
        "to_database_format": {"style_map": {type_:{"element": tag, 'props': {'class': 'font-type'}}}}
    }

    features.register_converter_rule("contentstate", feature_name, db_conversion)
    features.default_features.append(feature_name)

hooks에 관련 된 내용은 wagtail_hooks.py에 정리를 해 놓았다. 

 

여기서 몇가지 주의할 점이 있다면 tag는 p태그 대신 div 태그로 했다. p태그로 하니 richtext랑 충돌나는 부분이 있는 것 같아서 제대로 적용되지 않았다. 이후 div태그로 바꿔줘서 적용하니 정상적으로 작동 되었다.

 

p태그로 했을 때 저장은 되었지만 edit를 할 때 

End of block reached without closing inline style elements 오류가 발생하였다. p태그를 적용하게 되면 폰트 적용을 할때 줄바꿈이 일어나므로 오류가 생기는 것으로 파악하고 있다. p태그 이외는 정상적으로 적용이 된다. 태그는 상황에 맞게 바꿔서 사용을 하면 될 것으로 생각한다.

 

그리고 control 부분에 style을 살펴보면 폰트를 가져올 때, 원래라면 link 태그를 통해 css를 가져오나 여기선 style 내부에 정의를 해줘야 하므로 src로 가져왔다. 

 

'Back-End > Wagtail, Django' 카테고리의 다른 글

Wagtail Custom Widget (+Model)  (0) 2021.09.29
Wagtail Custom Field, Panel  (0) 2021.09.29
Wagtail Custom Block Field 만들기  (0) 2021.09.19
Wagtail 중첩 StreamField  (0) 2021.09.01
Wagtail Custom User Field  (0) 2021.09.01

Code mirror를 적용해야해서 Wagtail Admin 에서 Code Block Field를 만들어 보기로 했다. 많은 시행착오가 있었지만 결국 해결이 되어 적용방법을 적어보려고 한다. 

https://docs.wagtail.io/en/latest/advanced_topics/customisation/streamfield_blocks.html

 

How to build custom StreamField blocks — Wagtail Documentation 2.15a0 documentation

 

docs.wagtail.io

Wagtail 공식 문서 custom StreamField Block 이다. Wagtail 문서에 찾는 모든 것이 있는데 전부 영어로 되어 있어서 간과하고 지나간 것이 많다. 꼼꼼하게 읽어보니 필요한 내용을 찾을 수 있었다. 

 

문서에서 자바스크립트를 적용한 forms를 만드는 방법에 대해서 나와 있었다.

문서의 코드보단 직접 적용한 코드를 적어보려고 한다. 

 

 

1. Custom Block 정의

Custom block을 하나 만들어서 CodingBlock으로 적용했다. 

card/widgets.html

from wagtail.core.blocks import FieldBlock
from django import forms


class CodingBlock(FieldBlock):
    
    def __init__(self, required=True, help_text=None, **kwargs):
        self.field = forms.CharField(required=False, widget=forms.Textarea()) # attrs={'class':'codingblock'}
        super().__init__(**kwargs)

Wagtail에서는 FieldBlock을 상속받아 나만의 custom block을 만들 수 있는데 Code mirror에서는 Textarea가 필요해서 Textarea로 만들었다. 바로 forms.Textarea()로 적용하려 했더니 되지 않아서 검색해 본 결과 Textarea를 적용하려면 위에 같이 적용해야한다고 한다.

참고로 Custom block을 설정하면 추가 attrs를 적용할 수 있다. 

self.field = forms.CharField(required=False, widget=forms.Textarea(attrs={'class':'codingblock'}))

이런식으로 적용하면 class를 codingblock이라는 textarea를 만들 수가 있다. 하지만 id값은 재정의 되지 않는다. 

 

2. Block 정의

card/models.py

from django import forms
from django.utils.functional import cached_property
from card.widgets import CodingBlock
from django.forms import Media


class CodeBlock(StructBlock):
    code = CodingBlock()


class CodeBlockAdapter(StructBlockAdapter):
    js_constructor = 'card.blocks.CodeBlock'

    @cached_property
    def media(self):
        structblock_media = super().media
        return forms.Media(
            js=structblock_media._js + ['js/mycode.js'],
        )

register(CodeBlockAdapter(), CodeBlock)

StructBlock을 상속받아 CodeBlock을 만들어 준다. 

wagtail에는 StructBlockAdapter라는 것이 있다. 이걸 사용해서 자신이 원하는 블럭의 js파일을 적용 할 수 있다. 

return forms.Media(
            js=structblock_media._js + ['js/mycode.js'],
        )

여기에서 자신이 원하는 js 파일을 넣어주면 된다. 

 

3. JavaScript 정의

static/js/mycode.js

class CodeBlockDefinition extends window.wagtailStreamField.blocks.StructBlockDefinition {
    render(placeholder, prefix, initialState, initialError) {
        const block = super.render(placeholder, prefix, initialState, initialError);
        const textareaid = prefix + '-code';

        var editor = CodeMirror.fromTextArea(document.getElementById(textareaid), {
            mode: "python",
            theme: "dracula",
            lineNumbers: true,
        });
        editor.setSize("100%","100%"); //사이즈 설정으로 codemirror 크기를 조절할 수 있다.
        return block;
    }
}
window.telepath.register('card.blocks.CodeBlock', CodeBlockDefinition);

다만, 기존처럼 js코드만 쓰면 되는 것이 아니고 telepath를 이용하여 textarea의 정보를 가져와야 한다. 여기서 prefix라는 것은 block의 행순서를 말한다. block이 여러개가 추가 될 수록 prefix 값이 달라지고 이를 통해서 구별하게 된다. 그리고 이를 id 값으로 사용한다. 정리하자면

body-0-value-body-0-value

body-0-value-body-1-value

body-0-value-body-2-value

이런식으로 저장되어진다. 

그리고 뒤에 필드명인 code가 붙어서

body-0-value-body-0-value-code 이런형태로 id값이 설정되게 된다. 나는 Code Mirror를 적용하였다.

Code Mirror는 id 값을 입력 해줘야하므로 prefix와 field명인 code를 합한 각 textarea의 아이디명을 textareaid 변수에 저장하고 입력해주었다.

적용된 code mirror

4. 추가 JS, CSS 적용

Code mirror의 기본 코드는 admin 페이지 자체에 wagtail hook을 통해 설정하였다. wagtail_hooks.py를 통해 wagtail admin에 추가 정보를 입력할 수 있다. 

card/wagtail_hooks.py

@hooks.register('insert_editor_css')
def editor_css():
    css_files = [
        'codemirror/lib/codemirror.css',
        'codemirror/theme/dracula.css',
    ]
    css_includes = format_html_join('\n', '<link rel="stylesheet" href="{0}"></link>',
        ((static(filename),) for filename in css_files)
    )
    return css_includes


@hooks.register('insert_editor_js')
def editor_js():
    js_files = [
        'codemirror/lib/codemirror.js',
        'codemirror/mode/xml/xml.js',
        'codemirror/mode/python/python.js',
    ]
    js_includes = format_html_join('\n', '<script src="{0}"></script>',
        ((static(filename),) for filename in js_files)
    )
    return js_includes

wagtail hook에 대해서는 다음에 문서로 전체 정리해보려고 한다. 당장은 필요한 부분만 기술하였다. 

(wagtail_hooks.py파일은 wagtail이 실행될 때 파일을 찾아서 자동으로 실행시키게 된다.) 

 

insert_global_admin_js와 insert_editor_js가 있는데 둘의 차이는 script 위치를 어디에다가 적용할 것이냐의 차이가 있다. 여러개의 css파일이나 js파일을 적용하고 싶다면 위와 같이 정의해 놓으면 된다. 

 

https://docs.wagtail.io/en/stable/reference/hooks.html#admin-hooks

추가적인 정보가 필요하면 윗 문서를 참고하자.

추가)Custom editing interfaces for StructBlock

Page를 생성할 때 StruckBlock의 layout 설정이나 js,css를 적용하려고 할 때 참고하자.

myapp/blocks.py

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

    def get_form_context(self, value, prefix='', errors=None):
        context = super().get_form_context(value, prefix=prefix, errors=errors)
        context['suggested_first_names'] = ['John', 'Paul', 'George', 'Ringo']
        return context

    class Meta:
        icon = 'user'
        form_template = 'myapp/block_forms/person.html'

공식문서 그대로 코드를 가져왔다. class Meta에 form_template의 수정을 통해 원하는 레이아웃이나 css를 적용할 수 있다.

 

templates/block_forms/person.html

{% load wagtailadmin_tags  %}

<div class="{{ classname }}">
    {% if help_text %}
        <span>
            <div class="help">
                {% icon name="help" class_name="default" %}
                {{ help_text }}
            </div>
        </span>
    {% endif %}

    {% for child in children.values %}
        <div class="field {% if child.block.required %}required{% endif %}" data-contentpath="{{ child.block.name }}">
            {% if child.block.label %}
                <label class="field__label" {% if child.id_for_label %}for="{{ child.id_for_label }}"{% endif %}>{{ child.block.label }}</label>
            {% endif %}
            {{ child.render_form }}
        </div>
    {% endfor %}
</div>

위가 wagtail의 admin field의 기본 구조이다. 

{% for child in children.values %}
        <div class="field {% if child.block.required %}required{% endif %}" data-contentpath="{{ child.block.name }}">
            {% if child.block.label %}
                <label class="field__label" {% if child.id_for_label %}for="{{ child.id_for_label }}"{% endif %}>{{ child.block.label }}</label>
            {% endif %}
            {{ child.render_form }}
        </div>
    {% endfor %}

child가 우리가 작성한 field들의 정보가 들어 있는 곳이다. 

child.block.name이 필드의 변수 명이므로 if 문을 사용하여 필드를 구별해서 원하는대로 custom을 하면 된다. 

ex) {% if child.block.name == "변수이름" %} . . . {% endif %}

'Back-End > Wagtail, Django' 카테고리의 다른 글

Wagtail Custom Field, Panel  (0) 2021.09.29
Wagtail hooks로 RichTextField에 구글 폰트 적용  (0) 2021.09.19
Wagtail 중첩 StreamField  (0) 2021.09.01
Wagtail Custom User Field  (0) 2021.09.01
Wagtail All Auth(Google Login)  (0) 2021.08.25

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.변수명으로 작성해주면 된다. 

 

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

Wagtail에서 Custom User를 정의하는 방법에 대해서 작성하려고한다. (User에 새로운 필드 추가)

ex) 닉네임

 

https://docs.wagtail.io/en/stable/advanced_topics/customisation/custom_user_models.html

 

Custom user models — Wagtail Documentation 2.14.1 documentation

 

docs.wagtail.io

 

1. User app 생성

먼저 User를 정의하기 위해 새로운 User app을 생성 해줘야한다. (물론 기존 app으로도 가능하지만 대신 models.py에 Page 모델이 존재하면 안된다. Wagtail에서 Page 모델은 User 모델을 포함하기 때문이다. 순환 참조 오류가 발생하니 그냥 user app을 새로 생성해주자.)

 

2. User app 정의

user/models.py

from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    country = models.CharField(verbose_name='country', max_length=255)
    status = models.ForeignKey(MembershipStatus, on_delete=models.SET_NULL, null=True, default=1)

유저 모델을 커스텀 하기 위해서는 AbstractUser를 사용한다. 

 

settings/base.py

AUTH_USER_MODEL = 'users.User'

그리고 base.py에서 우리가 커스텀 하는 유저 모델을 재정의 해준다. 

 

wagtail admin Page의 setting에 있는 user 모델에도 우리가 추가한 필드를 정의해주기 위해서 다음과 같이 forms.py 파일을 만들고 작성한다. 

 

user/forms.py

from django import forms
from django.utils.translation import gettext_lazy as _

from wagtail.users.forms import UserEditForm, UserCreationForm

from users.models import MembershipStatus


class CustomUserEditForm(UserEditForm):
    country = forms.CharField(required=True, label=_("Country"))
    status = forms.ModelChoiceField(queryset=MembershipStatus.objects, required=True, label=_("Status"))


class CustomUserCreationForm(UserCreationForm):
    country = forms.CharField(required=True, label=_("Country"))
    status = forms.ModelChoiceField(queryset=MembershipStatus.objects, required=True, label=_("Status"))

forms.py에다가 우리가 추가한 필드의 생성과 수정을 할 때 사용할 form을 정의 해준다. 

그리고 users form.html도 수정이 필요하다. templates 폴더를 만들고 wagtailusers폴더을 만들어주고 다시 users폴더도 만들어주자 그리고 나선  create.html과 edit.html이라는 파일을 만들고 아래와 같이 정의해주자. 

 

3. User tempate 정의

templates/wagtailusers/users/create.html

{% extends "wagtailusers/users/create.html" %}

{% block extra_fields %}
    {% include "wagtailadmin/shared/field_as_li.html" with field=form.country %}
    {% include "wagtailadmin/shared/field_as_li.html" with field=form.status %}
{% endblock extra_fields %}

templates/wagtailusers/users/edit.html

{% extends "wagtailusers/users/edit.html" %}

{% block extra_fields %}
    {% include "wagtailadmin/shared/field_as_li.html" with field=form.country %}
    {% include "wagtailadmin/shared/field_as_li.html" with field=form.status %}
{% endblock extra_fields %}

간단히 정의하자면 country와 status라는 이름의 custom form을 추가하여 사용하겠다는 뜻이다.

 

4. settings 수정

settings/base.py에 우리가 만든 form을 정의 해주어야 한다. 

settings/base.py

WAGTAIL_USER_EDIT_FORM = 'users.forms.CustomUserEditForm'
WAGTAIL_USER_CREATION_FORM = 'users.forms.CustomUserCreationForm'
WAGTAIL_USER_CUSTOM_FIELDS = ['country', 'status']

이제 확인해보면 user form이 추가 되어 있는 것을 확인 할 수 있을 것이다. 

'Back-End > Wagtail, Django' 카테고리의 다른 글

Wagtail Custom Block Field 만들기  (0) 2021.09.19
Wagtail 중첩 StreamField  (0) 2021.09.01
Wagtail All Auth(Google Login)  (0) 2021.08.25
Wagtail Docker 사용 방법  (0) 2021.08.18
Wagtail demo site (breads)  (0) 2021.08.17

+ Recent posts