Django 非常好用的一個(gè)功能就是后臺(tái)界面的自動(dòng)管理。
from django.contrib import admin
from django.utils.safestring import mark_safe
class xxxConfig(admin.ModelAdmin):
def deletes(self):
return mark_safe('<a href=''>刪除</a>')
list_display = [, deletes] # 里面的字段不能是 ManyToMany 字段
list_filter = ()
list_display_links = ()
search_fields = ()
def patch_init(self, request, queryset):
queryset.update(xxx=xxx)
patch_init.short_description = "批量處理"
actions = [patch_init, ]
所有字段都在 admin.ModelAdmin 中可見
admin.site.register(模型, '你自定義的類')
Admin 的實(shí)現(xiàn)流程:?jiǎn)?dòng),注冊(cè),設(shè)計(jì)url
- 啟動(dòng)
在INSTALLED_APPS中的注冊(cè) app 中,掃描admin文件,并執(zhí)行
def autodiscover():
autodiscover_modules('admin', register_to=site)
- 注冊(cè),在每個(gè)安裝的 app 下面的
admin文件中注冊(cè)site,即admin.site.register(模型, '你自定義的類') - 設(shè)計(jì) url
在項(xiàng)目的根目錄下url文件中會(huì)有path('admin/', admin.site.urls),這么一行注冊(cè) url 的函數(shù)
綜上,其實(shí)這是很明顯的一個(gè)單例模式,因?yàn)楹髢刹街卸加玫搅艘粋€(gè)特殊的對(duì)象admin.site,admin是從django.contrib中導(dǎo)入的,而admin中又導(dǎo)入了site:from django.contrib.admin.sites import AdminSite, site,之后進(jìn)入到site中,發(fā)現(xiàn)了也就這么一個(gè)東西:
class AdminSite:
pass
site = AdminSite()
這其實(shí)用到了 Python 中對(duì)于單例模式的一個(gè)經(jīng)典的用法,模塊就是一個(gè)特殊的單例,因?yàn)槟K無論聲明多少次,最終導(dǎo)入到 Python 解釋器中只會(huì)有一次,就是剛開始那次。
接下來我們看一看源碼做了什么?
# admin 的 __init__.py
from django.contrib.admin.sites import AdminSite, site
def autodiscover():
autodiscover_modules('admin', register_to=site)
這里隨即進(jìn)入 AdminSite:
# sites.py
pass
class AdminSite:
def __init__(self, name='admin'):
self._registry = {} # model_class class -> admin_class instance
self.name = name
pass
def register(self, model_or_iterable, admin_class=None, **options):
if not admin_class:
admin_class = ModelAdmin
pass
site = AdminSite()
注意這里的 site = AdminSite(),在 Django 的其它地方導(dǎo)入它以后,它就是一個(gè)單例了,它的 id 就不變了。在 AdminSite 中定義了一個(gè)字典 _registry,里面存放的是注冊(cè)的 model 和 其配置類,配置類默認(rèn)是 ModelAdmin,也就是 Django 自己的那個(gè)后臺(tái)。這里完成了 url 的注冊(cè)。
接下來看一看它里面是如何構(gòu)造 url 的。還記得在項(xiàng)目根目錄下的 url(r'admin', admin.site.urls),嗎,進(jìn)入它:
# 跟上面的一樣是 sites.py
def get_urls(self):
pass
for model, model_admin in self._registry.items():
urlpatterns += [
path('%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls)),
]
if model._meta.app_label not in valid_app_labels:
valid_app_labels.append(model._meta.app_label)
return urlpatterns
@property
def urls(self):
return self.get_urls(), 'admin', self.name
這里面 include(model_admin.urls) 及其重要,Django 默認(rèn)將自己的配置類注冊(cè)到 _register 了,它是 ModelAdmin 的 urls:
# 注:這是 Django 2 的有些部分可能不一樣,不過不影響
def get_urls(self):
pass
info = self.model._meta.app_label, self.model._meta.model_name
urlpatterns = [
path('', wrap(self.changelist_view), name='%s_%s_changelist' % info),
path('add/', wrap(self.add_view), name='%s_%s_add' % info),
path('autocomplete/', wrap(self.autocomplete_view), name='%s_%s_autocomplete' % info),
path('<path:object_id>/history/', wrap(self.history_view), name='%s_%s_history' % info),
path('<path:object_id>/delete/', wrap(self.delete_view), name='%s_%s_delete' % info),
path('<path:object_id>/change/', wrap(self.change_view), name='%s_%s_change' % info),
# For backwards compatibility (was the change url before 1.9)
path('<path:object_id>/', wrap(RedirectView.as_view(
pattern_name='%s:%s_%s_change' % ((self.admin_site.name,) + info)
))),
]
return urlpatterns
@property
def urls(self):
return self.get_urls()
這里重要的是 Django 對(duì) url 做了兩級(jí)分發(fā),而且這兩級(jí) url 不在同一個(gè)類中,這樣做的目的就是為了方便構(gòu)造模板。最后那一級(jí) url 放在了 ModelAdmin 中,就是增刪改查那四個(gè)功能。
說明:這里二級(jí)分發(fā) url 主要是為了方便后面模板構(gòu)造數(shù)據(jù),因?yàn)闃?gòu)造數(shù)據(jù)要訪問 model,如果兩級(jí) url 全部放在一個(gè)類中,那么需要找到 model 便需要根據(jù) url 來匹配,但是將二級(jí) url 放在默認(rèn)的類中,那么它便可以找到對(duì)應(yīng)的 model 了,因?yàn)?_register 中存放的是 model 和 配置類形成的字典。而默認(rèn)配置類里面會(huì)注冊(cè)進(jìn)來 對(duì)應(yīng)的 model,所以在配置類中可以找到 model。
先暫時(shí)到這,可能有諸多不清楚的,后面再補(bǔ)充。
順便說一句,AdminSite 的源碼連注釋,加空行總共 500 行代碼,有空了解一下。??
Django 是如何找到每個(gè) app 下的
admin.py文件呢,在每個(gè) app 下都有一個(gè)配置類,如 admin 的AdminConfig,它里面會(huì)定義一個(gè)ready方法,django 在加載INSTALLED_APPS時(shí),會(huì)調(diào)用里面的方法。
class AdminConfig(SimpleAdminConfig):
def ready(self):
super().ready()
self.module.autodiscover()
在我們自己構(gòu)建組件時(shí),可以這樣寫:
from django.apps improt AppConfig
from django.uitls.moudle_loading improt autodiscover_moudles
class xxxConfig(AppConfig):
name = xxx
def ready(self):
autodiscover_moudles('里面放需要加載文件的名稱')
設(shè)計(jì) url
首先來了解一下 url 注冊(cè)的另一種方式:
def test01(request):
return HttpResponse('test01')
def test02(request):
pass
def test03(request):
pass
urlpatterns = [
url(r'^test/', ([
url(r't1/', test01),
url(r't2/', test02),
url(r't3/', test03),
], None, None))
]
這種相當(dāng)于是將 include 中的 url 放到當(dāng)前 url 中了。
構(gòu)造 url:
get_urls2():
temp = []
temp.append(url(r'^$'), view_name)
temp.append(url(r'^add$'), view_name)
temp.append(url(r'^(\d+)/change/$'), view_name)
temp.append(url(r'^(\d+)/delete/$'), view_name)
return temp
def get_urls():
res = []
for model, admin_class_obj in admin.site_register.items():
app_name = model._meta.app_label
model_name = model._meta.model_name
temp.append(url(r'{0}/{1}'.format(app_name, model_name), (get_urls2(), None, None)), )
return res
urlpatterns = [
url(r'^xadmin/', (get_urls(), None, None)),
]
以上有幾個(gè)函數(shù)很有趣:
model._meta.app_label --> 獲取模型所屬 app 的名稱字符串
model._meta.model_name --> 獲取模型名稱字符串
obj = model._meta.get_field("field") --> 獲取模型內(nèi)某個(gè)屬性,別忘記,其實(shí)模型內(nèi)的屬性也是一個(gè)類
所以:obj.verbose_name 即可取到其對(duì)應(yīng)的屬性。