zoukankan      html  css  js  c++  java
  • admin的路由系统剖析

    admin路由系统本质剖析

    在Django中创建一个项目APP时需要在配置文件中的将项目的名字加入如图:

    其实在Django中已经有一个文件--apps.py:

    所以配置文件我们也可以写成下面的形式:

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app01.apps.App01Config',
    ]
    

    两种写法的功能都相同,但是上述写法会多一种方法如下apps.py文件:

    from django.apps import AppConfig
    
    
    class App01Config(AppConfig):
        name = 'app01'
        def ready(self):
            pass
    

    这多的一种方法就是ready方法,该方法在代码未执行前先执行即在没路由系统执行前先执行它。

    查看admin的执行文件:

    这里我们在回头看admin.py文件:

    admin.site.register(models.UserGroup,)
    admin.site.register(models.Role)
    

    我们知道在admin.py文件中注册的数据表都有对应的以下4种url

    		c. 自动生成URL
    			/admin/app01/userinfo/   			 	列表 change_list_view
    			/admin/app01/userinfo/add/  			增加 方法
    			/admin/app01/userinfo/2/change/			修改 方法
    			/admin/app01/userinfo/2/delete/			删除 方法
    

    所以可以知道在生成这些url之前是先执行了这些数据表类的实例化,并存放在某个位置

    site

    在执行实例化之前我们知道是通过admin.site这句先执行,所以查看其源码:

    可以看到site实例化了AdminSite这个类,因为实例化一个对象就会执行其init方法,所以查看其init方法:

    def __init__(self, name='admin'):
        self._registry = {}  # model_class class ->  admin_class instance
        self.name = name
        self._actions = {'delete_selected': actions.delete_selected}
        self._global_actions = self._actions.copy()
        all_sites.add(self)
    

    接着执行一个admin.site.register的方法,查看register的方法源码:

    def register(self, model_or_iterable, admin_class=None, **options):
        """
        Registers the given model(s) with the given admin class.
    
        The model(s) should be Model classes, not instances.
    
        If an admin class isn't given, it will use ModelAdmin (the default
        admin options). If keyword arguments are given -- e.g., list_display --
        they'll be applied as options to the admin class.
    
        If a model is already registered, this will raise AlreadyRegistered.
    
        If a model is abstract, this will raise ImproperlyConfigured.
        """
        if not admin_class:
            admin_class = 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__)
    
            # Ignore the registration if the model has been
            # swapped out.
            if not model._meta.swapped:
                # If we got **options then dynamically construct a subclass of
                # admin_class with those **options.
                if options:
                    # For reasons I don't quite understand, without a __module__
                    # the created class appears to "live" in the wrong place,
                    # which causes issues later on.
                    options['__module__'] = __name__
                    admin_class = type("%sAdmin" % model.__name__, (admin_class,), options)
    
                # Instantiate the admin class to save in the registry
                self._registry[model] = admin_class(model, self)
    

    从上到下,这段代码相当于只执行了最小面那句代码:

    self._registry[model] = admin_class(model, self)
    

    所以这里admin.site.register(models.UserInfo,)这句代码的执行代码的过程如下代码所示:

    from django.contrib import admin
    from . import models
    
    
    class UserInfoAdmin(admin.ModelAdmin):
        pass
    
    admin.site.register(models. UserInfo,UserInfoAdmin)
    admin.site.register(models.UserGroup,admin.ModelAdmin)
    
    
    
    相当于执行下面的过程:
    #class AdminSite(object):
    #    def __init__(self):
    #        self._registry = {}
    #    def register(self,model,model_admin):
    #        self._registry[model] = model_admin(model,self)
    
    #1.实例化self._registry = {}
    #2.register方法
    #self._registry = {
    models.UserInfo:UserInfoAdmin(models.UserInfo,site对象)
    models.UserInfo:UserInfoAdmin(models.UserInfo,site对象)
    
    # }
    

    url

    from django.conf.urls import url
    from django.contrib import admin
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
    ]
    

    在url中admin.site.urls,也是执行了admin.site方法,此时的admin.site方法中init定义的字典中(self._registry )已经有了值即你定义的数据表

    #self._registry = {
    models.UserInfo:UserInfoAdmin(models.UserInfo,site对象)
    models.UserInfo:UserInfoAdmin(models.UserInfo,site对象)
    
    # }
    

    现在进入url源码中查看:

    get_urls方法:

    下面这是重点:

        for model, model_admin in self._registry.items():
            urlpatterns += [
                url(r'^%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)
    

    在get_urls中上述的for循环循环的就是self._registry的字典

       	urlpatterns += [
                    url(r'^%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls)),
    

    这个循环体结合self._registry字典

    #self._registry = {
    models.UserInfo:UserInfoAdmin(models.UserInfo,site对象)
    models.UserInfo:UserInfoAdmin(models.UserInfo,site对象)
    
    # }
    

    最后生成的就是下面这种形式的url路径:

    /admin/app01/userinfo/add/  
    

    所以可以知道admin的路由系统流程

    1.执行admin.py文件生成self._registry字典

    		self._registry = {
    			 models.UserInfo: UserInfoAdmin(models.UserInfo,site对象)[add,change..],
    			 models.UserGroup: ModelAdmin(models.UserGroup,site对象),
    		 }
    

    2 路由系统:
    url(r'^admin/', admin.site.urls),

    3.启动之前,执行代码:admin.py

    这里执行admin.py文件前取决于先执行了下面的代码:

    def autodiscover():
        autodiscover_modules('admin', register_to=site)
    

    所以通过这点我们可以自定义autodiscover方法:

    在执行代码之前先执行autodiscover方法,这个方法即在所有的APP文件中找到“xxxxx”文件并执行:

    验证结果如下:

    System check identified no issues (0 silenced).
    October 18, 2017 - 23:55:54
    Django version 1.11.4, using settings 'lijianCrm.settings'
    Starting development server at http://127.0.0.1:8000/
    Quit the server with CTRL-BREAK.
    先执行了我!!!
    Performing system checks...
    

    所以通过这个关系可以自己做一个独立的app并生成增删改查的url。

    独立的路由系统

    1.创建多个APP项目

    2.在其中一个注册的app的文件夹的apps文件中写入下面的代码:

    from django.apps import AppConfig

    class App02Config(AppConfig):
        name = 'whatmini'
    
    class App01Config(AppConfig):
        name = 'app01'
        def ready(self):
            from django.utils.module_loading import autodiscover_modules
            autodiscover_modules("nb")
    

    3.在所有的app项目中都创建一个nb.py文件

    那么到这一步,当Django程序运行时,其会先执行whatmini文件中的apps.py文件,而apps.py文件会要求找到所有的APP项目中的nb.py 文件并执行这个文件。所以这样我们就可以对nb.py这个文件进行自定义了

    4.自定义nb.py

    为了统一性先在whatmini文件中在创建一个service文件,然后在这个文件下创建一个v1.py文件

    class ModelNb(object):
        def __init__(self,model_class,site):
            self.model_class = model_class
            self.site = site
    
    class Nbsite(object):
        def __init__(self):
            self._registry = {}
        def registry(self,model,model_nb=None):
            if not model_nb:
                model_nb = ModelNb
            self._registry[model] = model_nb(model,self)
    site =Nbsite()
    

    将上述代码放在v1.py文件中,然后在每个app项目内的nb.py文件中将这个文件导入,并执行,则所有的项目都能执行这个文件如下:

    from whatmini.service import v1
    from . import models
    v1.site.registry(models.UserInfo)
    

    5.生成路由系统

    路由系统的两种方式:

    一种是常规的如下图所示:

    另一种include路由分发:

    from django.conf.urls import url,include
    from django.contrib import admin
    from app01 import urls
    from django.shortcuts import render,HttpResponse
    def index(request):
        return HttpResponse("index")
    urlpatterns = [
        # url(r'^admin/', admin.site.urls),
        url(r'^index/', index),
        #url(r'^app01/', include("app01.urls ")),  #这里通过对include的源码分析,其执行后返回一个元组,所以我们这里也可以直接写一个元组类型
       # url(r'^app01/', (urls,"asd","asda")),  #同样的源include也可以传一个模块和两个字符串
        url(r'^app01/', ([
                            url(r'^index/', index),
                            url(r'^index2/', index),
                         ],"asd","asda")),  #同样的源include也可以传一个列表和两个字符串
    
    ]
  • 相关阅读:
    Java RandomAccessFile与MappedByteBuffer
    Apache httpClient
    gitolite migration to bitbucket
    logback身份证脱敏
    身份证号码-正则表达式
    webservice-整理
    Java高编译低运行错误(ConcurrentHashMap.keySet)
    IIS(互联网信息服务)
    ASP.NET MVC:UrlHelper.cs
    TortoiseSVN 和 VisualSVN
  • 原文地址:https://www.cnblogs.com/lijian-22huxiaoshan/p/7738375.html
Copyright © 2011-2022 走看看