在Django框架中,自帶一個後臺管理頁面admin,這個管理頁面很全,可是,有些並非咱們須要的,因此咱們能夠根據admin的實現流程來自定義本身的需求,即根據admin的實現方式來實現自定製--Xadminhtml
首先,咱們先解析admin的流程,在Django中,咱們在建立項目的時候,Django自帶一個admin的url,實現了不一樣模型表的增刪改查,那麼admin是如何實現url的分發的?django
咱們能夠從三部分來看admin的路由分發實現session
1,啓動app
咱們能夠經過from django.contrib import admin來看admin是如何啓動的框架
Django啓動後,會在manage.py文件中加載配置文件settings.py ,在settings.py中有一個INSTALLED_APPS這個配置項,Django會按照配置項的內容一次加載每個app.ide
# 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時,會加載admin相關的,咱們點擊admin,進入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)
執行auto_discover這個接口,會自動的加載全部APP中的admin.py這個文件,這就是admin的啓動文件this
2,註冊url
自動加載全部APP下的admin.py文件時,會一次記錄全部執行了admin.site.register(模型類)這個方法的模型類,從而完成模型類的註冊。spa
全部的模型類都執行admin.site.register()這個方法後完成的註冊,那麼這個方法內部都作了些什麼?
咱們能夠點擊site進入admin的源碼中,獲得這個:site = AdminSite() site是一個對象,是AdminSite這個類的一個對象,而且是一個單例對象,那麼咱們就能夠確定,register是這個單例對象的一個方法,那這個方法中到底作了什麼?
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 # 關於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對象時,會生成一個self._registry={}這個對象屬性,若是咱們沒有指定admin_class,那麼admin_class就是ModelAdmin,由此能夠看出,admin_class是admin提供給咱們定製頁面的一個自定義類,這個類必須繼承ModelAdmin這個類,如下是:
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__)
因此無論有沒有自定製樣式類,都會執行self._registry[model] = admin_class(model, self),也就是說,註冊一個模型類,就會在對象的_registry這個字典中添加一個鍵值對,這個鍵是咱們註冊的這個模型類 model,值是繼承ModelAdmin的樣式類或者ModelAdmin這個類的一個對象。
3,設計url
由於site是一個單例對象,因此admin在執行完全部admin.py文件後,就會獲得整個全局的一個包含全部註冊模型的字典_registry,獲得這個self._registry(也就是admin.site對象)字典,咱們能夠for循環這個admin.site._registry字典,獲得這些鍵值對,那麼獲得這些鍵值對有什麼用呢?這就是admin設計url時會用到的。
咱們訪問admin這個後臺管理頁面會發現,這個頁面關於咱們註冊的全部模型都會實現增刪改查功能,每一個功能界面對應不一樣的模型類時,除了數據不一樣外,是同樣的,也就是說,admin對於不一樣的模型類用到是一套url,一套模板,那麼這個url是怎麼設計的呢?
咱們分別訪問不一樣的模型類的這四個功能頁面,會發現一個規律:
查詢頁面url: http://IP:PORT/admin/app名/模型類的名字(所有小寫)/
添加頁面url: http://IP:PORT/admin/app名/模型類的名字(所有小寫)/add
編輯頁面url:http://IP:PORT/admin/app名/模型類的名字(所有小寫)/id值/change
刪除頁面url:http://IP:PORT/admin/app名/模型類的名字(所有小寫)/id值/delete
經過這個規律能夠看出url分發的實現
可是在這個實現的過程當中,咱們有一個須要注意的時,用戶訪問的請求攜帶的路徑是一個字符串的類型,咱們能夠經過request.path獲得用戶訪問的路徑,也就能獲得用戶訪問的是哪一個APP下的哪一個模型類,可是這兩個參數都是字符串的類型,如何經過字符串獲得用戶訪問的哪張表,這是個麻煩,這時,可能咱們會想到使用importlib模塊來獲得這個類,可是,這樣這個過程很麻煩,Django爲咱們封裝好了相應的方法:咱們能夠經過這個模型類的類名,經過._meta.model_name這個方法獲得對應的字符串形式的類名,一樣的咱們也能夠經過這個類名._meta.app_label獲得字符串格式的相應的APP名
# 使用model代指模型類的類名 model._meta.model_name #獲得字符串格式的類名(全小寫的) model._meta.app_label #能夠獲得當前類所在APP的字符串的名字
# 補充
model._meta.get_field("字符串格式的字段屬性") # 獲得一個字段屬性的對象field_obj,這樣咱們就能夠利用這個字段對象取得屬性
# 好比: field_obj.verbose_name
經過這兩個封裝的方法,就完美的解決了咱們頭疼的問題
from django.conf.urls import url from django.contrib import admin def get_urls_operate(): emp = [] emp.append(url(r'^$',查詢頁面的視圖函數)) emp.append(url(r'^add/$',添加頁面的視圖函數)) emp.append(url(r'^(\d+)/change/$',編輯頁面的視圖函數)) emp.append(url(r'^(\d+)/delete/$',刪除頁面的視圖函數)) 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))) #實現第二層路由的分發 return temp urlpatterns = [ url(r'^Xadmin/',(get_urls(),None,None)) #自定義一個前綴 實現第一層路由的分發 ]
這樣咱們就實現一個經過一個路由實現不一樣場景的分發
經過這三部分,咱們能夠按照admin的實現方式,來自定製Xadmin
咱們瞭解了admin內部的實現流程,咱們能夠將這個實現過程封裝到一個類中。
咱們自定製Xadmin時,也要按照admin的流程實現,首先是啓動項,admin中,Django啓動時,會自動的執行每一個app下的admin.py文件,咱們能夠自定製爲啓動時,自動執行app下的每一個自定製的.py文件,好比Xadmin.py 文件,那麼如何達到Django啓動的時候幫咱們自動掃描加載咱們自定製的Xadmin.py文件呢?這是個問題
咱們觀察能夠發現,Django在啓動時,會加載配置文件settings.py ,在settings.py文件中,有一個INSTALLED_APPS這個列表,這個列表中,放置着咱們在Django項目中的全部app的配置信息,咱們觀察這個列表:咱們本身開啓的APP,在設置配置信息時,會在APP名字後加一個.apps.(app名首字母大寫)Config這個東西,而對應的在咱們的app下,Django會給咱們自動的配置一個apps.py文件,那麼這個apps.py文件有什麼做用呢,咱們打開這個apps.py文件,看看裏面的配置信息:
以blogs這個APP爲例:
from django.apps import AppConfig class BlogsConfig(AppConfig): name = 'blogs'
咱們能夠發現,裏面定義了一個類,這個類的類名就是settings配置信息中apps後面跟的那個東東,這個類中有一個靜態屬性name是當前的APP名,這個類繼承了AppConfig這個類。這就是咱們從這個.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.
#這句話的語義爲:在子類中重寫此方法,以便在Django啓動時運行代碼 """
查看整個AppConfig,咱們能夠把咱們的要啓動的代碼放置在重寫的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啓動時會自動掃描每一個app下的Xadmin.py文件
這樣,咱們就完成了自定製Xadmin的啓動階段,啓動後咱們就要進行下一步註冊
在每一個app下的Xadmin.py 文件中註冊模型
以blogs這個APP下的UserInfo、Book爲例:
from blogs import models from Xadmin.service.Xadmin import site site.register(models.UserInfo) site.register(models.Book)
接下了就是設計url了,咱們能夠仿照admin的方式,在單例對象admin.site的這個類中封裝好這些方法
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() 請函數內部全部的鍵值對存儲 == {"data_list":data_list} def add(self,request): return HttpResponse('添加頁面') def edit(self,request, pk): return HttpResponse('編輯頁面') def delete(self,request, pk): return HttpResponse('刪除頁面') @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()
備註:一個關鍵點,爲何把第二層分發設置在了ModelXadmin這個類中,那確定是放在這個類中能有什麼好處,若是咱們把這個二級分發放在XadminSite中,那麼咱們要取得每個模型的數據是很麻煩的,可是,若是咱們把這個放在ModelXadmin中,因爲,在register(註冊)時,咱們給class_admin(XadminSite)傳了每個模型類,因此放在這個類中,咱們能夠經過self.model這個屬性得到每一個模型類的數據(self.model.objects.all()),這樣就很容易獲得這個模型表。
原文出處:https://www.cnblogs.com/zhaopanpan/p/9124012.html