폼 빌더 사용자 정의

기본 사용 예시는 폼 빌더 사용법을 참조하십시오.

사용자 지정 폼 제출 모델

추가 데이터를 저장해야 하는 경우 사용자 지정 폼 제출 모델을 사용할 수 있습니다. 이렇게 하려면 다음을 수행해야 합니다.

  • wagtail.contrib.forms.models.AbstractFormSubmission 을 확장하는 모델을 정의합니다.

  • 페이지 모델에서 get_submission_classprocess_form_submission 메서드를 재정의합니다.

예시:

import json

from django.conf import settings
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.admin.panels import (
    FieldPanel, FieldRowPanel,
    InlinePanel, MultiFieldPanel
)
from wagtail.fields import RichTextField
from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField, AbstractFormSubmission


class FormField(AbstractFormField):
    page = ParentalKey('FormPage', on_delete=models.CASCADE, related_name='form_fields')


class FormPage(AbstractEmailForm):
    intro = RichTextField(blank=True)
    thank_you_text = RichTextField(blank=True)

    content_panels = AbstractEmailForm.content_panels + [
        FieldPanel('intro'),
        InlinePanel('form_fields', label="폼 필드"),
        FieldPanel('thank_you_text'),
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('from_address', classname="col6"),
                FieldPanel('to_address', classname="col6"),
            ]),
            FieldPanel('subject'),
        ], "이메일"),
    ]

    def get_submission_class(self):
        return CustomFormSubmission

    def process_form_submission(self, form):
        return self.get_submission_class().objects.create(
            form_data=form.cleaned_data,
            page=self, user=form.user
        )


class CustomFormSubmission(AbstractFormSubmission):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

CSV 내보내기에 사용자 지정 데이터 추가

CSV 내보내기에 사용자 지정 데이터를 추가하려면 다음을 수행해야 합니다.

  • 페이지 모델에서 get_data_fields 메서드를 재정의합니다.

  • 제출 모델에서 get_data 를 재정의합니다.

아래 예시는 CSV 내보내기에 사용자 이름을 추가하는 방법을 보여줍니다. 이 코드는 제출 목록 뷰도 변경합니다.

import json

from django.conf import settings
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.admin.panels import (
    FieldPanel, FieldRowPanel,
    InlinePanel, MultiFieldPanel
)
from wagtail.fields import RichTextField
from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField, AbstractFormSubmission


class FormField(AbstractFormField):
    page = ParentalKey('FormPage', on_delete=models.CASCADE, related_name='form_fields')


class FormPage(AbstractEmailForm):
    intro = RichTextField(blank=True)
    thank_you_text = RichTextField(blank=True)

    content_panels = AbstractEmailForm.content_panels + [
        FieldPanel('intro'),
        InlinePanel('form_fields', label="폼 필드"),
        FieldPanel('thank_you_text'),
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('from_address', classname="col6"),
                FieldPanel('to_address', classname="col6"),
            ]),
            FieldPanel('subject'),
        ], "이메일"),
    ]

    def get_data_fields(self):
        data_fields = [
            ('username', '사용자 이름'),
        ]
        data_fields += super().get_data_fields()

        return data_fields

    def get_submission_class(self):
        return CustomFormSubmission

    def process_form_submission(self, form):
        return self.get_submission_class().objects.create(
            form_data=form.cleaned_data,
            page=self, user=form.user
        )


class CustomFormSubmission(AbstractFormSubmission):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

    def get_data(self):
        form_data = super().get_data()
        form_data.update({
            'username': self.user.username,
        })

        return form_data

사용자에 대한 제출이 이미 존재하는지 확인

사용자가 폼을 한 번 이상 작성하는 것을 방지하려면 페이지 모델에서 serve 메서드를 재정의해야 합니다.

예시:

import json

from django.conf import settings
from django.db import models
from django.shortcuts import render
from modelcluster.fields import ParentalKey
from wagtail.admin.panels import (
    FieldPanel, FieldRowPanel,
    InlinePanel, MultiFieldPanel
)
from wagtail.fields import RichTextField
from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField, AbstractFormSubmission


class FormField(AbstractFormField):
    page = ParentalKey('FormPage', on_delete=models.CASCADE, related_name='form_fields')


class FormPage(AbstractEmailForm):
    intro = RichTextField(blank=True)
    thank_you_text = RichTextField(blank=True)

    content_panels = AbstractEmailForm.content_panels + [
        FieldPanel('intro'),
        InlinePanel('form_fields', label="폼 필드"),
        FieldPanel('thank_you_text'),
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('from_address', classname="col6"),
                FieldPanel('to_address', classname="col6"),
            ]),
            FieldPanel('subject'),
        ], "이메일"),
    ]

    def serve(self, request, *args, **kwargs):
        if self.get_submission_class().objects.filter(page=self, user__pk=request.user.pk).exists():
            return render(
                request,
                self.template,
                self.get_context(request)
            )

        return super().serve(request, *args, **kwargs)

    def get_submission_class(self):
        return CustomFormSubmission

    def process_form_submission(self, form):
        return self.get_submission_class().objects.create(
            form_data=form.cleaned_data,
            page=self, user=form.user
        )


class CustomFormSubmission(AbstractFormSubmission):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

    class Meta:
        unique_together = ('page', 'user')

템플릿은 다음과 같아야 합니다.

{% load wagtailcore_tags %}
<html>
    <head>
        <title>{{ page.title }}</title>
    </head>
    <body>
        <h1>{{ page.title }}</h1>

        {% if user.is_authenticated and user.is_active or request.is_preview %}
            {% if form %}
                <div>{{ page.intro|richtext }}</div>
                <form action="{% pageurl page %}" method="POST">
                    {% csrf_token %}
                    {{ form.as_p }}
                    <input type="submit">
                </form>
            {% else %}
                <div>폼을 한 번만 작성할 수 있습니다.</div>
            {% endif %}
        {% else %}
            <div>폼을 작성하려면 로그인해야 합니다.</div>
        {% endif %}
    </body>
</html>

다단계 폼

다음 예시는 다단계 폼을 만드는 방법을 보여줍니다.

from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.shortcuts import render
from modelcluster.fields import ParentalKey
from wagtail.admin.panels import (
    FieldPanel, FieldRowPanel,
    InlinePanel, MultiFieldPanel
)
from wagtail.fields import RichTextField
from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField


class FormField(AbstractFormField):
    page = ParentalKey('FormPage', on_delete=models.CASCADE, related_name='form_fields')


class FormPage(AbstractEmailForm):
    intro = RichTextField(blank=True)
    thank_you_text = RichTextField(blank=True)

    content_panels = AbstractEmailForm.content_panels + [
        FieldPanel('intro'),
        InlinePanel('form_fields', label="폼 필드"),
        FieldPanel('thank_you_text'),
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('from_address', classname="col6"),
                FieldPanel('to_address', classname="col6"),
            ]),
            FieldPanel('subject'),
        ], "이메일"),
    ]

    def get_form_class_for_step(self, step):
        return self.form_builder(step.object_list).get_form_class()

    def serve(self, request, *args, **kwargs):
        """
        간단한 다단계 폼을 구현합니다.

        각 단계를 세션에 저장합니다.
        마지막 단계가 올바르게 제출되면 전체 폼을 DB에 저장합니다.
        """

        session_key_data = 'form_data-%s' % self.pk
        is_last_step = False
        step_number = request.GET.get('p', 1)

        paginator = Paginator(self.get_form_fields(), per_page=1)
        try:
            step = paginator.page(step_number)
        except PageNotAnInteger:
            step = paginator.page(1)
        except EmptyPage:
            step = paginator.page(paginator.num_pages)
            is_last_step = True

        if request.method == 'POST':
            # 첫 번째 단계는 step_number == 2로 제출됩니다.
            # 따라서 이전 단계에서 폼을 가져와야 합니다.
            # 예외 처리 - 마지막 단계 제출
            prev_step = step if is_last_step else paginator.page(step.previous_page_number())

            # 제출된 단계에 대해서만 폼 생성
            prev_form_class = self.get_form_class_for_step(prev_step)
            prev_form = prev_form_class(request.POST, page=self, user=request.user)
            if prev_form.is_valid():
                # 단계에 대한 데이터가 유효하면 세션 업데이트
                form_data = request.session.get(session_key_data, {})
                form_data.update(prev_form.cleaned_data)
                request.session[session_key_data] = form_data

                if prev_step.has_next():
                    # 다음 단계가 있는 경우 다음 단계에 대한 새 폼 생성
                    form_class = self.get_form_class_for_step(step)
                    form = form_class(page=self, user=request.user)
                else:
                    # 다음 단계가 없으면 모든 필드에 대한 폼 생성
                    form = self.get_form(
                        request.session[session_key_data],
                        page=self, user=request.user
                    )

                    if form.is_valid():
                        # 전체 폼에 대해 다시 유효성 검사 수행.
                        # 유효성 검사 성공 후 DB에 데이터 저장,
                        # 그리고 세션에서 제거.
                        form_submission = self.process_form_submission(form)
                        del request.session[session_key_data]
                        # 랜딩 페이지 렌더링
                        return self.render_landing_page(request, form_submission, *args, **kwargs)
            else:
                # 단계에 대한 데이터가 유효하지 않으면
                # 오류와 함께 폼을 다시 표시해야 하므로
                # 이전 상태 복원.
                form = prev_form
                step = prev_step
        else:
            # POST가 아닌 요청에 대한 빈 폼 생성
            form_class = self.get_form_class_for_step(step)
            form = form_class(page=self, user=request.user)

        context = self.get_context(request)
        context['form'] = form
        context['fields_step'] = step
        return render(
            request,
            self.template,
            context
        )

이 폼 페이지의 템플릿은 다음과 같아야 합니다.

{% load wagtailcore_tags %}
<html>
    <head>
        <title>{{ page.title }}</title>
    </head>
    <body>
        <h1>{{ page.title }}</h1>

        <div>{{ page.intro|richtext }}</div>
        <form action="{% pageurl page %}?p={{ fields_step.number|add:"1" }}" method="POST">
            {% csrf_token %}
            {{ form.as_p }}
            <input type="submit">
        </form>
    </body>
</html>

이전에 표시된 예시는 사용자가 이전 단계로 돌아가거나 첫 번째 단계를 제출하지 않고 두 번째 단계를 열 수 있도록 합니다. 요구 사항에 따라 추가 확인을 추가해야 할 수도 있습니다.

결과 표시

설문 조사 또는 설문지를 구현하는 경우 제출 후 결과를 표시할 수 있습니다. 다음 예시는 이를 수행하는 방법을 보여줍니다.

먼저 아래와 같이 결과를 수집해야 합니다.

from modelcluster.fields import ParentalKey
from wagtail.admin.panels import (
    FieldPanel, FieldRowPanel,
    InlinePanel, MultiFieldPanel
)
from wagtail.fields import RichTextField
from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField


class FormField(AbstractFormField):
    page = ParentalKey('FormPage', on_delete=models.CASCADE, related_name='form_fields')


class FormPage(AbstractEmailForm):
    intro = RichTextField(blank=True)
    thank_you_text = RichTextField(blank=True)

    content_panels = AbstractEmailForm.content_panels + [
        FieldPanel('intro'),
        InlinePanel('form_fields', label="폼 필드"),
        FieldPanel('thank_you_text'),
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('from_address', classname="col6"),
                FieldPanel('to_address', classname="col6"),
            ]),
            FieldPanel('subject'),
        ], "이메일"),
    ]

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request, *args, **kwargs)

        # 랜딩 페이지에서만 결과를 표시해야 하는 경우,
        # request.method를 확인해야 할 수도 있습니다.

        results = dict()
        # 폼 필드에 대한 정보 가져오기
        data_fields = [
            (field.clean_name, field.label)
            for field in self.get_form_fields()
        ]

        # 현재 페이지에 대한 모든 제출 가져오기
        submissions = self.get_submission_class().objects.filter(page=self)
        for submission in submissions:
            data = submission.get_data()

            # 각 질문에 대한 결과 계산
            for name, label in data_fields:
                answer = data.get(name)
                if answer is None:
                    # 데이터에 문제가 있습니다.
                    # 아마도 질문을 변경했고
                    # 이제 이전 질문에 대한 답변을 받고 있습니다.
                    그냥 건너뛰세요.
                    continue

                if type(answer) is list:
                    # 필드 유형이 '체크박스'인 경우 답변은 목록입니다.
                    answer = u', '.join(answer)

                question_stats = results.get(label, {})
                question_stats[answer] = question_stats.get(answer, 0) + 1
                results[label] = question_stats

        context.update({
            'results': results,
        })
        return context

다음으로, 결과를 표시하도록 템플릿을 변환해야 합니다.

{% load wagtailcore_tags %}
<html>
    <head>
        <title>{{ page.title }}</title>
    </head>
    <body>
        <h1>{{ page.title }}</h1>

        <h2>결과</h2>
        {% for question, answers in results.items %}
            <h3>{{ question }}</h3>
            {% for answer, count in answers.items %}
                <div>{{ answer }}: {{ count }}</div>
            {% endfor %}
        {% endfor %}

        <div>{{ page.intro|richtext }}</div>
        <form action="{% pageurl page %}" method="POST">
            {% csrf_token %}
            {{ form.as_p }}
            <input type="submit">
        </form>
    </body>
</html>

랜딩 페이지에 결과를 표시할 수도 있습니다.

사용자 지정 랜딩 페이지 리디렉션

폼이 제출될 때 렌더링되는 내용을 변경하려면 FormPage 에서 render_landing_page 메서드를 재정의할 수 있습니다.

아래 예시에서는 폼 제출 후 선택한 페이지로 사용자 지정 리디렉션을 가능하게 하는 thank_you_page 필드를 추가했습니다.

render_landing_page 메서드를 재정의할 때 연결된 thank_you_page 가 있는지 확인한 다음 존재하면 해당 페이지로 리디렉션합니다.

마지막으로, form_submission 이 존재하면 id 를 기반으로 URL 매개변수를 추가합니다.

from django.shortcuts import redirect
from wagtail.admin.panels import FieldPanel, FieldRowPanel, InlinePanel, MultiFieldPanel
from wagtail.contrib.forms.models import AbstractEmailForm

class FormPage(AbstractEmailForm):

    # intro, thank_you_text, ...

    thank_you_page = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
    )

    def render_landing_page(self, request, form_submission=None, *args, **kwargs):
        if self.thank_you_page:
            url = self.thank_you_page.url
            # 랜딩 페이지를 미리 볼 때 form_submission 인스턴스가 없을 것입니다.
            # form_submission 인스턴스가 사용 가능하면 URL에 id를 추가합니다.
            if form_submission:
                url += '?id=%s' % form_submission.id
            return redirect(url, permanent=False)
        # thank_you_page가 설정되지 않은 경우 기본 랜딩 페이지 렌더링
        return super().render_landing_page(request, form_submission, *args, **kwargs)

    content_panels = AbstractEmailForm.content_panels + [
        FieldPanel('intro'),
        InlinePanel('form_fields'),
        FieldPanel('thank_you_text'),
        FieldPanel('thank_you_page'),
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('from_address', classname='col6'),
                FieldPanel('to_address', classname='col6'),
            ]),
            FieldPanel('subject'),
        ], '이메일'),
    ]

Wagtail 관리자에서 폼 제출 목록 사용자 정의

폼 제출의 관리자 목록은 폼 페이지 모델에 submissions_list_view_class 속성을 설정하여 사용자 정의할 수 있습니다.

목록 뷰 클래스는 wagtail.contrib.forms.viewsSubmissionsListView 의 서브클래스여야 하며, 이는 wagtail.admin.views.generic.base.BaseListingView 및 Django의 클래스 기반 ListView 의 서브클래스입니다.

예시:

from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField
from wagtail.contrib.forms.views import SubmissionsListView


class CustomSubmissionsListView(SubmissionsListView):
    paginate_by = 50  # 페이지당 더 많은 제출 표시, 기본값은 20
    default_ordering = ('submit_time',)  # 제출을 가장 오래된 것부터 정렬, 일반적으로 가장 최신부터 정렬
    ordering_csv = ('-submit_time',)  # CSV 내보내기를 가장 최신부터 정렬, 일반적으로 가장 오래된 것부터 정렬

    # CSV 파일 이름 생성 메서드 재정의
    def get_csv_filename(self):
        """ 페이지 슬러그가 시작 부분에 있는 CSV 파일의 파일 이름을 반환합니다."""
        filename = super().get_csv_filename()
        return self.form_page.slug + '-' + filename


class FormField(AbstractFormField):
    page = ParentalKey('FormPage', related_name='form_fields')


class FormPage(AbstractEmailForm):
    """사용자 지정 제출 목록 뷰가 있는 폼 페이지"""

    # 사용자 지정 뷰 클래스를 클래스 속성으로 설정
    submissions_list_view_class = CustomSubmissionsListView

    intro = RichTextField(blank=True)
    thank_you_text = RichTextField(blank=True)

    # content_panels = ...

내장 필드 유형의 위젯 사용자 정의

폼을 렌더링할 때 각 필드 유형은 기본 위젯을 사용하여 렌더링되며, 사용되는 위젯은 다음과 같이 재정의할 수 있습니다.

사용자 지정 위젯을 정의합니다. 이 예시에서는 향상된 EmailInput 위젯으로 이메일 유형을 재정의합니다.

# myapp/widgets.py
from django import forms
from django.utils.translation import gettext as _

#... 기타 가져오기

class CustomEmailInputWidget(forms.EmailInput):
    """
    이메일 필드에 대한 사용자 지정 입력 유형으로, 교차 브라우저 호환성을 위해
    세련된 추가 속성을 포함합니다.
    """

    def __init__(self, attrs={}):

        attrs = {
            "autocapitalize": "off",
            "autocomplete": "email",
            "autocorrect": "off",
            "placeholder": _("email@example.com"),
            "spellcheck": "false",
            **attrs, # 제공된 attrs가 새 기본값을 재정의하도록 허용
        }

        super().__init__(attrs=attrs)

email 유형에 대한 create_TYPE_field 메서드를 재정의하여 사용자 입력으로 정의된 필드의 옵션에 위젯을 할당합니다.

from wagtail.contrib.forms.forms import FormBuilder

from myapp.widgets import CustomEmailInputWidget


class CustomFormBuilder(FormBuilder):
    # 다른 필드 유형에 적용하려면 `create_TYPE_field` 중 하나를 재정의합니다.
    # 예: create_singleline_field, create_checkboxes_field, create_url_field
    def create_email_field(self, field, options):
        options["widget"] = CustomEmailInputWidget
        return super().create_email_field(field, options)

    # 또는 사용자 지정 속성으로 위젯을 직접 인스턴스화할 수 있습니다.
    def create_singleline_field(self, field, options):
        options["widget"] = forms.TextInput(attrs={"class": "custom-class"})
        return super().create_singleline_field(field, options)

다른 사용자 지정과 마찬가지로 폼 페이지 모델에 form_builder 속성을 설정했는지 확인하십시오.

from wagtail.contrib.forms.models import AbstractEmailForm


class FormPage(AbstractEmailForm):
    # intro, thank_you_text, edit_handlers, etc...

    # 위에 정의된 사용자 지정 폼 빌더 사용
    form_builder = CustomFormBuilder

사용자 지정 필드 유형 추가

먼저 FormField 모델을 변경하여 페이지 편집기에서 새 필드 유형을 사용할 수 있도록 합니다.

  • 원래 FORM_FIELD_CHOICES 와 사용할 새 필드 유형을 포함하는 새 선택 항목 세트를 만듭니다.

  • 각 선택 항목에는 고유한 키와 필드의 사람이 읽을 수 있는 이름(예: ('slug', 'URL 슬러그'))이 포함되어야 합니다.

  • 이러한 선택 항목을 사용하여 choices 속성으로 FormField 모델의 field_type 필드를 재정의합니다.

  • 이 단계 후에 ./manage.py makemigrations./manage.py migrate 를 실행해야 합니다.

그런 다음 새 폼 빌더 클래스를 만들고 사용합니다.

  • FormBuilder 클래스를 확장하는 새 폼 빌더 클래스를 정의합니다.

  • 새 필드 유형에 대해 생성된 Django 폼 필드를 반환하는 메서드를 추가합니다.

  • 이름은 create_<field_type_key>_field 형식이어야 합니다. 예를 들어 create_slug_field

  • 새 폼 빌더 클래스를 사용하도록 폼 페이지 모델의 form_builder 속성을 재정의합니다.

예시:

from django import forms
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.contrib.forms.forms import FormBuilder
from wagtail.contrib.forms.models import (
    AbstractEmailForm, AbstractFormField, FORM_FIELD_CHOICES)


class FormField(AbstractFormField):
    # 내장 필드 유형 선택 항목 확장
    # 필드 유형 키는 'ipaddress'가 됩니다.
    CHOICES = FORM_FIELD_CHOICES + (('ipaddress', 'IP 주소'),)

    page = ParentalKey('FormPage', related_name='form_fields')
    # 확장된 선택 항목으로 field_type 필드 재정의
    field_type = models.CharField(
        verbose_name='필드 유형',
        max_length=16,
        # 위에 정의된 선택 항목 튜플 사용
        choices=CHOICES
    )


class CustomFormBuilder(FormBuilder):
    # 인스턴스화된 Django 폼 필드를 반환하는 함수 생성
    # 함수 이름은 create_<field_type_key>_field와 일치해야 합니다.
    def create_ipaddress_field(self, field, options):
        # `forms.GenericIPAddressField(**options)` 를 반환하고 `forms.SlugField` 는 반환하지 않습니다.
        # 전달된 옵션으로 생성된 폼 필드를 반환합니다.
        return forms.GenericIPAddressField(**options)


class FormPage(AbstractEmailForm):
    # intro, thank_you_text, edit_handlers, etc...

    # 위에 정의된 사용자 지정 폼 빌더 사용
    form_builder = CustomFormBuilder

사용자 지정 render_email 메서드

폼이 제출될 때 전송되는 이메일의 내용을 변경하려면 render_email 메서드를 재정의할 수 있습니다.

이렇게 하려면 다음을 수행해야 합니다.

  • wagtail.contrib.forms.models.AbstractEmailForm 을 확장하는 폼 모델이 정의되어 있는지 확인합니다.

  • 페이지 모델에서 render_email 메서드를 재정의합니다.

예시:

from datetime import date
# ... 추가 wagtail 가져오기
from wagtail.contrib.forms.models import AbstractEmailForm


class FormPage(AbstractEmailForm):
    # ... 필드, content_panels 등

    def render_email(self, form):
        # 원본 내용(문자열) 가져오기
        email_content = super().render_email(form)

        # 제목 추가(원본 메서드의 일부가 아님)
        title = '{}: {}'.format('폼', self.title)

        content = [title, '', email_content, '']

        # 폼 페이지 링크 추가
        content.append('{}: {}'.format('제출 경로', self.full_url))

        # 폼 제출 날짜 추가
        submitted_date_str = date.today().strftime('%x')
        content.append('{}: {}'.format('제출 날짜', submitted_date_str))

        # 각 텍스트 줄을 구분하기 위해 새 줄로 내용 연결
        content = '\n'.join(content)

        return content

사용자 지정 send_mail 메서드

폼이 제출될 때 이메일이 전송되는 방식의 제목 또는 다른 부분을 변경하려면 send_mail 메서드를 재정의할 수 있습니다.

이렇게 하려면 다음을 수행해야 합니다.

  • wagtail.contrib.forms.models.AbstractEmailForm 을 확장하는 폼 모델이 정의되어 있는지 확인합니다.

  • models.py 파일에서 wagtail.admin.mail.send_mail 함수를 가져옵니다.

  • 페이지 모델에서 send_mail 메서드를 재정의합니다.

예시:

from datetime import date
# ... 추가 wagtail 가져오기
from wagtail.admin.mail import send_mail
from wagtail.contrib.forms.models import AbstractEmailForm


class FormPage(AbstractEmailForm):
    # ... 필드, content_panels 등

    def send_mail(self, form):
        # `self` 는 FormPage이고, `form` 은 제출 시 폼의 POST 데이터입니다.

        # 이메일 주소는 FormPage의 addresses 필드에서 구문 분석됩니다.
        addresses = [x.strip() for x in self.to_address.split(',')]

        # 제목을 조정할 수 있습니다(제출 날짜 추가). 폼의 정의된 제목 필드를 포함해야 합니다.
        submitted_date_str = date.today().strftime('%x')
        subject = f"{self.subject} - {submitted_date_str}"

        send_mail(subject, self.render_email(form), addresses, self.from_address,)

사용자 지정 clean_name 생성

  • FormField 가 추가될 때마다 사용자 입력 label 을 기반으로 clean_name 도 생성됩니다.

  • AbstractFormField 에는 AnyAscii 라이브러리를 사용하여 레이블을 HTML 유효한 lower_snake_case ASCII 문자열로 변환하는 get_field_clean_name 메서드가 있으며, 사용자 지정 변환을 생성하도록 재정의할 수 있습니다.

  • 확인된 clean_name 은 렌더링된 HTML 폼에서 폼 필드 이름으로도 사용됩니다.

  • FormPage 인스턴스 내에서 충돌을 일으키지 않도록 모든 변환이 충분히 고유한지 확인하십시오.

  • 이 메서드는 새 필드를 생성할 때만 호출되며, 따라서 자체 Page 또는 pk 에 액세스할 수 없습니다. 레이블이 편집될 때 호출되지 않습니다. 폼 응답이 제출된 후 clean_name 을 수정하면 해당 필드 응답이 검색되지 않기 때문입니다.

  • 이 메서드는 폼 미리 보기 및 중복 레이블 유효성 검사에도 호출됩니다.

    import uuid

    from django.db import models
    from modelcluster.fields import ParentalKey

    # ... 기타 필드 및 edit_handler 가져오기
    from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField


    class FormField(AbstractFormField):
        page = ParentalKey('FormPage', on_delete=models.CASCADE, related_name='form_fields')

        def get_field_clean_name(self):
            clean_name = super().get_field_clean_name()
            id = str(uuid.uuid4())[:8] # 짧은 uuid
            return f"{id}_{clean_name}"


    class FormPage(AbstractEmailForm):
        # ... 페이지 정의

다른 Page 서브클래스와 함께 사용하기 위한 FormMixin 또는 EmailFormMixin 사용

추가 클래스를 확장하면서 폼 동작을 추가해야 하는 경우 추상 모델 대신 기본 믹스인을 사용할 수 있습니다.

from wagtail.models import Page
from wagtail.contrib.forms.models import EmailFormMixin, FormMixin


class BasePage(Page):
    """
    프로젝트 전체에서 사용되는 공유 기본 페이지입니다.
    """

    # ...

class FormPage(FormMixin, BasePage):
    intro = RichTextField(blank=True)
    # ...

class EmailFormPage(EmailFormMixin, FormMixin, BasePage):
    intro = RichTextField(blank=True)
    # ...

관리자 폼 페이지에 대한 사용자 지정 유효성 검사

기본적으로 FormMixin 에서 상속하는 페이지는 편집자가 추가한 각 필드에 고유한 clean_name 이 있는지 유효성을 검사합니다.

사용자 지정 유효성 검사를 추가해야 하는 경우 WagtailAdminFormPageForm 의 서브클래스를 만들고 자체 clean 정의를 추가한 다음 Page 모델에 base_form_class 를 설정합니다.

참고

유효성 검사는 편집자가 폼 빌더를 사용하여 Wagtail 관리자에 필드를 추가할 때만 적용되며, 최종 사용자가 폼을 제출할 때는 적용되지 않습니다.

from wagtail.models import Page
from wagtail.contrib.forms.models import FormMixin, WagtailAdminFormPageForm


class CustomWagtailAdminFormPageForm(WagtailAdminFormPageForm):
    def clean(self):
        cleaned_data = super().clean()
        # 여기에 사용자 지정 유효성 검사를 삽입합니다. 예시는 `WagtailAdminFormPageForm.clean` 을 참조하십시오.
        return cleaned_data


class FormPage(AbstractForm):
    base_form_class = CustomWagtailAdminFormPageForm