본문 바로가기
파이썬/장고(django)

[Django] 튜토리얼7. 어드민 사이트 커스터마이징하기

by 책 읽는 개발자_테드 2022. 6. 28.
반응형

이 글은 장고 공식 홈페이지의 튜토리얼을 번역하고, 직접 실습하는 과정을 정리합니다.

여섯 번째 튜토리얼(https://scshim.tistory.com/599)과 이어지는 일곱 번째 글입니다.

 

목차

· 어드민 form 커스터마이즈

· 연관 객체 더하기

· 관리자 change list 커스터마이즈

· 어드민 룩앤필(look and feel) 커스터마이즈

· 어드민 색인 페이지 커스터마이즈


어드민 form 커스터마이즈


Question 모델을 admin.site.register(Question)에 등록함으로써 장고는 기본 폼 표현을 구성할 수 있었다. 하지만 어드민 폼 양식의 모양과 작동 방식을 사용자 정의해야하는 상황이 올 수 있다. 객체를 등록할 때 원하는 옵션을 장고에 알려서 이 작업을 수행할 수 있다.

 

편집 폼에서 필드를 재정렬하여 이것이 어떻게 작동하는지 확인하자. admin.site.register(Question) 줄을 다음으로 교체한다.

 

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()의 두 번째 인자로 넘긴다.

 

위 변경 사항을 통해 "Publication date" 필드가 "Question"필드 앞에 오게 된다. 

 

필드가 두 개인 경우 그다지 인상적이지 않지만 수십 개의 필드가 있는 관리 양식의 경우 직관적인 순서를 선택하는 것이 사용성 세부 사항에 중요하다.

 

수십 개의 필드가 있는 폼이라면, 폼을 필드셋(fieldsets)로 분할할 수 있다.

 

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)

 

필드셋 각 튜플의 첫 요소는 필드셋의 제목이다. 이제 폼이 다음과 같이 표시된다.

 

연관 객체 더하기


Question에는 여러 Choice 항목이 있지만 어드민 페이지에서는 Choice 항목이 표시되지 않는다. 이 문제를 해결하는 방법은 두 가지가 있다. 첫 번째는 Question에 했던 것 처럼 Choice를 어드민에 등록하는 것이다.

 

from django.contrib import admin

from .models import Choice, Question
# ...
admin.site.register(Choice)

 

위 코드를 추가하면, 장고 admin에서 Choice가 이용 가능한 옵션이 된다.

 

· 위 폼에서 Question 필드는 데이터베이스의 모든 질문을 포함하는 선택 상자다.  

- 장고는 외래키가 어드민에서 어드민에서 선택 상자로 표시되어야 한다는 것을 알고 있다.

 

· Question 옆에 +(Add Another)버튼은 외래키 관계에 있는 객체와 연결되어있다. 해당 버튼을 클릭하면 Choice와 연결시킬 Question을 추가하는 "Add Question" 폼이 있는 팝업 창이 나타난다.

 

- 해당 창에 질문을 추가하고 "SAVE" 버튼을 클릭하면 장고는 질문을 데이터베이스에 저장하고, 선택한 choice의 "Add choice Add" 폼에 동적으로 추가한다.

 

이러한 방식은 비효율적이다. Question 객체를 생성할 때 여러 Choice를 직접 추가할 수 있다면 더 좋을 것이다. Choice 모델에 대한 register() 호출을 제거하고, Question 등록 코드를 다음과 같이 수정한다.

 

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)

 

· 위 코드는 장고에게 "Choice 객체는 Question 어드민 페이지에서 수정된다. 기본적으로, 3가지 Choice에 대해 충분한 필드를 제공해라."라고 말한다.

 

어떻게 보이는지 확인하기 위해 "Add question" 페이지를 로드한다.

 

· extrac에 의해 지정된 관련 Choice를 위한 3개의 슬롯이 있고, 이미 생성된 객체에 대한 변경 페이지로 돌아올 때 마다 또 다른 3개의 추가 슬롯을 얻는다.

· 세 개의 현재 슬롯 끝에 "Add another Choice" 링크가 있다. 클릭하면, 새 슬롯이 추가된다. 추가된 슬롯을 제거하려면 추가된 슬롯의 오른쪽 상단에 있는 X를 클릭하면 된다.

 

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

 

class ChoiceInline(admin.TabularInline):

 

TabularInline을 사용하면 관련 객체가 더 간결한 테이블 기반 형식으로 표시된다.

 

관리자 변경 목록(change list) 커스터마이즈


모든 질문을 표시하는 변경 목록 페이지를 수정해보자.

 

기본적으로 장고는 각 객체의 str()을 표시한다. 그러나 때로는 개별 필드를 표시할 수 있다면 더 도움이 될 것이다. 그렇게 하기 위해 객체의 변경 목록 페이지에 열로 표시할 필드 이름의 튜플인 list_display 관리 옵션을 사용한다.

 

polls/admin.py

class QuestionAdmin(admin.ModelAdmin):
    # ...
    list_display = ('question_text', 'pub_date', 'was_published_recently')

- was_published_recently()는 튜토리얼 2에서 추가되었다. https://scshim.tistory.com/593

 

· 열 헤더를 클릭하여 해당 값을 기준으로 정렬할 수 있다.

- was_published_recently 헤더의 경우는 예외다. 임의의 메서드의 출력을 기준으로 정렬하는 것은 지원되지 않기 때문이다.

 

· 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.py 파일을 수정하고, Question 변경 사항을 추가한다. 다음 코드를 QuestionAdmin에 추가한다.

 

list_filter = ['pub_date']

 

이렇게하면 pub_date 필드를 기준으로 변경 목록을 필터링할 수 있는 "Filter" 사이드바가 추가된다.

 

표시되는 필터 유형은 필터링하는 필드 유형에 따라 다르다. 

- pub_date는 DateTimeField이기 때문에 장고는 "Any date", "Today", "Past 7 days" 등 적절한 필터 옵션을 제공해야 한다는 것을 안다.

 

이제 검색 기능을 추가해보자.

 

search_fields = ['question_text']

 

이렇게하면 변경 목록 상단에 검색 상자가 추가된다. 검색어를 입력하면 장고는 question_text 필드를 검색한다. 

- 원하는 만큼 필드를 사용할 수 있지만, LIKE 쿼리를 사용하는 기능이므로 검색 필드 수를 적절한 수로 제한하면 데이터베이스에서 검색을 수행하기가 더 쉬워진다.

 

· 변경 목록은 페이징을 제공한다.

- 기본값은 페이지 당 100개 항목을 표시한다.

 

어드민 룩앤필(look and feel) 커스터마이즈


현재는 각 어드민 페이지 상단에 "Django administration"이라는 텍스트가 있다. 장고의 템플릿 시스템을 사용하여 이를 변경할 수 있다. 장고 어드민은 장고 자체에 의해 구동되며, 해당 인터페이스는 장고 자체 템플릿 시스템을 사용한다.

 

mysite/settings.py 파일을 열고, 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는 장고 템플릿들을 로딩할 때 확인을 위한 파일시스템 디렉터리들의 목록이다. 이는 검색 경로다.

템플릿 구성

정적 파일과 마찬가지로 하나의 큰 템플릿 디렉터리에 모든 템플릿을 함께 사용할 수 있으면, 완벽하게 작동한다. 그러나 특정 응용 프로그램에 속하는 템플릿은 프로젝트가 아닌 해당 응용 프로그램의 템플릿 디렉터리(예: polls/templates)에 배치해야한다.
- 더 자세한 내용: https://docs.djangoproject.com/en/4.0/intro/reusable-apps/

 

이제 templates 내부에 admin이라는 디렉터리를 생성하고, 장고 내부 소스코드의 기본 장고 어드민 템플릿 디렉터리(django/contrib/admin/templates/admin)에서 admin/base_site.html 템플릿을 복사한다.

 

그런 다음 파일을 편집하고,

{{ site_header|default:_('Django administration') }}

위 코드 부분을 원하는 본인의 사이트 이름으로 변경한다.

<h1 id="site-name"><a href="{% url 'admin:index' %}">Ted's Administration</a></h1>

 

다시 어드민 페이지로 이동하면, 헤더 부분이 다음과 같이 변경되어있다.

 

· 위 방법은 템플릿을 재정의하는 방법을 알려준다. 하지만, 실제 프로젝트에서는 django.contrib.admin.AdminSite.site_header 속성을 사용하여 커스터마이징을 더 쉽게 수행할 수 있다.

 

· 장고의 기본 관리 템플릿은 모두 오버라이드할 수 있다. 템플릿을 재정의하려면 base_site.html과 동일한 작업을 수행한다.  즉, 기본 디렉터리에서 사용자 지정 디렉터리로 복사하고 변경한다.

 

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

· DIRS가 기본적으로 비어 있는 경우 장고는 어떻게 기본 어드민 템플릿을 찾을까? 

- APP_DIRS가 True로 설정되어 있어 예비로 사용한다. 따라서 자동으로 애플리케이션 패키지 내에서 templates/ 하위 디렉터리를 찾는다.

 

· 지금까지 만들어온 polls 애플리케이션은 복잡하지 않으므로 사용자 지정 어드민 템플릿이 필요하지 않다. 그러나 더 정교해지고 일부 기능에 대해 장고의 표준 관리 템플릿을 수정해야 한다면, 프로젝트에 있는 템플릿보다 애플리케이션의 템플릿을 수정하는 것이 더 합리적이다.

- 이렇게 하면 새 프로젝트에 polls 응용 프로그램을 포함할 수 있고, 필요한 사용자 지정 템플릿을 찾을 수 있다. 

 

어드민 색인 페이지 커스터마이즈


· 마찬가지로 장고 어드민 색인 페이지의 룩앤필을 커스터마이징할 수 있다.

· 기본적으로 어드민 색인 페이지는 INSTALLED_APP에 어드민 애플리케이션으로 등록된 모든 앱을 알파벳 순서로 보여준다.

 

이전 섹션에 admin/base_site.html에 한 것과 동일하게 admin/index.html 템플릿을 커스터마이징해보자.

 

해당 파일을 기본 디렉터리에서 커스터마이징 템플릿 디렉터리로 복사한다.

 

해당 파일을 수정하면 app_list라는 템플릿 변수를 사용하는 것을 볼 수 있다.

 

…
{% block content %}
<div id="content-main">
 {% include "admin/app_list.html" with app_list=app_list show_changelinks=True %}
</div>
{% endblock %}
…

 

해당 변수에는 설치된 모든 장고 앱이 포함되어있다. 이것을 사용하는 대신에 사용자가 생각하는 최선의 방법으로 하드코드한 링크를 넣는 식으로 커스터마이징할 수 있다.

 

출처

https://docs.djangoproject.com/en/4.0/intro/tutorial07/

반응형

댓글