Admin 페이지는 아래와 같이 모델을 임포트해 등록만 해주면 해당 페이지에서 모델에 대한 CRUD가 가능하다는 편리함이 있었다.
하지만 CRUD는 어쩌면 가장 단순한 기능일지도 모른다. 관리자의 입맛에 맞게 Admin 페이지를 편집한다면, 더 편리하게 사용할 수 있을 것이다.
Admin 편집 페이지 커스터마이징
polls 폴더 아래에 있는 admin.py를 아래와 같이 수정해보자. Question 모델을 직접 설정한 class(QustionAdmin) 형식으로 등록해준다.
# mysite/polls/admin.py
from django.contrib import admin
from .models import *
# Register your models here.
admin.site.register(Choice)
# 형식 직접 지정
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('생성일', {'fields': ['pub_date']}),
]
# 위 class 형태로 Question 모델을 등록
admin.site.register(Question, QuestionAdmin)
위와 같이 코드를 수정한 뒤 서버를 실행시켜 admin페이지에 접속하게 되면 우선 큰 차이는 없어 보인다.
그러나 Question 레코드 중 하나를 선택해 세부 페이지로 진입하게 되면,
위와 같이 에러 페이지가 나타나게 된다. 에러 메시지를 읽어보면 pub_date가 수정(편집)을 허용하지 않는 필드라고 하는데, models.py에서 pub_date에 대해 살펴보면, auto_now_add=True로 되어 있어 해당 에러가 발생하는 것 같다. auto_now_add를 지워주자.
# mysite/polls/models.py
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField()
수정한 뒤 새로고침을 눌러주면,
위와 같이 정상적으로 노출되는 것을 확인할 수 있다.
위 화면에서 Question text와 Pub date간의 가장 큰 차이는 무엇일까? '생성일'이라고 표시되는 일종의 구분선의 유무이다. 앞서 admin.py에서 fieldsets에 전달해줄 때, Question text에는 None으로 전달했기 때문에 구분선이 노출되지 않는 것이다. 이를 흔히 섹션 구분이라고 한다.
해당 필드들은 코드에 입력한 순서로 나타난다. 현재는 question text, pub date 순으로 나타나지만, 코드를 아래와 같이 변경하면 순서도 그에 맞게 변한다.
from django.contrib import admin
from .models import *
# Register your models here.
admin.site.register(Choice)
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
('생성일', {'fields': ['pub_date']}),
('질문', {'fields': ['question_text']}),
]
admin.site.register(Question, QuestionAdmin)
readonly_fields
위에서 발생한 에러는 pub_date가 편집이 가능하도록 되어야 하는데 auto_now_add 인자가 True로 전달되어 발생한 것이었다. 하지만 auto_now_add를 사용한 상태로 admin페이지를 볼 수 있는 방법은 없을까?
auto_now_add와 같이 시스템 상 자동으로 설정되는 필드를 '읽기 전용'으로 설정해준다면 편집할 일도 없으므로 에러가 발생하지 않을 것이다. readonly_fields를 admin.py에 아래와 같이 설정해주자.
from django.contrib import admin
from .models import *
# Register your models here.
admin.site.register(Choice)
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
('생성일', {'fields': ['pub_date']}),
('질문', {'fields': ['question_text']}),
]
# 읽기 전용 필드
readonly_fields = ['pub_date']
admin.site.register(Question, QuestionAdmin)
그리고 models.py에서 pub_date에 대해 auto_now_add를 True로 다시 설정해준 뒤, 새로고침한다면
위 이미지와 같이 pub date는 수정이 되지 않는 형식으로 변한 것을 알 수 있다.
부모 모델에서 자식 모델의 옵션 수정하는 기능 추가 (Quesiton 모델에서 Choice 옵션 수정)
1. StackedInline
부모 모델인 Question 수정 페이지에서 Choice에 대한 옵션을 수정하는 기능도 추가할 수 있다. 아래와 같이 ChoiceInline이라는 클래스를 생성해 QuestionAdmin에도 내용을 추가해보자.
# mysite/polls/admin.py
from django.contrib import admin
from .models import *
# Register your models here.
class ChoiceInline(admin.StackedInline):
model = Choice
extra = 3
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
('생성일', {'fields': ['pub_date']}),
('질문', {'fields': ['question_text']}),
]
readonly_fields = ['pub_date']
inlines = [ChoiceInline]
admin.site.register(Question, QuestionAdmin)
위와 같이 질문 레코드에 연결되어 있는 답변(choice)들이 함께 나타나게 되어 같이 편집할 수 있다.
아직 연결된 답변이 없는 질문에도 3개의 빈칸이 주어진다. 이는 위 코드의 ChoiceInline 클래스에 extra로 전달했던 인자가 3이었기 때문에, 기본 값으로 3개의 빈칸이 주어진다. (모든 질문에서 동일)
하지만 질문에 대한 답변들이 무수히 많아진다면 어떻게될까? 상하로 이어진 리스트가 많아져 편집하기 어려울지도 모른다. ChoiceInline이 StackedInline으로 구성되어 있기 때문이다. 그럴 때는 아래와 같이 TabularInline을 사용하면 보기 수월해지는 효과가 있다.
2. TabularInline
from django.contrib import admin
from .models import *
# Register your models here.
# TabularInline으로 변경
class ChoiceInline(admin.TabularInline):
model = Choice
extra = 3
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
('생성일', {'fields': ['pub_date']}),
('질문', {'fields': ['question_text']}),
]
readonly_fields = ['pub_date']
inlines = [ChoiceInline]
admin.site.register(Question, QuestionAdmin)
위와 달리 votes 옵션이 오른쪽으로 오면서, 한결 보기 편해진 모습을 알 수 있다.
특정 필드 숨김 처리
위에서 pub date에 대해 자동으로 설정되도록 했기 때문에, 굳이 '생성일' 섹션을 볼 필요는 없다. 그럴 때는 아래와 같이 해당 필드에 classes를 추가해주면 해당 섹션을 숨김처리할 수 있다.
# '생성일' 섹션에 ''classes': ['collapse']' 추가
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
('생성일', {'fields': ['pub_date'], 'classes': ['collapse']}),
('질문', {'fields': ['question_text'],}),
]
readonly_fields = ['pub_date']
inlines = [ChoiceInline]
admin.site.register(Question, QuestionAdmin)
위와 같이 생성일 섹션이 숨김처리된 것을 알 수 있다.(Show를 누르면 이전과 똑같이 노출된다.)
Admin 목록 페이지 커스터마이징
위에서는 상세 편집 페이지에 대한 커스터마이징을 진행했다. 하지만 아직 끝이 아니다. 그 상위페이지인 목록 페이지도 관리자의 입맛에 맞게 커스터마이징을 할 수 있다.
우선 위와 같이 날짜와 질문 내용이 쭉 이어서 노출되는 것이 아닌, 질문내용/날짜로 칼럼을 나눠서 목록을 보고자 하면 어떻게 해야할까? admin.py에 list_display라는 파라미터를 QuestionAdmin 클래스에 추가해보자.
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
('생성일', {'fields': ['pub_date'], 'classes': ['collapse']}),
('질문', {'fields': ['question_text'],}),
]
# list_display 추가
list_display = ('question_text', 'pub_date')
readonly_fields = ['pub_date']
inlines = [ChoiceInline]
위와 같이 깔끔한 목록이 노출되는 것을 확인할 수 있다.
이전에 만들었던 메소드(모델 메소드(Model Method))도 위 목록에서 표시할 수 있다. 1일 이내의 게시글인지를 판단하는 'was_published_recently' 메소드를 list에 추가하기만 하면 된다.
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
('생성일', {'fields': ['pub_date'], 'classes': ['collapse']}),
('질문', {'fields': ['question_text'],}),
]
list_display = ('question_text', 'pub_date', 'was_published_recently')
readonly_fields = ['pub_date']
inlines = [ChoiceInline]
위와 같이 True/False 값으로 반환되게 된다.
칼럼 이름 수정
위 목록에 표시되는 칼럼 이름도 커스터마이징 가능하다. 이는 admin.py가 아닌 models.py에서 수정할 수 있다.
# mysite/polls/models.py
# admin 데코레이터 사용을 위한 라이브러리 임포트
from django.contrib import admin
# model 생성
class Question(models.Model):
# verbose_name 설정
question_text = models.CharField(max_length=200, verbose_name='질문')
pub_date = models.DateTimeField(auto_now_add=True, verbose_name='생성일')
# admin.display 데코레이터 사용해 description 설정
@admin.display(boolean=True, description='최근 생성일',)
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
위와 같이 필드는 verbose_name을, 메소드는 @admin.display 데코레이터를 사용해 description을 설정해주어야 한다.
위 목록에서 칼럼 이름이 verbose_name과 description으로 노출되는 것을 확인할 수 있다.
목록에서 특정 필드 기준 필터 추가
특정 필드를 기준으로 하는 필터를 추가할 수 있다. admin.py에서 list_filter라는 파라미터를 추가해보자. 생성일(pub_date)을 기준이라고 가정한다.
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
('생성일', {'fields': ['pub_date'], 'classes': ['collapse']}),
('질문', {'fields': ['question_text'],}),
]
list_display = ('question_text', 'pub_date', 'was_published_recently')
list_filter = ['pub_date']
readonly_fields = ['pub_date']
inlines = [ChoiceInline]
우측 상단에 '생성일'을 기준으로 하는 필터가 생성된 것을 볼 수 있다.
목록에서 특정 필드 검색 필터 추가
특정 필드의 내용을 검색하는 필터도 추가할 수 있다. search_fields라는 파라미터를 추가하면 된다.
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
('생성일', {'fields': ['pub_date'], 'classes': ['collapse']}),
('질문', {'fields': ['question_text'],}),
]
list_display = ('question_text', 'pub_date', 'was_published_recently')
list_filter = ['pub_date']
search_fields = ['question_text']
readonly_fields = ['pub_date']
inlines = [ChoiceInline]
question_text를 search_fields로 전달하자 위와 같은 검색 폼이 생겼다.
위와 같이 질문 내용(question_text)을 검색으로 필터링할 수 있다.
만약 답변을 검색하고 싶으면 어떻게 해야할까? shell을 통해 필터링 했던 기억(모델 필터링(Model Filtering))
을 떠올려, 아래와 같이 파라미터로 전달하는 값을 추가하면 된다.
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
('생성일', {'fields': ['pub_date'], 'classes': ['collapse']}),
('질문', {'fields': ['question_text'],}),
]
list_display = ('question_text', 'pub_date', 'was_published_recently')
list_filter = ['pub_date']
# question의 choice의 choice_text를 shell의 filter를 통해 검색하는 과정을 떠올리자
search_fields = ['question_text', 'choice__choice_text']
readonly_fields = ['pub_date']
inlines = [ChoiceInline]
이제 답변 옵션을 검색해도 그에 해당하는 질문 레코드를 찾을 수 있다.
'Minding's Programming > Django' 카테고리의 다른 글
[Django] GET, POST, PUT, DELETE (1) | 2024.10.10 |
---|---|
[Django] Serilaizer (4) | 2024.10.10 |
[Django] 폼(Forms) (0) | 2024.10.08 |
[Django] 뷰(views)와 템플릿(templates) (0) | 2024.10.08 |
[Django] 모델 메소드(Model Method) (1) | 2024.10.08 |