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 )

  




原文地址:https://www.cnblogs.com/yanxiaoge/p/10628160.html