第四章、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

king/index.html
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的數據了
