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

Wagtail은 Django base로 만들어진 CMS이므로 user 관리가 Django랑 같다.

바로 allauth를 사용하면 된다. 순서대로 실행해보자

 

1. All Auth 설치, Setting

$ pip install django-allauth

 

이후에 settings/base.py를 다음과 같이 추가하고 수정해준다. 

# base.py

TEMPLATES = [
    {
        # ...,
        'OPTIONS': {
            'context_processors': [
                # ...
                'django.template.context_processors.request', # <- this is the one you NEED to include
                # ...
            ],
        },
    },
]

Allauth backend 인증을 위해 추가해준다. 

# base.py 

# Authentication Backends
AUTHENTICATION_BACKENDS = (
    # Needed to login by username in Django admin, regardless of `allauth`
    'django.contrib.auth.backends.ModelBackend',

    # `allauth` specific authentication methods, such as login by e-mail
    'allauth.account.auth_backends.AuthenticationBackend',
)

Authentications_backend는 Django에서 back를 어떻게 사용할지 정보를 적어주는 곳이다. 

 

'django.contrib.auth.backends.ModelBackend'는 All auth와 상관없이 username으로 Django admin에 로그인 하기 위해 사용한다고 한다. 

 

'allauth.account.auth_backends.AuthenticationBackend' 이메일 로그인과 같이 구체적인 로그인 방법을 위해 사용한다고 한다.

 

자세한 내용을 아래 문서를 참고하자

https://docs.djangoproject.com/en/3.2/ref/contrib/auth/#authentication-backends-reference

 

그리고 allauth 기능을 사용하기 위해서 6가지를 추가해줘야한다. 

# base.py 

INSTALLED_APPS = (
    # ... other apps 

    # The following apps are required:
    'django.contrib.auth',
    'django.contrib.messages',
    'django.contrib.sites',

    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    # ...
)

그리고 base.py에 다음과 같은 내용을 추가시켜준다.

# base.py 

SITE_ID = 1

DEFAULT_AUTO_FIELD='django.db.models.AutoField'

SITE_ID는 우리가 사용할 사이트가 단일 사이트면 1을 입력해주면 된다. 자세한 내용은

https://stackoverflow.com/questions/25468676/django-sites-model-what-is-and-why-is-site-id-1 에서 확인할 수 있다.

 

마지막으로 urls.py에 urlpatterns에 다음을 추가한다. 

# urls.py

urlpatterns = [
    # .. Existing urls 
    path('accounts/', include('allauth.urls')), # Creates urls like yourwebsite.com/login/
  
    # Wagtail URLs
    path('', include(wagtail_urls)),
]

이후에 migrate를 진행하면 정상적으로 적용이 될 것이다. 다만 여기에서 migrations 충돌이 일어났는데 기존 migrations을 초기화 시켜주고 다시 실행하니 정상적으로 작동하였다. 

 

로그인 접속 url은 accounts/login으로 접속하면 된다. 

 

Login 관련하여 여러가지 옵션을 선택할 수 있는데 관련 내용은 allauth 공식 문서에 정리 되어 있다.

https://django-allauth.readthedocs.io/en/latest/configuration.html

 

참고로 몇가지 적용한걸 보자면 

# base.py 

LOGIN_URL = '/login/'
LOGIN_REDIRECT_URL = '/'
ACCOUNT_AUTHENTICATION_METHOD = "username_email"
ACCOUNT_CONFIRM_EMAIL_ON_GET = True
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True
ACCOUNT_LOGOUT_ON_GET = True
ACCOUNT_LOGIN_ON_PASSWORD_RESET = True
ACCOUNT_LOGOUT_REDIRECT_URL = '/login/'
ACCOUNT_PRESERVE_USERNAME_CASING = False
ACCOUNT_SESSION_REMEMBER = True
ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE = False
ACCOUNT_USERNAME_BLACKLIST = ["admin", "god"]
ACCOUNT_USERNAME_MIN_LENGTH = 2

변수명만 봐도 어떤 기능을 하는지 이해가 될 것이다. 

 

2. Google 로그인 설정

구글 로그인을 설정하려면 몇가지를 추가해주면 된다. 

settings/base.py

INSTALLED_APPS = [
 
    'allauth.socialaccount.providers.google',
]

google로 로그인기능을 활성화 시키려면 위와 같이 입력하면된다. 

 

그리고 구글 로그인의 ID값 하고 Key 값을 적어야한다. 

http://127.0.0.1:8000/django-admin/ 으로 접속한다. 

 

Social applications에서 add 버튼을 누르고 

 

위와 같이 설정을 해주면 완료된다. sites가 example.com이라고 되어 있으면 Sites로 가서 이름을 바꿔주면 된다. 이 사이트가 우리가 settings.py에서 설정한 SITE_ID = 1 이다. (1번째 사이트)

 

Client id와 Secret Key은

 

구글은 https://console.developers.google.com/apis/credentials

네이버는 https://developers.naver.com/products/login/api/api.md 

 

3. template 작성

{% load socialaccount %}

<a class="button" href="{% provider_login_url 'google' %}"><strong>로그인</strong></a>

template에서는 위와 같이 작성 해주면 된다.

(네이버일 경우에는 google을 naver로만 바꿔주면 된다.)

만약 social 로그인을 사용하지 않을 것이라면 href="accounts/login"으로 해주면 된다. 

 

참고) settings/base.py에 다음과 같은 설정을 해주면 로그아웃을 할 때 accounts 페이지 이동없이 바로 로그아웃 된다.

ACCOUNT_LOGOUT_ON_GET = True
<a class="button" href="{% url 'account_logout' %}"><span>로그 아웃</span></a>

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

Wagtail 중첩 StreamField  (0) 2021.09.01
Wagtail Custom User Field  (0) 2021.09.01
Wagtail Docker 사용 방법  (0) 2021.08.18
Wagtail demo site (breads)  (0) 2021.08.17
Wagtail demo site (blog)  (0) 2021.08.17

+ Recent posts