관리자 뷰 생성하기

Wagtail 관리자에 사용자 정의 뷰를 추가하는 가장 일반적인 용도는 Django 모델 관리를 위한 인터페이스를 제공하는 것입니다. Wagtail은 스니펫를 사용하여 최소한의 설정으로 Django 모델을 나열, 생성 및 편집할 수 있는 기성 뷰를 제공합니다.

이 패턴에 맞지 않는 다른 종류의 관리자 뷰의 경우, 직접 Django 뷰를 작성하고 훅(hooks)을 통해 Wagtail 관리자의 일부로 등록할 수 있습니다. 이 예제에서는 Python 표준 라이브러리의 calendar 모듈을 사용하여 현재 연도의 달력을 표시하는 뷰를 구현해 보겠습니다.

뷰 정의하기

Wagtail 프로젝트 내에서 ./manage.py startapp wagtailcalendar 명령으로 새로운 wagtailcalendar 앱을 생성하고 프로젝트의 INSTALLED_APPS 에 추가합니다. (이 경우, 표준 라이브러리의 calendar 모듈과의 충돌을 피하기 위해 ‘wagtailcalendar’라는 이름을 사용합니다. 일반적으로 ‘wagtail’ 접두사를 사용할 필요는 없습니다.)

views.py 를 다음과 같이 편집합니다. 이것은 Wagtail 관련 코드가 없는 순수 Django 뷰입니다.

import calendar

from django.http import HttpResponse
from django.utils import timezone


def index(request):
    current_year = timezone.now().year
    calendar_html = calendar.HTMLCalendar().formatyear(current_year)

    return HttpResponse(calendar_html)

URL 라우트 등록하기

이 시점에서 Django 프로젝트의 표준적인 방법은 프로젝트의 최상위 URL 설정 모듈에 이 뷰에 대한 URL 라우트를 추가하는 것입니다. 하지만 이 경우에는 뷰가 로그인한 사용자에게만 제공되고, Wagtail이 관리하는 /admin/ URL 네임스페이스 내에 나타나기를 원합니다. 이는 Register Admin URLs 훅을 통해 수행됩니다.

시작 시 Wagtail은 각 설치된 앱 내에서 wagtail_hooks 서브 모듈을 찾습니다. 이 서브 모듈에서는 관리자용 URL 설정을 빌드하고 주 메뉴를 구성하는 등 Wagtail 작동의 다양한 시점에서 실행될 함수를 정의할 수 있습니다.

wagtailcalendar 앱 내에 wagtail_hooks.py 파일을 만들고 다음 내용을 포함합니다.

from django.urls import path
from wagtail import hooks

from .views import index


@hooks.register('register_admin_urls')
def register_calendar_url():
    return [
        path('calendar/', index, name='calendar'),
    ]

이제 달력은 /admin/calendar/ URL에서 볼 수 있습니다.

스타일이 적용되지 않은 HTML로 표시된 달력

템플릿 추가하기

현재 이 뷰는 순수한 HTML 조각을 출력하고 있습니다. Wagtail의 기본 템플릿인 "wagtailadmin/base.html" 을 확장하는 템플릿을 만들어 이를 일반적인 Wagtail 관리자 페이지 구조에 삽입해 보겠습니다.

참고

기본 템플릿과 HTML 구조는 Wagtail API의 안정적인 부분으로 간주되지 않으며 향후 릴리스에서 변경될 수 있습니다.

views.py 를 다음과 같이 업데이트합니다.

import calendar
from django.shortcuts import render
from django.utils import timezone

def index(request):
    current_year = timezone.now().year
    calendar_html = calendar.HTMLCalendar().formatyear(current_year)

    return render(request, 'wagtailcalendar/index.html', {
        'current_year': current_year,
        'calendar_html': calendar_html,
    })

이제 wagtailcalendar 앱 내에 templates/wagtailcalendar/ 폴더를 만들고, 그 안에 index.htmlcalendar.css 를 다음과 같이 포함시킵니다.

{% extends "wagtailadmin/base.html" %}
{% load static %}

{% block titletag %}{{ current_year }} calendar{% endblock %}

{% block extra_css %}
    {{ block.super }}
    <link rel="stylesheet" href="{% static 'css/calendar.css' %}">
{% endblock %}

{% block content %}
    {% include "wagtailadmin/shared/header.html" with title="Calendar" icon="date" %}

    <div class="nice-padding">
        {{ calendar_html|safe }}
    </div>
{% endblock %}
/* calendar.css */
table.month {
    margin: 20px;
}

table.month td,
table.month th {
    padding: 5px;
}

여기서는 기본 템플릿에 정의된 세 개의 블록을 재정의하고 있습니다: titletag(HTML <title> 태그의 내용을 설정), extra_css(이 페이지에 특정한 추가 CSS 스타일을 제공), 그리고 content(페이지의 주 콘텐츠 영역). 또한 표준 헤더 바 컴포넌트를 포함하고 제목과 아이콘을 설정하고 있습니다. 인식되는 아이콘 식별자 목록은 스타일 가이드를 참조하세요.

/admin/calendar/ 를 다시 방문하면 이제 Wagtail 관리자 페이지 구조 내에 달력이 표시됩니다.

스타일이 적용되지 않은 HTML로 표시된 달력

메뉴 항목 추가하기

이제 달력 뷰는 완성되었지만, 관리자 백엔드의 다른 곳에서 접근할 방법이 없습니다. 사이드바 메뉴에 항목을 추가하기 위해 또 다른 훅인 Register Admin Menu Item을 사용하겠습니다. wagtail_hooks.py 를 다음과 같이 업데이트합니다.

from django.urls import path, reverse

from wagtail.admin.menu import MenuItem
from wagtail import hooks

from .views import index


@hooks.register('register_admin_urls')
def register_calendar_url():
    return [
        path('calendar/', index, name='calendar'),
    ]


@hooks.register('register_admin_menu_item')
def register_calendar_menu_item():
    return MenuItem('Calendar', reverse('calendar'), icon_name='date')

이제 메뉴에 ‘Calendar’ 항목이 나타납니다.

날짜 아이콘과 함께 "Calendar" 메뉴 항목을 보여주는 Wagtail 관리자 사이드바 메뉴

메뉴 항목 그룹 추가하기

때로는 사이드바의 단일 메뉴 항목에 사용자 정의 뷰들을 그룹화하고 싶을 수 있습니다. 현재 달력 월만 표시하는 또 다른 뷰를 만들어 보겠습니다.

import calendar
from django.shortcuts import render
from django.utils import timezone


def index(request):
    current_year = timezone.now().year
    calendar_html = calendar.HTMLCalendar().formatyear(current_year)

    return render(request, 'wagtailcalendar/index.html', {
        'current_year': current_year,
        'calendar_html': calendar_html,
    })

def month(request):
    current_year = timezone.now().year
    current_month = timezone.now().month
    calendar_html = calendar.HTMLCalendar().formatmonth(current_year, current_month)

    return render(request, 'wagtailcalendar/index.html', {
        'current_year': current_year,
        'calendar_html': calendar_html,
    })

또한 wagtail_hooks.py 를 업데이트하여 관리자 인터페이스에 URL을 등록해야 합니다.

from django.urls import path
from wagtail import hooks

from .views import index, month


@hooks.register('register_admin_urls')
def register_calendar_url():
    return [
        path('calendar/', index, name='calendar'),
        path('calendar/month/', month, name='calendar-month'),
    ]

이제 달력은 /admin/calendar/month/ URL에서 볼 수 있습니다.

단일 달력 월

마지막으로, wagtail_hooks.py 를 변경하여 사용자 정의 메뉴 항목 그룹을 포함할 수 있습니다. 이는 단일 항목을 추가하는 것과 유사하지만 MenuSubmenuMenuItem 이라는 두 개의 클래스를 더 가져와야 합니다.

from django.urls import path, reverse

from wagtail.admin.menu import Menu, MenuItem, SubmenuMenuItem
from wagtail import hooks


from .views import index, month


@hooks.register('register_admin_urls')
def register_calendar_url():
    return [
        path('calendar/', index, name='calendar'),
        path('calendar/month/', month, name='calendar-month'),
    ]


@hooks.register('register_admin_menu_item')
def register_calendar_menu_item():
    submenu = Menu(items=[
        MenuItem('Calendar', reverse('calendar'), icon_name='date'),
        MenuItem('Current month', reverse('calendar-month'), icon_name='date'),
    ])

    return SubmenuMenuItem('Calendar', submenu, icon_name='date')

이제 ‘Calendar’ 항목이 메뉴 항목 그룹으로 나타납니다. 확장하면 ‘Calendar’ 항목에 두 개의 사용자 정의 메뉴 항목이 표시됩니다.

날짜 아이콘이 있는 확장된 "Calendar" 그룹 메뉴 항목을 보여주는 Wagtail 관리자 사이드바 메뉴, 'Calendar'와 'Month'라는 두 개의 하위 메뉴 항목을 보여줌.

ViewSet 을 사용하여 사용자 정의 관리자 뷰 그룹화하기

URL 및 메뉴 항목과 함께 관리자 뷰를 등록하는 것은 Wagtail에서 일반적인 패턴입니다. 이는 종종 우리가 작업하는 모델 및 관련 아이콘과 같은 공유 속성을 가진 여러 관련 뷰를 포함합니다. 이 패턴을 지원하기 위해 Wagtail은 뷰셋(viewset) 개념을 구현합니다. 이를 통해 뷰와 해당 URL 묶음을 집합적으로 정의하고, register_admin_viewset 훅을 통해 단일 작업으로 관리자 앱에 메뉴 항목을 등록할 수 있습니다.

예를 들어, 이전 예제의 달력 뷰들을 views.pyViewSet 서브클래스를 만들어 단일 메뉴 항목으로 그룹화할 수 있습니다.

from wagtail.admin.viewsets.base import ViewSet

...

class CalendarViewSet(ViewSet):
    add_to_admin_menu = True
    menu_label = "Calendar"
    icon = "date"
    # `name` 은 URL 접두사와 URL 네임스페이스 모두에 사용됩니다.
    # `url_prefix` 와 `url_namespace` 를 통해 개별적으로 사용자 정의할 수 있습니다.
    name = "calendar"

    def get_urlpatterns(self):
        return [
            # 이것은 `/admin/calendar/` 에서 접근할 수 있으며
            # `calendar:index` 이름으로 역-확인(reverse-resolved)될 수 있습니다.
            # 이 첫 번째 URL은 메뉴 항목에 사용되지만,
            # `menu_url` 속성을 재정의하여 사용자 정의할 수 있습니다.
            path('', index, name='index'),

            # 이것은 `/admin/calendar/month/` 에서 접근할 수 있으며
            # `calendar:month` 이름으로 역-확인될 수 있습니다.
            path('month/', month, name='month'),
        ]

그런 다음, wagtail_hooks.py 에서 register_admin_urlsregister_admin_menu_item 훅을 제거하고, register_admin_viewset 훅으로 ViewSet 서브클래스를 등록합니다.

from .views import CalendarViewSet

@hooks.register("register_admin_viewset")
def register_viewset():
    return CalendarViewSet()

두 개의 개별 훅을 사용한 이전 예제와 비교하여, 이것은 /admin/calendar/ URL로 이동하는 단일 메뉴 항목 “Calendar”를 생성합니다. 두 번째 URL은 자체 메뉴 항목을 갖지 않지만, 여전히 /admin/calendar/month/ 에서 접근할 수 있습니다. 이는 반드시 자체 메뉴 항목이 필요하지 않은 관련 뷰들을 함께 그룹화하는 데 유용합니다.

추가적인 사용자 정의는 ViewSet 문서를 참조하세요.

ViewSetGroup 을 사용하여 여러 ViewSet 결합하기

ViewSetGroup 클래스는 여러 ViewSet 을 최상위 메뉴 항목 내에 그룹화하는 데 사용할 수 있습니다. 예를 들어, 이전 예제의 CalendarViewSet 과 그룹화하려는 다른 뷰셋(예: EventViewSet)이 있는 경우, views.pyViewSetGroup 서브클래스를 만들어 그렇게 할 수 있습니다.

from wagtail.admin.viewsets.base import ViewSetGroup

...

class AgendaViewSetGroup(ViewSetGroup):
    menu_label = "Agenda"
    menu_icon = "table"
    # `items` 에 `ViewSet` 의 인스턴스나 서브클래스를 지정할 수 있습니다.
    items = (CalendarViewSet(), EventViewSet)

그런 다음, 뷰셋에서 add_to_admin_menu 를 제거하고 wagtail_hooks.pyregister_admin_viewset 훅을 업데이트하여 개별 뷰셋 대신 ViewSetGroup 을 등록합니다.

from .views import AgendaViewSetGroup

@hooks.register("register_admin_viewset")
def register_viewset():
    return AgendaViewSetGroup()

이렇게 하면 “Agenda”라는 최상위 메뉴 항목이 생성되고, 그 아래에 “Calendar”와 “Events”와 같은 두 뷰셋의 메뉴 항목이 하위 항목으로 표시됩니다.

추가적인 사용자 정의는 ViewSetGroup 문서를 참조하세요.

관리자 뷰에 링크 추가하기

스니펫 (Snippets)

예제로 Wagtail Bakery 데모BreadTypeSnippet 을 사용하겠습니다.

스니펫 URL 이름은 기본적으로 wagtailsnippets_{app_label}_{model_name}:{list/edit/inspect/copy/delete} 패턴을 따릅니다.

Python에서는 get_url_name() 을 사용하여 스니펫 뷰 URL의 이름을 얻을 수 있습니다. (예: BreadTypeSnippet.get_url_name("list"))

따라서 BreadTypeSnippet URL은 템플릿에서 다음과 같이 보입니다.

{% url 'wagtailsnippets_breads_breadtype:list' %}
{% url 'wagtailsnippets_breads_breadtype:edit' object.id %}
{% url 'wagtailsnippets_breads_breadtype:inspect' object.id %}
{% url 'wagtailsnippets_breads_breadtype:copy' object.id %}
{% url 'wagtailsnippets_breads_breadtype:delete' object.id %}

페이지 (Pages)

새 페이지

{% url 'wagtailadmin_pages:add' content_type_app_name content_type_model_name parent_id %}

페이지 사용 현황

{% url 'wagtailadmin_pages:usage' page_id %}

페이지 수정

{% url 'wagtailadmin_pages:edit' page_id %}

페이지 삭제

{% url 'wagtailadmin_pages:delete' page_id %}

페이지 복사

{% url 'wagtailadmin_pages:copy' page_id }

이미지 (Images)

이미지 목록

{% url 'wagtailimages:index' %}

이미지 수정

{% url 'wagtailimages:edit' image_id %}

이미지 삭제

{% url 'wagtailimages:delete' image_id %}

새 이미지

{% url 'wagtailimages:add' %}

이미지 사용 현황

{% url 'wagtailimages:image_usage' image_id %}

AdminURLFinder

관리자에서 모든 모델의 URL을 찾기 위해 AdminURLFinder 클래스를 사용할 수 있습니다.

from wagtail.admin.admin_url_finder import AdminURLFinder

finder = AdminURLFinder()

finder.get_edit_url(model_instance)