django--admin源码分析
django有一套强大的admin后台数据库管理工具,通过url(r'^admin/', admin.site.urls)完成对已注册model的增删改成,注册方法是admin.site.register(Book)
基于admin的stark组件
先创建一个app,完成数据库迁移
startapp app01 -->在项目中创建一个应用app01
makemigrations 负责基于你的模型修改创建一个新的迁移
migrate 负责应用迁移,以及取消应用并列出其状态。
在项目的settings.py文件中找到INSTALLED_APPS
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', 'app02.apps.App02Config', 'stark.apps.StarkConfig' ]
找到创建的组件stark下的apps.py文件,在ready()方法里面写上
autodiscover_modules('stark'),作用是可以像admin一样在项目启动时,会在所有的app中扫描加载stark.py.
from django.apps import AppConfig from django.utils.module_loading import autodiscover_modules class StarkConfig(AppConfig): name = 'stark' def ready(self): ''' #在INSTALLED_APPS注册过该应用之后,在启动项目时,会找到name=stark的这个app,然后执行ready方法, 这个方法中主要是做一些初始化任务. ''' autodiscover_modules('stark') #项目启动时,会把项目中所有app下面的的stark的文件全部加载,和admin一样,都是在启动时扫描所有的admin文件,并加载
分别在app01和app02的models.py中创建model模型
from django.db import models # Create your models here. class Book(models.Model): title=models.CharField(max_length=32) def __str__(self): return self.title
from django.db import models # Create your models here. class UserInfo(models.Model): name=models.CharField(max_length=32) pwd=models.CharField(max_length=32,default=123) email=models.EmailField() roles=models.ManyToManyField(to="Role") def __str__(self): return self.name class Role(models.Model): title=models.CharField(max_length=32) def __str__(self): return self.title
分别在app01和app02的stark.py文件中进行注册
from stark.service.sites import site from .models import * site.register(Book)
from stark.service.sites import site from .models import * site.register(UserInfo) site.register(Role)
在项目的urls.py中写上自定义组件的路由分发
from django.conf.urls import url from django.contrib import admin from stark.service.sites import site urlpatterns = [ url(r'^admin/', admin.site.urls), url('^stark/',site.urls),#模拟admin做的stark组件, ]
在stark应用中创建service包,在包下创建site.py来处理url以及url分发
from django.conf.urls import url from django.shortcuts import HttpResponse,render class ModelStark(object): def __init__(self,model,site): self.model=model #原因是StarkSite类中,self.registry[model]=model_config(model,self) self.site=site def show_list(self,request): ret=self.model.objects.all() return render(request,"stark/show_list.html",locals()) def add_view(self,request): return HttpResponse("add_view123.") def change_view(self,request,id): return HttpResponse("change_view123") def del_view(self,request,id): return HttpResponse("del_view123") def get_urls(self): temp=[] temp.append(url("^$", self.show_list)) temp.append(url("^add/$", self.add_view)) temp.append(url("^(\d+)/change/$", self.change_view)) temp.append(url("^(\d+)/delete/$", self.del_view)) return temp @property def urls(self): return self.get_urls() class StarkSite(object): def __init__(self): self._registry={} def register(self,model,model_config=None): if not model_config: model_config=ModelStark self._registry[model]=model_config(model,self) def get_urls(self): temp=[] # 从已经注册好了的site._registry中取出注册了哪些表名,做url分发 for model,model_config in self._registry.items(): model_name = model._meta.model_name app_label = model._meta.app_label u=url('^%s/%s/'%(app_label,model_name),(model_config.urls,None,None))#做二级url分发 temp.append(u) return temp @property def urls(self): return self.get_urls(),None,None site=StarkSite()
知识点:
model类变量下的_meta属性
类名._meta.model_name ==>字符串类型的类名,且全部是小写
类名._meta.app_label ==>app的名字,
print(UserInfo._meta.model_name) #"userinfo"
print(UserInfo._meta.app_label) #"app02" UserInfo是在app02的models.py中定义的类
url方法的分发: 一级分发: url("路径规则",视图) url("路径规则1",([ url("路径规则2",视图1), url("路径规则3",视图2), url("路径规则4",视图3), ]None,None)) 路径规则1/路径规则2--->视图1 路径规则1/路径规则3-->视图2 路径规则1/路径规则4-->视图3 二级分发: url("路径规则",视图) url("路径规则1",([ url("路径规则2",视图1), url("路径规则3",([ url("路径规则4",视图2), url("路径规则5",视图3), ],None,None)), url("路径规则6",视图4), ]None,None)) 路径规则1/路径规则2--->视图1 路径规则1/路径规则3/路径规则4-->视图2 路径规则1/路径规则3/路径规则5-->视图3
之所以在sites.py中创建两个类,StarkSite是做url分发的,ModelStark是样式类.原因是admin中源码中也是这么定义的,下面就进入到admin的源码分析
-----------------------------------------------------------------------------------------------------------
源码分析
如果要使用django封装好了的admin,就需要在app中创建好model模型之后需要到app的admin.py中进行注册
一.注册
from django.contrib import admin # Register your models here. from .models import * admin.site.register(UserInfo) class RoleConfig(admin.ModelAdmin): change_list_template="change_list.html" list_display = ["id","title"] admin.site.register(Role,RoleConfig)
1.当django项目启动时,admin应用首先扫描(加载)每一个app下面的admin.py
from django.utils.module_loading import autodiscover_modules def autodiscover(): autodiscover_modules('admin', register_to=site) #autodiscover_modules是项目启动时就扫描所有的app下的admin.py
2.(1)admin.py文件中
from django.contrib import admin --->点进去admin,from django.contrib.admin.sites import AdminSite,site
加载admin-->sites--->site=AdminSite() (单例模式,无论在什么地方引入,使用的都是同一个对象site)
(2)admin.py
#进行注册 admin.site.register(Book)
源码:
class AdminSite(object): def __init__(self, name='admin'): self._registry = {} self.name = name def register(self, model_or_iterable, admin_class=None, **options): #指的是类名字,admin_class指的是样式类 if not admin_class: admin_class = ModelAdmin self._registry[model] = admin_class(model, self)
def get_urls(self):
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<content_type_id>\d+)/(?P<object_id>.+)/$', wrap(contenttype_views.shortcut),
name='view_on_site'),
]
valid_app_labels = []
for model, model_admin in self._registry.items():#项目启动时会扫描所有app下的admin,此时self._registry中存放的是{Book:BookConfig}
urlpatterns += [ #设计url
url(r'^%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls)),
]#model._meta.app_label是app的名字,model._meta.model_name是app下面的注册的类名,include(model_admin.urls)又做了路由分发,点进去看urls,其实是看的样式类
ModelAdmin中的urls方法
return urlpatterns
@property
def urls(self): return self.get_urls(), 'admin', self.name
site=AdminSite() #这里的self._registry[model]中的self值的是site,model指的是models.py中的类名,admin_class也就是样式类,admin_class(model, self)中的self也是指的site
ModelAdmin==>针对某一个model的样式类
URL设计:
admin的url规范:
http://127.0.0.1:8000/admin/app01/book/
http://127.0.0.1:8000/admin/app01/book/add/
http://127.0.0.1:8000/admin/app01/book/3/change/
http://127.0.0.1:8000/admin/app01/book/3/delete/
项目的urls.py
urlpatterns = [ url(r'^admin/', admin.site.urls),]
点进去urls,执行了
AdminSite类中的 def get_urls(self): 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<content_type_id>\d+)/(?P<object_id>.+)/$', wrap(contenttype_views.shortcut), name='view_on_site'), ] return urlpatterns @property def urls(self): return self.get_urls(), 'admin', self.name
源码:
class ModelAdmin(BaseModelAdmin):
....
def __init__(self, model, admin_site):#admin_site指的是site
self.model = model
self.opts = model._meta
self.admin_site = admin_site
super(ModelAdmin, self).__init__()
def get_urls(self):
from django.conf.urls import url
...
info = self.model._meta.app_label, self.model._meta.model_name
urlpatterns = [
url(r'^$', wrap(self.changelist_view), name='%s_%s_changelist' % info),
url(r'^add/$', wrap(self.add_view), name='%s_%s_add' % info),
url(r'^(.+)/history/$', wrap(self.history_view), name='%s_%s_history' % info),
url(r'^(.+)/delete/$', wrap(self.delete_view), name='%s_%s_delete' % info),
url(r'^(.+)/change/$', wrap(self.change_view), name='%s_%s_change' % info),
# For backwards compatibility (was the change url before 1.9)
url(r'^(.+)/$', wrap(RedirectView.as_view(
pattern_name='%s:%s_%s_change' % ((self.admin_site.name,) + info)
))),
]
return urlpatterns
@property
def urls(self):
return self.get_urls()


浙公网安备 33010602011771号