django----admin源码流程
Admin源码分析
1、启动 python manage.py runserver 时候,会自动执行每一个 app 下的 admin.py
2、注册模型(执行每一个app下的admin.py 文件)
3、设计url
将AdminSite实例挂载到指定的URLconfig中
admin.site.urls 目的是动态生成 一系列 url 将他们添加到 urlpatterns 中(后面有分析)
对 AdminSite 类分析
每一个模型都可以指定一个ModelAdmin ,封装了该模型的特定的管理功能
本质上实例化的是AdminSite()
一部分AdmainSite 源码
class AdminSite: def __init__(self, name='admin'): self._registry = {} # AdminSite 初始化一个空字典 self.name = name self._actions = {'delete_selected': actions.delete_selected} self._global_actions = self._actions.copy() all_sites.add(self) def register(self, model_or_iterable, admin_class=None, **options): #用来注册 admin_class = admin_class or ModelAdmin if isinstance(model_or_iterable, ModelBase): model_or_iterable = [model_or_iterable] for model in model_or_iterable: if model._meta.abstract: raise ImproperlyConfigured( 'The model %s is abstract, so it cannot be registered with admin.' % model.__name__ ) if model in self._registry: raise AlreadyRegistered('The model %s is already registered' % model.__name__) if not model._meta.swapped: if options: # options 指的是UserAdmin,看是否用户自定制了admin, admin.site.register(models.UserInfo, UserAdmin) options['__module__'] = __name__ admin_class = type("%sAdmin" % model.__name__, (admin_class,), options) self._registry[model] = admin_class(model, self)
admin.site.urls 中的 urls 属性
admin.site.urls 返回的是一个元祖 元祖的第一个参数是一个列表 由 get_urls() 返回
get_urls() 的目的为了将app_name 和 model_name 动态添加到 urlpatterns = [] 里面 (包括所有的url,例如 <URLPattern 'logout/' [name='logout']>等 login)
class AdminSite:
@property
def urls(self):
return self.get_urls(), 'admin', self.name
def get_urls(self):
from django.urls import include, path, re_path
from django.contrib.contenttypes import views as contenttype_views
def wrap(view, cacheable=False):
def wrapper(*args, **kwargs):
return self.admin_view(view, cacheable)(*args, **kwargs)
wrapper.admin_site = self
return update_wrapper(wrapper, view)
urlpatterns = [
path('', wrap(self.index), name='index'),
path('login/', self.login, name='login'),
path('logout/', wrap(self.logout), name='logout'),
path('password_change/', wrap(self.password_change, cacheable=True), name='password_change'),
path(
'password_change/done/',
wrap(self.password_change_done, cacheable=True),
name='password_change_done',
),
path('jsi18n/', wrap(self.i18n_javascript, cacheable=True), name='jsi18n'),
path(
'r/<int:content_type_id>/<path:object_id>/',
wrap(contenttype_views.shortcut),
name='view_on_site',
),
]
valid_app_labels = []
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)
if valid_app_labels:
regex = r'^(?P<app_label>' + '|'.join(valid_app_labels) + ')/$'
urlpatterns += [
re_path(regex, wrap(self.app_index), name='app_list'),
]
return urlpatterns
超级简单模拟 admin.site.urls
高级操作。类似Django中的admin.site.urls
admin.site.urls的本质:它读取_registry所有字典里面的数据,为字典里面的每一个类生成了4个url
from django.shortcuts import render,HttpResponse from django.contrib import admin from django.urls import path,re_path def login(request): return HttpResponse("ok") def change_list(request): return HttpResponse("列表页面") def add_view(request): return HttpResponse("添加页面") def change_view(request,nid): return HttpResponse("修改页面") def delete_view(request,nid): return HttpResponse("删除页面") def test(req): return HttpResponse('test') def index(request): return HttpResponse("index") class AdminSite: def __init__(self, name='admin'): self.name = name @property def urls(self): print("zhxign") urlpatterns = [ path('',index), #相当于 admin "http://127.0.0.1:8000/admin/" path('login',login) ] for model_class, v in admin.site._registry.items(): print(model_class) # 打印的是每一个类<class 'app01.models.UserInfo'> cls_name = model_class._meta.model_name # 当前类名称的小写 app_name = model_class._meta.app_label # 当前app的名称 urls_list = re_path(r'^{0}/{1}/$'.format(app_name, cls_name), change_list, name="login") urlpatterns.append(urls_list) #查 add_url = re_path(r'^{0}/{1}/add/$'.format(app_name, cls_name), add_view, name="login") #增 urlpatterns.append(add_url) change_url = re_path(r'^{0}/{1}/(\d+)/change/$'.format(app_name, cls_name), change_view, name="login") #改 urlpatterns.append(change_url) del_url = re_path(r'^{0}/{1}/(\d+)/del/$'.format(app_name, cls_name), delete_view, name="login") #删 urlpatterns.append(del_url) return urlpatterns,"admin",self.name site = AdminSite()
补充:利用include 做分发url 功能
include函数的返回值 return (urlconf_module, app_name, namespace) 为一个数组
如果不使用 include
urlpatterns = [ re_path(r'admin/', admin.site.urls), path('index/',([ path('', views.index), path('login', views.login) ],None,None)), ]
include的本质就是:返回了一个元组,元组的第一个是这个模块
include里面
既可以写一个列表include([]),利用include做分发
也可以返回一个字符串:帮我们去找到这个模块,找到所有的映射关系
总结
- admin源码流程 a. 运行程序,找到每一个app中的 admin.py 文件,并加载 - app01.admin.py - 创建admin.site中的对象 - 执行对象的 register方法,目的:将注册类添加到 _registry中 _registry = { key是传进来的model value:是ModelAdmin的对象,传了两个参数 models.Role: ModelAdmin(models.Role,admin.site), models.UserInfo: ModelAdmin(models.UserInfo,admin.site) models.UserType: ModelAdmin(models.UserType,admin.site) } - app02.admin.py - 用app01.admin中创建那个admin.site对象 - 执行对象的 register方法,目的:讲注册类添加到 _registry中 _registry = { models.Role: ModelAdmin(models.Role,admin.site), models.UserInfo: ModelAdmin(models.UserInfo,admin.site) models.UserType: ModelAdmin(models.UserType,admin.site) models.Article: ModelAdmin(models.Article,admin.site) } admin.site是一个对象(单例模式创建),其中封装了: _registry = { models.Role: ModelAdmin(models.Role,admin.site), models.UserInfo: ModelAdmin(models.UserInfo,admin.site) models.UserType: ModelAdmin(models.UserType,admin.site) models.Article: ModelAdmin(models.Article,admin.site) } b. urls.py 再次调用 admin.site 对象的 urls属性: urlpatterns = [ url(r'^admin/', admin.site.urls), ] class ModelAdmin(object): def __init__(self,model_class,site): self.model_class = model_class self.site = site def changelist_view(self,request): data_list = self.model_class.objects.all() #是动态的 return HttpResponse('列表页面') def add_view(self,request): return HttpResponse('添加页面') def delete_view(self,request,nid): return HttpResponse('删除页面') def change_view(self,request,nid): return HttpResponse('修改页面') def get_urls(self): urlpatterns = [ url(r'^$', self.changelist_view), url(r'^add/$', self.add_view), url(r'^(.+)/delete/$', self.delete_view), url(r'^(.+)/change/$', self.change_view), ] return urlpatterns @property def urls(self): return self.get_urls() class AdminSite(object): def __init__(self): self._registry = {} def register(self,model_class,model_admin): self._registry[model_class] = model_admin(model_class,self) def get_urls(self): """ models.Role: ModelAdmin(models.Role,admin.site), models.UserInfo: ModelAdmin(models.UserInfo,admin.site) models.UserType: ModelAdmin(models.UserType,admin.site) models.Article: ModelAdmin(models.Article,admin.site) """ url_list = [] for model_class,model_admin in self._registry.items(): #利用利用之前已经注册的_registry,动态生成一系列 url,添加到路由中 model_class是一个类 app_name = model_class._meta.app_label model_name = model_class._meta.model_name url_list += [ url('%s/%s' %(app_name,model_name,), include(model_admin.urls)) ] return url_list @property def urls(self): return (self.get_urls(), None,None )