(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 }}
{% csrf_token %} {{ form.as_p }}
{% 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 }}
{% csrf_token %} {{ form.as_p }}
``` 이전에 표시된 예시는 사용자가 이전 단계로 돌아가거나 첫 번째 단계를 제출하지 않고 두 번째 단계를 열 수 있도록 합니다. 요구 사항에 따라 추가 확인을 추가해야 할 수도 있습니다. ## 결과 표시 설문 조사 또는 설문지를 구현하는 경우 제출 후 결과를 표시할 수 있습니다. 다음 예시는 이를 수행하는 방법을 보여줍니다. 먼저 아래와 같이 결과를 수집해야 합니다. ```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 }}
{% csrf_token %} {{ form.as_p }}
``` 랜딩 페이지에 결과를 표시할 수도 있습니다. (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 ```