(form_builder_customization)=
# 폼 빌더 사용자 정의
기본 사용 예시는 [폼 빌더 사용법](form_builder_usage)을 참조하십시오.
## 폼 필드에 대한 사용자 지정 `related_name`
폼 필드에 대한 `related_name` 을 변경하려면(기본적으로 `AbstractForm` 및 `AbstractEmailForm` 은 `form_fields` 가 정의되어 있다고 예상함), `get_form_fields` 메서드를 재정의해야 합니다.
다음과 같이 할 수 있습니다.
```python
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='custom_form_fields')
class FormPage(AbstractEmailForm):
intro = RichTextField(blank=True)
thank_you_text = RichTextField(blank=True)
content_panels = AbstractEmailForm.content_panels + [
FieldPanel('intro'),
InlinePanel('custom_form_fields', label="폼 필드"),
FieldPanel('thank_you_text'),
MultiFieldPanel([
FieldRowPanel([
FieldPanel('from_address', classname="col6"),
FieldPanel('to_address', classname="col6"),
]),
FieldPanel('subject'),
], "이메일"),
]
def get_form_fields(self):
return self.custom_form_fields.all()
```
## 사용자 지정 폼 제출 모델
추가 데이터를 저장해야 하는 경우 사용자 지정 폼 제출 모델을 사용할 수 있습니다.
이렇게 하려면 다음을 수행해야 합니다.
- `wagtail.contrib.forms.models.AbstractFormSubmission` 을 확장하는 모델을 정의합니다.
- 페이지 모델에서 `get_submission_class` 및 `process_form_submission` 메서드를 재정의합니다.
예시:
```python
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 내보내기에 사용자 이름을 추가하는 방법을 보여줍니다.
이 코드는 제출 목록 뷰도 변경합니다.
```python
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` 메서드를 재정의해야 합니다.
예시:
```python
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')
```
템플릿은 다음과 같아야 합니다.
```html+django
{% load wagtailcore_tags %}
{{ page.title }}
{{ page.title }}
{% if user.is_authenticated and user.is_active or request.is_preview %}
{% if form %}
{{ page.intro|richtext }}
{% else %}
폼을 한 번만 작성할 수 있습니다.
{% endif %}
{% else %}
폼을 작성하려면 로그인해야 합니다.
{% endif %}
```
## 다단계 폼
다음 예시는 다단계 폼을 만드는 방법을 보여줍니다.
```python
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
)
```
이 폼 페이지의 템플릿은 다음과 같아야 합니다.
```html+django
{% load wagtailcore_tags %}
{{ page.title }}
{{ page.title }}
{{ page.intro|richtext }}
```
이전에 표시된 예시는 사용자가 이전 단계로 돌아가거나 첫 번째 단계를 제출하지 않고 두 번째 단계를 열 수 있도록 합니다.
요구 사항에 따라 추가 확인을 추가해야 할 수도 있습니다.
## 결과 표시
설문 조사 또는 설문지를 구현하는 경우 제출 후 결과를 표시할 수 있습니다.
다음 예시는 이를 수행하는 방법을 보여줍니다.
먼저 아래와 같이 결과를 수집해야 합니다.
```python
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
```
다음으로, 결과를 표시하도록 템플릿을 변환해야 합니다.
```html+django
{% load wagtailcore_tags %}
{{ page.title }}
{{ page.title }}
결과
{% for question, answers in results.items %}
{{ question }}
{% for answer, count in answers.items %}
{{ answer }}: {{ count }}
{% endfor %}
{% endfor %}
{{ page.intro|richtext }}
```
랜딩 페이지에 결과를 표시할 수도 있습니다.
(form_builder_custom_landing_page_redirect)=
## 사용자 지정 랜딩 페이지 리디렉션
폼이 제출될 때 렌더링되는 내용을 변경하려면 `FormPage` 에서 `render_landing_page` 메서드를 재정의할 수 있습니다.
아래 예시에서는 폼 제출 후 선택한 페이지로 사용자 지정 리디렉션을 가능하게 하는 `thank_you_page` 필드를 추가했습니다.
`render_landing_page` 메서드를 재정의할 때 연결된 `thank_you_page` 가 있는지 확인한 다음 존재하면 해당 페이지로 리디렉션합니다.
마지막으로, `form_submission` 이 존재하면 `id` 를 기반으로 URL 매개변수를 추가합니다.
```python
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'),
], '이메일'),
]
```
(custom_form_submission_listing)=
## Wagtail 관리자에서 폼 제출 목록 사용자 정의
폼 제출의 관리자 목록은 폼 페이지 모델에 `submissions_list_view_class` 속성을 설정하여 사용자 정의할 수 있습니다.
목록 뷰 클래스는 `wagtail.contrib.forms.views` 의 `SubmissionsListView` 의 서브클래스여야 하며, 이는 `wagtail.admin.views.generic.base.BaseListingView` 및 Django의 클래스 기반 {class}`~django.views.generic.list.ListView` 의 서브클래스입니다.
예시:
```python
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 = ...
```
(form_builder_custom_field_type_widgets)=
## 내장 필드 유형의 위젯 사용자 정의
폼을 렌더링할 때 각 필드 유형은 기본 위젯을 사용하여 렌더링되며, 사용되는 위젯은 다음과 같이 재정의할 수 있습니다.
사용자 지정 위젯을 정의합니다. 이 예시에서는 향상된 `EmailInput` 위젯으로 이메일 유형을 재정의합니다.
```py
# 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` 메서드를 재정의하여 사용자 입력으로 정의된 필드의 옵션에 위젯을 할당합니다.
```python
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` 속성을 설정했는지 확인하십시오.
```python
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` 형식이어야 합니다. 예를 들어 `create_slug_field`
- 새 폼 빌더 클래스를 사용하도록 폼 페이지 모델의 `form_builder` 속성을 재정의합니다.
예시:
```python
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와 일치해야 합니다.
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
```
(form_builder_render_email)=
## 사용자 지정 `render_email` 메서드
폼이 제출될 때 전송되는 이메일의 내용을 변경하려면 `render_email` 메서드를 재정의할 수 있습니다.
이렇게 하려면 다음을 수행해야 합니다.
- `wagtail.contrib.forms.models.AbstractEmailForm` 을 확장하는 폼 모델이 정의되어 있는지 확인합니다.
- 페이지 모델에서 `render_email` 메서드를 재정의합니다.
예시:
```python
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` 메서드를 재정의합니다.
예시:
```python
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](https://pypi.org/project/anyascii/) 라이브러리를 사용하여 레이블을 HTML 유효한 `lower_snake_case` ASCII 문자열로 변환하는 `get_field_clean_name` 메서드가 있으며, 사용자 지정 변환을 생성하도록 재정의할 수 있습니다.
- 확인된 `clean_name` 은 렌더링된 HTML 폼에서 폼 필드 이름으로도 사용됩니다.
- `FormPage` 인스턴스 내에서 충돌을 일으키지 않도록 모든 변환이 충분히 고유한지 확인하십시오.
- 이 메서드는 새 필드를 생성할 때만 호출되며, 따라서 자체 `Page` 또는 `pk` 에 액세스할 수 없습니다. 레이블이 편집될 때 호출되지 않습니다. 폼 응답이 제출된 후 `clean_name` 을 수정하면 해당 필드 응답이 검색되지 않기 때문입니다.
- 이 메서드는 폼 미리 보기 및 중복 레이블 유효성 검사에도 호출됩니다.
```python
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):
# ... 페이지 정의
```
(form_builder_mixins)=
## 다른 `Page` 서브클래스와 함께 사용하기 위한 `FormMixin` 또는 `EmailFormMixin` 사용
추가 클래스를 확장하면서 폼 동작을 추가해야 하는 경우 추상 모델 대신 기본 믹스인을 사용할 수 있습니다.
```python
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)
# ...
```
(form_builder_custom_admin_validation)=
## 관리자 폼 페이지에 대한 사용자 지정 유효성 검사
기본적으로 `FormMixin` 에서 상속하는 페이지는 편집자가 추가한 각 필드에 고유한 `clean_name` 이 있는지 유효성을 검사합니다.
사용자 지정 유효성 검사를 추가해야 하는 경우 `WagtailAdminFormPageForm` 의 서브클래스를 만들고 자체 `clean` 정의를 추가한 다음 `Page` 모델에 `base_form_class` 를 설정합니다.
```{note}
유효성 검사는 편집자가 폼 빌더를 사용하여 Wagtail 관리자에 필드를 추가할 때만 적용되며,
최종 사용자가 폼을 제출할 때는 적용되지 않습니다.
```
```python
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
```