SlideShare uma empresa Scribd logo
1 de 67
Baixar para ler 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
감사합니다.

Mais conteúdo relacionado

Mais procurados

Celery의 빛과 그림자
Celery의 빛과 그림자Celery의 빛과 그림자
Celery의 빛과 그림자Minyoung Jeong
 
스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해beom kyun choi
 
[수정본] 우아한 객체지향
[수정본] 우아한 객체지향[수정본] 우아한 객체지향
[수정본] 우아한 객체지향Young-Ho Cho
 
DDD로 복잡함 다루기
DDD로 복잡함 다루기DDD로 복잡함 다루기
DDD로 복잡함 다루기beom kyun choi
 
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축Youngil Cho
 
Backup, Restore, and Disaster Recovery
Backup, Restore, and Disaster RecoveryBackup, Restore, and Disaster Recovery
Backup, Restore, and Disaster RecoveryMongoDB
 
Django Forms: Best Practices, Tips, Tricks
Django Forms: Best Practices, Tips, TricksDjango Forms: Best Practices, Tips, Tricks
Django Forms: Best Practices, Tips, TricksShawn Rider
 
도메인 주도 설계의 본질
도메인 주도 설계의 본질도메인 주도 설계의 본질
도메인 주도 설계의 본질Young-Ho Cho
 
Mongo DB 성능최적화 전략
Mongo DB 성능최적화 전략Mongo DB 성능최적화 전략
Mongo DB 성능최적화 전략Jin wook
 
DDD 구현기초 (거의 Final 버전)
DDD 구현기초 (거의 Final 버전)DDD 구현기초 (거의 Final 버전)
DDD 구현기초 (거의 Final 버전)beom kyun choi
 
서비스중인 게임 DB 설계 (쿠키런 편)
서비스중인 게임 DB 설계 (쿠키런 편)서비스중인 게임 DB 설계 (쿠키런 편)
서비스중인 게임 DB 설계 (쿠키런 편)_ce
 
우아한 객체지향
우아한 객체지향우아한 객체지향
우아한 객체지향Young-Ho Cho
 
Django Introduction & Tutorial
Django Introduction & TutorialDjango Introduction & Tutorial
Django Introduction & Tutorial之宇 趙
 
우아한테크세미나-우아한멀티모듈
우아한테크세미나-우아한멀티모듈우아한테크세미나-우아한멀티모듈
우아한테크세미나-우아한멀티모듈용근 권
 
Django for Beginners
Django for BeginnersDjango for Beginners
Django for BeginnersJason Davies
 
Mongoose: MongoDB object modelling for Node.js
Mongoose: MongoDB object modelling for Node.jsMongoose: MongoDB object modelling for Node.js
Mongoose: MongoDB object modelling for Node.jsYuriy Bogomolov
 
도메인구현 KSUG 20151128
도메인구현 KSUG 20151128도메인구현 KSUG 20151128
도메인구현 KSUG 20151128beom kyun choi
 

Mais procurados (20)

Celery의 빛과 그림자
Celery의 빛과 그림자Celery의 빛과 그림자
Celery의 빛과 그림자
 
스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해
 
[수정본] 우아한 객체지향
[수정본] 우아한 객체지향[수정본] 우아한 객체지향
[수정본] 우아한 객체지향
 
DDD로 복잡함 다루기
DDD로 복잡함 다루기DDD로 복잡함 다루기
DDD로 복잡함 다루기
 
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
 
Backup, Restore, and Disaster Recovery
Backup, Restore, and Disaster RecoveryBackup, Restore, and Disaster Recovery
Backup, Restore, and Disaster Recovery
 
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 성능최적화 전략
 
DDD 구현기초 (거의 Final 버전)
DDD 구현기초 (거의 Final 버전)DDD 구현기초 (거의 Final 버전)
DDD 구현기초 (거의 Final 버전)
 
Node.js Express Framework
Node.js Express FrameworkNode.js Express Framework
Node.js Express Framework
 
서비스중인 게임 DB 설계 (쿠키런 편)
서비스중인 게임 DB 설계 (쿠키런 편)서비스중인 게임 DB 설계 (쿠키런 편)
서비스중인 게임 DB 설계 (쿠키런 편)
 
Express JS
Express JSExpress JS
Express JS
 
우아한 객체지향
우아한 객체지향우아한 객체지향
우아한 객체지향
 
Django Introduction & Tutorial
Django Introduction & TutorialDjango Introduction & Tutorial
Django Introduction & Tutorial
 
우아한테크세미나-우아한멀티모듈
우아한테크세미나-우아한멀티모듈우아한테크세미나-우아한멀티모듈
우아한테크세미나-우아한멀티모듈
 
JQuery introduction
JQuery introductionJQuery introduction
JQuery introduction
 
Django for Beginners
Django for BeginnersDjango for Beginners
Django for Beginners
 
Mongoose: MongoDB object modelling for Node.js
Mongoose: MongoDB object modelling for Node.jsMongoose: MongoDB object modelling for Node.js
Mongoose: MongoDB object modelling for Node.js
 
도메인구현 KSUG 20151128
도메인구현 KSUG 20151128도메인구현 KSUG 20151128
도메인구현 KSUG 20151128
 

Semelhante a 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승엽 신
 

Semelhante a 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 감사합니다.