博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
django-admin源码解析
阅读量:4327 次
发布时间:2019-06-06

本文共 9110 字,大约阅读时间需要 30 分钟。

django有一套强大的admin后台数据库管理工具,通过url(r'^admin/', admin.site.urls)完成对已注册model的增删改成,注册方法是admin.site.register(Publish)

我们创建一个app,然后创建一个model对象,然后迁移数据库

class Publish(models.Model):    title = models.CharField(max_length=32) def __str__(self): return self.title

 

admin后台可以通过如下的url分发,实现数据库表的增删改查

http://127.0.0.1:8000/admin/app01/publish/ http://127.0.0.1:8000/admin/app01/publish/1/change/ http://127.0.0.1:8000/admin/app01/publish/1/delete/ http://127.0.0.1:8000/admin/app01/publish/add/

 

url.py中我们通过url(r'^admin/', admin.site.urls)一条配置,竟然可以实现对一个model模型类对应的数据库表,进行增删改查操作,那么django到底是如何,实现一条url配置实现众多次分发的,今天我们就了解下admin源码

我们在urlpatterns配置列表处经常看到如下两行模块导入代码

from django.conf.urls import urlfrom django.contrib import admin

 

url(r'^admin/', admin.site.urls),是调用了urls模块下的url方法 

我们先从admin.site.urls参数入手,看看这个参数做了什么操作处理 
admin是隶属于from django.contrib import admin该模块,我们先看下admin模块代码

from django.contrib.admin.decorators import registerfrom django.contrib.admin.filters import (    AllValuesFieldListFilter, BooleanFieldListFilter, ChoicesFieldListFilter,    DateFieldListFilter, FieldListFilter, ListFilter, RelatedFieldListFilter,    RelatedOnlyFieldListFilter, SimpleListFilter,)from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME from django.contrib.admin.options import ( HORIZONTAL, VERTICAL, ModelAdmin, StackedInline, TabularInline, ) from django.contrib.admin.sites import AdminSite, site from django.utils.module_loading import autodiscover_modules __all__ = [ "register", "ACTION_CHECKBOX_NAME", "ModelAdmin", "HORIZONTAL", "VERTICAL", "StackedInline", "TabularInline", "AdminSite", "site", "ListFilter", "SimpleListFilter", "FieldListFilter", "BooleanFieldListFilter", "RelatedFieldListFilter", "ChoicesFieldListFilter", "DateFieldListFilter", "AllValuesFieldListFilter", "RelatedOnlyFieldListFilter", "autodiscover", ] def autodiscover(): autodiscover_modules('admin', register_to=site) default_app_config = 'django.contrib.admin.apps.AdminConfig'

 

这个模块__init__.py中只有autodiscover方法,和default_app_config变量,另外还有头部引入的其他的模块,我们从代码上就能猜到autodiscover意思大概就是自动扫描,也就是加载、导入的意思,那么到底是哪里执行了autodiscover方法呢?

django调用python manage.py runserver启动项目时候,利用CommandParser构造并且解析数据,然后执行django.setup方法,调用django.apps模块来读取项目文件中的settings.py拿到这面这几个app,然后交给django.apps的registry.py和config.py来进行统一的配置加载,然后最后self.fetch_command(subcommand).run_from_argv(self.argv)加载

 

那我们就从那里入手去找找代码,看看能不能发现思路线索,结果在如下的代码中 

from django.apps import apps类下的populate方法中找到了答案

for app_config in self.get_app_configs():       app_config.ready()

 

找到对应的AdminConfig,在from django.contrib.admin.apps import AdminConfig

class AdminConfig(SimpleAdminConfig):    """The default AppConfig for admin which does autodiscovery.""" def ready(self): super(AdminConfig, self).ready() self.module.autodiscover()

 

那么我就接着上篇的博客,来继续跟代码,去研究admin代码是怎么样的一个执行流程吧 

那我们就把目光回到from django.contrib import admin模块,看如下的扫描代码

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

 

继续看autodiscover_modules方法具体代码,贴出如下

def autodiscover_modules(*args, **kwargs):    from django.apps import apps register_to = kwargs.get('register_to') for app_config in apps.get_app_configs(): for module_to_search in args: # Attempt to import the app's module. try: if register_to: before_import_registry = copy.copy(register_to._registry) import_module('%s.%s' % (app_config.name, module_to_search)) except Exception: if register_to: register_to._registry = before_import_registry if module_has_submodule(app_config.module, module_to_search): raise

 

在上篇博客我们提到过最后封装了一个结果集(大概类似如下封装)

app_configs odict_values([
,
,
,
,
,
,
,
])

 

循环遍历结果集,在autodiscover_modules方法中传递的2个参数'admin', register_to=site,通过参数for module_to_search in args:if register_to为True的使用,最后执行如下代码,从而导入系列模块

import_module('%s.%s' % (app_config.name, module_to_search)) 

导入的格式如下,这里我列出格式如下:

app_config.name---- django.contrib.admin adminapp_config.name---- django.contrib.auth admin app_config.name---- django.contrib.contenttypes admin app_config.name---- django.contrib.sessions admin app_config.name---- django.contrib.messages admin app_config.name---- django.contrib.staticfiles admin app_config.name---- app01 admin app_config.name---- app02 admin app_config.name---- _stark admin

 

我们回过头来看最开始的配置代码url(r'^admin/', admin.site.urls),以上分析的只是admin模块autodiscover的方法,我们继续看下from django.contrib.admin import site做了哪些操作吧,该模块代码如下:

all_sites = WeakSet()class AlreadyRegistered(Exception):    pass class NotRegistered(Exception): pass class AdminSite(object): 省略n行代码 。。。。。。 site = AdminSite()

 

site = AdminSite()是获取一个AdminSite类对象,然后通过该对象去执行admin.site.urls方法,我们看看这个对象初始化做了什么,以及有哪些关键方法做了什么?

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)

 

初始化方法我们要留意下self._registry = {}这个字典,后续我们在分析它

到此为止,我们还去看哪块的代码呢?我们都知道当我们在admin后台,去操作一个model类映射的数据库表时,我们都是需要在admin.py中进行如下配置

admin.site.register(Publish)

 

所以我们就去看看register方法中做了哪些操作吧,我先将全部代码贴出如下:

def register(self, model_or_iterable, admin_class=None, **options):        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__) if not model._meta.swapped: # If we got **options then dynamically construct a subclass of # admin_class with those **options. if options: 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)

 

接下来我们逐行分析上述代码,我们先看如下代码

if not admin_class:   admin_class = ModelAdmin

 

这个ModelAdmin是什么东西呢?我们看如下代码,我们可以通过PermissionConf继承admin.ModelAdminPermission模型类,定制样式,这样在我们访问admin后台时,会展现我们的样式,每个模型类默认样式就是ModelAdmin

class PermissionConf(admin.ModelAdmin):    list_display = ["title", "url", "permission_group", "code"] ordering = ["id"] admin.site.register(Permission,PermissionConf)

 

所以admin_class = ModelAdmin代码意思就是,如果没有为model模型类定制样式,那么就使用默认的ModelAdmin样式,继续执行如下代码

if isinstance(model_or_iterable, ModelBase):     model_or_iterable = [model_or_iterable]

 

admin模块中,是依次扫描,然后去执行每个admin.py下的register方法 

最后执行self._registry[model] = admin_class(model, self) 
_registry就是AdminSite初始化构造的一个空字典{},具体是什么类型的字典呢? 
这个字典的每个键值对中的键就是对应的model对象模型,值就是admin_class(model, self) 
值是调用了类ModelAdmin,继而构造对象构成model对象模型对应的值

这里我们看下封装的数据结构吧:

 

至此site = AdminSite()对象的register方法就分析完毕

我们接下里继续看下如下代码,我再次贴出来

url(r'^admin/', admin.site.urls),

admin模块、AdminSite对象register方法、均分析完毕,接下来就看看admin.site.urls这个函数,property特性是方法转属性的用法,最后返回一个<class 'tuple'>元组类型

@property def urls(self): return self.get_urls(), 'admin', self.name

 

最后就看下方法get_urls(),我将代码贴出如下:

def get_urls(self):        from django.conf.urls import url, include 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) # Admin-site-wide views. urlpatterns = [ url(r'^$', wrap(self.index), name='index'), url(r'^login/$', self.login, name='login'), url(r'^logout/$', wrap(self.logout), name='logout'), url(r'^password_change/$', wrap(self.password_change, cacheable=True), name='password_change'), url(r'^password_change/done/$', wrap(self.password_change_done, cacheable=True), name='password_change_done'), url(r'^jsi18n/$', wrap(self.i18n_javascript, cacheable=True), name='jsi18n'), url(r'^r/(?P
\d+)/(?P
.+)/$', wrap(contenttype_views.shortcut), name='view_on_site'), ] valid_app_labels = [] 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) if valid_app_labels: regex = r'^(?P
' + '|'.join(valid_app_labels) + ')/$' urlpatterns += [ url(regex, wrap(self.app_index), name='app_list'), ] return urlpatterns

 

去循环遍历for model, model_admin in self._registry.items(): 

获取对应的键和值 
如下是打印输出,我只创建了一个Publish模型类,另外默认还有GroupUser

auth.GroupAdmin
auth.UserAdmin
app01.ModelAdmin

 

url(r'^admin/', admin.site.urls),分发url,如果没有在urlpatterns中,那么就拼接填入valid_app_labels.append(model._meta.app_label)

以下是get_urls的结果集

[
,
,
,
,
\d+)/(?P
.+)/$>,
(None:None) ^auth/group/>,
(None:None) ^auth/user/>,
(None:None) ^app01/publish/>,
auth|app01)/$>]

 

以上就是`url(r’^admin/’, admin.site.urls)的代码执行流程 

转载于:https://www.cnblogs.com/weiwu1578/articles/8747586.html

你可能感兴趣的文章
Thread Runnable 区别
查看>>
ORACLE JOB 设置
查看>>
微信小程序想要最短服务路径
查看>>
HDU - 4812 D Tree 点分治
查看>>
POJ 2763 Housewife Wind
查看>>
MinGW安装与配置
查看>>
【UVA11806 Cheerleaders】 题解
查看>>
TCP三次握手和四次挥手
查看>>
【SVN】win7 搭建SVN服务器
查看>>
iOS第三方做滤镜最主流的开源框架GPUImage
查看>>
面向对象三大特性
查看>>
网络架构与七层参考模式简介
查看>>
用python实现经典排序
查看>>
node-1
查看>>
Chessboard(规律)&&阿里巴巴和n个大盗(规律)
查看>>
设置Linux防火墙
查看>>
Nginx+Keepalived实现高可用站点
查看>>
php 获取文件信息相关基础函数
查看>>
读属性文件直接执行其中的方法
查看>>
mysql 引擎区分
查看>>