自定義 Django admin
核心觀點
- UI是很重要的。
- 考慮初學者,DashBoard。
- 導航。
- 使用者工作流。
- Django admin 缺少 WYSIWYG,檔案管理,undo。
建議
- 了解使用者的行業和工作流。
- 了解客戶團隊中的各個不同角色。
- 使用客戶習語。
- 了解之前使用的工具,及其優缺點。
實作
ModelAdmin Media
class ArticleAdmin(admin.ModelAdmin):
class Media:
css = {
"all": ("my_styles.css",)
}
js = ("my_code.js",)
優點:對于“一次性”項目簡單。
缺點:隻對Change Form有效。
Custom Templates
關鍵模闆
admin/base.html
admin/index.html
admin/change_form.html
admin/change_list.html
app_index.html
delete_confirmation.html
object_history.html
覆寫範圍
Across an entire project
admin/change_form.html
Across an application
admin/<my_app>/change_form.html
For an individual model
admin/<my_app>/<my_model>/change_form.html
例如:
{% extends "admin/change_list.html" %}
{% block object-tools %}
<h1 class="errornote">
Look Here!
</h1>
{{ block.super }}
{% endblock %}
自定義模闆的建議:
Extend, don't override
Use {{ block.super }} to extend blocks
Extend a symlink of the admin templates in the event of recursion
Extend the extrahead block in base.html for admin-wide media
已有項目:
http://code.google.com/p/sorl-curator/
http://code.google.com/p/django-grappelli/
ModelAdmin/ModelForm Hacking
注冊ModelAdmin
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from demo_app.models import UserProfile
class UserProfileInline(admin.TabularInline):
model = UserProfile
fk_name = 'user'
max_num = 1
class CustomUserAdmin(UserAdmin):
inlines = [UserProfileInline, ]
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
行級别的權限
class ArticleAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form,
change):
obj.user = request.user
obj.save()
def queryset(self, request):
qs = self.model._default_manager.filter(user=request.user)
return qs
ModelForm
class AuthorForm(forms.ModelForm):
exclude_states = ['AS', 'GU', 'MP', 'VI',]
def __init__(self, *args, **kwargs):
super(AuthorForm, self).__init__(*args,
**kwargs)
w = self.fields['state'].widget
choices = []
for key, value in w.choices:
if key not in self.exclude_states:
choices.append((key, value))
w.choices = choices
class AuthorAdmin(admin.ModelAdmin):
form = AuthorForm
Custom Views
Custom View URL
class PostAdmin(admin.ModelAdmin):
def my_view(self, request):
return admin_my_view(request, self)
def get_urls(self):
urls = super(PostAdmin, self).get_urls()
my_urls = patterns('',
(r'^my_view/$', self.my_view)
)
return my_urls + urls
Custom View
@permission_required('blog.add_post')
def admin_my_view(request, model_admin):
opts = model_admin.model._meta
admin_site = model_admin.admin_site
has_perm = request.user.has_perm(opts.app_label /
+ '.' + opts.get_change_permission())
context = {'admin_site': admin_site.name,
'title': "My Custom View",
'opts': opts,
'root_path': '/%s' % admin_site.root_path,
'app_label': opts.app_label,
'has_change_permission': has_perm}
template = 'admin/demo_app/admin_my_view.html'
return render_to_response(template, context,
context_instance=RequestContext(request))
Custom View Template
{% extends "admin/base_site.html" %}
{% load i18n %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="../../../" target="_blank" rel="external nofollow" >{% trans "Home" %}</a> ›
<a href="../../" target="_blank" rel="external nofollow" >{{ app_label|capfirst|
escape }}</a> ›
{% if has_change_permission %}<a
href="../" target="_blank" rel="external nofollow" >{{ opts.verbose_name_plural|
capfirst }}</a>{% else %}{{ opts.verbose_name_plural|
capfirst }}{% endif %} › My Custom View
</div>
{% endblock %}
{% block content %}
<!-- do stuff here -->
{% endblock %}
Django的内置模闆
base.html
<head>
<title> {% block title %}{% endblock %} </title>
{% block extrastyle %}{% endblock %}
{% block extrahead %}{% endblock %}
</head>
{% load i18n %}
<body class="{% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}">
<!-- Container -->
<div id="container">
<div id="header">
<div id="branding">
{% block branding %}{% endblock %}
</div>
{% block nav-global %}{% endblock %}
</div>
<!-- END Header -->
{% block breadcrumbs %}<div class="breadcrumbs"><a href="/" target="_blank" rel="external nofollow" >{% trans 'Home' %}</a>{% if title %} › {{ title }}{% endif %}</div>{% endblock %}
<!-- Content -->
<div id="content" class="{% block coltype %}colM{% endblock %}">
{% block pretitle %}{% endblock %}
{% block content_title %}{% if title %}<h1>{{ title }}</h1>{% endif %}{% endblock %}
{% block content %}
{% block object-tools %}{% endblock %}
{{ content }}
{% endblock %}
{% block sidebar %}{% endblock %}
<br class="clear" />
</div>
<!-- END Content -->
{% block footer %}<div id="footer"></div>{% endblock %}
</div>
<!-- END Container -->
</body>
base_site.html
{% extends "admin/base.html" %}
{% load i18n %}
{% block title %}{{ title }} | {% trans 'Django site admin' %}{% endblock %}
{% block branding %}
<h1 id="site-name">
{% trans 'Customized Admin' %}
</h1>
{% endblock %}
{% block dashboard %}
{% endblock %}
{% block nav-global %}{% endblock %}
已有項目的調研
grappelli 用起來挺不錯的。尤其是那個bookmarks很有用。至于navigation,感覺應該添加到每一個app的首頁。