Django仿Admin定制插件(一)

程序启动时查找所有注册了的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.service.v1

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/'
Settings

 

posted @ 2017-09-07 23:10  Mitsuis  阅读(1142)  评论(0编辑  收藏  举报