第四章、kingadmin开发设计
4.1.kingadmin设计
django admin注册model的写法
crm/admin.py
class CustomerAdmin(admin.ModelAdmin): #显示 list_display = ['name','source','contact_type','contact','consultant','consult_content','status','date'] #过滤 list_filter = ['source','consultant','status','date'] #搜索,consultant是外键,必须加“__字段名” search_fields = ['contact','consultant__name'] admin.site.register(models.CustomerInfo,CustomerAdmin)
后台显示
这是后台显示的样子,如果我们想让前端也显示类似这样的页面该怎么做呢?这就需要照django自带的admin写法,自己自定义个kingadmin(模仿admin)
kingadmin
(1)创建app kingadmin
python manage.py startapp kingadmin
添加到settings的INSTALL_APPS里面
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'crm', 'kingadmin', ]
因为想让kingadmin app以后可以直接移植到其它项目中,所以在kingadmin目录下单独创建templates/kingadmin和static目录,把之前的静态文件和模板拷贝进去
(2) settings里面设置kingadmin静态文件和templates路径
STATIC_URL = '/static/' STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'statics'), os.path.join(BASE_DIR, 'kingadmin/statics'), )
(4)PerfectCRM/url.py添加路由分发
urlpatterns = [ url(r'^kingadmin/', include('kingadmin.urls')), ]
(5)kingamdin/urls.py
# kingadmin/urls.py from django.conf.urls import url from kingadmin import views urlpatterns = [ url(r'^login/', views.acc_login,name='login'), url(r'^logout/', views.acc_logout,name='logout'), ]
(6)kingamdin/views.py
登录界面也单独创建
# kingadmin/views.py from django.shortcuts import render,redirect from django.contrib.auth import authenticate,login,logout def acc_login(request): error_msg = '' if request.method == 'POST': username = request.POST.get('username',None) password = request.POST.get('password',None) #user是一个对象 #验证 user = authenticate(username=username,password=password) if user: #登录(已生成session) login(request, user) #如果有next值就获取next值,没有就跳转到首页 return redirect(request.GET.get('next','/kingadmin/')) else: error_msg = '用户名或密码错误!' return render(request,'kingadmin/login.html',{'error_msg':error_msg}) def acc_logout(request): logout(request) return redirect("/login/")
(7)kingamdin/urls.py
添加登录后跳转到“app_index.html”页面
urlpatterns = [ url(r'^$', views.app_index,name='app_index'), url(r'^login/', views.acc_login,name='login'), url(r'^logout/', views.acc_logout,name='logout'), ]
(8)kingadmin/views.py
def app_index(request): return render(request,'kingadmin/app_index.html')
(9)kingadmin/app_index.html
kingadmin/index.html中添加block right-content-container
app_index.html
{#templates/kingadmin/app_index.html#} {% extends 'kingadmin/index.html' %} {% block right-content-container %} <h2 class="page-header">APPS</h2> {% endblock %}
4.2.kingadmin自动发现及注册功能开发
想让app_index.html页面像后台一样显示所有注册的app以及下面的表名
(1)kingadmin/app_setup.py
# kingadmin/app_setup.py from django import conf def kingadmin_auto_discover(): for app_name in conf.settings.INSTALLED_APPS: try: #去每个app下面执行kingadmin.py文件 mod = __import__('%s.kingadmin'%app_name) #打印每个app已注册的model名字 print(mod.kingadmin) except ImportError: pass
(2)crm/kingadmin.py
# crm/kingadmin.py from kingadmin.sites import site from crm import models print('crm kingadmin....') #注册model class CustomerAdmin(object): list_display = ['name','source','contact_type','contact','consultant','consult_content','status','date'] list_filter = ['source','consultant','status','date'] search_fields = ['contact','consultant__name'] site.register(models.CustomerInfo,CustomerAdmin)
(3)student/kingadmin.py
创建app student
student/models.py
# student/models.py from django.db import models class Test(models.Model): name = models.CharField(max_length=64)
student/kingadmin.py
# student/kingadmin.py from student import models from kingadmin.sites import site print('student kingadmin.....') #注册model class TestAdmin(object): list_display = ['name'] site.register(models.Test,TestAdmin)
(4)kingadmin/views.py
# kingadmin/views.py from kingadmin import app_setup #程序一启动就自动执行 app_setup.kingadmin_auto_discover()
说明:
程序一启动,会执行每个app下面的kingadmin.py,注册全局的字典
from django import conf
conf.settings.INSTALL_APPS
动态获取settings里面所有添加的app名字
运行程序
(5)返回全局字典
我们想要的字典格式如下:
修改sites.py
# kingadmin/sites.py class AdminSite(object): def __init__(self): self.enable_admins = {} #两个参数,一个表名,一个自定义的admin类 def register(self,model_class,admin_class=None): '''注册admin表''' # print('register',model_class,admin_class) #获取app名字 app_name = model_class._meta.app_label #获取表名 model_name = model_class._meta.model_name if app_name not in self.enable_admins: self.enable_admins[app_name] = {} self.enable_admins[app_name][model_name] = admin_class #实例化,就可以调用register方法 site = AdminSite()
kingamdin/views.py中打印看看
from kingadmin import app_setup #程序已启动就自动执行 app_setup.kingadmin_auto_discover() from kingadmin.sites import site print('site',site.enable_admins)
运行程序
(6)前端页面显示
kingamdin/views.py
def app_index(request): return render(request,'kingadmin/app_index.html',{'site':site})
kingadmin/templates/app_index.html
{#templates/kingadmin/app_index.html#} {% extends 'kingadmin/index.html' %} {% block right-content-container %} <h2 class="page-header">APPS</h2> <div> {% for app_name,app_tables in site.enable_admins.items %} {{ app_name }}{{ app_tables }} {% endfor %} </div> {% endblock %}
4.3.kingadmin model obj list页面开发
把前端页面做成表格的格式,跟admin后台显示一样
bootstrap table: https://v3.bootcss.com/css/#tables
(1)kingadmin/app_index.html
{#templates/kingadmin/app_index.html#} {% extends 'kingadmin/index.html' %} {% block right-content-container %} <h2 class="page-header">APPS</h2> <div> {% for app_name,app_tables in site.enable_admins.items %} <table class="table table-striped"> <thead> <tr> <th>{{ app_name }}</th> </tr> </thead> <tbody> {% for model_name in app_tables %} <tr> <td><a href="{% url 'table_obj_list' app_name model_name %}">{{ model_name }}</a></td> <td>ADD</td> <td>Change</td> </tr> {% endfor %} </tbody> </table> {% endfor %} </div> {% endblock %}
(2)crm/kingadmin.py注册三个model
#注册model class CustomerAdmin(object): list_display = ['name','source','contact_type','contact','consultant','consult_content','status','date'] list_filter = ['source','consultant','status','date'] search_fields = ['contact','consultant__name'] site.register(models.CustomerInfo,CustomerAdmin) site.register(models.Role) site.register(models.Menus) site.register(models.UserProfile)
(3)kingadmin/url.py
urlpatterns = [ url(r'^(w+)/(w+)/$', views.table_obj_list,name='table_obj_list'), ]
(4)kingadmin/sites.py
class AdminSite(object): . . . def register(self,model_class,admin_class=None): . . . #获取app名字 app_name = model_class._meta.app_label #获取表名 model_name = model_class._meta.model_name #把model_class赋值给了admin_class,然后在视图中可以通过admin_class找到对应的model类(表名字) admin_class.model = model_class . . .
此时运行发现会报错
是因为我们在注册model的时候,有的写了自定义的model类,有的没写,而我们都统一的赋值,导致那些没写自定义model类(空的)赋值的时候就会报NoneType错误
django自带的自定义admin类的写法继承了ModelAdmin,那注册的时候为什么有的没写自定义admin类没有报错呢?
是因为继承的ModelAdmin帮我们写了(里面其实都定义为空了),我们模仿django admin的写法,也写个父类。
(5)kingadmin/admin_base.py
新建个admin_base.py,写个父类
# kingadmin/admin_base.py class BaseKingAdmin(object): pass
(6)crm/kingadmin.py
# crm/kingadmin.py from kingadmin.sites import site from crm import models from kingadmin.admin_base import BaseKingAdmin # print('crm kingadmin....') #注册model class CustomerAdmin(BaseKingAdmin): list_display = ['name','source','contact_type','contact','consultant','consult_content','status','date'] list_filter = ['source','consultant','status','date'] search_fields = ['contact','consultant__name'] site.register(models.CustomerInfo,CustomerAdmin) site.register(models.Role) site.register(models.Menus) site.register(models.UserProfile)
继承BaseKingAdmin
(7)kingadmin/sites.py
# kingadmin/sites.py from kingadmin.admin_base import BaseKingAdmin class AdminSite(object): def __init__(self): self.enable_admins = {} #两个参数,一个表名,一个自定义的admin类 def register(self,model_class,admin_class=BaseKingAdmin): '''注册admin表''' # print('register',model_class,admin_class) #获取app名字 app_name = model_class._meta.app_label #获取表名 model_name = model_class._meta.model_name #把model_class赋值给了admin_class,然后在视图中可以通过admin_class找到对应的model类(表名字) admin_class.model = model_class if app_name not in self.enable_admins: self.enable_admins[app_name] = {} self.enable_admins[app_name][model_name] = admin_class #实例化,就可以调用register方法 site = AdminSite()
现在运行程序,就正常了,访问:http://127.0.0.1:8000/kingadmin/
(8)取出model里面的值
kingadmin/views.py
@login_required def table_obj_list(request, app_name, model_name): '''取出指定model里的数据返回给前端''' #拿到admin_class后,通过它找到拿到model admin_class = site.enable_admins[app_name][model_name] querysets = admin_class.model.objects.all() return render(request, 'kingadmin/table_obj_list.html',{'querysets':querysets})
(9)templates/kingadmin/table_obj_list.html
{#kingadmin/templates/kingadmin/table_obj_list.html#} {% extends 'kingadmin/index.html' %} {% block right-content-container %} <h2 class="page-header">app</h2> <div> {{ querysets }} <table class="table table-striped"> <thead> <tr> <th></th> </tr> </thead> <tbody> </tbody> </table> </div> {% endblock %}
现在拿到的是一个对象,但是有个问题就是:没注册三个model里面得到值是一样
因为没注册的三个mdoel都共享同一个BaseKingAdmin内存对象(三个model内存地址一样),我们只需要实例化就可以了(实例化后就都有单独的内存空间了)
修改kingadmin/sites.py
# kingadmin/sites.py from kingadmin.admin_base import BaseKingAdmin class AdminSite(object): def __init__(self): self.enable_admins = {} #两个参数,一个表名,一个自定义的admin类 def register(self,model_class,admin_class=None): '''注册admin表''' # print('register',model_class,admin_class) #获取app名字 app_name = model_class._meta.app_label #获取表名 model_name = model_class._meta.model_name #把model_class赋值给了admin_class,然后在视图中可以通过admin_class找到对应的model类(表名字) if not admin_class: # 实例化,如果没写注册的类,就用BaseKingAdmin admin_class = BaseKingAdmin() else: #如果写了注册的类,就实例化自己 admin_class = admin_class() admin_class.model = model_class if app_name not in self.enable_admins: self.enable_admins[app_name] = {} self.enable_admins[app_name][model_name] = admin_class #实例化,就可以调用register方法 site = AdminSite()
现在就可以取出对应model的数据了