客製化 Django Admin Layout

最近收到了一些 Request 要把之前用 Django Admin 搞的後台做一些 improve

雖然用了 Django 一陣子可是覺得自己還是只懂皮毛而已,還有蠻多奇奇怪怪的功能都沒用過

之前就聽說 Admin 的 layout 除了常見的那些欄位跟 filter 顯示,

其他都可以客製化,可是改起來頗麻煩,剛好趁這次機會紀錄一下改法。

其實官網文件都有寫,不過我還是先從思考方向先記錄起。

從何下手

首先我從 source code 下手想看看 Admin 裡面到底是怎麼把各 page render 出來,

到底哪些東西是可以調哪些不能調,個人覺得這招搭配看文件很有用。

所以就直接殺到的 django/contrib/admin/templates/

哦!沒錯就是這邊!

Admin 的各種 template 全都放在這,看來就是從這下手了!

以我這次要改的部分叫做 submit_line.html 為例,

我想透過參數去控制 submit row 在某些 model 下會出現新的客製 action。

所以我就看到 django/contrib/admin/templatetags/admin_modify.py

發現裡面有一個 function 是來控制這件事,

先附上 code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@register.inclusion_tag('admin/submit_line.html', takes_context=True)
def submit_row(context):
"""
Displays the row of buttons for delete and save.
"""
opts = context['opts']
change = context['change']
is_popup = context['is_popup']
save_as = context['save_as']
show_save = context.get('show_save', True)
show_save_and_continue = context.get('show_save_and_continue', True)
ctx = {
'opts': opts,
'show_delete_link': (
not is_popup and context['has_delete_permission'] and
change and context.get('show_delete', True)
),
'show_save_as_new': not is_popup and change and save_as,
'show_save_and_add_another': (
context['has_add_permission'] and not is_popup and
(not save_as or context['add'])
),
'show_save_and_continue': not is_popup and context['has_change_permission'] and show_save_and_continue,
'is_popup': is_popup,
'show_save': show_save,
'preserved_filters': context.get('preserved_filters'),
}
if context.get('original') is not None:
ctx['original'] = context['original']
return ctx

看到這邊就得到我的結論:Submit row 的按鈕都可以透過 admin.py 的參數設定去做開關,但是只限於原本就有的那些 action,如果你要新增 action button 或是某些特殊邏輯才 show 的話還是要透過 overwrite 的方式去做才可以!

Overwrite admin template

從剛剛的 source code 看到我要做兩件事,自訂 template 跟 自訂 tag

自訂 template

官網文件 The Django admin site

開一個資料夾 MYAPP/templates/admin 把要 overwrite 的 template 丟進去

change_form.html

1
2
3
4
5
{% extends "admin/change_form.html" %}
{% load custom_submit_line %}
{% block submit_buttons_bottom %}{% custom_submit_line %}{% endblock %}

這邊可以發現我定義了一個 tag 叫做 custom_submit_line

並且把原本在 admin/change_form.html 內的

1
{% block submit_buttons_bottom %}{% submit_row %}{% endblock %}

submit_row overwrite 成 custom_submit_line

custom_submit_line.html

1
2
3
4
5
6
7
8
9
10
11
12
{% load i18n admin_urls %}
<div class="submit-row">
{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save" />{% endif %}
{% if show_delete_link %}
{% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %}
<p class="deletelink-box"><a href="{% add_preserved_filters delete_url %}" class="deletelink">{% trans "Delete" %}</a></p>
{% endif %}
{% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew" />{% endif %}
{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" />{% endif %}
{% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue" />{% endif %}
{% if show_something %}<input type="submit" value="{% trans 'Aloha!' %}" name="_custom" />{% endif %}
</div>

最後別忘了在 settings.py 內設定一下 TEMPLATES 的 DIR 參數

不然會噴 not found 的錯誤!

自訂 tag

官網文件 Custom template tags and filters

開一個資料夾 MYAPP/APP_NAME/templatetags 把要 overwrite 的 tag 丟進去

custom_submit_line.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import os
from django import template
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
SUBMIT_LINE_HTML = os.path.normpath(
os.path.join(CURRENT_PATH, '../../templates/admin/custom_submit_line.html')
)
register = template.Library()
@register.inclusion_tag(SUBMIT_LINE_HTML, takes_context=True)
def custom_submit_line(context):
"""
Displays the row of buttons for delete and save.
"""
opts = context['opts']
change = context['change']
is_popup = context['is_popup']
save_as = context['save_as']
show_save = context.get('show_save', True)
show_save_and_continue = context.get('show_save_and_continue', True)
ctx = {
'opts': opts,
'show_delete_link': (
not is_popup and context['has_delete_permission'] and
change and context.get('show_delete', True)
),
'show_save_as_new': not is_popup and change and save_as,
'show_save_and_add_another': (
context['has_add_permission'] and not is_popup and
(not save_as or context['add'])
),
'show_save_and_continue': not is_popup and context['has_change_permission'] and show_save_and_continue,
'is_popup': is_popup,
'show_save': show_save,
'preserved_filters': context.get('preserved_filters'),
}
if context.get('original') is not None:
ctx['original'] = context['original']
if opts.model_name == 'card':
ctx['show_something'] = True
return ctx

跟原本的長很像我只有在最後做了一些邏輯,

然後把 template 換成 custom_submit_line.html 如此而已!

這樣就完成了客製化工作啦~

後記

Trace 了 source code 之後發現幾乎每一個頁面都可以客製化,真心覺得 django admin 很方便,

幫我處理掉了很多冗事,但是它裡面的邏輯很多都設計好了,要改的話要小心把預設的邏輯改爆。

如果真的要改很大的話,也許自己寫會方便許多,按照專案需求決定會是比較好的選擇!

Reference

  1. 某個 Github Repo