본문 바로가기
Django

첫 번째 Django 앱 만들기 (Part 7: Customizing the admin site)

by AlbertIm 2024. 8. 23.

시작

Part 6에 이어서 간단한 설문조사(Polls) 앱을 만드는 과정을 통해 Django의 Admin 사이트 커스터마이징 방법을 알아보려고 합니다. 본 포스트에서는 macOS와 IntelliJ IDEA Ultimate을 사용합니다.

본문

Admin Form 커스터마이징

admin.site.register(Question)과 같이 Question을 등록하면 Django는 기본 form을 구성할 수 있습니다

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)

 

이 변경으로 "Publication date"가 "Question" 필드 앞에 오게 됩니다.

이렇게 form의 필드 순서를 관리할 수 있습니다.

또한 form의 여러 필드를 세트로 나누어 구성할 수도 있습니다.

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

admin.site.register(Question, QuestionAdmin)

 

 

fieldsets에 있는 각 튜플에서 첫 번째 요소는 fieldset의 제목이 됩니다.

관련 객체 추가하기

Question 관리 페이지에는 Question만 표시되고 Choices가 없습니다.

이 문제를 해결하는 방법에는 두 가지가 있습니다. 첫 번째는 Choice 모델을 등록하는 것입니다.

admin.site.register(Choice)

 

해당 form에서 Question 필드는 데이터베이스의 모든 Question을 포함하는 선택 상자가 됩니다. Django는 ForeignKey가 관리자 페이지에서 <select> 박스로 표시되어야 한다는 것을 알고 있습니다.

하지만 실제로 이렇게 Choice 객체를 추가하는 것은 비효율적입니다. Question 객체를 만들 때 여러 Choice를 직접 추가하는 방법이 더 효율적일 수 있습니다.

 

polls/admin.py를 수정합니다.

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개의 선택 항목을 제공해야 한다고 알리게 됩니다.

 

Question 추가 페이지에 Choice가 아래와 같이 추가됩니다.

 

하지만 한 가지 작은 문제가 있습니다. 관련된 Choice 객체를 입력하기 위해 많은 화면 공간을 차지합니다. 이러한 이유로 Django는 inline 관련 객체를 표시하는 tabular 방식을 제공합니다. ChoiceInline을 변경합니다.

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

 

 

StackedInline 대신 TabularInline을 사용하면 아래처럼 간결한 테이블 형식으로 표시됩니다.

Admin Change List 커스터마이징

아래 Question change list 페이지를 약간 커스터마이징하겠습니다.

 

기본적으로 Django는 각 객체의 str()을 표시합니다. 하지만 때로는 개별 필드를 표시하는 것이 더 좋을 수 있습니다. 이를 해결하기 위해 객체의 변경 목록 페이지에 표시할 필드 이름 목록인 list_display 관리 옵션을 사용할 수 있습니다.

 

polls/admin.pyQuestionAdminlist_display를 추가합니다.

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

 

 

Question change list가 아래처럼 변경됩니다.

 

일반적으로 각 열의 헤더를 클릭하면 해당 값을 기준으로 정렬할 수 있습니다. 그러나 was_published_recently 헤더와 같이 임의 메서드의 출력값으로는 정렬이 지원되지 않습니다. 기본적으로 메서드 이름(밑줄은 공백으로 대체됨)이 헤더 이름이 됩니다.

 

다음과 같이 polls/models.py에서 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

 

그리고 polls/admin.pyQuestionAdminlist_filter를 사용하여 필터링 기능을 추가할 수 있습니다.

class QuestionAdmin(admin.ModelAdmin):  
    # ...  
    list_filter = ["pub_date"]

 

아래처럼 필터링 사이드바가 추가됩니다.

 

표시되는 필터 유형은 필터링하는 필드 유형에 따라 다릅니다. pub_dateDateTimeField이기 때문에 Django언제나,오늘,지난 7일,이번 달,이번 해와 같은 적절한 필터 옵션을 제공합니다.

 

검색 기능도 추가해 보겠습니다.

class QuestionAdmin(admin.ModelAdmin):  
    # ...
    search_fields = ["question_text"]

 

아래처럼 검색 박스가 추가됩니다.

이 검색은 LIKE 쿼리를 사용합니다.

Admin Look and Feel을 커스터마이징

프로젝트 템플릿 사용자 정의

설정 파일(mysite/settings.py)을 열고 설정 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는 Django가 템플릿을 로드할 때 확인하는 디렉터리 목록으로 검색 경로 지정하는 역할을 합니다.

 

이제 django/contrib/admin/templates/admin에 있는 base_site.html를 복사하여 templates/admin/base_site.html를 만듭니다.

{% extends "admin/base.html" %}  

{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}  

{% block branding %}  
<div id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('Django administration') }}</a></div>  
{% if user.is_anonymous %}  
  {% include "admin/color_theme_toggle.html" %}  
{% endif %}  
{% endblock %}  

{% block nav-global %}{% endblock %}

 

그런 다음 {{ site_header|default:_('Django administration') }}Polls Administration으로 변경합니다.

{% block branding %}
<div id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></div>
{% if user.is_anonymous %}
  {% include "admin/color_theme_toggle.html" %}
{% endif %}
{% endblock %}

 

이런 방법을 사용하여 템플릿을 재정의할 수 있습니다.

실제 프로젝트에서는 django.contrib.admin.AdminSite.site_header 속성을 사용하여 이러한 사용자 정의를 더 쉽게 수행할 수 있습니다.

from django.contrib import admin

from .models import MyModel


class MyAdminSite(admin.AdminSite):
    site_header = "Monty Python administration"


admin_site = MyAdminSite(name="myadmin")
admin_site.register(MyModel)

 

 

DIRS가 기본적으로 비어 있었는데 Django가 기본 관리자 템플릿을 어떻게 찾고 있었을까요?

APP_DIRS = True로 설정되어 있기 때문에 Django는 자동으로 각 애플리케이션 패키지 내의 templates/ 하위 디렉터리를 찾습니다.

마무리

Part 7에서는 간단히 Django Admin 사이트를 커스터마이징 하는 다양한 방법을 살펴보았습니다. Django의 기본 제공 기능만으로도 강력한 관리자 페이지를 쉽게 구축할 수 있고 커스터마이징이 필요할 때도 쉽게 가능하다는 점에서 유연성이 뛰어납니다. 이를 통해 개발 속도가 빨라지고 더 편리하게 작업할 수 있을 것 같습니다.

참고자료

댓글