SlideShare a Scribd company logo
1 of 67
Download to read offline
Django admin site
커스텀하여 적극적으로 활용하기
박영우
2년 조금 넘는 기간 동안,
스타트업에서 이것저것 개발하며 배운 것 중,
공유하고 싶은 것이 있어서 발표를 하게 되었습니다.
‘캠쿠’
대학생활 개선 앱
‘트웬티’
대학생 미디어
‘ALT’
뉴미디어 스타트업
RESTful API
CMS
(Contents Management System)
‘트웬티’ 앱을 개발하던 2015년 9월쯤,
Python과 Django를 알게 됐고,
덕분에 RESTful API와 CMS를 쉽고 빠르게 만들 수 있었습니다.
그런데, 6개월이 지나서야
Django admin site의 존재를 알게 됐습니다.
CMS를 모두 다 구현하고 난 뒤에…
Django admin site를 커스텀해서 사용하는 것이,
직접 만든 CMS 보다
훨씬 쉽고, 빠르고, 안정적이었습니다.
이번 파이콘 주제가 ‘Back to the basic’ 인 걸 보고,
경험을 공유하고 싶다는 생각이 들었습니다.
이 발표에서 다루는 내용
• Django admin site의 기본적인 사용 방법
• Django admin site를 커스텀 하는 여러가지 방법과 그 결과
• Python
• Django 내부 동작, 구현
들어가기 전에… Django ?
• Documentation
• Middleware
• Model (ORM)
• Form
• Class based view
• Template
Django admin site
발표의 모든 내용은 여기에 있습니다.
• https://docs.djangoproject.com/en/1.11/ref/contrib/admin
• ModelAdmin, InlineModelAdmin, AdminSite, LogEntry
• Overriding admin templates, Reversing admin urls
• https://docs.djangoproject.com/en/1.11/ref/contrib/admin/actions/
• Writing actions, Advanced action techniques
• https://docs.djangoproject.com/en/1.11/ref/contrib/admin/admindocs/
• https://docs.djangoproject.com/en/1.11/ref/contrib/admin/javascript/
순서
1.Django 설치
2.Model을 admin site에 등록하기
3.기본적인 사용법
4.조금 더 심화된 사용법
5.커스텀 페이지 추가
6.UI 변경하기
7.Admin site 분리하기
8.마지막으로 (간단한) 문서화!
예제를 위한 모델
MEMBER
name 이름
email 이메일
permission 권한
certification_date 인증일
is_certificated 인증상태
POST
member 작성자
category 카테고리
title 제목
subtitle 부제목
content 내용
is_deleted 삭제여부
created_at 작성일
COMMENT
member 작성자
post 원글
content 내용
report_count 신고수
created_at 작성일
CATEGORY
name 이름
* permission (관리자, 에디터, 일반)
예제를 위한 모델 - Member
class Member(AbstractBaseUser):
TYPE_PERMISSIONS = (
('AD', '관리자'),
('ET', '에디터'),
('MB', '일반'),
)
email = models.EmailField('이메일', max_length=255, unique=True)
username = models.CharField('닉네임', max_length=30)
permission = models.CharField('권한', max_length=2, choices=TYPE_PERMISSIONS, default='MB')
certification_date = models.DateField('인증일', default=None, null=True, blank=True)
is_certificated = models.BooleanField('인증여부', default=False)
예제를 위한 모델 - Post
class Category(models.Model):
name = models.CharField('카테고리 이름', max_length=20)
class Post(models.Model):
member = models.ForeignKey(Member, verbose_name='작성자')
category = models.ForeignKey(Category, verbose_name='카테고리')
title = models.CharField('제목', max_length=255)
content = models.TextField('내용')
is_deleted = models.BooleanField('삭제된 글', default=False)
created_at = models.DateTimeField('작성일', auto_now_add=True)
class Comment(models.Model):
member = models.ForeignKey(Member, verbose_name='작성자')
post = models.ForeignKey(Post, verbose_name=‘원본글’)
content = models.TextField()
is_blocked = models.BooleanField('노출 제한', default=False)
순서
1.Django 설치
2.Model을 admin site에 등록하기
3.기본적인 사용법
4.조금 더 심화된 사용법
5.커스텀 페이지 추가
6.UI 변경하기
7.Admin site 분리하기
8.마지막으로 (간단한) 문서화!
Django 설치
example $ pyenv virtualenv 3.6.2 pyconExample # 가상 환경 추가
example $ pyenv shell envExample # 가상 환경 실행
(envExample) example $ pip install django # django 설치
(envExample) example $ django-admin.py startproject example # django 프로젝트 생성
(envExample) example $ python manage.py startapp member # member, post 앱 추가
(envExample) example $ python manage.py startapp post
(envExample) example $ python manage.py createsuperuser # 관리자 추가
(envExample) example $ python manage.py runserver # 실행
http://localhost:8000 http://localhost:8000/admin/
순서
1.Django 설치
2.Model을 admin site에 등록하기
3.기본적인 사용법
4.조금 더 심화된 사용법
5.커스텀 페이지 추가
6.UI 변경하기
7.Admin site 분리하기
8.마지막으로 (간단한) 문서화!
Model을 admin site에 등록
# member/admin.py
from django.contrib import admin
from member.models import Member
admin.site.register(Member)
# post/amdin.py
from django.contrib import admin
from post.models import Category, Post, Comment
admin.site.register(Post)
admin.site.register(Category)
admin.site.register(Comment)
Model을 admin site에 등록
# post/models.py
class Category(models.Model):
class Meta:
verbose_name_plural = "categories"
name = models.CharField(max_length=20)
순서
1.Django 설치
2.Model을 admin site에 등록하기
3.기본적인 사용법
4.조금 더 심화된 사용법
5.커스텀 페이지 추가
6.UI 변경하기
7.Admin site 분리하기
8.마지막으로 (간단한) 문서화!
기본 - List
# member/admin.py
class MemberAdmin(admin.ModelAdmin):
기본 - List
# member/admin.py
class MemberAdmin(admin.ModelAdmin):
list_per_page = 5
기본 - List
# member/admin.py
class MemberAdmin(admin.ModelAdmin):
list_per_page = 5
list_display = (
'id', 'email', ‘username',
'permission', ‘is_certificated',
‘certification_date’, ‘post_count', )
기본 - List
# member/admin.py
class MemberAdmin(admin.ModelAdmin):
list_per_page = 5
list_display = (
'id', 'email', ‘username',
'permission', ‘is_certificated',
‘certification_date’, ‘post_count', )
list_editable = ('permission', )
기본 - List
# member/admin.py
class MemberAdmin(admin.ModelAdmin):
list_per_page = 5
list_display = (
'id', 'email', ‘username',
'permission', ‘is_certificated',
‘certification_date’, ‘post_count', )
list_editable = ('permission', )
list_filter = ('permission', )
기본 - List
# member/admin.py
class MemberAdmin(admin.ModelAdmin):
list_per_page = 5
list_display = (
'id', 'email', ‘username',
'permission', ‘is_certificated',
‘certification_date’, ‘post_count', )
list_editable = ('permission', )
list_filter = ('permission', )
search_fields = ('username', )
기본 - List
# member/admin.py
class MemberAdmin(admin.ModelAdmin):
list_per_page = 5
list_display = (
'id', 'email', ‘username',
'permission', ‘is_certificated',
‘certification_date’, ‘post_count', )
list_editable = ('permission', )
list_filter = ('permission', )
search_fields = ('username', )
ordering = ('-id', 'email', 'permission', )
기본 - List
# member/admin.py
class MemberAdmin(admin.ModelAdmin):
list_per_page = 5
list_display = (
'id', 'email', ‘username',
'permission', ‘is_certificated',
‘certification_date’, ‘post_count', )
list_editable = ('permission', )
list_filter = ('permission', )
search_fields = ('username', )
ordering = ('-id', 'email', 'permission', )
def post_count(self, obj):
return Post.objects.filter(member=obj).count()
post_count.short_description = '작성한 글 수'
기본 - List
# member/admin.py
class MemberAdmin(admin.ModelAdmin):
list_per_page = 5
list_display = (
'id', 'email', ‘username',
'permission', ‘is_certificated',
‘certification_date’, ‘post_count', )
list_editable = ('permission', )
list_filter = ('permission', )
search_fields = ('username', )
ordering = ('-id', 'email', 'permission', )
def post_count(self, obj):
return Post.objects.filter(member=obj).count()
post_count.short_description = '작성한 글 수'
admin.site.register(Member, MemberAdmin)
기본 - Form
# post/admin.py
class PostAdmin(admin.ModelAdmin):
list_per_page = 10
list_display = (
'id', 'title', ‘member',
'is_deleted', 'created_at', )
list_editable = ('is_deleted', )
list_filter = (
‘member__permission',
'category__name', 'is_deleted', )
fields = ('member', 'category', 'title', )
admin.site.register(Category)
admin.site.register(Post, PostAdmin)
admin.site.register(Comment)
기본 - Form
# post/admin.py
class PostAdmin(admin.ModelAdmin):
. . .
fieldsets = (
('기본 정보', {
'fields': (('member', 'category', ), )
}),
('제목 및 내용', {
'fields': (
'title', 'subtitle', ‘content',
)
}),
('삭제', {
'fields': ('is_deleted', 'deleted_at', )
})
)
. . .
기본 - Custom Validation
# post/forms.py
class MyPostAdminForm(forms.ModelForm):
def clean_content(self): # clean_{field_name}
ModelForm Documentation
https://docs.djangoproject.com/en/1.11/topics/forms/modelforms
기본 - Custom Validation
# post/forms.py
class MyPostAdminForm(forms.ModelForm):
def clean_content(self): # clean_{field_name}
content = self.cleaned_data['content']
words = ['심심하다', ‘관리자’, ‘금지어’, ]
error_message =
'[{0}] {1}'.format(', '.join(words), ‘와…’)
if any(word in content for word in words):
raise forms.ValidationError(error_message)
return content
ModelForm Documentation
https://docs.djangoproject.com/en/1.11/topics/forms/modelforms
기본 - Custom Validation
# post/forms.py
class MyPostAdminForm(forms.ModelForm):
def clean_content(self): # clean_{field_name}
content = self.cleaned_data['content']
words = ['심심하다', ‘관리자’, ‘금지어’, ]
error_message =
'[{0}] {1}'.format(', '.join(words), ‘와…’)
if any(word in content for word in words):
raise forms.ValidationError(error_message)
return content
# post/admin.py
class PostAdmin(admin.ModelAdmin):
form = MyPostAdminForm
. . .
ModelForm Documentation
https://docs.djangoproject.com/en/1.11/topics/forms/modelforms
기본 - Custom Validation
# post/forms.py
class MyPostAdminForm(forms.ModelForm):
def clean_content(self): # clean_{field_name}
content = self.cleaned_data['content']
words = ['심심하다', ‘관리자’, ‘금지어’, ]
error_message =
'[{0}] {1}'.format(', '.join(words), ‘와…’)
if any(word in content for word in words):
raise forms.ValidationError(error_message)
return content
# post/admin.py
class PostAdmin(admin.ModelAdmin):
form = MyPostAdminForm
. . .
ModelForm Documentation
https://docs.djangoproject.com/en/1.11/topics/forms/modelforms
[심심하다, 관리자, 금지어]와 같은 단어들은 입력하실 수 없습니다.
순서
1.Django 설치
2.Model을 admin site에 등록하기
3.기본적인 사용법
4.조금 더 심화된 사용법
5.커스텀 페이지 추가
6.UI 변경하기
7.Admin site 분리하기
8.마지막으로 (간단한) 문서화!
심화 - Custom list filter
# post/filters.py
class CreatedDateFilter(admin.SimpleListFilter):
title = '작성일'
parameter_name = 'date'
def lookups(self, request, model_admin):
results = []
for i in range(-3, 6):
date = datetime.date.today() + datetime.timedelta(days=i)
display_str = '{0} [{1}개]'.format(
date,
Post.objects.filter(created_at__date=date).count()
)
display_str += ' - 오늘' if i == 0 else ''
results.append((date, display_str))
return results
def queryset(self, request, queryset):
if self.value():
return queryset.filter(created_at__date=self.value())
else:
return queryset.all()
심화 - Custom list filter
# post/filters.py
class CreatedDateFilter(admin.SimpleListFilter):
title = '작성일'
parameter_name = 'date'
def lookups(self, request, model_admin):
results = []
for i in range(-3, 6):
date = datetime.date.today() + datetime.timedelta(days=i)
display_str = '{0} [{1}개]'.format(
date,
Post.objects.filter(created_at__date=date).count()
)
display_str += ' - 오늘' if i == 0 else ''
results.append((date, display_str))
return results
def queryset(self, request, queryset):
if self.value():
return queryset.filter(created_at__date=self.value())
else:
return queryset.all()
심화 - Custom list filter
# post/filters.py
class CreatedDateFilter(admin.SimpleListFilter):
title = '작성일'
parameter_name = 'date'
def lookups(self, request, model_admin):
results = []
for i in range(-3, 6):
date = datetime.date.today() + datetime.timedelta(days=i)
display_str = '{0} [{1}개]'.format(
date,
Post.objects.filter(created_at__date=date).count()
)
display_str += ' - 오늘' if i == 0 else ''
results.append((date, display_str))
return results
def queryset(self, request, queryset):
if self.value():
return queryset.filter(created_at__date=self.value())
else:
return queryset.all()
심화 - Custom action
# member/admin.py
from member.forms import SetCertificationDateForm
class MemberAdmin(admin.ModelAdmin):
actions = ['set_certification_date']
action_form = SetCertificationDateForm # SelectDateWidget
심화 - Custom action
# member/admin.py
from member.forms import SetCertificationDateForm
class MemberAdmin(admin.ModelAdmin):
actions = ['set_certification_date']
action_form = SetCertificationDateForm # SelectDateWidget
def set_certification_date(self, request, queryset):
year, month, day = . . . # POST Request에서 값을 꺼냄
if year and month and day:
date_str = '{0}-{1}-{2}'.format(year, month, day)
date = strptime(date_str, "%Y-%d-%m").date()
for member in queryset:
Member.objects
.filter(id=member.id)
.update(is_certificated=True, certification_date=date)
messages.success(request, '{0}명의 회원을 인증했습니다.'.format(len(queryset)))
else:
messages.error(request, '날짜가 선택되지 않았습니다.')
심화 - Custom action
# member/admin.py
from member.forms import SetCertificationDateForm
class MemberAdmin(admin.ModelAdmin):
actions = ['set_certification_date']
action_form = SetCertificationDateForm # SelectDateWidget
def set_certification_date(self, request, queryset):
year, month, day = . . . # POST Request에서 값을 꺼냄
if year and month and day:
date_str = '{0}-{1}-{2}'.format(year, month, day)
date = strptime(date_str, "%Y-%d-%m").date()
for member in queryset:
Member.objects
.filter(id=member.id)
.update(is_certificated=True, certification_date=date)
messages.success(request, '{0}명의 회원을 인증했습니다.'.format(len(queryset)))
else:
messages.error(request, '날짜가 선택되지 않았습니다.')
set_certification_date.short_description = '선택된 유저를 해당 날짜 기준으로 인증합니다.'
순서
1.Django 설치
2.Model을 admin site에 등록하기
3.기본적인 사용법
4.조금 더 심화된 사용법
5.커스텀 페이지 추가
6.UI 변경하기
7.Admin site 분리하기
8.마지막으로 (간단한) 문서화!
페이지 추가하기
# post/admin.py
class PostAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super(PostAdmin, self).get_urls()
post_urls = [
url(r'^status/$', self.admin_site.admin_view(self.post_status_view))
]
return post_urls + urls
def post_status_view(self, request):
context = dict(
self.admin_site.each_context(request),
posts=Post.objects.all(),
key1=value1,
key2=value2,
)
return TemplateResponse(request, "admin/post_status.html", context)
페이지 추가하기
# post/admin.py
class PostAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super(PostAdmin, self).get_urls()
post_urls = [
url(r'^status/$', self.admin_site.admin_view(self.post_status_view))
]
return post_urls + urls
def post_status_view(self, request):
context = dict(
self.admin_site.each_context(request),
posts=Post.objects.all(),
key1=value1,
key2=value2,
)
return TemplateResponse(request, "admin/post_status.html", context)
페이지 추가하기
# post/admin.py
class PostAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super(PostAdmin, self).get_urls()
post_urls = [
url(r'^status/$', self.admin_site.admin_view(self.post_status_view))
]
return post_urls + urls
def post_status_view(self, request):
context = dict(
self.admin_site.each_context(request),
posts=Post.objects.all(),
key1=value1,
key2=value2,
)
return TemplateResponse(request, "admin/post_status.html", context)
페이지 추가하기
# templates/admin/post_status.html
{% extends "admin/base_site.html" %}
{% block content %}
<h2>Post Status</h2>
<ul>
{% for post in posts %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
{% endblock %}
http://localhost:8000/admin/post/post/status/
순서
1.Django 설치
2.Model을 admin site에 등록하기
3.기본적인 사용법
4.조금 더 심화된 사용법
5.커스텀 페이지 추가
6.UI 변경하기
7.Admin site 분리하기
8.마지막으로 (간단한) 문서화!
UI 변경하기
├── example # Project Directory
│   ├── assets
│   │   └── admin
│   │   ├── css
│   │   │   ├── custom.css # 전체 레이아웃을 수정하는 CSS
│   │   │   ├── dropdown.css # 상단 메뉴바에 드롭다운 메뉴를 적용하기 위한 CSS
• https://github.com/bbayoung/django-admin-site-custom-example/blob/master/example/assets/admin/css/custom.css
• https://github.com/bbayoung/django-admin-site-custom-example/blob/master/example/assets/admin/css/dropdown.css
# example/settings.py
. . .
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'assets'),
)
. . .
• https://github.com/django/django/
UI 변경하기
├── admin
│   ├── templates
│   │   ├── admin
│   │   │   ├── 404.html
│   │   │   ├── 500.html
│   │   │   ├── actions.html
│   │   │   ├── app_index.html
│   │   │   ├── auth
│   │   │   ├── base.html
│   │   │   ├── base_site.html
│   │   │   ├── change_form.html
│   │   │   └── index.html
base.html
base_site.html
app_index.htmlindex.html login.html
# templates/admin/base_site.html
{% extends "admin/base.html" %}
{% load static %}
{% block title %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
{% block extrastyle %}
{% endblock %}
{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('Django
administration') }}</a></h1>
{% endblock %}
{% block nav-global %}{% endblock %}
UI 변경하기
# templates/admin/base_site.html
{% extends "admin/base.html" %}
{% load static %}
{% block title %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
{% block extrastyle %}
<link rel="stylesheet" type="text/css" href="{% static "admin/css/dropdown.css" %}" />
<link rel="stylesheet" type="text/css" href="{% static "admin/css/custom.css" %}" />
{% endblock %}
{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('Django
administration') }}</a></h1>
{% endblock %}
{% block nav-global %}{% endblock %}
UI 변경하기
# example/settings.py
admin.site.site_title = '파이콘 한국 2017’ # 브라우저 타이틀
admin.site.site_header = 'Back to the basic’ # 웹사이트 header 부분 타이틀
UI 변경하기
UI 변경하기
# example/context_processors.py
def gnb_menus(request):
menus = [
{
'name': '회원',
'sub_menus': [
{'name': '관리자', 'url': '/admin/member/member/?permission__exact=AD'},
{'name': '에디터', 'url': '/admin/member/member/?permission__exact=ET'},
{'name': '일반', 'url': '/admin/member/member/?permission__exact=MB'},
]
},
{
'name': ' 글 ',
'sub_menus': [
{'name': 'GENDER', 'url': '/admin/post/post/?category__name=GENDER'},
{'name': 'SOCIAL', 'url': '/admin/post/post/?category__name=SOCIAL'},
{'name': 'POLITICS', 'url': '/admin/post/post/?category__name=POLITICS'},
{'name': '통계', 'url': '/admin/post/post/status/'},
]
}
]
return {'gnb_menus': menus}
UI 변경하기
# example/settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(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',
‘example.context_processors.gnb_apps', # 장고에 추가한 기본 앱 메뉴
‘example.context_processors.gnb_menus', # 이전 페이지에서 직접 정의한 상단 메뉴
],
},
},
]
UI 변경하기
# templates/admin/base_site.html
{% block nav-global %}
<div id="gnb">
<div id="gnb-app-list">
<ul class="drop-down-menu">
{% for menu in gnb_menus %}
// 커스텀 메뉴 출력 - - - - - - - - - - - - - (1)
{% endfor %}
{% if gnb_apps %}
// Django 전체 모델 출력 - - - - - - - - - (2)
{% endif %}
</ul>
</div>
</div>
{% endblock %}
UI 변경하기
# templates/admin/base_site.html
. . . (1)
{% for menu in gnb_menus %}
<li>
<a {% if menu.url %}href="{{ menu.url }}"{% endif %}>{{ menu.name }}</a>
<ul>
{% for sub_menu in menu.sub_menus %}
<li><a href="{{ sub_menu.url }}">{{ sub_menu.name }}</a></li>
{% endfor %}
</ul>
</li>
{% endfor %}
. . .
UI 변경하기
# templates/admin/base_site.html
. . . (2)
{% if gnb_apps %}
<li><a>전체 앱</a>
<ul>
{% for app in gnb_apps %}
<li><a href="/admin/{{ app.app_url }}">{{ app.name }}</a>
<ul>
{% for model in app.models %}
<li><a href="{{ model.admin_url }}">{{ model.name }}</a></li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</li>
{% endif %}
. . .
UI 변경하기
UI 변경하기
순서
1.Django 설치
2.Model을 admin site에 등록하기
3.기본적인 사용법
4.조금 더 심화된 사용법
5.커스텀 페이지 추가
6.UI 변경하기
7.Admin site 분리하기
8.마지막으로 (간단한) 문서화!
Django admin site 분리하기
# post/admin.py
from django.contrib.admin import AdminSite
class CommentAdminSite(AdminSite):
site_header = 'Comment administration'
comment_admin = CommentAdminSite(name='comment admin')
comment_admin.register(Comment, CommentAdmin)
Django admin site 분리하기
# post/admin.py
from django.contrib.admin import AdminSite
class CommentAdminSite(AdminSite):
site_header = 'Comment administration'
comment_admin = CommentAdminSite(name='comment admin')
comment_admin.register(Comment, CommentAdmin)
# example/urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^admin/comment/', comment_admin.urls),
]
순서
1.Django 설치
2.Model을 admin site에 등록하기
3.기본적인 사용법
4.조금 더 심화된 사용법
5.커스텀 페이지 추가
6.UI 변경하기
7.Admin site 분리하기
8.마지막으로 (간단한) 문서화!
문서화
(envExample) example $ pip install docutils # docutils 설치
# example/urls.py
urlpatterns = [
. . .
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
. . .
]
# example/settings.py
INSTALLED_APPS = [
. . .
'django.contrib.admindocs',
. . .
]
문서화
# post/models.py
class Comment(models.Model):
"""
사용들이 작성한 글에 대한 댓글입니다.
댓글은 :model:`post.Post` 와 :model:`member.Member`. 모델과 1:N 관계입니다.
"""
member = models.ForeignKey(Member, verbose_name='작성자')
post = models.ForeignKey(Post, verbose_name='원본글')
content = models.TextField(verbose_name='내용', help_text='댓글 내용입니다.')
• 시간이 길지 않아, 준비한 내용은 여기까지입니다.
• 이 외에도 공식 문서에 추가적인 커스텀 방법들이 소개되어 있습니다.
• 예제 코드는 아래에서 확인하실 수 있습니다.

https://github.com/bbayoung/django-admin-site-custom-example
감사합니다.

More Related Content

What's hot

더 나은 개발자 되기
더 나은 개발자 되기더 나은 개발자 되기
더 나은 개발자 되기JeongHun Byeon
 
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축Youngil Cho
 
REST API 설계
REST API 설계REST API 설계
REST API 설계Terry Cho
 
우아한 모노리스
우아한 모노리스우아한 모노리스
우아한 모노리스Arawn Park
 
Sonatype nexus 로 docker registry 관리하기
Sonatype nexus 로 docker registry 관리하기Sonatype nexus 로 docker registry 관리하기
Sonatype nexus 로 docker registry 관리하기KwangSeob Jeong
 
[OKKYCON] 정진욱 - 테스트하기 쉬운 코드로 개발하기
[OKKYCON] 정진욱 - 테스트하기 쉬운 코드로 개발하기[OKKYCON] 정진욱 - 테스트하기 쉬운 코드로 개발하기
[OKKYCON] 정진욱 - 테스트하기 쉬운 코드로 개발하기OKKY
 
새해 일어난 일
새해 일어난 일새해 일어난 일
새해 일어난 일Eunhyang Kim
 
숨겨진 마이크로서비스: 초고속 응답과 고가용성을 위한 캐시 서비스 디자인
숨겨진 마이크로서비스: 초고속 응답과 고가용성을 위한 캐시 서비스 디자인숨겨진 마이크로서비스: 초고속 응답과 고가용성을 위한 캐시 서비스 디자인
숨겨진 마이크로서비스: 초고속 응답과 고가용성을 위한 캐시 서비스 디자인VMware Tanzu Korea
 
인프런 - 스타트업 인프랩 시작 사례
인프런 - 스타트업 인프랩 시작 사례인프런 - 스타트업 인프랩 시작 사례
인프런 - 스타트업 인프랩 시작 사례Hyung Lee
 
카카오스토리 웹팀의 코드리뷰 경험
카카오스토리 웹팀의 코드리뷰 경험카카오스토리 웹팀의 코드리뷰 경험
카카오스토리 웹팀의 코드리뷰 경험Ohgyun Ahn
 
웹서버 부하테스트 실전 노하우
웹서버 부하테스트 실전 노하우웹서버 부하테스트 실전 노하우
웹서버 부하테스트 실전 노하우IMQA
 
[NDC17] Kubernetes로 개발서버 간단히 찍어내기
[NDC17] Kubernetes로 개발서버 간단히 찍어내기[NDC17] Kubernetes로 개발서버 간단히 찍어내기
[NDC17] Kubernetes로 개발서버 간단히 찍어내기SeungYong Oh
 
Node.js API 서버 성능 개선기
Node.js API 서버 성능 개선기Node.js API 서버 성능 개선기
Node.js API 서버 성능 개선기JeongHun Byeon
 
코딩 테스트 및 알고리즘 문제해결 공부 방법 (고려대학교 KUCC, 2022년 4월)
코딩 테스트 및 알고리즘 문제해결 공부 방법 (고려대학교 KUCC, 2022년 4월)코딩 테스트 및 알고리즘 문제해결 공부 방법 (고려대학교 KUCC, 2022년 4월)
코딩 테스트 및 알고리즘 문제해결 공부 방법 (고려대학교 KUCC, 2022년 4월)Suhyun Park
 
d.ts 만들기
d.ts 만들기d.ts 만들기
d.ts 만들기DaeSeon Jeong
 
Django Forms: Best Practices, Tips, Tricks
Django Forms: Best Practices, Tips, TricksDjango Forms: Best Practices, Tips, Tricks
Django Forms: Best Practices, Tips, TricksShawn Rider
 
손코딩뇌컴파일눈디버깅을 소개합니다.
손코딩뇌컴파일눈디버깅을 소개합니다.손코딩뇌컴파일눈디버깅을 소개합니다.
손코딩뇌컴파일눈디버깅을 소개합니다.Kwangsung Ha
 
개발을잘하고싶어요-네이버랩스 송기선님
개발을잘하고싶어요-네이버랩스 송기선님개발을잘하고싶어요-네이버랩스 송기선님
개발을잘하고싶어요-네이버랩스 송기선님NAVER D2
 
Mongo DB 성능최적화 전략
Mongo DB 성능최적화 전략Mongo DB 성능최적화 전략
Mongo DB 성능최적화 전략Jin wook
 
서버학개론(백엔드 서버 개발자를 위한)
서버학개론(백엔드 서버 개발자를 위한)서버학개론(백엔드 서버 개발자를 위한)
서버학개론(백엔드 서버 개발자를 위한)수보 김
 

What's hot (20)

더 나은 개발자 되기
더 나은 개발자 되기더 나은 개발자 되기
더 나은 개발자 되기
 
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
 
REST API 설계
REST API 설계REST API 설계
REST API 설계
 
우아한 모노리스
우아한 모노리스우아한 모노리스
우아한 모노리스
 
Sonatype nexus 로 docker registry 관리하기
Sonatype nexus 로 docker registry 관리하기Sonatype nexus 로 docker registry 관리하기
Sonatype nexus 로 docker registry 관리하기
 
[OKKYCON] 정진욱 - 테스트하기 쉬운 코드로 개발하기
[OKKYCON] 정진욱 - 테스트하기 쉬운 코드로 개발하기[OKKYCON] 정진욱 - 테스트하기 쉬운 코드로 개발하기
[OKKYCON] 정진욱 - 테스트하기 쉬운 코드로 개발하기
 
새해 일어난 일
새해 일어난 일새해 일어난 일
새해 일어난 일
 
숨겨진 마이크로서비스: 초고속 응답과 고가용성을 위한 캐시 서비스 디자인
숨겨진 마이크로서비스: 초고속 응답과 고가용성을 위한 캐시 서비스 디자인숨겨진 마이크로서비스: 초고속 응답과 고가용성을 위한 캐시 서비스 디자인
숨겨진 마이크로서비스: 초고속 응답과 고가용성을 위한 캐시 서비스 디자인
 
인프런 - 스타트업 인프랩 시작 사례
인프런 - 스타트업 인프랩 시작 사례인프런 - 스타트업 인프랩 시작 사례
인프런 - 스타트업 인프랩 시작 사례
 
카카오스토리 웹팀의 코드리뷰 경험
카카오스토리 웹팀의 코드리뷰 경험카카오스토리 웹팀의 코드리뷰 경험
카카오스토리 웹팀의 코드리뷰 경험
 
웹서버 부하테스트 실전 노하우
웹서버 부하테스트 실전 노하우웹서버 부하테스트 실전 노하우
웹서버 부하테스트 실전 노하우
 
[NDC17] Kubernetes로 개발서버 간단히 찍어내기
[NDC17] Kubernetes로 개발서버 간단히 찍어내기[NDC17] Kubernetes로 개발서버 간단히 찍어내기
[NDC17] Kubernetes로 개발서버 간단히 찍어내기
 
Node.js API 서버 성능 개선기
Node.js API 서버 성능 개선기Node.js API 서버 성능 개선기
Node.js API 서버 성능 개선기
 
코딩 테스트 및 알고리즘 문제해결 공부 방법 (고려대학교 KUCC, 2022년 4월)
코딩 테스트 및 알고리즘 문제해결 공부 방법 (고려대학교 KUCC, 2022년 4월)코딩 테스트 및 알고리즘 문제해결 공부 방법 (고려대학교 KUCC, 2022년 4월)
코딩 테스트 및 알고리즘 문제해결 공부 방법 (고려대학교 KUCC, 2022년 4월)
 
d.ts 만들기
d.ts 만들기d.ts 만들기
d.ts 만들기
 
Django Forms: Best Practices, Tips, Tricks
Django Forms: Best Practices, Tips, TricksDjango Forms: Best Practices, Tips, Tricks
Django Forms: Best Practices, Tips, Tricks
 
손코딩뇌컴파일눈디버깅을 소개합니다.
손코딩뇌컴파일눈디버깅을 소개합니다.손코딩뇌컴파일눈디버깅을 소개합니다.
손코딩뇌컴파일눈디버깅을 소개합니다.
 
개발을잘하고싶어요-네이버랩스 송기선님
개발을잘하고싶어요-네이버랩스 송기선님개발을잘하고싶어요-네이버랩스 송기선님
개발을잘하고싶어요-네이버랩스 송기선님
 
Mongo DB 성능최적화 전략
Mongo DB 성능최적화 전략Mongo DB 성능최적화 전략
Mongo DB 성능최적화 전략
 
서버학개론(백엔드 서버 개발자를 위한)
서버학개론(백엔드 서버 개발자를 위한)서버학개론(백엔드 서버 개발자를 위한)
서버학개론(백엔드 서버 개발자를 위한)
 

Similar to Django admin site 커스텀하여 적극적으로 활용하기

QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기
QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기
QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기Kwangyoun Jung
 
레거시 시스템에 Django 들이밀기
레거시 시스템에 Django 들이밀기레거시 시스템에 Django 들이밀기
레거시 시스템에 Django 들이밀기Jiyong Jung
 
XE Open seminar 테마만들기
XE Open seminar 테마만들기XE Open seminar 테마만들기
XE Open seminar 테마만들기Sungbum Hong
 
경상대 멋쟁이사자처럼 8기 정기세션 (6장 Blog프로젝트 - Model,admin).pptx
경상대 멋쟁이사자처럼 8기 정기세션 (6장 Blog프로젝트 - Model,admin).pptx경상대 멋쟁이사자처럼 8기 정기세션 (6장 Blog프로젝트 - Model,admin).pptx
경상대 멋쟁이사자처럼 8기 정기세션 (6장 Blog프로젝트 - Model,admin).pptxLee Dahae
 
Word camp seoul-2012-track3-3
Word camp seoul-2012-track3-3Word camp seoul-2012-track3-3
Word camp seoul-2012-track3-3082net
 
Html5&css 3장
Html5&css 3장Html5&css 3장
Html5&css 3장홍준 김
 
워드프레스 For 플러그인
워드프레스 For 플러그인워드프레스 For 플러그인
워드프레스 For 플러그인082net
 
컴포넌트 관점에서 개발하기
컴포넌트 관점에서 개발하기컴포넌트 관점에서 개발하기
컴포넌트 관점에서 개발하기우영 주
 
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃Kwangyoun Jung
 
현대고등학교 PHP 강의 - 7,8차시 (설리번 프로젝트)
현대고등학교 PHP 강의 - 7,8차시 (설리번 프로젝트)현대고등학교 PHP 강의 - 7,8차시 (설리번 프로젝트)
현대고등학교 PHP 강의 - 7,8차시 (설리번 프로젝트)Ukjae Jeong
 
20171209pycon_fbv_cbv
20171209pycon_fbv_cbv20171209pycon_fbv_cbv
20171209pycon_fbv_cbvseokhunkim4
 
파이썬 플라스크 이해하기
파이썬 플라스크 이해하기 파이썬 플라스크 이해하기
파이썬 플라스크 이해하기 Yong Joon Moon
 
[XECon+PHPFest 2014] jQuery 개발자에서 AngularJS 개발자 되기
[XECon+PHPFest 2014] jQuery 개발자에서 AngularJS 개발자 되기[XECon+PHPFest 2014] jQuery 개발자에서 AngularJS 개발자 되기
[XECon+PHPFest 2014] jQuery 개발자에서 AngularJS 개발자 되기Jeado Ko
 
자바스크립트 프레임워크 살펴보기
자바스크립트 프레임워크 살펴보기자바스크립트 프레임워크 살펴보기
자바스크립트 프레임워크 살펴보기Jeado Ko
 
Daejeon IT Developer Conference Hibernate3
Daejeon IT Developer Conference Hibernate3Daejeon IT Developer Conference Hibernate3
Daejeon IT Developer Conference Hibernate3plusperson
 
거 XE 모듈 개발하기 좋은 날씨네 - XECon + PHPFest 2014
거 XE 모듈 개발하기 좋은 날씨네 - XECon + PHPFest 2014거 XE 모듈 개발하기 좋은 날씨네 - XECon + PHPFest 2014
거 XE 모듈 개발하기 좋은 날씨네 - XECon + PHPFest 2014승엽 신
 

Similar to Django admin site 커스텀하여 적극적으로 활용하기 (20)

QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기
QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기
QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기
 
레거시 시스템에 Django 들이밀기
레거시 시스템에 Django 들이밀기레거시 시스템에 Django 들이밀기
레거시 시스템에 Django 들이밀기
 
XE Open seminar 테마만들기
XE Open seminar 테마만들기XE Open seminar 테마만들기
XE Open seminar 테마만들기
 
경상대 멋쟁이사자처럼 8기 정기세션 (6장 Blog프로젝트 - Model,admin).pptx
경상대 멋쟁이사자처럼 8기 정기세션 (6장 Blog프로젝트 - Model,admin).pptx경상대 멋쟁이사자처럼 8기 정기세션 (6장 Blog프로젝트 - Model,admin).pptx
경상대 멋쟁이사자처럼 8기 정기세션 (6장 Blog프로젝트 - Model,admin).pptx
 
Meteor2015 codelab
Meteor2015 codelab Meteor2015 codelab
Meteor2015 codelab
 
Word camp seoul-2012-track3-3
Word camp seoul-2012-track3-3Word camp seoul-2012-track3-3
Word camp seoul-2012-track3-3
 
Html5&css 3장
Html5&css 3장Html5&css 3장
Html5&css 3장
 
Xe hack
Xe hackXe hack
Xe hack
 
워드프레스 For 플러그인
워드프레스 For 플러그인워드프레스 For 플러그인
워드프레스 For 플러그인
 
컴포넌트 관점에서 개발하기
컴포넌트 관점에서 개발하기컴포넌트 관점에서 개발하기
컴포넌트 관점에서 개발하기
 
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
 
현대고등학교 PHP 강의 - 7,8차시 (설리번 프로젝트)
현대고등학교 PHP 강의 - 7,8차시 (설리번 프로젝트)현대고등학교 PHP 강의 - 7,8차시 (설리번 프로젝트)
현대고등학교 PHP 강의 - 7,8차시 (설리번 프로젝트)
 
딥러닝이 바꾸는 애자일 테스팅
딥러닝이 바꾸는 애자일 테스팅딥러닝이 바꾸는 애자일 테스팅
딥러닝이 바꾸는 애자일 테스팅
 
20171209pycon_fbv_cbv
20171209pycon_fbv_cbv20171209pycon_fbv_cbv
20171209pycon_fbv_cbv
 
파이썬 플라스크 이해하기
파이썬 플라스크 이해하기 파이썬 플라스크 이해하기
파이썬 플라스크 이해하기
 
[XECon+PHPFest 2014] jQuery 개발자에서 AngularJS 개발자 되기
[XECon+PHPFest 2014] jQuery 개발자에서 AngularJS 개발자 되기[XECon+PHPFest 2014] jQuery 개발자에서 AngularJS 개발자 되기
[XECon+PHPFest 2014] jQuery 개발자에서 AngularJS 개발자 되기
 
자바스크립트 프레임워크 살펴보기
자바스크립트 프레임워크 살펴보기자바스크립트 프레임워크 살펴보기
자바스크립트 프레임워크 살펴보기
 
Daejeon IT Developer Conference Hibernate3
Daejeon IT Developer Conference Hibernate3Daejeon IT Developer Conference Hibernate3
Daejeon IT Developer Conference Hibernate3
 
거 XE 모듈 개발하기 좋은 날씨네 - XECon + PHPFest 2014
거 XE 모듈 개발하기 좋은 날씨네 - XECon + PHPFest 2014거 XE 모듈 개발하기 좋은 날씨네 - XECon + PHPFest 2014
거 XE 모듈 개발하기 좋은 날씨네 - XECon + PHPFest 2014
 
3-2. selector api
3-2. selector api3-2. selector api
3-2. selector api
 

Django admin site 커스텀하여 적극적으로 활용하기

  • 1. Django admin site 커스텀하여 적극적으로 활용하기 박영우
  • 2. 2년 조금 넘는 기간 동안, 스타트업에서 이것저것 개발하며 배운 것 중, 공유하고 싶은 것이 있어서 발표를 하게 되었습니다.
  • 3. ‘캠쿠’ 대학생활 개선 앱 ‘트웬티’ 대학생 미디어 ‘ALT’ 뉴미디어 스타트업 RESTful API CMS (Contents Management System)
  • 4. ‘트웬티’ 앱을 개발하던 2015년 9월쯤, Python과 Django를 알게 됐고, 덕분에 RESTful API와 CMS를 쉽고 빠르게 만들 수 있었습니다.
  • 5. 그런데, 6개월이 지나서야 Django admin site의 존재를 알게 됐습니다. CMS를 모두 다 구현하고 난 뒤에…
  • 6. Django admin site를 커스텀해서 사용하는 것이, 직접 만든 CMS 보다 훨씬 쉽고, 빠르고, 안정적이었습니다.
  • 7. 이번 파이콘 주제가 ‘Back to the basic’ 인 걸 보고, 경험을 공유하고 싶다는 생각이 들었습니다.
  • 8. 이 발표에서 다루는 내용 • Django admin site의 기본적인 사용 방법 • Django admin site를 커스텀 하는 여러가지 방법과 그 결과 • Python • Django 내부 동작, 구현
  • 9. 들어가기 전에… Django ? • Documentation • Middleware • Model (ORM) • Form • Class based view • Template Django admin site
  • 10. 발표의 모든 내용은 여기에 있습니다. • https://docs.djangoproject.com/en/1.11/ref/contrib/admin • ModelAdmin, InlineModelAdmin, AdminSite, LogEntry • Overriding admin templates, Reversing admin urls • https://docs.djangoproject.com/en/1.11/ref/contrib/admin/actions/ • Writing actions, Advanced action techniques • https://docs.djangoproject.com/en/1.11/ref/contrib/admin/admindocs/ • https://docs.djangoproject.com/en/1.11/ref/contrib/admin/javascript/
  • 11. 순서 1.Django 설치 2.Model을 admin site에 등록하기 3.기본적인 사용법 4.조금 더 심화된 사용법 5.커스텀 페이지 추가 6.UI 변경하기 7.Admin site 분리하기 8.마지막으로 (간단한) 문서화!
  • 12. 예제를 위한 모델 MEMBER name 이름 email 이메일 permission 권한 certification_date 인증일 is_certificated 인증상태 POST member 작성자 category 카테고리 title 제목 subtitle 부제목 content 내용 is_deleted 삭제여부 created_at 작성일 COMMENT member 작성자 post 원글 content 내용 report_count 신고수 created_at 작성일 CATEGORY name 이름 * permission (관리자, 에디터, 일반)
  • 13. 예제를 위한 모델 - Member class Member(AbstractBaseUser): TYPE_PERMISSIONS = ( ('AD', '관리자'), ('ET', '에디터'), ('MB', '일반'), ) email = models.EmailField('이메일', max_length=255, unique=True) username = models.CharField('닉네임', max_length=30) permission = models.CharField('권한', max_length=2, choices=TYPE_PERMISSIONS, default='MB') certification_date = models.DateField('인증일', default=None, null=True, blank=True) is_certificated = models.BooleanField('인증여부', default=False)
  • 14. 예제를 위한 모델 - Post class Category(models.Model): name = models.CharField('카테고리 이름', max_length=20) class Post(models.Model): member = models.ForeignKey(Member, verbose_name='작성자') category = models.ForeignKey(Category, verbose_name='카테고리') title = models.CharField('제목', max_length=255) content = models.TextField('내용') is_deleted = models.BooleanField('삭제된 글', default=False) created_at = models.DateTimeField('작성일', auto_now_add=True) class Comment(models.Model): member = models.ForeignKey(Member, verbose_name='작성자') post = models.ForeignKey(Post, verbose_name=‘원본글’) content = models.TextField() is_blocked = models.BooleanField('노출 제한', default=False)
  • 15. 순서 1.Django 설치 2.Model을 admin site에 등록하기 3.기본적인 사용법 4.조금 더 심화된 사용법 5.커스텀 페이지 추가 6.UI 변경하기 7.Admin site 분리하기 8.마지막으로 (간단한) 문서화!
  • 16. Django 설치 example $ pyenv virtualenv 3.6.2 pyconExample # 가상 환경 추가 example $ pyenv shell envExample # 가상 환경 실행 (envExample) example $ pip install django # django 설치 (envExample) example $ django-admin.py startproject example # django 프로젝트 생성 (envExample) example $ python manage.py startapp member # member, post 앱 추가 (envExample) example $ python manage.py startapp post (envExample) example $ python manage.py createsuperuser # 관리자 추가 (envExample) example $ python manage.py runserver # 실행 http://localhost:8000 http://localhost:8000/admin/
  • 17. 순서 1.Django 설치 2.Model을 admin site에 등록하기 3.기본적인 사용법 4.조금 더 심화된 사용법 5.커스텀 페이지 추가 6.UI 변경하기 7.Admin site 분리하기 8.마지막으로 (간단한) 문서화!
  • 18. Model을 admin site에 등록 # member/admin.py from django.contrib import admin from member.models import Member admin.site.register(Member) # post/amdin.py from django.contrib import admin from post.models import Category, Post, Comment admin.site.register(Post) admin.site.register(Category) admin.site.register(Comment)
  • 19. Model을 admin site에 등록 # post/models.py class Category(models.Model): class Meta: verbose_name_plural = "categories" name = models.CharField(max_length=20)
  • 20. 순서 1.Django 설치 2.Model을 admin site에 등록하기 3.기본적인 사용법 4.조금 더 심화된 사용법 5.커스텀 페이지 추가 6.UI 변경하기 7.Admin site 분리하기 8.마지막으로 (간단한) 문서화!
  • 21. 기본 - List # member/admin.py class MemberAdmin(admin.ModelAdmin):
  • 22. 기본 - List # member/admin.py class MemberAdmin(admin.ModelAdmin): list_per_page = 5
  • 23. 기본 - List # member/admin.py class MemberAdmin(admin.ModelAdmin): list_per_page = 5 list_display = ( 'id', 'email', ‘username', 'permission', ‘is_certificated', ‘certification_date’, ‘post_count', )
  • 24. 기본 - List # member/admin.py class MemberAdmin(admin.ModelAdmin): list_per_page = 5 list_display = ( 'id', 'email', ‘username', 'permission', ‘is_certificated', ‘certification_date’, ‘post_count', ) list_editable = ('permission', )
  • 25. 기본 - List # member/admin.py class MemberAdmin(admin.ModelAdmin): list_per_page = 5 list_display = ( 'id', 'email', ‘username', 'permission', ‘is_certificated', ‘certification_date’, ‘post_count', ) list_editable = ('permission', ) list_filter = ('permission', )
  • 26. 기본 - List # member/admin.py class MemberAdmin(admin.ModelAdmin): list_per_page = 5 list_display = ( 'id', 'email', ‘username', 'permission', ‘is_certificated', ‘certification_date’, ‘post_count', ) list_editable = ('permission', ) list_filter = ('permission', ) search_fields = ('username', )
  • 27. 기본 - List # member/admin.py class MemberAdmin(admin.ModelAdmin): list_per_page = 5 list_display = ( 'id', 'email', ‘username', 'permission', ‘is_certificated', ‘certification_date’, ‘post_count', ) list_editable = ('permission', ) list_filter = ('permission', ) search_fields = ('username', ) ordering = ('-id', 'email', 'permission', )
  • 28. 기본 - List # member/admin.py class MemberAdmin(admin.ModelAdmin): list_per_page = 5 list_display = ( 'id', 'email', ‘username', 'permission', ‘is_certificated', ‘certification_date’, ‘post_count', ) list_editable = ('permission', ) list_filter = ('permission', ) search_fields = ('username', ) ordering = ('-id', 'email', 'permission', ) def post_count(self, obj): return Post.objects.filter(member=obj).count() post_count.short_description = '작성한 글 수'
  • 29. 기본 - List # member/admin.py class MemberAdmin(admin.ModelAdmin): list_per_page = 5 list_display = ( 'id', 'email', ‘username', 'permission', ‘is_certificated', ‘certification_date’, ‘post_count', ) list_editable = ('permission', ) list_filter = ('permission', ) search_fields = ('username', ) ordering = ('-id', 'email', 'permission', ) def post_count(self, obj): return Post.objects.filter(member=obj).count() post_count.short_description = '작성한 글 수' admin.site.register(Member, MemberAdmin)
  • 30. 기본 - Form # post/admin.py class PostAdmin(admin.ModelAdmin): list_per_page = 10 list_display = ( 'id', 'title', ‘member', 'is_deleted', 'created_at', ) list_editable = ('is_deleted', ) list_filter = ( ‘member__permission', 'category__name', 'is_deleted', ) fields = ('member', 'category', 'title', ) admin.site.register(Category) admin.site.register(Post, PostAdmin) admin.site.register(Comment)
  • 31. 기본 - Form # post/admin.py class PostAdmin(admin.ModelAdmin): . . . fieldsets = ( ('기본 정보', { 'fields': (('member', 'category', ), ) }), ('제목 및 내용', { 'fields': ( 'title', 'subtitle', ‘content', ) }), ('삭제', { 'fields': ('is_deleted', 'deleted_at', ) }) ) . . .
  • 32. 기본 - Custom Validation # post/forms.py class MyPostAdminForm(forms.ModelForm): def clean_content(self): # clean_{field_name} ModelForm Documentation https://docs.djangoproject.com/en/1.11/topics/forms/modelforms
  • 33. 기본 - Custom Validation # post/forms.py class MyPostAdminForm(forms.ModelForm): def clean_content(self): # clean_{field_name} content = self.cleaned_data['content'] words = ['심심하다', ‘관리자’, ‘금지어’, ] error_message = '[{0}] {1}'.format(', '.join(words), ‘와…’) if any(word in content for word in words): raise forms.ValidationError(error_message) return content ModelForm Documentation https://docs.djangoproject.com/en/1.11/topics/forms/modelforms
  • 34. 기본 - Custom Validation # post/forms.py class MyPostAdminForm(forms.ModelForm): def clean_content(self): # clean_{field_name} content = self.cleaned_data['content'] words = ['심심하다', ‘관리자’, ‘금지어’, ] error_message = '[{0}] {1}'.format(', '.join(words), ‘와…’) if any(word in content for word in words): raise forms.ValidationError(error_message) return content # post/admin.py class PostAdmin(admin.ModelAdmin): form = MyPostAdminForm . . . ModelForm Documentation https://docs.djangoproject.com/en/1.11/topics/forms/modelforms
  • 35. 기본 - Custom Validation # post/forms.py class MyPostAdminForm(forms.ModelForm): def clean_content(self): # clean_{field_name} content = self.cleaned_data['content'] words = ['심심하다', ‘관리자’, ‘금지어’, ] error_message = '[{0}] {1}'.format(', '.join(words), ‘와…’) if any(word in content for word in words): raise forms.ValidationError(error_message) return content # post/admin.py class PostAdmin(admin.ModelAdmin): form = MyPostAdminForm . . . ModelForm Documentation https://docs.djangoproject.com/en/1.11/topics/forms/modelforms [심심하다, 관리자, 금지어]와 같은 단어들은 입력하실 수 없습니다.
  • 36. 순서 1.Django 설치 2.Model을 admin site에 등록하기 3.기본적인 사용법 4.조금 더 심화된 사용법 5.커스텀 페이지 추가 6.UI 변경하기 7.Admin site 분리하기 8.마지막으로 (간단한) 문서화!
  • 37. 심화 - Custom list filter # post/filters.py class CreatedDateFilter(admin.SimpleListFilter): title = '작성일' parameter_name = 'date' def lookups(self, request, model_admin): results = [] for i in range(-3, 6): date = datetime.date.today() + datetime.timedelta(days=i) display_str = '{0} [{1}개]'.format( date, Post.objects.filter(created_at__date=date).count() ) display_str += ' - 오늘' if i == 0 else '' results.append((date, display_str)) return results def queryset(self, request, queryset): if self.value(): return queryset.filter(created_at__date=self.value()) else: return queryset.all()
  • 38. 심화 - Custom list filter # post/filters.py class CreatedDateFilter(admin.SimpleListFilter): title = '작성일' parameter_name = 'date' def lookups(self, request, model_admin): results = [] for i in range(-3, 6): date = datetime.date.today() + datetime.timedelta(days=i) display_str = '{0} [{1}개]'.format( date, Post.objects.filter(created_at__date=date).count() ) display_str += ' - 오늘' if i == 0 else '' results.append((date, display_str)) return results def queryset(self, request, queryset): if self.value(): return queryset.filter(created_at__date=self.value()) else: return queryset.all()
  • 39. 심화 - Custom list filter # post/filters.py class CreatedDateFilter(admin.SimpleListFilter): title = '작성일' parameter_name = 'date' def lookups(self, request, model_admin): results = [] for i in range(-3, 6): date = datetime.date.today() + datetime.timedelta(days=i) display_str = '{0} [{1}개]'.format( date, Post.objects.filter(created_at__date=date).count() ) display_str += ' - 오늘' if i == 0 else '' results.append((date, display_str)) return results def queryset(self, request, queryset): if self.value(): return queryset.filter(created_at__date=self.value()) else: return queryset.all()
  • 40. 심화 - Custom action # member/admin.py from member.forms import SetCertificationDateForm class MemberAdmin(admin.ModelAdmin): actions = ['set_certification_date'] action_form = SetCertificationDateForm # SelectDateWidget
  • 41. 심화 - Custom action # member/admin.py from member.forms import SetCertificationDateForm class MemberAdmin(admin.ModelAdmin): actions = ['set_certification_date'] action_form = SetCertificationDateForm # SelectDateWidget def set_certification_date(self, request, queryset): year, month, day = . . . # POST Request에서 값을 꺼냄 if year and month and day: date_str = '{0}-{1}-{2}'.format(year, month, day) date = strptime(date_str, "%Y-%d-%m").date() for member in queryset: Member.objects .filter(id=member.id) .update(is_certificated=True, certification_date=date) messages.success(request, '{0}명의 회원을 인증했습니다.'.format(len(queryset))) else: messages.error(request, '날짜가 선택되지 않았습니다.')
  • 42. 심화 - Custom action # member/admin.py from member.forms import SetCertificationDateForm class MemberAdmin(admin.ModelAdmin): actions = ['set_certification_date'] action_form = SetCertificationDateForm # SelectDateWidget def set_certification_date(self, request, queryset): year, month, day = . . . # POST Request에서 값을 꺼냄 if year and month and day: date_str = '{0}-{1}-{2}'.format(year, month, day) date = strptime(date_str, "%Y-%d-%m").date() for member in queryset: Member.objects .filter(id=member.id) .update(is_certificated=True, certification_date=date) messages.success(request, '{0}명의 회원을 인증했습니다.'.format(len(queryset))) else: messages.error(request, '날짜가 선택되지 않았습니다.') set_certification_date.short_description = '선택된 유저를 해당 날짜 기준으로 인증합니다.'
  • 43. 순서 1.Django 설치 2.Model을 admin site에 등록하기 3.기본적인 사용법 4.조금 더 심화된 사용법 5.커스텀 페이지 추가 6.UI 변경하기 7.Admin site 분리하기 8.마지막으로 (간단한) 문서화!
  • 44. 페이지 추가하기 # post/admin.py class PostAdmin(admin.ModelAdmin): def get_urls(self): urls = super(PostAdmin, self).get_urls() post_urls = [ url(r'^status/$', self.admin_site.admin_view(self.post_status_view)) ] return post_urls + urls def post_status_view(self, request): context = dict( self.admin_site.each_context(request), posts=Post.objects.all(), key1=value1, key2=value2, ) return TemplateResponse(request, "admin/post_status.html", context)
  • 45. 페이지 추가하기 # post/admin.py class PostAdmin(admin.ModelAdmin): def get_urls(self): urls = super(PostAdmin, self).get_urls() post_urls = [ url(r'^status/$', self.admin_site.admin_view(self.post_status_view)) ] return post_urls + urls def post_status_view(self, request): context = dict( self.admin_site.each_context(request), posts=Post.objects.all(), key1=value1, key2=value2, ) return TemplateResponse(request, "admin/post_status.html", context)
  • 46. 페이지 추가하기 # post/admin.py class PostAdmin(admin.ModelAdmin): def get_urls(self): urls = super(PostAdmin, self).get_urls() post_urls = [ url(r'^status/$', self.admin_site.admin_view(self.post_status_view)) ] return post_urls + urls def post_status_view(self, request): context = dict( self.admin_site.each_context(request), posts=Post.objects.all(), key1=value1, key2=value2, ) return TemplateResponse(request, "admin/post_status.html", context)
  • 47. 페이지 추가하기 # templates/admin/post_status.html {% extends "admin/base_site.html" %} {% block content %} <h2>Post Status</h2> <ul> {% for post in posts %} <li>{{ post.title }}</li> {% endfor %} </ul> {% endblock %} http://localhost:8000/admin/post/post/status/
  • 48. 순서 1.Django 설치 2.Model을 admin site에 등록하기 3.기본적인 사용법 4.조금 더 심화된 사용법 5.커스텀 페이지 추가 6.UI 변경하기 7.Admin site 분리하기 8.마지막으로 (간단한) 문서화!
  • 49. UI 변경하기 ├── example # Project Directory │   ├── assets │   │   └── admin │   │   ├── css │   │   │   ├── custom.css # 전체 레이아웃을 수정하는 CSS │   │   │   ├── dropdown.css # 상단 메뉴바에 드롭다운 메뉴를 적용하기 위한 CSS • https://github.com/bbayoung/django-admin-site-custom-example/blob/master/example/assets/admin/css/custom.css • https://github.com/bbayoung/django-admin-site-custom-example/blob/master/example/assets/admin/css/dropdown.css # example/settings.py . . . STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'assets'), ) . . .
  • 50. • https://github.com/django/django/ UI 변경하기 ├── admin │   ├── templates │   │   ├── admin │   │   │   ├── 404.html │   │   │   ├── 500.html │   │   │   ├── actions.html │   │   │   ├── app_index.html │   │   │   ├── auth │   │   │   ├── base.html │   │   │   ├── base_site.html │   │   │   ├── change_form.html │   │   │   └── index.html base.html base_site.html app_index.htmlindex.html login.html
  • 51. # templates/admin/base_site.html {% extends "admin/base.html" %} {% load static %} {% block title %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %} {% block extrastyle %} {% endblock %} {% block branding %} <h1 id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('Django administration') }}</a></h1> {% endblock %} {% block nav-global %}{% endblock %} UI 변경하기
  • 52. # templates/admin/base_site.html {% extends "admin/base.html" %} {% load static %} {% block title %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %} {% block extrastyle %} <link rel="stylesheet" type="text/css" href="{% static "admin/css/dropdown.css" %}" /> <link rel="stylesheet" type="text/css" href="{% static "admin/css/custom.css" %}" /> {% endblock %} {% block branding %} <h1 id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('Django administration') }}</a></h1> {% endblock %} {% block nav-global %}{% endblock %} UI 변경하기 # example/settings.py admin.site.site_title = '파이콘 한국 2017’ # 브라우저 타이틀 admin.site.site_header = 'Back to the basic’ # 웹사이트 header 부분 타이틀
  • 55. # example/context_processors.py def gnb_menus(request): menus = [ { 'name': '회원', 'sub_menus': [ {'name': '관리자', 'url': '/admin/member/member/?permission__exact=AD'}, {'name': '에디터', 'url': '/admin/member/member/?permission__exact=ET'}, {'name': '일반', 'url': '/admin/member/member/?permission__exact=MB'}, ] }, { 'name': ' 글 ', 'sub_menus': [ {'name': 'GENDER', 'url': '/admin/post/post/?category__name=GENDER'}, {'name': 'SOCIAL', 'url': '/admin/post/post/?category__name=SOCIAL'}, {'name': 'POLITICS', 'url': '/admin/post/post/?category__name=POLITICS'}, {'name': '통계', 'url': '/admin/post/post/status/'}, ] } ] return {'gnb_menus': menus} UI 변경하기
  • 56. # example/settings.py TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(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', ‘example.context_processors.gnb_apps', # 장고에 추가한 기본 앱 메뉴 ‘example.context_processors.gnb_menus', # 이전 페이지에서 직접 정의한 상단 메뉴 ], }, }, ] UI 변경하기
  • 57. # templates/admin/base_site.html {% block nav-global %} <div id="gnb"> <div id="gnb-app-list"> <ul class="drop-down-menu"> {% for menu in gnb_menus %} // 커스텀 메뉴 출력 - - - - - - - - - - - - - (1) {% endfor %} {% if gnb_apps %} // Django 전체 모델 출력 - - - - - - - - - (2) {% endif %} </ul> </div> </div> {% endblock %} UI 변경하기
  • 58. # templates/admin/base_site.html . . . (1) {% for menu in gnb_menus %} <li> <a {% if menu.url %}href="{{ menu.url }}"{% endif %}>{{ menu.name }}</a> <ul> {% for sub_menu in menu.sub_menus %} <li><a href="{{ sub_menu.url }}">{{ sub_menu.name }}</a></li> {% endfor %} </ul> </li> {% endfor %} . . . UI 변경하기
  • 59. # templates/admin/base_site.html . . . (2) {% if gnb_apps %} <li><a>전체 앱</a> <ul> {% for app in gnb_apps %} <li><a href="/admin/{{ app.app_url }}">{{ app.name }}</a> <ul> {% for model in app.models %} <li><a href="{{ model.admin_url }}">{{ model.name }}</a></li> {% endfor %} </ul> </li> {% endfor %} </ul> </li> {% endif %} . . . UI 변경하기
  • 61. 순서 1.Django 설치 2.Model을 admin site에 등록하기 3.기본적인 사용법 4.조금 더 심화된 사용법 5.커스텀 페이지 추가 6.UI 변경하기 7.Admin site 분리하기 8.마지막으로 (간단한) 문서화!
  • 62. Django admin site 분리하기 # post/admin.py from django.contrib.admin import AdminSite class CommentAdminSite(AdminSite): site_header = 'Comment administration' comment_admin = CommentAdminSite(name='comment admin') comment_admin.register(Comment, CommentAdmin)
  • 63. Django admin site 분리하기 # post/admin.py from django.contrib.admin import AdminSite class CommentAdminSite(AdminSite): site_header = 'Comment administration' comment_admin = CommentAdminSite(name='comment admin') comment_admin.register(Comment, CommentAdmin) # example/urls.py urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^admin/comment/', comment_admin.urls), ]
  • 64. 순서 1.Django 설치 2.Model을 admin site에 등록하기 3.기본적인 사용법 4.조금 더 심화된 사용법 5.커스텀 페이지 추가 6.UI 변경하기 7.Admin site 분리하기 8.마지막으로 (간단한) 문서화!
  • 65. 문서화 (envExample) example $ pip install docutils # docutils 설치 # example/urls.py urlpatterns = [ . . . url(r'^admin/doc/', include('django.contrib.admindocs.urls')), . . . ] # example/settings.py INSTALLED_APPS = [ . . . 'django.contrib.admindocs', . . . ]
  • 66. 문서화 # post/models.py class Comment(models.Model): """ 사용들이 작성한 글에 대한 댓글입니다. 댓글은 :model:`post.Post` 와 :model:`member.Member`. 모델과 1:N 관계입니다. """ member = models.ForeignKey(Member, verbose_name='작성자') post = models.ForeignKey(Post, verbose_name='원본글') content = models.TextField(verbose_name='내용', help_text='댓글 내용입니다.')
  • 67. • 시간이 길지 않아, 준비한 내용은 여기까지입니다. • 이 외에도 공식 문서에 추가적인 커스텀 방법들이 소개되어 있습니다. • 예제 코드는 아래에서 확인하실 수 있습니다.
 https://github.com/bbayoung/django-admin-site-custom-example 감사합니다.