Django中自定義admin Xadmin的實(shí)現(xiàn)代碼
在Django框架中,自帶一個(gè)后臺(tái)管理頁(yè)面admin,這個(gè)管理頁(yè)面很全,但是,有些并不是我們需要的,所以我們可以根據(jù)admin的實(shí)現(xiàn)流程來自定義自己的需求,即根據(jù)admin的實(shí)現(xiàn)方式來實(shí)現(xiàn)自定制--Xadmin
首先,我們先解析admin的流程,在Django中,我們?cè)趧?chuàng)建項(xiàng)目的時(shí)候,Django自帶一個(gè)admin的url,實(shí)現(xiàn)了不同模型表的增刪改查,那么admin是如何實(shí)現(xiàn)url的分發(fā)的?
我們可以從三部分來看admin的路由分發(fā)實(shí)現(xiàn)
1,啟動(dòng)
我們可以通過from django.contrib import admin
來看admin是如何啟動(dòng)的
Django啟動(dòng)后,會(huì)在manage.py文件中加載配置文件settings.py ,在settings.py中有一個(gè)INSTALLED_APPS這個(gè)配置項(xiàng),Django會(huì)按照配置項(xiàng)的內(nèi)容一次加載每一個(gè)app.
# Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'Xadmin.apps.XadminConfig', 'blogs.apps.BlogsConfig', 'bigs.apps.BigsConfig', ]
加載到django.contrib.admin
時(shí),會(huì)加載admin相關(guān)的,我們點(diǎn)擊admin,進(jìn)入admin的__init__.py文件,
from django.contrib.admin.sites import AdminSite, site from django.utils.module_loading import autodiscover_modules def autodiscover(): autodiscover_modules('admin', register_to=site)
執(zhí)行auto_discover這個(gè)接口,會(huì)自動(dòng)的加載所有APP中的admin.py
這個(gè)文件,這就是admin的啟動(dòng)文件
2,注冊(cè)
自動(dòng)加載所有APP下的admin.py文件時(shí),會(huì)一次記錄所有執(zhí)行了admin.site.register(模型類)這個(gè)方法的模型類,從而完成模型類的注冊(cè)。
所有的模型類都執(zhí)行admin.site.register()
這個(gè)方法后完成的注冊(cè),那么這個(gè)方法內(nèi)部都做了些什么?
我們可以點(diǎn)擊site進(jìn)入admin的源碼中,得到這個(gè):site = AdminSite() site是一個(gè)對(duì)象,是AdminSite這個(gè)類的一個(gè)對(duì)象,并且是一個(gè)單例對(duì)象,那么我們就可以肯定,register是這個(gè)單例對(duì)象的一個(gè)方法,那這個(gè)方法中到底做了什么?
class AdminSite(object): """ An AdminSite object encapsulates an instance of the Django admin application, ready to be hooked in to your URLconf. Models are registered with the AdminSite using the register() method, and the get_urls() method can then be used to access Django view functions that present a full admin interface for the collection of registered models. """ def __init__(self, name='admin'): self._registry = {} # model_class class -> admin_class instance self.name = name # 關(guān)于register的方法 def register(self, model_or_iterable, admin_class=None, **options): if not admin_class: admin_class = ModelAdmin # Instantiate the admin class to save in the registry self._registry[model] = admin_class(model, self) #備注:截取的源碼中的一部分
示例site對(duì)象時(shí),會(huì)生成一個(gè)self._registry={}
這個(gè)對(duì)象屬性,如果我們沒有指定admin_class,那么admin_class就是ModelAdmin,由此可以看出,admin_class
是admin提供給我們定制頁(yè)面的一個(gè)自定義類,這個(gè)類必須繼承ModelAdmin這個(gè)類,以下是:
class ModelAdmin(BaseModelAdmin): "Encapsulates all admin options and functionality for a given model." list_display = ('__str__',) list_display_links = () list_filter = () list_select_related = False list_per_page = 100 list_max_show_all = 200 list_editable = () search_fields = () date_hierarchy = None save_as = False save_as_continue = True save_on_top = False paginator = Paginator preserve_filters = True inlines = [] # Custom templates (designed to be over-ridden in subclasses) add_form_template = None change_form_template = None change_list_template = None delete_confirmation_template = None delete_selected_confirmation_template = None object_history_template = None popup_response_template = None # Actions actions = [] action_form = helpers.ActionForm actions_on_top = True actions_on_bottom = False actions_selection_counter = True checks_class = ModelAdminChecks def __init__(self, model, admin_site): self.model = model self.opts = model._meta self.admin_site = admin_site super(ModelAdmin, self).__init__() def __str__(self): return "%s.%s" % (self.model._meta.app_label, self.__class__.__name__)
所以不管有沒有自定制樣式類,都會(huì)執(zhí)行self._registry[model] = admin_class(model, self),
也就是說,注冊(cè)一個(gè)模型類,就會(huì)在對(duì)象的_registry這個(gè)字典中添加一個(gè)鍵值對(duì),這個(gè)鍵是我們注冊(cè)的這個(gè)模型類 model,值是繼承ModelAdmin的樣式類或者M(jìn)odelAdmin這個(gè)類的一個(gè)對(duì)象。
3,設(shè)計(jì)url
因?yàn)閟ite是一個(gè)單例對(duì)象,所以admin在執(zhí)行完所有admin.py文件后,就會(huì)得到整個(gè)全局的一個(gè)包含所有注冊(cè)模型的字典_registry,得到這個(gè)self._registry(也就是admin.site對(duì)象)字典,我們可以for循環(huán)這個(gè)admin.site._registry字典,得到這些鍵值對(duì),那么得到這些鍵值對(duì)有什么用呢?這就是admin設(shè)計(jì)url時(shí)會(huì)用到的。
我們?cè)L問admin這個(gè)后臺(tái)管理頁(yè)面會(huì)發(fā)現(xiàn),這個(gè)頁(yè)面關(guān)于我們注冊(cè)的所有模型都會(huì)實(shí)現(xiàn)增刪改查功能,每個(gè)功能界面對(duì)應(yīng)不同的模型類時(shí),除了數(shù)據(jù)不同外,是一樣的,也就是說,admin對(duì)于不同的模型類用到是一套u(yù)rl,一套模板,那么這個(gè)url是怎么設(shè)計(jì)的呢?
我們分別訪問不同的模型類的這四個(gè)功能頁(yè)面,會(huì)發(fā)現(xiàn)一個(gè)規(guī)律:
查詢頁(yè)面url: http://IP:PORT/admin/app名/模型類的名字(全部小寫)/
添加頁(yè)面url: http://IP:PORT/admin/app名/模型類的名字(全部小寫)/add
編輯頁(yè)面url:http://IP:PORT/admin/app名/模型類的名字(全部小寫)/id值/change
刪除頁(yè)面url:http://IP:PORT/admin/app名/模型類的名字(全部小寫)/id值/delete
通過這個(gè)規(guī)律可以看出url分發(fā)的實(shí)現(xiàn)
但是在這個(gè)實(shí)現(xiàn)的過程中,我們有一個(gè)需要注意的時(shí),用戶訪問的請(qǐng)求攜帶的路徑是一個(gè)字符串的類型,我們可以通過request.path得到用戶訪問的路徑,也就能得到用戶訪問的是哪個(gè)APP下的哪個(gè)模型類,但是這兩個(gè)參數(shù)都是字符串的類型,如何通過字符串得到用戶訪問的哪張表,這是個(gè)麻煩,這時(shí),可能我們會(huì)想到使用importlib模塊來得到這個(gè)類,但是,這樣這個(gè)過程很麻煩,Django為我們封裝好了相應(yīng)的方法:我們可以通過這個(gè)模型類的類名,通過._meta.model_name這個(gè)方法得到對(duì)應(yīng)的字符串形式的類名,同樣的我們也可以通過這個(gè)類名._meta.app_label得到字符串格式的相應(yīng)的APP名
# 使用model代指模型類的類名 model._meta.model_name #得到字符串格式的類名(全小寫的) model._meta.app_label #可以得到當(dāng)前類所在APP的字符串的名字 # 補(bǔ)充 model._meta.get_field("字符串格式的字段屬性") # 得到一個(gè)字段屬性的對(duì)象field_obj,這樣我們就可以利用這個(gè)字段對(duì)象取得屬性 # 比如: field_obj.verbose_name
通過這兩個(gè)封裝的方法,就完美的解決了我們頭疼的問題
from django.conf.urls import url from django.contrib import admin def get_urls_operate(): emp = [] emp.append(url(r'^$',查詢頁(yè)面的視圖函數(shù))) emp.append(url(r'^add/$',添加頁(yè)面的視圖函數(shù))) emp.append(url(r'^(\d+)/change/$',編輯頁(yè)面的視圖函數(shù))) emp.append(url(r'^(\d+)/delete/$',刪除頁(yè)面的視圖函數(shù))) return emp def get_urls(): temp = [] for model,main_class_obj in admin.site._registry.items(): app_name = model._meta.app_label model_name = model._meta.model_name temp.append(url(r'^{}/{}/'.format(app_name,model_name),(get_urls_operate(),None,None))) #實(shí)現(xiàn)第二層路由的分發(fā) return temp urlpatterns = [ url(r'^Xadmin/',(get_urls(),None,None)) #自定義一個(gè)前綴 實(shí)現(xiàn)第一層路由的分發(fā) ]
這樣我們就實(shí)現(xiàn)一個(gè)通過一個(gè)路由實(shí)現(xiàn)不同場(chǎng)景的分發(fā)
通過這三部分,我們可以按照admin的實(shí)現(xiàn)方式,來自定制Xadmin
我們了解了admin內(nèi)部的實(shí)現(xiàn)流程,我們可以將這個(gè)實(shí)現(xiàn)過程封裝到一個(gè)類中。
我們自定制Xadmin時(shí),也要按照admin的流程實(shí)現(xiàn),首先是啟動(dòng)項(xiàng),admin中,Django啟動(dòng)時(shí),會(huì)自動(dòng)的執(zhí)行每個(gè)app下的admin.py文件,我們可以自定制為啟動(dòng)時(shí),自動(dòng)執(zhí)行app下的每個(gè)自定制的.py文件,比如Xadmin.py 文件,那么如何達(dá)到Django啟動(dòng)的時(shí)候幫我們自動(dòng)掃描加載我們自定制的Xadmin.py文件呢?這是個(gè)問題
我們觀察可以發(fā)現(xiàn),Django在啟動(dòng)時(shí),會(huì)加載配置文件settings.py ,在settings.py文件中,有一個(gè)INSTALLED_APPS這個(gè)列表,這個(gè)列表中,放置著我們?cè)贒jango項(xiàng)目中的所有app的配置信息,我們觀察這個(gè)列表:我們自己開啟的APP,在設(shè)置配置信息時(shí),會(huì)在APP名字后加一個(gè).apps.(app名首字母大寫)Config這個(gè)東西,而對(duì)應(yīng)的在我們的app下,Django會(huì)給我們自動(dòng)的配置一個(gè)apps.py文件,那么這個(gè)apps.py文件有什么作用呢,我們打開這個(gè)apps.py文件,看看里面的配置信息:
以blogs這個(gè)APP為例:
from django.apps import AppConfig class BlogsConfig(AppConfig): name = 'blogs'
我們可以發(fā)現(xiàn),里面定義了一個(gè)類,這個(gè)類的類名就是settings配置信息中apps后面跟的那個(gè)東東,這個(gè)類中有一個(gè)靜態(tài)屬性name是當(dāng)前的APP名,這個(gè)類繼承了AppConfig這個(gè)類。這就是我們從這個(gè).py文件中所能得的所有東西,乍一看,沒有什么有用的信息,那么我們只能從它繼承的類中找了
MODELS_MODULE_NAME = 'models' class AppConfig(object): """ Class representing a Django application and its configuration. """ def __init__(self, app_name, app_module): # Full Python path to the application eg. 'django.contrib.admin'. self.name = app_name def ready(self): """ Override this method in subclasses to run code when Django starts. #這句話的語(yǔ)義為:在子類中重寫此方法,以便在Django啟動(dòng)時(shí)運(yùn)行代碼 """
查看整個(gè)AppConfig,我們可以把我們的要啟動(dòng)的代碼放置在重寫的ready方法中即可
from django.apps import AppConfig from django.utils.module_loading import autodiscover_modules class XadminConfig(AppConfig): name = 'Xadmin' def ready(self): autodiscover_modules('Xadmin') #Django啟動(dòng)時(shí)會(huì)自動(dòng)掃描每個(gè)app下的Xadmin.py文件
這樣,我們就完成了自定制Xadmin的啟動(dòng)階段,啟動(dòng)后我們就要進(jìn)行下一步注冊(cè)
在每個(gè)app下的Xadmin.py 文件中注冊(cè)模型
以blogs這個(gè)APP下的UserInfo、Book為例:
from blogs import models from Xadmin.service.Xadmin import site site.register(models.UserInfo) site.register(models.Book)
接下了就是設(shè)計(jì)url了,我們可以仿照admin的方式,在單例對(duì)象admin.site的這個(gè)類中封裝好這些方法
from django.conf.urls import url from django.shortcuts import HttpResponse,render class ModelXadmin(object): def __init__(self,model,site): self.model = model self.site = site def show(self,request): data_list = self.model.objects.all() return render(request,'show.html',locals()) #locals() 請(qǐng)函數(shù)內(nèi)部所有的鍵值對(duì)存儲(chǔ) == {"data_list":data_list} def add(self,request): return HttpResponse('添加頁(yè)面') def edit(self,request, pk): return HttpResponse('編輯頁(yè)面') def delete(self,request, pk): return HttpResponse('刪除頁(yè)面') @property def get_urls_operate(self): emp = [] emp.append(url(r'^$', self.show)) emp.append(url(r'^add/$', self.add)) emp.append(url(r'^(\d+)/change/$', self.edit)) emp.append(url(r'^(\d+)/delete/$', self.delete)) return emp @property def urls(self): return self.get_urls_operate,None,None class XadminSite(object): def __init__(self,name='xadmin'): self._registry = {} def register(self,model,class_main=None,**option): if not class_main: class_main = ModelXadmin self._registry[model] = class_main(model,self) @property def get_urls(self): # print(admin.site._registry) temp = [] for model, model_admin_object in self._registry.items(): model_name = model._meta.model_name model_app = model._meta.app_label temp.append(url(r'^{}/{}/'.format(model_app, model_name), model_admin_object.urls)) return temp @property def urls(self): return self.get_urls,None,None site = XadminSite()
備注:一個(gè)關(guān)鍵點(diǎn),為什么把第二層分發(fā)設(shè)置在了ModelXadmin這個(gè)類中,那肯定是放在這個(gè)類中能有什么好處,如果我們把這個(gè)二級(jí)分發(fā)放在XadminSite中,那么我們要取得每一個(gè)模型的數(shù)據(jù)是很麻煩的,但是,如果我們把這個(gè)放在ModelXadmin中,由于,在register(注冊(cè))時(shí),我們給class_admin(XadminSite)傳了每一個(gè)模型類,所以放在這個(gè)類中,我們可以通過self.model這個(gè)屬性獲得每個(gè)模型類的數(shù)據(jù)(self.model.objects.all()
),這樣就很容易得到這個(gè)模型表。
總結(jié)
以上所述是小編給大家介紹的Django中自定義admin Xadmin的實(shí)現(xiàn)代碼 ,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
如果你覺得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!
相關(guān)文章
python 用所有標(biāo)點(diǎn)符號(hào)分隔句子的示例
今天小編就為大家分享一篇python 用所有標(biāo)點(diǎn)符號(hào)分隔句子的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-07-07python中?conda?虛擬環(huán)境管理和jupyter內(nèi)核管理
這篇文章主要介紹了python中?conda?虛擬環(huán)境管理和jupyter內(nèi)核管理,文章基于pyhton以及conda的虛擬環(huán)境創(chuàng)建、刪除、jupyter添加、刪除虛擬kernel的方法,需要的朋友可以參考一下2022-04-04使用python批量修改文件名的方法(視頻合并時(shí))
這篇文章主要介紹了視頻合并時(shí)使用python批量修改文件名的方法,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-08-08Python實(shí)現(xiàn)socket庫(kù)網(wǎng)絡(luò)通信套接字
socket又叫套接字,實(shí)現(xiàn)網(wǎng)絡(luò)通信的兩端就是套接字。分為服務(wù)器對(duì)應(yīng)的套接字和客戶端對(duì)應(yīng)的套接字,本文給大家介紹Python實(shí)現(xiàn)socket庫(kù)網(wǎng)絡(luò)通信套接字的相關(guān)知識(shí),包括套接字的基本概念,感興趣的朋友跟隨小編一起看看吧2021-06-06使用Python實(shí)現(xiàn)批量ping操作方法
這篇文章主要介紹了使用Python實(shí)現(xiàn)批量ping操作方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05Python實(shí)現(xiàn)的matplotlib動(dòng)畫演示之細(xì)胞自動(dòng)機(jī)
這篇文章主要介紹了Python實(shí)現(xiàn)的matplotlib動(dòng)畫演示之細(xì)胞自動(dòng)機(jī),用python來模擬,首先嘗試表示Beacon,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-04-04