Django 관리자 사이트 커스터마이징

Beginner

소개

이 튜토리얼은 Add a Stylesheet and an Image에서 다룬 내용을 기반으로 시작합니다. 웹 투표 애플리케이션을 계속 개발하며, Set Up the Database에서 처음 살펴본 Django 의 자동 생성된 관리자 사이트를 커스터마이징하는 데 집중할 것입니다.

관리자 폼 커스터마이징

Question 모델을 admin.site.register(Question)으로 등록함으로써 Django 는 기본 폼 표현을 구성할 수 있었습니다. 종종 관리자 폼의 모양과 작동 방식을 커스터마이징하고 싶을 것입니다. 객체를 등록할 때 원하는 옵션을 Django 에 알려줌으로써 이를 수행합니다.

편집 폼에서 필드의 순서를 변경하여 이 작업이 어떻게 작동하는지 살펴보겠습니다. admin.site.register(Question) 라인을 다음으로 바꿉니다.

~/project/mysite/polls/admin.py 파일을 다음과 같이 편집합니다.

from django.contrib import admin

from .models import Question


class QuestionAdmin(admin.ModelAdmin):
    fields = ["pub_date", "question_text"]


admin.site.register(Question, QuestionAdmin)

모델에 대한 관리자 옵션을 변경해야 할 때마다 이 패턴을 따릅니다. 즉, 모델 관리자 클래스를 생성한 다음 admin.site.register()에 두 번째 인수로 전달합니다.

Django 개발 서버를 실행합니다.

cd ~/project/mysite
python manage.py runserver

Firefox 또는 데스크톱 환경에서 http://127.0.0.1:8000/admin/을 열고 "Questions" 링크를 클릭합니다. 다음과 같은 폼이 표시됩니다.

위의 특정 변경 사항은 "게시 날짜"가 "질문" 필드 앞에 오도록 합니다.

Admin form field reorder

필드가 두 개뿐이므로 인상적이지 않지만, 수십 개의 필드가 있는 관리자 폼의 경우 직관적인 순서를 선택하는 것이 중요한 사용성 세부 사항입니다.

그리고 수십 개의 필드가 있는 폼에 대해 말하자면, 폼을 필드셋으로 분할하고 싶을 수 있습니다.

from django.contrib import admin

from .models import Question


class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None, {"fields": ["question_text"]}),
        ("Date information", {"fields": ["pub_date"]}),
    ]


admin.site.register(Question, QuestionAdmin)

~django.contrib.admin.ModelAdmin.fieldsets의 각 튜플의 첫 번째 요소는 필드셋의 제목입니다. 이제 폼은 다음과 같습니다.

Admin form with fieldsets

관련 객체 추가

자, Question 관리자 페이지가 있지만, Question에는 여러 개의 Choice가 있으며, 관리자 페이지는 선택 사항을 표시하지 않습니다.

아직은요.

이 문제를 해결하는 두 가지 방법이 있습니다. 첫 번째는 Question과 마찬가지로 Choice를 관리자에 등록하는 것입니다.

from django.contrib import admin

from .models import Choice, Question

## ...
admin.site.register(Choice)

이제 "Choices"는 Django 관리자에서 사용 가능한 옵션입니다. "Add choice" 폼은 다음과 같습니다.

Add Choice form interface

해당 폼에서 "Question" 필드는 데이터베이스의 모든 질문을 포함하는 선택 상자입니다. Django 는 ~django.db.models.ForeignKey가 관리자에서 <select> 상자로 표시되어야 함을 알고 있습니다. 이 경우, 현재 시점에는 질문이 하나만 존재합니다.

또한 "Question" 옆에 있는 "Add another question" 링크에 유의하십시오. 다른 객체와의 ForeignKey 관계가 있는 모든 객체는 이를 무료로 얻습니다. "Add another question"을 클릭하면 "Add question" 폼이 있는 팝업 창이 나타납니다. 해당 창에서 질문을 추가하고 "Save"를 클릭하면 Django 는 질문을 데이터베이스에 저장하고 보고 있는 "Add choice" 폼에서 선택된 선택 사항으로 동적으로 추가합니다.

하지만, 실제로 이것은 Choice 객체를 시스템에 추가하는 비효율적인 방법입니다. Question 객체를 생성할 때 직접적으로 여러 개의 Choices 를 추가할 수 있다면 더 좋을 것입니다. 그렇게 해 봅시다.

Choice 모델에 대한 register() 호출을 제거합니다. 그런 다음 Question 등록 코드를 다음과 같이 편집합니다.

from django.contrib import admin

from .models import Choice, Question


class ChoiceInline(admin.StackedInline):
    model = Choice
    extra = 3


class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None, {"fields": ["question_text"]}),
        ("Date information", {"fields": ["pub_date"], "classes": ["collapse"]}),
    ]
    inlines = [ChoiceInline]


admin.site.register(Question, QuestionAdmin)

이것은 Django 에게 다음과 같이 알려줍니다. "Choice 객체는 Question 관리자 페이지에서 편집됩니다. 기본적으로 3 개의 선택 사항에 대한 충분한 필드를 제공합니다."

"Add question" 페이지를 로드하여 모양을 확인합니다.

Question admin with choices

다음과 같이 작동합니다. extra로 지정된 대로 관련 Choices 에 대한 세 개의 슬롯이 있으며, 이미 생성된 객체의 "Change" 페이지로 돌아올 때마다 세 개의 추가 슬롯이 더 제공됩니다.

현재 세 개의 슬롯 끝에서 "Add another Choice" 링크를 찾을 수 있습니다. 이를 클릭하면 새 슬롯이 추가됩니다. 추가된 슬롯을 제거하려면 추가된 슬롯의 오른쪽 상단에 있는 X 를 클릭하면 됩니다. 이 이미지는 추가된 슬롯을 보여줍니다.

Additional slot added dynamically

하지만 한 가지 작은 문제가 있습니다. 관련 Choice 객체를 입력하기 위한 모든 필드를 표시하는 데 많은 화면 공간이 필요합니다. 이러한 이유로 Django 는 인라인 관련 객체를 표시하는 표 형식의 방법을 제공합니다. 이를 사용하려면 ChoiceInline 선언을 다음과 같이 변경합니다.

class ChoiceInline(admin.TabularInline):
    ...

TabularInline ( StackedInline 대신) 을 사용하면 관련 객체가 더 간결한 테이블 기반 형식으로 표시됩니다.

Tabular inline choices display

"Add another Choice" 버튼을 사용하여 추가된 행과 이미 저장된 행을 제거할 수 있는 추가 "Delete?" 열이 있음에 유의하십시오.

관리자 변경 목록 커스터마이징

이제 Question 관리자 페이지가 보기 좋게 되었으니, 시스템의 모든 질문을 표시하는 "변경 목록" 페이지를 약간 수정해 보겠습니다.

현재 모습은 다음과 같습니다.

Polls change list page

기본적으로 Django 는 각 객체의 str()을 표시합니다. 하지만 개별 필드를 표시할 수 있다면 더 유용할 때가 있습니다. 이를 위해 ~django.contrib.admin.ModelAdmin.list_display 관리자 옵션을 사용합니다. 이 옵션은 객체의 변경 목록 페이지에 열로 표시할 필드 이름의 튜플입니다.

class QuestionAdmin(admin.ModelAdmin):
    ## ...
    list_display = ["question_text", "pub_date"]

확실하게 하기 위해 **Set Up the Database**에서 was_published_recently() 메서드도 포함해 보겠습니다.

class QuestionAdmin(admin.ModelAdmin):
    ## ...
    list_display = ["question_text", "pub_date", "was_published_recently"]

이제 질문 변경 목록 페이지는 다음과 같습니다.

Question change list view

해당 값으로 정렬하려면 열 머리글을 클릭할 수 있습니다. 단, 임의의 메서드의 출력으로 정렬하는 것은 지원되지 않으므로 was_published_recently 머리글은 예외입니다. 또한 was_published_recently에 대한 열 머리글은 기본적으로 메서드의 이름 (밑줄을 공백으로 바꿈) 이고 각 줄에는 출력의 문자열 표현이 포함되어 있습니다.

polls/models.py에서 해당 메서드에 ~django.contrib.admin.display 데코레이터를 사용하여 이를 개선할 수 있습니다.

from django.contrib import admin


class Question(models.Model):
    ## ...
    @admin.display(
        boolean=True,
        ordering="pub_date",
        description="Published recently?",
    )
    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now

데코레이터를 통해 구성 가능한 속성에 대한 자세한 내용은 ~django.contrib.admin.ModelAdmin.list_display를 참조하십시오.

polls/admin.py 파일을 다시 편집하고 Question 변경 목록 페이지에 개선 사항을 추가합니다. ~django.contrib.admin.ModelAdmin.list_filter를 사용하여 필터를 추가합니다. QuestionAdmin에 다음 줄을 추가합니다.

list_filter = ["pub_date"]

그러면 사람들이 pub_date 필드로 변경 목록을 필터링할 수 있는 "Filter" 사이드바가 추가됩니다.

Admin list filter sidebar

표시되는 필터 유형은 필터링하는 필드의 유형에 따라 다릅니다. pub_date~django.db.models.DateTimeField이므로 Django 는 적절한 필터 옵션 ("Any date", "Today", "Past 7 days", "This month", "This year") 을 제공해야 함을 알고 있습니다.

이것은 잘 진행되고 있습니다. 검색 기능을 추가해 보겠습니다.

search_fields = ["question_text"]

그러면 변경 목록 상단에 검색 상자가 추가됩니다. 누군가 검색어를 입력하면 Django 는 question_text 필드를 검색합니다. 원하는 만큼 많은 필드를 사용할 수 있습니다. 하지만 백그라운드에서 LIKE 쿼리를 사용하므로 검색 필드 수를 적절한 수로 제한하면 데이터베이스에서 검색을 더 쉽게 수행할 수 있습니다.

이제 변경 목록에서 무료 페이지 매김을 제공한다는 점도 언급할 때입니다. 기본값은 페이지당 100 개의 항목을 표시하는 것입니다. Change list pagination <django.contrib.admin.ModelAdmin.list_per_page>, search boxes <django.contrib.admin.ModelAdmin.search_fields>, filters <django.contrib.admin.ModelAdmin.list_filter>, date-hierarchies <django.contrib.admin.ModelAdmin.date_hierarchy>, 및 column-header-ordering <django.contrib.admin.ModelAdmin.list_display>는 모두 예상대로 함께 작동합니다.

관리자 모양 및 느낌 커스터마이징

분명히, 각 관리자 페이지 상단에 "Django administration"이 있는 것은 터무니없습니다. 이는 단지 자리 표시자 텍스트일 뿐입니다.

하지만 Django 의 템플릿 시스템을 사용하여 변경할 수 있습니다. Django 관리자는 Django 자체에서 제공되며, 해당 인터페이스는 Django 자체의 템플릿 시스템을 사용합니다.

프로젝트 템플릿 커스터마이징

프로젝트 디렉토리 ( manage.py가 포함된 디렉토리) 에 templates 디렉토리를 만듭니다. 템플릿은 Django 가 액세스할 수 있는 파일 시스템의 어느 곳에나 있을 수 있습니다. (Django 는 서버가 실행되는 사용자로 실행됩니다.) 그러나 프로젝트 내에 템플릿을 유지하는 것이 좋은 관례입니다.

설정 파일 ( mysite/settings.py 기억하세요) 을 열고 TEMPLATES 설정에 DIRS <TEMPLATES-DIRS> 옵션을 추가합니다.

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]

DIRS <TEMPLATES-DIRS>는 Django 템플릿을 로드할 때 확인할 파일 시스템 디렉토리 목록입니다. 즉, 검색 경로입니다.

템플릿 구성

정적 파일과 마찬가지로 모든 템플릿을 하나의 큰 템플릿 디렉토리에 함께 넣을 수 있으며, 완벽하게 작동합니다. 그러나 특정 애플리케이션에 속하는 템플릿은 프로젝트의 템플릿 ( templates) 이 아닌 해당 애플리케이션의 템플릿 디렉토리 (예: polls/templates) 에 배치해야 합니다. reusable apps tutorial </intro/reusable-apps>에서 이렇게 하는 이유에 대해 자세히 설명합니다.

이제 templates 내에 admin이라는 디렉토리를 만들고 Django 자체의 소스 코드 ( django/contrib/admin/templates) 의 기본 Django 관리자 템플릿 디렉토리 내에서 admin/base_site.html 템플릿을 해당 디렉토리로 복사합니다.

Django 소스 파일은 어디에 있습니까?

시스템에서 Django 소스 파일의 위치를 찾는 데 어려움이 있는 경우 다음 명령을 실행합니다.

python -c "import django; print(django.__path__)"
['/home/labex/.local/lib/python3.10/site-packages/django']

그런 다음 파일을 편집하고 {{ site_header|default:_('Django administration') }}(중괄호 포함) 을 원하는 대로 사이트 이름으로 바꿉니다. 다음과 같은 코드 섹션이 생성되어야 합니다.

{% block branding %}
<div id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a><div>
{% endblock %}

이 접근 방식을 사용하여 템플릿을 재정의하는 방법을 가르칩니다. 실제 프로젝트에서는 이 특정 사용자 지정을 더 쉽게 수행하기 위해 django.contrib.admin.AdminSite.site_header 속성을 사용할 것입니다.

이 템플릿 파일에는 {% block branding %}{{ title }}와 같은 많은 텍스트가 포함되어 있습니다. {%{{ 태그는 Django 의 템플릿 언어의 일부입니다. Django 가 admin/base_site.html을 렌더링하면 이 템플릿 언어가 **Creating the Public Interface Views**에서 본 것처럼 최종 HTML 페이지를 생성하기 위해 평가됩니다.

Django 의 기본 관리자 템플릿은 재정의할 수 있습니다. 템플릿을 재정의하려면 base_site.html과 동일한 작업을 수행합니다. 즉, 기본 디렉토리에서 사용자 지정 디렉토리로 복사하고 변경합니다.

애플리케이션 템플릿 커스터마이징

예리한 독자들은 질문할 것입니다. 하지만 DIRS <TEMPLATES-DIRS>가 기본적으로 비어 있었다면 Django 는 어떻게 기본 관리자 템플릿을 찾았습니까? 대답은 APP_DIRS <TEMPLATES-APP_DIRS>True로 설정되어 있으므로 Django 는 대체로 사용할 각 애플리케이션 패키지 내에서 templates/ 하위 디렉토리를 자동으로 찾습니다 ( django.contrib.admin이 애플리케이션이라는 것을 잊지 마세요).

Poll 애플리케이션은 그다지 복잡하지 않으며 사용자 지정 관리자 템플릿이 필요하지 않습니다. 그러나 더 정교해지고 일부 기능에 대해 Django 의 표준 관리자 템플릿을 수정해야 하는 경우 프로젝트의 템플릿이 아닌 애플리케이션의 템플릿을 수정하는 것이 더 합리적입니다. 그렇게 하면 모든 새 프로젝트에 Poll 애플리케이션을 포함하고 필요한 사용자 지정 템플릿을 찾을 수 있습니다.

Django 가 템플릿을 찾는 방법에 대한 자세한 내용은 template loading documentation <template-loading>을 참조하십시오.

관리자 인덱스 페이지 커스터마이징

비슷한 맥락에서 Django 관리자 인덱스 페이지의 모양과 느낌을 커스터마이징하고 싶을 수 있습니다.

기본적으로 INSTALLED_APPS에 있는 모든 앱을 관리자 애플리케이션에 등록된 순서대로 알파벳순으로 표시합니다. 레이아웃을 크게 변경하고 싶을 수 있습니다. 결국, 인덱스는 아마도 관리자의 가장 중요한 페이지이며 사용하기 쉬워야 합니다.

커스터마이징할 템플릿은 admin/index.html입니다. (이전 섹션에서 admin/base_site.html과 동일한 작업을 수행합니다. 즉, 기본 디렉토리에서 사용자 지정 템플릿 디렉토리로 복사합니다.) 파일을 편집하면 app_list라는 템플릿 변수를 사용한다는 것을 알 수 있습니다. 해당 변수에는 설치된 모든 Django 앱이 포함되어 있습니다. 이를 사용하는 대신, 가장 좋다고 생각하는 방식으로 객체별 관리자 페이지에 대한 링크를 하드 코딩할 수 있습니다.

요약

축하합니다! Django 의 관리자 사이트 커스터마이징 랩을 완료했습니다. LabEx 에서 더 많은 랩을 연습하여 실력을 향상시킬 수 있습니다.