DjangoAdmin本身就是一套大而全的系統,官方檔案中介紹了很多設定方法,但仍然有大量的騷操作是檔案中沒有的,所以遇到特殊需求的時候,求助檔案不一定有用。
在我看來 DjangoAdmin 雖然能快速生成一套管理後臺,但如果要做大量特殊需求的客製化,其成本不亞於用 Vue/React 重新開發一套,簡單的使用成本不高,但深入客製化的話需要對 DjangoAdmin 的工作流程比較熟悉,把原始碼啃熟了(有些 Python 原始碼沒有型別註解是很難讀懂的),才能在原有基礎上雕花,有時候還存在著後續維護的問題。
不過還是瑕不掩瑜了,誰能拒絕設定了幾行程式碼就可以用的管理後臺呢?
而且還不需要你做出一套 RESTFul API 來實現前後端分離,直接一把梭,起飛~
GitHub 上有很多 DjangoAdmin 的替換主題,所以不要抱怨 DjangoAdmin 的介面醜啦,好看的主題很多!
我最先使用的是 adminx,但這個侵入性太強了,需要對 admin 的設定程式碼做大量修改,實在是不划算,可能官方也意識到這個問題,後續應該是停更了。
到了 Django 2.x 時代後面,admin 的介面主題多了起來,有個國產的 SimpleUI 很不錯,基於 Vue + ElementUI 實現的,star很多,算是比較成熟的一類,我願稱之為國產之光。
其他的我也大多有測試,但用起來總有一些相容的問題,所以目前還是比較推薦國產之光。
已經在多個產品中使用,使用 vue+elementUI(非單頁應用),支援多分頁
地址:https://github.com/farridav/django-jazzmin
這個是偶然發現的,對於看膩了 ElementUI 的人來說,會有眼前一亮的感覺
使用 Bootstrap+AdminLTE 重寫,效果還不錯
(就是偶爾會莫名卡死
而且細節方面做得不如 SimpleUI,比如搜尋方塊沒有 placeholder 的提示之類的。
本文只記錄特殊需求的實現,對於 DjangoAdmin 的常規設定就不復制貼上了,網上隨便一搜都有很多。
我之前已經寫過不少 DjangoAdmin 的客製化案例文章了,最近也做了不少客製化,不過我不想寫新文章來單獨記錄某個需求的實現過程了,直接在本文裡記錄,同時保持本文更新~
本案例基於 SimpleUI
這裡使用的是 ElementUI 的 Tag 元件,檔案:https://element.eleme.cn/
前面提到過 SimpleUI 不是單頁應用,是直接在網頁上使用 vue 和 elementUI,並沒有webpack環境
所以要加入 elementUI 的元件不能直接簡單的
<el-tag type="success">標籤</el-tag>
而是要用 webpack 生成出來的
<div class="el-tag el-tag--success el-tag--light">標籤</div>
ok,開始上Python程式碼
假設有個 model 叫 Invoice
,中文名發票,定義如下
class Invoice(models.Model):
invoice_type = models.CharField('發票型別')
在需要自定義的 ModelAdmin 中,增加一個方法
# 發票型別顏色
@admin.display(description='發票類別')
def invoice_type_tag(self, obj: Invoice):
def el_tag(color_type, content):
"""
生成 ElementUI 的 tag 元件
:param color_type: success, info, warning, danger
:param content:
:return:
"""
from django.utils.safestring import mark_safe
type_class = '' if len(color_type) == 0 else f'el-tag--{color_type}'
return mark_safe(f'<div class="el-tag el-tag--small {type_class} el-tag--light">{content}</div>')
if obj.invoice_type.startswith('普通'):
return el_tag('', obj.invoice_type)
if obj.invoice_type.startswith('專用'):
return el_tag('danger', obj.invoice_type)
if obj.invoice_type.startswith('電子專票'):
return el_tag('info', obj.invoice_type)
if obj.invoice_type.startswith('電子普票'):
return el_tag('warning', obj.invoice_type)
然後把這個 invoice_type_tag
加到 list_display
中即可
PS:這裡的 @admin.display()
裝飾器是Django3.2版本之後新增的,很方便,相當於以前的
invoice_type_tag.short_description = '發票類別'
PS:注意HTML程式碼需要用 mark_safe
方法包裝起來,才能正常渲染,不然會被跳脫!
原理同上面的新增自定義列
程式碼如下
# 進度條
@admin.display(description='進度條')
def progress_bar(self, obj):
html = f'''
<div role="progressbar" aria-valuenow="{obj.progress}" aria-valuemin="0" aria-valuemax="100"
class="el-progress el-progress--line is-light el-progress--text-inside">
<div class="el-progress-bar">
<div class="el-progress-bar__outer" style="height: 22px;">
<div class="el-progress-bar__inner" style="width: {obj.progress}%;">
<div class="el-progress-bar__innerText">{obj.progress}%</div>
</div>
</div>
</div>
</div>
'''
from django.utils.safestring import mark_safe
return mark_safe(html)
這個功能比較麻煩,因為需要魔改 template
首先我們要知道,這個列表對應的是哪個 template,在 admin 包的 templates 目錄下面的找了半天,最終發現這個頁面是 change_list
,而且因為頁面比較複雜,被分成了好幾部分
我們只需要修改 change_list.html
這個檔案就行了。
OK,模板程式碼先不管,我們來寫Python程式碼計算總金額。
要實現將資料放在 context
裡傳給 template,得重寫個 ChangeList
物件
from django.db.models import Sum
from django.contrib.admin.views.main import ChangeList
class InvoiceChangeList(ChangeList):
def get_results(self, request):
super(InvoiceChangeList, self).get_results(request)
totals = self.result_list.aggregate(Sum('amount'))
self.total_amount = totals['amount__sum']
使用 Sum
這個聚合方法,計算總金額。
通過Python語言的動態特性,加 total_amount
這個屬性新增到 ChangeList
物件中
這樣在 template 裡就能通過 {{ cl.total_amount }}
的方式拿到這個屬性。
然後改一下 ModelAdmin :
class InvoiceAdmin(ImportExportModelAdmin):
# 如果你改了 template 的名稱,這裡可以對應修改,否則預設即可
change_list_template = 'change_list.html'
# 新增這個程式碼
def get_changelist(self, request, **kwargs):
return InvoiceChangeList
後端部分搞定了,接下來是前端的模板部分。
為了在頁面上新增新元素,我們來修改 change_list.html
檔案。
注意,不要直接複製這個檔案來修改!原因是你修改完的 template 會覆蓋其他元件,這樣以後換 admin 主題,或者使用 import-export 這類會修改 admin 頁面的外掛時無法生效,也就是所謂的相容問題。
Django也想到了這種情況,這些 template 都是元件化的,我們寫一個擴充套件 template 就可以了。
在專案的 templates/admin
目錄下新建 change_list.html
檔案,程式碼如下
{% extends "admin/import_export/change_list.html" %}
{% block result_list %}
{{ block.super }}
<div style="text-align: right; margin: 20px 5px; font-size: 20px;">
總金額:{{ cl.total_amount }} 元
</div>
{% endblock %}
注意:如果用了 django-import-export 外掛,則要根據使用到的功能來新增 object-tools-items
block。
比如你的 ModelAdmin 繼承自 ImportExportModelAdmin
,那我們轉到原始碼,可以看到它重寫了 template
class ImportExportMixin(ImportMixin, ExportMixin):
"""
Import and export mixin.
"""
#: template for change_list view
change_list_template = 'admin/import_export/change_list_import_export.html'
class ImportExportModelAdmin(ImportExportMixin, admin.ModelAdmin):
"""
Subclass of ModelAdmin with import/export functionality.
"""
然後再看看 admin/import_export/change_list_import_export.html
這個檔案
{% extends "admin/import_export/change_list.html" %}
{% block object-tools-items %}
{% include "admin/import_export/change_list_import_item.html" %}
{% include "admin/import_export/change_list_export_item.html" %}
{{ block.super }}
{% endblock %}
可以看到它在 object-tools-items
中新增了倆元件,把這一塊 block
的程式碼複製到我們的 change_list.html
中即可。
這部分記錄我在逛GitHub時發現的比較有意思的擴充套件庫,記錄一下
地址:https://github.com/jsocol/django-adminplus
可以方便的給admin增加新頁面
地址:https://github.com/saxix/django-adminactions
可以給admin新增一系列的actions