제네릭 뷰

Wagtail은 모델 인스턴스 생성/편집 및 선택기(chooser) 모달과 같은 일반적인 작업을 처리하기 위한 여러 제네릭 뷰를 제공합니다. 편의를 위해 이러한 뷰는 뷰셋(viewsets)에 번들로 제공됩니다.

ModelViewSet

ModelViewSet 클래스는 모델 인스턴스의 목록 보기, 생성, 편집 및 삭제를 위한 뷰를 제공합니다. 예를 들어, 다음과 같은 모델이 있다고 가정해 봅시다:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)

    def __str__(self):
        return "%s %s" % (self.first_name, self.last_name)

다음 정의(같은 앱의 views.py 에 위치)는 Person 인스턴스를 관리하기 위한 뷰 세트를 생성합니다:

from wagtail.admin.viewsets.model import ModelViewSet
from .models import Person


class PersonViewSet(ModelViewSet):
    model = Person
    form_fields = ["first_name", "last_name"]
    icon = "user"
    add_to_admin_menu = True
    copy_view_enabled = False
    inspect_view_enabled = True


person_viewset = PersonViewSet("person")  # /admin/person/을 기본 URL로 정의합니다

이 뷰셋은 Wagtail 관리자에 등록하여 /admin/person/ URL에서 사용할 수 있도록 할 수 있습니다. wagtail_hooks.py 에 다음을 추가하면 됩니다:

from wagtail import hooks

from .views import person_viewset


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

뷰셋은 다른 속성과 메서드를 재정의하여 추가로 사용자 정의할 수 있습니다.

아이콘

ModelViewSeticon 속성을 정의하여 뷰셋의 뷰 전반에 사용될 아이콘을 지정할 수 있습니다. iconWagtail 아이콘 라이브러리에 등록되어야 합니다.

URL 접두사 및 네임스페이스

url_prefixurl_namespace 속성을 재정의하여 뷰에 대한 사용자 정의 URL 접두사 및 네임스페이스를 사용할 수 있습니다. 설정하지 않으면 모델의 model_name 으로 기본값이 설정됩니다.

메뉴 항목

기본적으로 ModelViewSet 을 등록해도 주 메뉴 항목이 등록되지 않습니다. 메뉴 항목을 추가하려면 add_to_admin_menuTrue 로 설정하십시오. 또는 “설정” 메뉴 내에 메뉴 항목을 추가하려면 add_to_settings_menuTrue 로 설정할 수 있습니다. menu_icon 이 지정되지 않은 경우, 메뉴는 뷰에 사용된 것과 동일한 icon 을 사용합니다. menu_url 속성을 재정의하여 메뉴 항목의 링크를 사용자 정의할 수 있으며, 기본값은 모델의 목록 보기입니다.

지정하지 않으면 메뉴 항목은 모델의 상세 이름(verbose name)으로 레이블이 지정됩니다. menu_label, menu_name, menu_order 속성을 각각 설정하여 메뉴 항목의 레이블, 이름 및 순서를 사용자 정의할 수 있습니다. MenuItem 인스턴스를 완전히 사용자 정의하려면 get_menu_item() 메서드를 재정의할 수 있습니다.

ModelViewSetGroup 클래스를 사용하여 여러 ModelViewSet 의 메뉴 항목을 단일 최상위 메뉴 항목으로 그룹화할 수 있습니다. 이는 ViewSetGroup 과 유사하지만, 첫 번째 뷰셋 모델의 app_label 을 기본 menu_label 로 사용한다는 점이 다릅니다. 자세한 내용은 ViewSetGroup 예제를 참조하십시오.

목록 보기

list_display 속성을 설정하여 목록 보기에 표시할 열을 지정할 수 있습니다. 페이지당 표시할 항목 수를 사용자 정의하려면 list_per_page 속성을 설정할 수 있습니다. 또한 ordering 속성을 사용하여 목록 보기에 구성된 default_ordering 을 재정의할 수 있습니다.

list_filter 속성을 정의하고 필터링할 필드 목록을 지정하여 목록 보기에 필터링 기능을 추가할 수 있습니다. Wagtail은 내부적으로 django-filter 패키지를 사용하며, 이 속성은 django-filter의 FilterSet.Meta.fields 속성으로 전달됩니다. 즉, 필드 이름을 조회 목록에 매핑하는 사전을 전달할 수도 있습니다.

필터링 메커니즘을 추가로 사용자 정의하려면 filterset_class 속성을 재정의하여 사용자 정의 wagtail.admin.filters.WagtailFilterSet 서브클래스를 사용할 수도 있습니다. filterset_class 가 설정되면 list_filter 속성은 무시됩니다. 자세한 내용은 django-filter 문서를 참조하십시오.

list_export 속성을 설정하여 내보낼 열을 지정함으로써 목록 보기를 스프레드시트로 내보내는 기능을 추가할 수 있습니다. export_filename 속성을 사용하여 내보낸 스프레드시트의 파일 이름을 사용자 정의할 수 있습니다.

생성 및 편집 뷰

ModelViewSet 또는 Django 모델에 panels 또는 edit_handler 속성을 정의하여 Wagtail의 패널 메커니즘을 사용할 수 있습니다. 자세한 내용은 패널를 참조하십시오.

panelsedit_handler 가 정의되지 않고 get_edit_handler() 메서드가 재정의되지 않은 경우, 폼은 일반 Django 폼으로 렌더링됩니다. form_fields 속성을 설정하여 폼에 표시할 필드를 지정함으로써 폼을 사용자 정의할 수 있습니다. 또는 exclude_form_fields 속성을 설정하여 폼에서 제외할 필드를 지정할 수 있습니다. 패널을 사용하지 않는 경우, get_form_class() 가 재정의되지 않는 한 form_fields 또는 exclude_form_fields 를 정의해야 합니다.

복사 뷰

복사 뷰는 기본적으로 활성화되어 있으며 모델에 대한 ‘add’ 권한이 있는 사용자가 접근할 수 있습니다. 비활성화하려면 copy_view_enabledFalse 로 설정하십시오.

뷰의 폼은 생성 또는 편집 폼과 동일한 방식으로 생성됩니다. 사용자 정의 폼을 사용하려면 copy_view_class 를 재정의하고 해당 클래스의 form_class 속성을 수정하십시오.

검사 뷰

검사 뷰는 대부분의 모델에 유용하지 않기 때문에 기본적으로 비활성화되어 있습니다. 그러나 사용자가 인스턴스를 편집할 수 있는 옵션 없이 인스턴스에 대한 자세한 정보를 볼 수 있는 뷰가 필요한 경우, ModelViewSet 클래스에서 inspect_view_enabled 를 설정하여 검사 뷰를 활성화할 수 있습니다.

검사 뷰가 활성화되면 목록 보기의 각 행에 ‘Inspect’ 버튼이 자동으로 나타나며, 이 버튼을 클릭하면 해당 인스턴스의 필드 값 목록을 보여주는 뷰로 이동합니다.

기본적으로 모든 ‘구체적인(concrete)’ 필드(필드 값이 모델의 데이터베이스 테이블에 열로 저장되는 경우)가 표시됩니다. ModelViewSet 클래스에서 inspect_view_fields 또는 inspect_view_fields_exclude 속성을 지정하여 표시되는 값을 사용자 정의할 수 있습니다.

템플릿

template_prefix 가 설정되면, Wagtail은 기본 템플릿을 사용하기 전에 프로젝트나 앱 내의 다음 디렉토리에서 뷰의 템플릿을 찾습니다:

  1. templates/{template_prefix}/{app_label}/{model_name}/

  2. templates/{template_prefix}/{app_label}/

  3. templates/{template_prefix}/

예를 들어 IndexView 에서 사용하는 템플릿을 재정의하려면 새 index.html 템플릿을 만들어 해당 위치 중 하나에 넣을 수 있습니다. 예를 들어, template_prefixcustom/campaign 이고 merch 앱에 Shirt 모델이 있는 경우, 사용자 정의 템플릿을 templates/custom/campaign/merch/shirt/index.html 로 추가할 수 있습니다.

일부 공통 뷰의 경우, Wagtail은 뷰셋에서 {view_name}_template_name 속성을 재정의하여 사용되는 템플릿을 재정의할 수도 있습니다. 다음은 뷰에 대한 사용자 정의 지점 목록입니다:

기타 사용자 정의

기본적으로 ModelViewSet 에 등록된 모델은 참조 인덱스에도 등록됩니다. 이 동작을 끄려면 add_to_reference_indexFalse 로 설정하면 됩니다.

뷰셋을 사용자 정의하는 데 사용할 수 있는 다양한 추가 속성이 있습니다 - ModelViewSet 문서를 참조하십시오.

ChooserViewSet

ChooserViewSet 클래스는 모달 선택기(chooser) 인터페이스를 구성하는 뷰를 제공하여 사용자가 모델 인스턴스 목록에서 선택하여 ForeignKey 필드를 채울 수 있도록 합니다. 동일한 Person 모델을 사용하여 다음 정의(views.py 에 위치)는 사람 선택기 모달을 위한 뷰를 생성합니다:

from wagtail.admin.viewsets.chooser import ChooserViewSet


class PersonChooserViewSet(ChooserViewSet):
    # 모델은 모델 클래스 또는 "app_label.model_name" 문자열로 지정할 수 있습니다.
    # 문자열을 사용하면 StreamField 블록 클래스에 접근할 때 순환 가져오기를 피할 수 있습니다 (아래 참조)
    model = "myapp.Person"

    icon = "user"
    choose_one_text = "사람 선택"
    choose_another_text = "다른 사람 선택"
    edit_item_text = "이 사람 편집"
    form_fields = ["first_name", "last_name"]  # "생성" 탭에 표시할 필드


person_chooser_viewset = PersonChooserViewSet("person_chooser")

이것 역시 register_admin_viewset 훅으로 등록할 수 있습니다:

from wagtail import hooks

from .views import person_chooser_viewset


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

선택기 뷰셋을 등록하면 해당 모델에 대한 ForeignKey 필드가 WagtailAdminModelForm 에 나타날 때마다 사용될 선택기 위젯도 설정됩니다 - 관리자 뷰에서 폼 사용하기를 참조하십시오. 특히, authorPerson 모델에 대한 외래 키인 FieldPanel("author") 와 같은 패널 정의는 자동으로 이 선택기 인터페이스를 사용하게 됩니다. 선택기 위젯 클래스는 뷰셋의 widget_class 속성으로 직접 검색할 수도 있습니다(예: 일반 Django 폼에서 사용). 예를 들어, 다음 코드를 widgets.py 에 배치하면 from myapp.widgets import PersonChooserWidget 으로 가져올 수 있는 선택기 위젯을 사용할 수 있습니다:

from .views import person_chooser_viewset

PersonChooserWidget = person_chooser_viewset.widget_class

뷰셋은 또한 get_block_class 메서드를 통해 StreamField 선택기 블록 클래스를 사용할 수 있게 합니다. 다음 코드를 blocks.py 에 배치하면 from myapp.blocks import PersonChooserBlock 을 가져와 StreamField 정의에서 사용할 수 있는 선택기 블록을 만들 수 있습니다:

from .views import person_chooser_viewset

PersonChooserBlock = person_chooser_viewset.get_block_class(
    name="PersonChooserBlock", module_path="myapp.blocks"
)

연결된 필드를 통한 선택 제한

선택기 뷰셋은 호출 페이지의 다른 입력 필드에 따라 선택기에 표시되는 옵션을 제한하는 메커니즘을 제공합니다. 예를 들어, 사람 모델에 국가 필드가 있다고 가정해 봅시다. 그러면 국가 드롭다운과 사람 선택기가 있는 페이지 모델을 설정할 수 있습니다. 여기서 편집자는 먼저 드롭다운에서 국가를 선택한 다음 사람 선택기를 열어 해당 국가의 사람들 목록을 볼 수 있습니다.

이를 설정하려면 ChooserViewSeturl_filter_parameters 속성을 정의하십시오. 이것은 결과를 필터링하기 위해 인식될 URL 매개변수 목록을 지정합니다. 이 매개변수들이 URL로 전달될 때마다 해당 이름의 필드에 대한 filter 절이 쿼리셋에 적용됩니다. 이 매개변수들은 또한 preserve_url_parameters 속성에도 나열되어야 선택기를 탐색할 때(예: 페이지네이션 링크를 따라갈 때) URL에 보존됩니다. 다음 정의는 사람 선택기를 국가별로 필터링할 수 있도록 합니다:

class PersonChooserViewSet(ChooserViewSet):
    model = "myapp.Person"
    url_filter_parameters = ["country"]
    preserve_url_parameters = ["multiple", "country"]

이제 선택기 위젯은 모달을 열 때 이러한 URL 매개변수를 전달하도록 구성해야 합니다. 이는 위젯의 생성자에 linked_fields 사전을 전달하여 수행됩니다. 여기서 키는 전달할 URL 매개변수의 이름이고 값은 호출 페이지의 해당 입력 필드에 대한 CSS 선택자입니다. 예를 들어, 국가 드롭다운과 사람 선택기가 있는 페이지 모델이 있다고 가정해 봅시다:

class BlogPage(Page):
    country = models.ForeignKey(Country, null=True, blank=True, on_delete=models.SET_NULL)
    author = models.ForeignKey(Person, null=True, blank=True, on_delete=models.SET_NULL)

    content_panels = Page.content_panels + [
        FieldPanel('country'),
        FieldPanel('author', widget=PersonChooserWidget(linked_fields={
            # id_country 입력에서 선택된 국가를 사람 선택기에
            # `country` 라는 URL 매개변수로 전달합니다
            'country': '#id_country',
        })),
    ]

다른 여러 조회 메커니즘을 사용할 수 있습니다:

PersonChooserWidget(linked_fields={
    'country': {'selector': '#id_country'}  # 'country': '#id_country'와 동일
})

# ID로 조회
PersonChooserWidget(linked_fields={
    'country': {'id': 'id_country'}
})

# 정규식 일치, ID가 동적인 StreamFields 및 InlinePanels에서 사용:
# 1) 현재 위젯의 폼 요소(PersonChooserWidget)의 ID를
#      정규식 '^id_blog_person_relationship-\d+-'와 일치시킵니다
# 2) 일치된 부분 문자열에 'country'를 추가합니다
# 3) 해당 ID를 가진 입력 필드를 검색합니다
PersonChooserWidget(linked_fields={
    'country': {'match': r'^id_blog_person_relationship-\d+-', 'append': 'country'},
})

모델이 아닌 데이터 소스를 위한 선택기 뷰셋

제네릭 선택기 뷰는 주로 Django 모델을 데이터 소스로 사용하도록 설계되었지만, REST API 엔드포인트와 같은 다른 소스를 기반으로 하는 선택기는 queryish 라이브러리를 사용하여 구현할 수 있습니다. 이 라이브러리를 사용하면 모든 데이터 소스를 Django QuerySet과 유사한 인터페이스로 래핑할 수 있습니다. 그런 다음 이를 일반 모델처럼 ChooserViewSet 에 전달할 수 있습니다. 예를 들어, queryish 문서의 포켓몬 예제는 다음과 같이 선택기로 만들 수 있습니다:

# views.py

import re
from queryish.rest import APIModel
from wagtail.admin.viewsets.chooser import ChooserViewSet


class Pokemon(APIModel):
    class Meta:
        base_url = "https://pokeapi.co/api/v2/pokemon/"
        detail_url = "https://pokeapi.co/api/v2/pokemon/%s/"
        fields = ["id", "name"]
        pagination_style = "offset-limit"
        verbose_name_plural = "pokemon"

    @classmethod
    def from_query_data(cls, data):
        return cls(
            id=int(re.match(r'https://pokeapi.co/api/v2/pokemon/(\d+)/', data['url']).group(1)),
            name=data['name'],
        )

    @classmethod
    def from_individual_data(cls, data):
        return cls(
            id=data['id'],
            name=data['name'],
        )

    def __str__(self):
        return self.name


class PokemonChooserViewSet(ChooserViewSet):
    model = Pokemon

    choose_one_text = "포켓몬 선택"
    choose_another_text = "다른 포켓몬 선택"

pokemon_chooser_viewset = PokemonChooserViewSet("pokemon_chooser")


# wagtail_hooks.py

from wagtail import hooks

from .views import pokemon_chooser_viewset


@hooks.register("register_admin_viewset")
def register_pokemon_chooser_viewset():
    return pokemon_chooser_viewset