페이지 모델

Wagtail의 각 페이지 유형(일명 콘텐츠 유형)은 Django 모델로 표현됩니다. 모든 페이지 모델은 wagtail.models.Page 클래스에서 상속해야 합니다.

모든 페이지 유형은 Django 모델이므로 Django가 제공하는 모든 필드 유형을 사용할 수 있습니다. 사용할 수 있는 필드 유형의 전체 목록은 모델 필드 참조를 참조하십시오. Wagtail은 또한 리치 텍스트 콘텐츠 편집을 위한 WYSIWYG 편집기를 제공하는 wagtail.fields.RichTextField 를 제공합니다.

참고

아직 Django 모델에 익숙하지 않다면 다음 링크를 통해 시작하는 데 도움이 될 것입니다.

Wagtail 페이지 모델 예시

이 예시는 일반적인 블로그 게시물을 나타냅니다.

from django.db import models

from modelcluster.fields import ParentalKey

from wagtail.models import Page, Orderable
from wagtail.fields import RichTextField
from wagtail.admin.panels import FieldPanel, MultiFieldPanel, InlinePanel
from wagtail.search import index


class BlogPage(Page):

    # 데이터베이스 필드

    body = RichTextField()
    date = models.DateField("게시 날짜")
    feed_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )


    # 검색 색인 구성

    search_fields = Page.search_fields + [
        index.SearchField('body'),
        index.FilterField('date'),
    ]


    # 편집기 패널 구성

    content_panels = Page.content_panels + [
        FieldPanel('date'),
        FieldPanel('body'),
        InlinePanel('related_links', heading="관련 링크", label="관련 링크"),
    ]

    promote_panels = [
        MultiFieldPanel(Page.promote_panels, "공통 페이지 구성"),
        FieldPanel('feed_image'),
    ]


    # 부모 페이지 / 하위 페이지 유형 규칙

    parent_page_types = ['blog.BlogIndex']
    subpage_types = []


class BlogPageRelatedLink(Orderable):
    page = ParentalKey(BlogPage, on_delete=models.CASCADE, related_name='related_links')
    name = models.CharField(max_length=255)
    url = models.URLField()

    panels = [
        FieldPanel('name'),
        FieldPanel('url'),
    ]

참고

필드 이름이 클래스 이름과 동일하지 않은지 확인하십시오. Django가 관계를 처리하는 방식 때문에 오류가 발생합니다(자세히 읽기). 예시에서는 각 모델 이름에 “Page”를 추가하여 이를 피했습니다.

페이지 모델 작성

여기서는 위의 예시의 각 섹션을 설명하여 자체 페이지 모델을 만드는 데 도움이 되도록 합니다.

데이터베이스 필드

각 Wagtail 페이지 유형은 Django 모델이며, 데이터베이스에 별도의 테이블로 표현됩니다.

각 페이지 유형은 자체 필드 집합을 가질 수 있습니다. 예를 들어, 뉴스 기사에는 본문 텍스트와 게시 날짜가 있을 수 있지만, 이벤트 페이지에는 장소 및 시작/종료 시간에 대한 별도의 필드가 필요할 수 있습니다.

Wagtail에서는 모든 Django 필드 클래스를 사용할 수 있습니다. 타사 앱에서 제공하는 대부분의 필드 클래스도 작동해야 합니다.

Wagtail은 또한 자체 필드 클래스를 몇 가지 제공합니다.

태그 지정의 경우 Wagtail은 django-taggit을 완벽하게 지원하므로 이를 사용하는 것이 좋습니다.

검색

search_fields 속성은 검색 색인에 추가되는 필드와 색인화되는 방법을 정의합니다.

이것은 SearchFieldFilterField 객체 목록이어야 합니다. SearchField 는 전체 텍스트 검색을 위한 필드를 추가합니다. FilterField 는 결과를 필터링하기 위한 필드를 추가합니다. 필드는 SearchFieldFilterField 로 동시에 색인화할 수 있습니다(각각 하나의 인스턴스만).

위 예시에서는 body 를 전체 텍스트 검색용으로 색인화하고 date 를 필터링용으로 색인화했습니다.

이러한 필드 유형이 허용하는 인수는 추가 필드 색인화에 문서화되어 있습니다.

편집기 패널

페이지 편집기 인터페이스에서 페이지 필드가 정렬되는 방식을 정의하는 몇 가지 속성이 있습니다.

  • content_panels - 본문 텍스트와 같은 콘텐츠용

  • promote_panels - 태그, 썸네일 이미지 및 SEO 제목과 같은 메타데이터용

  • settings_panels - 게시 날짜와 같은 설정용

이러한 각 속성은 Panel 객체 목록으로 설정되며, 이는 필드가 어떤 탭에 나타나고 각 탭에서 어떻게 구성되는지 정의합니다.

다음은 Wagtail이 기본적으로 제공하는 Panel 클래스에 대한 요약입니다. 전체 설명은 패널 유형을 참조하십시오.

기본

이것들은 모델 필드를 편집할 수 있도록 합니다. FieldPanel 클래스는 필드 유형에 따라 올바른 위젯을 선택합니다. 예를 들어 RichTextField 의 경우 리치 텍스트 편집기, 이미지 모델에 대한 ForeignKey 의 경우 이미지 선택기입니다. FieldPanel 은 페이지 모델에 대한 ForeignKey 에 대한 페이지 선택기 인터페이스도 제공하지만, 선택할 수 있는 페이지 유형을 더 세밀하게 제어하려면 PageChooserPanel 이 추가 구성 옵션을 제공합니다.

구조

이것들은 인터페이스에서 필드를 구조화하는 데 사용됩니다.

페이지 편집기 인터페이스 사용자 지정

페이지 편집기는 추가로 사용자 지정할 수 있습니다. 편집 인터페이스 사용자 지정을 참조하십시오.

부모 페이지 / 하위 페이지 유형 규칙

이 두 속성을 사용하면 사이트에서 페이지 유형을 사용할 수 있는 위치를 제어할 수 있습니다. 이를 통해 “블로그 항목은 블로그 색인 아래에서만 만들 수 있습니다”와 같은 규칙을 정의할 수 있습니다.

부모 및 하위 페이지 유형은 모델 클래스 또는 모델 이름 목록을 사용합니다. 모델 이름은 app_label.ModelName 형식입니다. app_label 이 생략되면 동일한 앱으로 간주됩니다.

  • parent_page_types 는 이 유형을 만들 수 있는 페이지 유형을 제한합니다.

  • subpage_types 는 이 유형 아래에 만들 수 있는 페이지 유형을 제한합니다.

기본적으로 모든 페이지 유형은 모든 페이지 유형 아래에 만들 수 있으며, 원하는 동작인 경우 이러한 속성을 설정할 필요가 없습니다.

parent_page_types 를 빈 목록으로 설정하는 것은 편집기 인터페이스에서 특정 페이지 유형이 생성되는 것을 방지하는 좋은 방법입니다.

페이지 설명

모든 Wagtail 페이지에는 help_text 모델 속성과 유사한 유용한 설명 텍스트를 추가할 수 있습니다. 페이지 모델에 page_description 을 추가하면 새 페이지를 만들거나 기존 페이지를 편집하거나 자식 페이지 유형을 선택하라는 메시지가 표시될 때 볼 수 있는 짧은 설명이 추가됩니다.

class LandingPage(Page):

    page_description = "사용자를 전환하는 데 이 페이지를 사용하십시오."

페이지 URL

페이지 URL을 검색하는 가장 일반적인 방법은 {% pageurl %} 또는 {% fullpageurl %} 템플릿 태그를 사용하는 것입니다. 템플릿에서 호출되므로 이러한 태그는 아래 언급된 최적화를 자동으로 포함합니다.

페이지 모델에는 페이지 URL을 재정의하거나 액세스하기 위한 몇 가지 하위 수준 메서드도 포함되어 있습니다.

페이지 모델에 대한 URL 패턴 사용자 지정

Page.get_url_parts(request) 메서드는 일반적으로 직접 호출되지 않지만, 주어진 페이지 모델에 대한 사용자 지정 URL 라우팅을 정의하기 위해 재정의될 수 있습니다. (site_id, root_url, page_path) 튜플을 반환해야 하며, 이는 get_urlget_full_url(아래 참조)에서 주어진 페이지 URL 유형을 구성하는 데 사용됩니다.

get_url_parts() 를 재정의할 때 *args, **kwargs 를 허용해야 합니다.

def get_url_parts(self, *args, **kwargs):

그리고 super 에서 get_url_parts 를 호출하는 지점에서 해당 인수를 전달해야 합니다(해당하는 경우). 예를 들어:

super().get_url_parts(*args, **kwargs)

request 키워드 인수만 전달할 수 있지만, 모든 인수를 있는 그대로 전달하면 이러한 메서드 시그니처에 대한 향후 변경 사항과의 호환성이 보장됩니다.

자세한 내용은 wagtail.models.Page.get_url_parts() 를 참조하십시오.

페이지 인스턴스에 대한 URL 얻기

페이지 URL이 필요할 때마다 Page.get_url(request) 메서드를 호출할 수 있습니다. 페이지가 현재 사이트에 있다고 판단되면( request 의 호스트 이름을 통해) 로컬 URL(프로토콜 또는 도메인 제외)을 반환하는 것을 기본으로 합니다. 그렇지 않으면 프로토콜 및 도메인을 포함한 전체 URL을 반환합니다. 가능한 한 request 선택적 인수를 포함하여 사이트 수준 URL 정보의 요청별 캐싱을 활성화하고 로컬 URL 생성을 용이하게 해야 합니다.

get_url(request) 의 일반적인 사용 사례는 탐색 메뉴 생성을 위해 프로젝트에 포함될 수 있는 모든 사용자 지정 템플릿 태그에 있습니다. 이러한 사용자 지정 템플릿 태그를 작성할 때 takes_context=True 를 포함하고 context.get('request') 를 사용하여 요청을 안전하게 전달하거나 컨텍스트에 요청이 없는 경우 None 을 전달해야 합니다.

자세한 내용은 wagtail.models.Page.get_url() 을 참조하십시오.

전체 URL(프로토콜 및 도메인 포함)을 검색하려면 Page.get_full_url(request) 를 사용하십시오. 가능한 한 request 선택적 인수를 포함하여 사이트 수준 URL 정보의 요청별 캐싱을 활성화해야 합니다.

자세한 내용은 wagtail.models.Page.get_full_url() 을 참조하십시오.

템플릿 렌더링

각 페이지 모델에는 사용자가 사이트 프런트엔드의 페이지를 탐색할 때 렌더링되는 HTML 템플릿이 제공될 수 있습니다. 이는 Wagtail 콘텐츠를 최종 사용자에게 제공하는 가장 간단하고 일반적인 방법입니다(그러나 유일한 방법은 아님).

페이지 모델에 대한 템플릿 추가

Wagtail은 앱 레이블과 모델 클래스 이름을 기반으로 템플릿 이름을 자동으로 선택합니다.

형식: <app_label>/<model_name (snake cased)>.html

예를 들어, 위 블로그 페이지의 템플릿은 blog/blog_page.html 이 됩니다.

이름으로 액세스할 수 있는 위치에 템플릿을 만들기만 하면 됩니다.

템플릿 컨텍스트

Wagtail은 page 변수가 렌더링되는 페이지 인스턴스에 바인딩된 템플릿을 렌더링합니다. 이를 사용하여 페이지 콘텐츠에 액세스합니다. 예를 들어, 현재 페이지의 제목을 가져오려면 {{ page.title }} 를 사용합니다. 컨텍스트 프로세서에서 제공하는 모든 변수도 사용할 수 있습니다.

템플릿 컨텍스트 사용자 지정

모든 페이지에는 템플릿이 렌더링될 때 호출되는 get_context 메서드가 있으며, 템플릿에 바인딩할 변수 사전을 반환합니다.

템플릿 컨텍스트에 더 많은 변수를 추가하려면 이 메서드를 재정의할 수 있습니다.

class BlogIndexPage(Page):
    ...

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

        # 추가 변수를 추가하고 업데이트된 컨텍스트 반환
        context['blog_entries'] = BlogPage.objects.child_of(self).live()
        return context

변수는 템플릿에서 사용할 수 있습니다.

{{ page.title }}

{% for entry in blog_entries %}
    {{ entry.title }}
{% endfor %}

템플릿 변경

클래스에 template 속성을 설정하여 다른 템플릿 파일을 사용합니다.

class BlogPage(Page):
    ...

    template = 'other_template.html'

템플릿 동적으로 선택

페이지 클래스에 get_template 메서드를 정의하여 인스턴스별로 템플릿을 동적으로 변경할 수 있습니다. 이 메서드는 페이지가 렌더링될 때마다 호출됩니다.

class BlogPage(Page):
    ...

    use_other_template = models.BooleanField()

    def get_template(self, request, *args, **kwargs):
        if self.use_other_template:
            return 'blog/other_blog_page.html'

        return 'blog/blog_page.html'

이 예시에서 use_other_template 부울 필드가 설정된 페이지는 blog/other_blog_page.html 템플릿을 사용합니다. 다른 모든 페이지는 기본 blog/blog_page.html 을 사용합니다.

Ajax 템플릿

페이지에 AJAX 기능을 추가하려면(예: 전체 페이지를 다시 로드하지 않고 페이지에서 제자리에서 업데이트되는 페이지네이션 목록) ajax_template 속성을 설정하여 AJAX 호출을 통해 페이지가 요청될 때 사용할 대체 템플릿을 지정할 수 있습니다(X-Requested-With: XMLHttpRequest HTTP 헤더로 표시됨).

class BlogPage(Page):
    ...

    ajax_template = 'other_template_fragment.html'
    template = 'other_template.html'

페이지 렌더링에 대한 더 많은 제어

모든 페이지 클래스에는 get_contextget_template 메서드를 내부적으로 호출하고 템플릿을 렌더링하는 serve() 메서드가 있습니다. 이 메서드는 Django 뷰 함수와 유사하며, Django Request 객체를 받아 Django Response 객체를 반환합니다.

이 메서드는 페이지 렌더링에 대한 완전한 제어를 위해 재정의할 수도 있습니다.

예를 들어, 페이지가 자체 JSON 표현으로 응답하도록 하는 방법은 다음과 같습니다.

from django.http import JsonResponse


class BlogPage(Page):
    ...

    def serve(self, request):
        return JsonResponse({
            'title': self.title,
            'body': self.body,
            'date': self.date,

            # 이미지 크기를 300px 너비로 조정하고 URL을 가져옵니다.
            'feed_image': self.feed_image.get_rendition('width-300').url,
        })

인라인 모델

Wagtail은 페이지 내에 다른 모델을 중첩할 수 있도록 합니다. 이는 관련 링크 또는 캐러셀에 표시할 항목과 같은 반복되는 필드를 만드는 데 유용합니다. 인라인 모델 콘텐츠도 페이지의 나머지 부분과 함께 버전 관리됩니다.

인라인 모델은 부모 모델을 가리키는 ParentalKey 를 가져야 합니다. 또한 관리 인터페이스에서 항목의 순서를 변경할 수 있도록 wagtail.models.Orderable 에서 상속할 수도 있습니다.

참고

모델 인라인 기능은 django-modelcluster에서 제공하며 ParentalKey 필드 유형은 거기에서 가져와야 합니다.

from modelcluster.fields import ParentalKey

ParentalKey 는 Django의 ForeignKey 의 서브클래스이며 동일한 인수를 사용합니다.

예를 들어, 다음 인라인 모델은 BlogPage 모델에 관련 링크(이름, URL 쌍 목록)를 추가하는 데 사용할 수 있습니다.

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


class BlogPageRelatedLink(Orderable):
    page = ParentalKey(BlogPage, on_delete=models.CASCADE, related_name='related_links')
    name = models.CharField(max_length=255)
    url = models.URLField()

    panels = [
        FieldPanel('name'),
        FieldPanel('url'),
    ]

관리 인터페이스에 이를 추가하려면 InlinePanel 편집 패널 클래스를 사용합니다.

content_panels = [
    ...

    InlinePanel('related_links', label="관련 링크"),
]

첫 번째 인수는 ParentalKeyrelated_name 속성 값과 일치해야 합니다. InlinePanel 이 사용하는 매개변수에 대한 간략한 설명은 InlinePanel 를 참조하십시오.

여러 페이지 유형에서 인라인 모델 재사용

위 예시에서 관련 링크는 BlogPage 페이지 유형의 자식 객체로 정의됩니다. 종종 동일한 종류의 인라인 자식 객체가 여러 페이지 유형에 나타나며, 이러한 경우 전체 모델 정의를 반복하는 것은 바람직하지 않습니다. 이는 공통 필드를 추상 모델로 리팩토링하여 피할 수 있습니다.

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

# 패널이 포함된 관련 링크에 대한 추상 모델
class RelatedLink(models.Model):
    name = models.CharField(max_length=255)
    url = models.URLField()

    panels = [
        FieldPanel('name'),
        FieldPanel('url'),
    ]

    class Meta:
        abstract = True

# 페이지 모델에 대한 ParentalKey 관계로 추상 모델을 확장하는 실제 모델입니다.
# 관계를 추가할 각 페이지 유형에 대해 반복할 수 있습니다.
# (예: NewsPageRelatedLink, PublicationPageRelatedLink 등).
class BlogPageRelatedLink(Orderable,RelatedLink):
    page = ParentalKey(BlogPage, on_delete=models.CASCADE, related_name='related_links')

또는 RelatedLink가 프로젝트에 정의된 상당수의 페이지 유형에 나타날 경우 기본 wagtailcore.Page 모델을 가리키는 단일 RelatedLink 모델을 설정하는 것이 더 적절할 수 있습니다.

class RelatedLink(Orderable):
    page = ParentalKey("wagtailcore.Page", on_delete=models.CASCADE, related_name='related_links')
    name = models.CharField(max_length=255)
    url = models.URLField()
    panels = [
        FieldPanel('name'),
        FieldPanel('url'),
    ]

이렇게 하면 related_links 가 모든 페이지 유형에서 관계로 사용할 수 있게 되지만, InlinePanel 을 패널 정의에 포함하는 페이지 유형에서만 편집할 수 있습니다. 다른 페이지 유형의 경우 관련 링크 집합은 비어 있습니다.

페이지 작업

Wagtail은 Django의 다중 테이블 상속 기능을 사용하여 동일한 트리에서 여러 페이지 모델을 사용할 수 있도록 합니다.

각 페이지는 Wagtail의 내장 Page 모델과 사용자 정의 모델(예: 이전에 생성된 BlogPage 모델)에 모두 추가됩니다.

페이지는 Python 코드에서 Page 인스턴스 또는 페이지 모델 인스턴스의 두 가지 형태로 존재할 수 있습니다.

여러 페이지 유형을 함께 작업할 때는 일반적으로 Wagtail의 Page 모델 인스턴스를 사용하며, 이 모델은 해당 유형에 특정한 필드에 액세스할 수 없습니다.

# 데이터베이스의 모든 페이지 가져오기
>>> from wagtail.models import Page
>>> Page.objects.all()
[<Page: 홈페이지>, <Page: 회사 소개>, <Page: 블로그>, <Page: 블로그 게시물>, <Page: 다른 블로그 게시물>]

단일 페이지 유형으로 작업할 때는 사용자 정의 모델 인스턴스로 작업할 수 있습니다. 이 모델은 Page 에서 사용할 수 있는 모든 필드와 해당 유형에 대한 사용자 정의 필드에 액세스할 수 있습니다.

# 데이터베이스의 모든 블로그 항목 가져오기
>>> BlogPage.objects.all()
[<BlogPage: 블로그 게시물>, <BlogPage: 다른 블로그 게시물>]

.specific 속성을 사용하여 Page 객체를 더 구체적인 사용자 정의 객체로 변환할 수 있습니다. 이로 인해 추가 데이터베이스 조회가 발생할 수 있습니다.

>>> page = Page.objects.get(title="블로그 게시물")
>>> page
<Page: 블로그 게시물>

# 참고: 블로그 게시물은 Page 인스턴스이므로 body, date 또는 feed_image에 액세스할 수 없습니다.

>>> page.specific
<BlogPage: 블로그 게시물>

친숙한 모델 이름

Django의 내부 Meta 클래스를 verbose_name 과 함께 사용하여 Wagtail 사용자에게 모델 이름을 더 친숙하게 만들 수 있습니다. 예를 들어:

class HomePage(Page):
    ...

    class Meta:
        verbose_name = "홈페이지"

사용자에게 만들 페이지를 선택할 수 있는 옵션이 주어지면 페이지 유형 목록은 각 대문자를 기준으로 모델 이름을 분할하여 생성됩니다. 따라서 HomePage 모델은 “Home Page”로 이름이 지정되어 약간 어색합니다. 위 예시와 같이 verbose_name 을 정의하면 “Homepage”로 변경되어 약간 더 일반적입니다.

페이지 QuerySet 정렬

Page 파생 모델은 내부 Meta 클래스에 ordering 속성을 추가하는 표준 Django 접근 방식을 사용하여 기본 정렬을 지정할 수 없습니다.

class NewsItemPage(Page):
    publication_date = models.DateField()
    ...

    class Meta:
        ordering = ('-publication_date', )  # 작동하지 않음

이는 Page 가 경로별로 QuerySet 정렬을 강제하기 때문입니다. 대신 QuerySet을 구성할 때 정렬을 명시적으로 적용해야 합니다.

news_items = NewsItemPage.objects.live().order_by('-publication_date')

사용자 지정 페이지 관리자

Page 클래스에 사용자 지정 Manager 를 추가할 수 있습니다. 모든 사용자 지정 Manager는 wagtail.models.PageManager 에서 상속해야 합니다.

from django.db import models
from wagtail.models import Page, PageManager

class EventPageManager(PageManager):
    """ 이벤트 페이지에 대한 사용자 지정 관리자 """

class EventPage(Page):
    start_date = models.DateField()

    objects = EventPageManager()

또는 추가 QuerySet 메서드만 추가해야 하는 경우 wagtail.models.PageQuerySet 에서 상속하여 사용자 지정 Manager 를 빌드할 수 있습니다.

from django.db import models
from django.utils import timezone
from wagtail.models import Page, PageManager, PageQuerySet

class EventPageQuerySet(PageQuerySet):
    def future(self):
        today = timezone.localtime(timezone.now()).date()
        return self.filter(start_date__gte=today)

EventPageManager = PageManager.from_queryset(EventPageQuerySet)

class EventPage(Page):
    start_date = models.DateField()

    objects = EventPageManager()