程序启动时查找所有注册了的apps.py 会执行def ready方法
MyAdmin.apps.py:
def ready(self):
super(MyadminConfig,self).ready()
from django.utils.module_loading import autodiscover_modules
autodiscover_modules('reg')
这里应该是收集所有的reg文件。执行reg.py中的注册函数
app01.reg.py:
from app01 import models
from MyAdmin.service import v1
#执行v1.site.register方法,将model_class传入
v1.site.register(models.UserInfo)
v1.site.register(models.Role)
MyAdmin.service.v1.py:
#site为MySite对象
site = MySite()
#通过执行并传入model的register函数,生成一个字典并保存:
ret={
"UserInfo":BaseAdmin(UserInfo,site)
"Role":BaseAdmin(Role,site)
}
def register(self,model_class,xxx = BaseAdmin):
self._registry[model_class] = xxx(model_class,self)
Django.urls.py:
from django.conf.urls import url
from MyAdmin.service import v1
urlpatterns = [
url(r'^md/', v1.site.urls),
]
当Django启动后,这里使用路由分发的原理 v1.site.urls 分发了预设的url以及
根据app名称跟类名拼接生成的url,在此基础上再次分发,使每个类都有
4个url:changelist、add、delete、change
MyAdmin.service.v1.py class
@property 1. def urls(self): return self.get_urls(),self.app_name,self.namespace 2. def get_urls(self): from django.conf.urls import url,include ret = [ url(r'^login/',self.login,name='login'), url(r'^logout/',self.login,name='logout'), ] """ model_cls为models类,admin_obj为BaseAdmin(model类, MySite()即site ),即传入2个参数的BaseAdmin对象, admin_obj.urls则执行BaseAdmin的urls方法 """ for model_cls,admin_obj in self._registry.items(): app_label = model_cls._meta.app_label model_name = model_cls._meta.model_name ret.append(url(r'^%s/%s' % (app_label,model_name),include(admin_obj.urls))) return ret 3.@property def urls(self): from django.conf.urls import url,include info = self.model_class._meta.app_label,self.model_class._meta.model_name """
这里的self.model_class为之前传入的model类,所以一样可以取得app和model类名,由此设置别名,
方便后续反向生成url.
"""
urlpatterns = [
url(r'^$',self.changelist_view,name='%s_%s_changelist' % info),
url(r'^add/$',self.add_view,name='%s_%s_add' % info),
url(r'^(.+)/delete/$',self.delete_view,name='%s_%s_delete' % info),
url(r'^(.+)/change/$',self.change_view,name='%s_%s_change' % info),
]
return urlpatterns
生成url后,我们需要对每个url指定不同的操作,以changelist为例子,每个类定义的字段,以及字段名都不同,我们又不可能为每一个类的url做单独的模版,假如有几十个类,这样增删改查工作量太大,因此我们需要为每个类的增删改查做统一的视图模版.
阶段一代码:
1.目录结构:
2.代码:
1 from django.http import HttpResponse 2 from django.shortcuts import render 3 4 class BaseAdmin(object): 5 list_display = "__all__" 6 7 def __init__(self,model_class,site): 8 self.model_class = model_class 9 self.site = site 10 self.request = None 11 12 @property 13 def urls(self): 14 from django.conf.urls import url 15 """ 16 这里的self.model_class为之前传入的model类,所以一样可以取得app和model类名,由此设置别名, 17 方便后续反向生成url. 18 """ 19 info = self.model_class._meta.app_label,self.model_class._meta.model_name 20 urlpatterns = [ 21 url(r'^$',self.changelist_view,name='%s_%s_changelist' % info), 22 url(r'^add/$',self.add_view,name='%s_%s_add' % info), 23 url(r'^(.+)/delete/$',self.delete_view,name='%s_%s_delete' % info), 24 url(r'^(.+)/change/$',self.change_view,name='%s_%s_change' % info), 25 ] 26 return urlpatterns 27 28 def add_view(self,request): 29 """ 30 新增数据 31 :param request: 32 :return: 33 """ 34 info = self.model_class._meta.app_label,self.model_class._meta.model_name 35 data = "%s_%s_add" % info 36 return HttpResponse(data) 37 38 def delete_view(self,request,pk): 39 """ 40 删除数据 41 :param request: 42 :return: 43 """ 44 # self.model_class.objects.filter(id=pk).delete() 45 info = self.model_class._meta.app_label, self.model_class._meta.model_name 46 data = "%s_%s_del" % info 47 return HttpResponse(data) 48 49 def change_view(self,request,pk): 50 """ 51 修改数据 52 :param request: 53 :return: 54 """ 55 info = self.model_class._meta.app_label, self.model_class._meta.model_name 56 data = "%s_%s_change" % info 57 return HttpResponse(data) 58 59 def changelist_view(self,request): 60 """ 61 查看列表 62 :param request: 63 :return: 64 """ 65 self.request = request 66 result_list = self.model_class.objects.all() 67 context = { 68 'result_list':result_list, 69 'list_display':self.list_display, 70 'admin_obj':self #此处self为自定制的Admin-models类对象 71 } 72 return render(request,'md/change_list.html',context) 73 74 75 class MySite(object): 76 def __init__(self): 77 self._registry = {} 78 self.namespace = 'MyAdmin' 79 self.app_name = 'MyAdmin' 80 81 def register(self,model_class,xxx = BaseAdmin): 82 self._registry[model_class] = xxx(model_class,self) 83 """{ 84 modle类:BaseAdmin(model类, MySite()即site ) 85 } 86 """ 87 88 def get_urls(self): 89 from django.conf.urls import url,include 90 ret = [ 91 url(r'^login/',self.login,name='login'), 92 url(r'^logout/',self.login,name='logout'), 93 ] 94 #通过循环items获得每一个model类所在的app名,以及小写的类名 95 for model_cls,admin_obj in self._registry.items(): 96 """ 97 model_cls为models类,admin_obj为BaseAdmin(model类, MySite()即site ),即传入2个参数的BaseAdmin对象, 98 admin_obj.urls则执行BaseAdmin的urls方法 99 """ 100 app_label = model_cls._meta.app_label 101 model_name = model_cls._meta.model_name 102 # print(app_label,model_name) 103 #拼接生成url,如/md/app01/userinfo/,再次分发拿到最终的/md/app01/userinfo/change_list等url 104 ret.append(url(r'^%s/%s' % (app_label,model_name),include(admin_obj.urls))) 105 106 return ret 107 108 @property 109 def urls(self): 110 return self.get_urls(),self.app_name,self.namespace 111 112 def login(self,request): 113 return HttpResponse('login') 114 115 site = MySite()
MyAdmin.templates.md.change_list.html:
1 {% load md_list %} 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 <title>Title</title> 7 </head> 8 <body> 9 <h1>数据列表</h1> 10 {% func result_list list_display admin_obj%} 11 </body> 12 </html>
MyAdmin.templates.md.md.html:
1 <table border="1"> 2 <thead> 3 4 </thead> 5 <tbody> 6 {% for item in res %} 7 <tr> 8 {% for val in item %} 9 <td>{{ val }}</td> 10 {% endfor %} 11 </tr> 12 {% endfor %} 13 </tbody> 14 </table>
MyAdmin.templatetags.md_list.py:
from django.template import Library from types import FunctionType register = Library() def table_body(result_list,list_display,admin_obj): """ 循环自定制的列表,生成对应的标签数据 :param result_list: 数据表所有数据 :param list_display: 自定制列表 ['id','name',fun] :param admin_obj: 继承自BaseAdmin的models类对象,如:AdminUserInfo() :return: """ for row in result_list: yield [name(admin_obj,row) if isinstance(name,FunctionType) else getattr(row,name) for name in list_display] @register.inclusion_tag("md/md.html") def func(result_list,list_dispaly,admin_obj): """ inclusion_tag,调用这个tag的模版,首先会将数据交给md.html根据html渲染出标签, 然后替换到调用这个tag的位置。 :param result_list: :param list_dispaly: :param admin_obj: :return: """ v = table_body(result_list,list_dispaly,admin_obj) return {'res':v}
MyAdmin.apps.py:
from django.apps import AppConfig class MyadminConfig(AppConfig): name = 'MyAdmin' def ready(self): super(MyadminConfig,self).ready() from django.utils.module_loading import autodiscover_modules autodiscover_modules('reg')
app01.reg.py:
from app01 import models from MyAdmin.service import v1 from django.utils.safestring import mark_safe class AdminUserInfo(v1.BaseAdmin): def func(self,obj): """ 反向生成url,使每一条数据可以跳转到详细页执行change视图 :param obj: 数据表行数据 :return: 编辑按钮跳转url """ from django.urls import reverse #反向生成url,需要namespace,app跟类名称在注册时传入 name = "{0}:{1}_{2}_change".format(self.site.namespace,self.model_class._meta.app_label,self.model_class._meta.model_name) #obj为查询出的数据表每一行数据 url = reverse(name, args=(obj.pk,)) return mark_safe("<a href='{0}'>编辑</a>".format(url)) def checkbox(self,obj): """ 生成checkbox标签 :param obj: 行数据 :return: 带有数据行id的checkbox标签 """ tag = "<input type='checkbox' value='{0}' />".format(obj.pk) return mark_safe(tag) list_display = [checkbox,'id','username',func] """ 这里注册后,self._registry[model_class] = xxx(model_class,self) 原来的字典就变为{UserInfo:AdminUserInfo(UserInfo,site)}, 因此传过去的context字典中的admin_obj不再是BaseAdmin对象,而是这里的AdminUserInfo对象,它除了能继承 BaseAdmin类的各种方法,还有自己定制的func 以及checkbox方法. """ v1.site.register(models.UserInfo,AdminUserInfo) class AdminRole(v1.BaseAdmin): list_display = ['id', 'name'] v1.site.register(models.Role,AdminRole)
settings配置文件:
1 """ 2 Django settings for Admin自定制 project. 3 4 Generated by 'django-admin startproject' using Django 1.11.2. 5 6 For more information on this file, see 7 https://docs.djangoproject.com/en/1.11/topics/settings/ 8 9 For the full list of settings and their values, see 10 https://docs.djangoproject.com/en/1.11/ref/settings/ 11 """ 12 13 import os 14 15 # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 18 19 # Quick-start development settings - unsuitable for production 20 # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ 21 22 # SECURITY WARNING: keep the secret key used in production secret! 23 SECRET_KEY = '@bfz%(znqs&m2907_q0ck%ly2=ghs^p@nptcwdrr^333vw%xl9' 24 25 # SECURITY WARNING: don't run with debug turned on in production! 26 DEBUG = True 27 28 ALLOWED_HOSTS = [] 29 30 31 # Application definition 32 33 INSTALLED_APPS = [ 34 # 'django.contrib.admin', 35 # 'django.contrib.auth', 36 'django.contrib.contenttypes', 37 'django.contrib.sessions', 38 # 'django.contrib.messages', 39 'django.contrib.staticfiles', 40 'app01', 41 'MyAdmin.apps.MyadminConfig' 42 ] 43 44 MIDDLEWARE = [ 45 'django.middleware.security.SecurityMiddleware', 46 'django.contrib.sessions.middleware.SessionMiddleware', 47 'django.middleware.common.CommonMiddleware', 48 'django.middleware.csrf.CsrfViewMiddleware', 49 # 'django.contrib.auth.middleware.AuthenticationMiddleware', 50 # 'django.contrib.messages.middleware.MessageMiddleware', 51 'django.middleware.clickjacking.XFrameOptionsMiddleware', 52 ] 53 54 ROOT_URLCONF = 'Admin自定制.urls' 55 56 TEMPLATES = [ 57 { 58 'BACKEND': 'django.template.backends.django.DjangoTemplates', 59 'DIRS': [os.path.join(BASE_DIR, 'templates')] 60 , 61 'APP_DIRS': True, 62 'OPTIONS': { 63 'context_processors': [ 64 'django.template.context_processors.debug', 65 'django.template.context_processors.request', 66 # 'django.contrib.auth.context_processors.auth', 67 # 'django.contrib.messages.context_processors.messages', 68 ], 69 }, 70 }, 71 ] 72 73 WSGI_APPLICATION = 'Admin自定制.wsgi.application' 74 75 76 # Database 77 # https://docs.djangoproject.com/en/1.11/ref/settings/#databases 78 79 DATABASES = { 80 'default': { 81 'ENGINE': 'django.db.backends.sqlite3', 82 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 83 } 84 } 85 86 87 # Password validation 88 # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators 89 90 AUTH_PASSWORD_VALIDATORS = [ 91 # { 92 # 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 93 # }, 94 # { 95 # 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 96 # }, 97 # { 98 # 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 99 # }, 100 # { 101 # 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 102 # }, 103 ] 104 105 106 # Internationalization 107 # https://docs.djangoproject.com/en/1.11/topics/i18n/ 108 109 LANGUAGE_CODE = 'en-us' 110 111 TIME_ZONE = 'UTC' 112 113 USE_I18N = True 114 115 USE_L10N = True 116 117 USE_TZ = True 118 119 120 # Static files (CSS, JavaScript, Images) 121 # https://docs.djangoproject.com/en/1.11/howto/static-files/ 122 123 STATIC_URL = '/static/'