# 편집 인터페이스 사용자 정의 (customizing_the_tabbed_interface)= ## 탭 인터페이스 사용자 정의 표준으로 Wagtail은 페이지용 패널을 '콘텐츠'와 '홍보'라는 두 개의 탭으로 구성합니다. 스니펫의 경우 Wagtail은 모든 패널을 한 페이지에 넣습니다. 사이트의 요구 사항에 따라 특정 페이지 유형이나 스니펫에 대해 이를 사용자 정의할 수 있습니다. 예를 들어 사이드바 콘텐츠에 대한 추가 탭을 추가할 수 있습니다. 이는 페이지 또는 스니펫 모델에 `edit_handler` 속성을 지정하여 수행할 수 있습니다. 예를 들어: ```python from wagtail.admin.panels import TabbedInterface, TitleFieldPanel, ObjectList class BlogPage(Page): # 필드 정의 생략 content_panels = [ TitleFieldPanel('title', classname="title"), FieldPanel('date'), FieldPanel('body'), ] sidebar_content_panels = [ FieldPanel('advert'), InlinePanel('related_links', heading="관련 링크", label="관련 링크"), ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='콘텐츠'), ObjectList(sidebar_content_panels, heading='사이드바 콘텐츠'), ObjectList(Page.promote_panels, heading='홍보'), ObjectList(Page.settings_panels, heading='설정'), # 기본 설정은 이제 사이드바에 표시되지만 `TabbedInterface` 에 있어야 합니다. ]) ``` `ObjectList` 에서 `permission` 을 사용하여 권한을 설정하여 전체 패널 그룹을 특정 사용자로 제한할 수 있습니다. ```python from wagtail.admin.panels import TabbedInterface, TitleFieldPanel, ObjectList class FundingPage(Page): # 필드 정의 생략 shared_panels = [ TitleFieldPanel('title', classname="title"), FieldPanel('date'), FieldPanel('body'), ] private_panels = [ FieldPanel('approval'), ] edit_handler = TabbedInterface([ ObjectList(shared_panels, heading='세부 정보'), ObjectList(private_panels, heading='관리자 전용', permission="superuser"), ObjectList(Page.promote_panels, heading='홍보'), ObjectList(Page.settings_panels, heading='설정'), # 기본 설정은 이제 사이드바에 표시되지만 `TabbedInterface` 에 있어야 합니다. ]) ``` `Panel` 및 `PanelGroup` 클래스 작업에 대한 자세한 내용은 [](forms_panels_overview)를 참조하십시오. (rich_text_field)= ## 서식 있는 텍스트(HTML) Wagtail은 서식 있는 텍스트 콘텐츠(HTML)를 만들고 이미지, 비디오, 문서와 같은 미디어를 포함하기 위한 범용 WYSIWYG 편집기를 제공합니다. 모델에 이를 포함하려면 모델 필드를 정의할 때 `RichTextField` 함수를 사용하십시오. ```python from wagtail.fields import RichTextField from wagtail.admin.panels import FieldPanel class BookPage(Page): body = RichTextField() content_panels = Page.content_panels + [ FieldPanel('body'), ] ``` `RichTextField` 는 Django의 기본 `TextField` 필드에서 상속되므로 일반 Django 필드를 사용하는 것처럼 모든 필드 매개변수를 `RichTextField` 에 전달할 수 있습니다. 이 필드는 특수 패널이 필요하지 않으며 `FieldPanel` 로 정의할 수 있습니다. 그러나 `RichTextField` 의 템플릿 출력은 특수하며 포함된 콘텐츠를 보존하기 위해 필터링해야 합니다. [](rich_text_filter)를 참조하십시오. `max_length` 가 지정되면 길이 유효성 검사는 서식 있는 텍스트 서식을 자동으로 무시합니다. 동일한 방식으로 최소 길이를 적용하려면 `validators` 인수의 일부로 `wagtail.rich_text.RichTextMinLengthValidator` 인스턴스를 전달하십시오. (rich_text_features)= ### 서식 있는 텍스트 필드의 기능 제한 기본적으로 서식 있는 텍스트 편집기는 사용자에게 텍스트 서식 지정 및 이미지와 같은 포함된 콘텐츠 삽입을 위한 다양한 옵션을 제공합니다. 그러나 서식 있는 텍스트 필드를 더 제한된 기능 집합으로 제한하고 싶을 수 있습니다. 예를 들어: - 필드는 색인 페이지에서 가져올 요약과 같은 짧은 텍스트 스니펫을 위한 것일 수 있으며, 여기서 포함된 이미지나 비디오는 부적절할 수 있습니다. - 페이지 콘텐츠가 [StreamField](../../topics/streamfield)를 사용하여 정의될 때 제목, 이미지, 비디오와 같은 요소는 일반적으로 일반 단락 텍스트에 사용되는 서식 있는 텍스트 블록 유형과 함께 자체 블록 유형이 지정됩니다. 이 경우 서식 있는 텍스트 콘텐츠 내에 제목과 이미지가 존재하도록 허용하는 것은 중복됩니다(그리고 일관성 없는 디자인을 초래할 수 있음). 이는 허용하려는 기능에 대한 식별자 목록과 함께 `features` 키워드 인수를 `RichTextField` 에 전달하여 달성할 수 있습니다. ```python body = RichTextField(features=['h2', 'h3', 'bold', 'italic', 'link']) ``` 기본 Wagtail 설치에서 제공되는 기능 식별자는 다음과 같습니다. - `h2`, `h3`, `h4` - 제목 요소 - `bold`, `italic` - 굵게/기울임꼴 텍스트 - `ol`, `ul` - 순서 있는/순서 없는 목록 - `hr` - 가로줄 - `link` - 페이지, 외부 및 이메일 링크 - `document-link` - 문서 링크 - `image` - 포함된 이미지 - `embed` - 포함된 미디어( [](embedded_content) 참조) 몇 가지 추가 기능 식별자도 있습니다. 기본적으로 활성화되어 있지는 않지만 식별자 목록에서 사용할 수 있습니다. 다음과 같습니다. - `h1`, `h5`, `h6` - 제목 요소 - `code` - 인라인 코드 - `superscript`, `subscript`, `strikethrough` - 텍스트 서식 - `blockquote` - 인용문 새 기능을 만드는 프로세스는 다음 페이지에 설명되어 있습니다. - [](../../extending/rich_text_internals) - [](../../extending/extending_draftail) 서식 있는 텍스트 기능 그룹의 이름을 지정하기 위한 설정을 제공할 수도 있습니다. [WAGTAILADMIN_RICH_TEXT_EDITORS](wagtailadmin_rich_text_editors)를 참조하십시오. (rich_text_image_formats)= ### 서식 있는 텍스트 편집기의 이미지 형식 로드 시 Wagtail은 `image_formats.py` 파일이 있는 모든 앱을 검색하고 내용을 실행합니다. 이는 `RichTextField` 편집기에서 이미지를 삽입할 때 편집기에게 표시되는 서식 옵션을 사용자 정의하는 방법을 제공합니다. 예를 들어 "thumbnail" 형식을 추가합니다. ```python # image_formats.py from wagtail.images.formats import Format, register_image_format register_image_format(Format('thumbnail', 'Thumbnail', 'richtext-image thumbnail', 'max-120x120')) ``` 시작하려면 `Format` 클래스, `register_image_format` 함수 및 선택적으로 `unregister_image_format` 함수를 가져옵니다. 새 `Format` 을 등록하려면 `Format` 개체를 인수로 사용하여 `register_image_format` 을 호출합니다. `Format` 클래스는 다음 생성자 인수를 사용합니다. **`name`** 형식을 식별하는 데 사용되는 고유 키입니다. 이 형식을 등록 취소하려면 이 문자열을 유일한 인수로 사용하여 `unregister_image_format` 을 호출합니다. **`label`** `RichTextField` 에 이미지를 삽입할 때 선택기 양식에 사용되는 레이블입니다. **`classname`** 생성된 `` 태그의 `class` 속성에 할당할 문자열입니다. ```{note} 제공하는 모든 클래스 이름에는 프런트엔드 CSS 코드의 일부로 별도로 작성된 CSS 규칙이 일치해야 합니다. `classname` 값을 `left` 로 지정하면 생성된 마크업에서 해당 클래스가 출력되도록만 보장되며 이미지가 왼쪽으로 정렬되지는 않습니다. ``` **`filter_spec`** 이미지 변환을 만드는 문자열 사양입니다. 자세한 내용은 [](image_tag)를 참조하십시오. 등록을 취소하려면 `Format` 의 `name` 문자열을 유일한 인수로 사용하여 `unregister_image_format` 을 호출합니다. ```{warning} ``Format`` 개체를 등록 취소하면 이를 참조하는 페이지를 보거나 편집할 때 오류가 발생합니다. ``` (date_field_validation)= ## 날짜 필드 유효성 검사 `NoFutureDateValidator` 는 사용자가 미래 날짜를 입력하는 것을 방지합니다. 이는 다음과 같이 과거 또는 현재 날짜만 포함해야 하는 필드에 특히 유용합니다. - 생년월일 - 역사적 사건 날짜 - 이미 게시된 콘텐츠의 게시 날짜 - 완료된 프로젝트의 완료 날짜 ```python from django.db import models from wagtail.fields import NoFutureDateValidator from wagtail.admin.panels import FieldPanel from wagtail.models import Page class EventPage(Page): event_date = models.DateField( validators=[NoFutureDateValidator()], help_text="이 이벤트가 발생한 날짜" ) birth_date = models.DateField( validators=[NoFutureDateValidator("생년월일은 미래 날짜일 수 없습니다.")], help_text="사람의 생년월일" ) content_panels = Page.content_panels + [ FieldPanel('event_date'), FieldPanel('birth_date'), ] ``` 유효성 검사기는 선택적 사용자 정의 오류 메시지도 허용합니다. ```python # 기본 메시지 사용: "날짜는 미래 날짜일 수 없습니다." event_date = models.DateField(validators=[NoFutureDateValidator()]) # 사용자 정의 메시지 사용 birth_date = models.DateField( validators=[NoFutureDateValidator("유효한 생년월일을 입력하십시오.")] ) ``` 입력한 날짜가 오늘 날짜 이후이면 유효성 검사기에서 유효성 검사 오류가 발생합니다. (custom_edit_handler_forms)= ## 생성된 양식 사용자 정의 ```{eval-rst} .. class:: wagtail.admin.forms.WagtailAdminModelForm .. class:: wagtail.admin.forms.WagtailAdminPageForm ``` Wagtail은 모델에 구성된 패널을 사용하여 양식을 자동으로 생성합니다. 기본적으로 이 양식은 페이지의 경우 [WagtailAdminModelForm](wagtail.admin.forms.WagtailAdminModelForm) 또는 [WagtailAdminPageForm](wagtail.admin.forms.WagtailAdminPageForm)을 하위 클래스로 지정합니다. 사용자 정의 기본 양식 클래스는 모든 모델에서 `base_form_class` 속성을 설정하여 구성할 수 있습니다. 스니펫에 대한 사용자 정의 양식은 [WagtailAdminModelForm](wagtail.admin.forms.WagtailAdminModelForm)을 하위 클래스로 지정해야 하며, 페이지에 대한 사용자 정의 양식은 [WagtailAdminPageForm](wagtail.admin.forms.WagtailAdminPageForm)을 하위 클래스로 지정해야 합니다. 이를 사용하여 양식에 비모델 필드를 추가하거나, 필드 콘텐츠를 자동으로 생성하거나, 모델에 대한 사용자 정의 유효성 검사 논리를 추가할 수 있습니다. ```python from django import forms from django.db import models import geocoder # Wagtail에 없음, 예시용 - https://geocoder.readthedocs.io/ from wagtail.admin.panels import TitleFieldPanel, FieldPanel from wagtail.admin.forms import WagtailAdminPageForm from wagtail.models import Page class EventPageForm(WagtailAdminPageForm): address = forms.CharField() def clean(self): cleaned_data = super().clean() # 이벤트가 끝나기 전에 시작하는지 확인 start_date = cleaned_data['start_date'] end_date = cleaned_data['end_date'] if start_date and end_date and start_date > end_date: self.add_error('end_date', '종료 날짜는 시작 날짜 이후여야 합니다.') return cleaned_data def save(self, commit=True): page = super().save(commit=False) # 제출된 날짜에서 기간 필드 업데이트 page.duration = (page.end_date - page.start_date).days # 주소를 지오코딩하여 위치 가져오기 page.location = geocoder.arcgis(self.cleaned_data['address']) if commit: page.save() return page class EventPage(Page): start_date = models.DateField() end_date = models.DateField() duration = models.IntegerField() location = models.CharField(max_length=255) content_panels = [ TitleFieldPanel('title'), FieldPanel('start_date'), FieldPanel('end_date'), FieldPanel('address'), ] base_form_class = EventPageForm ``` Wagtail은 이 양식의 새 하위 클래스를 모델에 대해 생성하여 `panels` 또는 `content_panels` 에 정의된 모든 필드를 추가합니다. 모델에 이미 정의된 모든 필드는 이러한 자동으로 추가된 필드로 재정의되지 않으므로 모델 필드에 대한 양식 필드는 사용자 정의 양식에 추가하여 재정의할 수 있습니다. (custom_page_copy_form)= ## 생성된 복사 페이지 양식 사용자 정의 ```{eval-rst} .. class:: wagtail.admin.forms.CopyForm ``` 페이지를 복사할 때 Wagtail은 사용자가 복사된 페이지를 수정할 수 있도록 양식을 생성합니다. 기본적으로 이 양식은 [CopyForm](wagtail.admin.forms.CopyForm)을 하위 클래스로 지정합니다. 사용자 정의 기본 양식 클래스는 모든 모델에서 `copy_form_class` 속성을 설정하여 구성할 수 있습니다. 사용자 정의 양식은 [CopyForm](wagtail.admin.forms.CopyForm)을 하위 클래스로 지정해야 합니다. 이를 사용하여 모델별로 복사된 양식에 대한 변경 사항을 지정할 수 있습니다. 예를 들어 슬러그 필드를 자동으로 증가시킵니다. ```python from django import forms from django.db import models from wagtail.admin.forms.pages import CopyForm from wagtail.admin.panels import FieldPanel from wagtail.models import Page class CustomCopyForm(CopyForm): def __init__(self, *args, **kwargs): """ 슬러그를 자동으로 증가시키기 위해 기본 복사 양식을 재정의합니다. """ super().__init__(*args, **kwargs) suffix = 2 # 초기 슬러그를 증가된 슬러그로 설정 parent_page = self.page.get_parent() if self.page.slug: try: suffix = int(self.page.slug[-1])+1 base_slug = self.page.slug[:-2] except ValueError: base_slug = self.page.slug new_slug = base_slug + f"-{suffix}" while not Page._slug_is_available(new_slug, parent_page): suffix += 1 new_slug = f"{base_slug}-{suffix}" self.fields["new_slug"].initial = new_slug class BlogPage(Page): copy_form_class = CustomCopyForm # 모든 EventPage 모델에 대한 사용자 정의 복사 양식 설정 introduction = models.TextField(blank=True) body = RichTextField() content_panels = Page.content_panels + [ FieldPanel('introduction'), FieldPanel('body'), ] ```