CURD插件(仿Django-admin版)

前言

如何提高本身的開發效率?css

每一個新項目都是本身經作過的項目(經驗所致),在項目開發過程當中不斷總結、封裝屬於本身的組件,html

例如:每一個web項目大部分都涉及增刪改查,分頁顯示,搜素,CRM就是這樣的組件,是一件頗有必要的事情;前端

 

CURD組件(arya):模仿DjangoAdmin編寫增刪改查插件;python

組件功能:經過在後臺註冊表名配置自定製類,操做、顯示數據庫中的內容;jquery

組件設計目標:把CRM模塊嵌套到不一樣具備增刪改查功能的Django程序上,實現快速開發基於web的後臺管理系統;web

 

組件配置項:數據庫

list_display=[ '字段',‘函數’] : 定製顯示頁面顯示的列名,和內容;django

action                                   : 定製action,批量操做;json

show_add_btn=True            :定製是否顯示添加按鈕bootstrap

model_form=None               :定製model_form 提供編輯、添加、刪除頁面,經過popup操做關聯表數據

lister_filter                            :組合篩選項

search_list = ['name', 'qq']     :模糊搜素

 

組件說明:

 

Site類:註冊和生成基礎url

---方法:

url(路由調用)

get_usrl(url方法調用)

register(註冊調用)

login(登陸視圖)

logout(登出視圖)

 

---字段:

namespace(url所需名稱空間)

self. _registry={ }(註冊生成

# {
# model.表名1,配置對象1
# model.表名2,配置對象2
# }

self.name  app名稱

 

 

Config類:生成基礎配置,生成增、刪、改、查url,處理request請求;

 

---方法

warpper()  經過裝飾器實現每次請求保存request信息

add_view()   增、刪、改、查視圖

changlist_view()

delete_view()

changge_view()

urls() 增、刪、改、查url

extra_urls()url擴展

 

類字段:

list_dispaly=[字段、函數   ]   配置list顯示頁面須要列 和內容   

get_listdisplay() 

 

actions = [函數]    配置選中chebox可執行的操做

get_actions()

 

add_btn=True  list顯示頁面是否顯示添加按鈕

get_ show_add_btn()

 

model_form = None   配置 增、刪、改。查操做的model_form

get_model_form_class()

 

list_filter = [字段、函數     ] 配置組合搜素內容

get_list_filter()

 

對象字段

self.model_class   (models.UserInfo)

self.request  (每一個對象的request請求信息)

self.site (註冊類 )

 

ChangList類:封裝list顯示頁面全部數據,經過對象的方式傳到前端模板;

 

方法:

add_html(self),gen_list_filter  生成添加按鈕  、生成組合搜素 須要的數據比較多不適合在前端模板生成,因此選擇後端;

 

類字段:

對象字段:

self.model_config_obj (由於list顯示頁面,須要list_display,action,show_add。。。不少配置類的屬性,全部乾脆把 配置對象封裝到ChangList類中  )

1 class ChangeList(object):  # 因爲使用inclusion_tag,須要傳不少值,就把這個些值封裝到類裏,一對象的形式遞給前端
2     def __init__(self, data_list, model_config_obj):
3         self.list_display = model_config_obj.get_list_play()
4         self.actions = model_config_obj.get_actions()
5         self.model_config_obj = model_config_obj
6         self.list_filter = model_config_obj.get_list_filter()
View Code

 

self.request = request 、self.datalist  用於分頁

 

-----------------------------------------------------------------------------------------------------------------

 

option類:組合搜素 配置項  

list_filter= [v1.OptionConfig('group',True), v1.OptionConfig('roles',False), ]

方法:

is_func(self): 判斷是不是函數

name(self): 生成函數名或者字段名

 

對象字段

"""
:param field: 字段名稱或函數
:param is_multi: 是否支持多選
:param text_func_name: 在Model中定義函數,顯示文本名稱,默認使用 str(對象)
:param val_func_name: 在Model中定義函數,顯示文本名稱,默認使用 對象.pk


RowItems類:生成組合搜素須要的A標籤

class RowItems():
    def __init__(self, option, data_list, params):
        self.option = option
        self.data_list = data_list
        self.param = copy.deepcopy(params)  # query_dict對象
        print(self.param)
        # print(self.param)  # <QueryDict: {'group': ['1'], 'roles': ['2']}>
        self.param._mutable = True

    def __iter__(self):
        if self.option.is_muti: #若是配置了多選
            current_pk_or_list = self.param.getlist(self.option.name)  #[1,2]
            print(current_pk_or_list)
        else:
            current_pk_or_list = self.param.get(self.option.name)  # [1]
        if self.param.get(self.option.name): #若是<QueryDict: {'roles': ['2'], 'group': ['4', '5', '3', '2', '1']}> 能獲取到 group  roles
            # 生成所有
            self.param.pop(self.option.name)  # 所有就pop本身
            all_url = self.param.urlencode()
            tmp = '<a href="?%s">所有</a>' % (all_url)
            yield mark_safe(tmp)
        else:
            all_url = self.param.urlencode()  #若是訪問的url 沒有攜帶參數
            tmp = '<a class="active" href="?%s">所有</a>' % (all_url)
            yield mark_safe(tmp)
        #生成所有右側 obj  data_list=[obj,obj,obj ]
        for obj in self.data_list:
            #每一個obj 生成 每一個 A標籤
            pk = str(obj.pk)  #1
            text = str(obj)   #阿斯蒂芬
            if self.option.is_muti:  #若是配置了多選
                if pk not in current_pk_or_list: #若是obj.pk不在【1.2】
                    tmp=[]
                    tmp.extend(current_pk_or_list)
                    tmp.append(pk)
                    self.param.setlist(self.option.name,tmp) #注意在原來不變的基礎上多加1個?group=2&group=3



            else:
                self.param[self.option.name]=pk
            # print(self.param)  #<QueryDict: {'group': ['2', '3'], 'roles': ['1']}>
            url = self.param.urlencode()
            if not self.option.is_muti: #單選生成A標籤
                if current_pk_or_list == pk:   #url中傳來的 get參數
                    tmp = '<a class="active" href="?%s">%s</a>' % (url, text)  # 設置選擇標籤顏色
                else:
                    tmp = '<a href="?%s">%s</a>' % (url, text)
                yield mark_safe(tmp)
            else:
                if pk in current_pk_or_list:  # url中傳來的 get參數
                    tmp = '<a class="active" href="?%s">%s</a>' % (url, text)  # 設置選擇標籤顏色
                else:
                    tmp = '<a href="?%s">%s</a>' % (url,text)
                yield mark_safe(tmp)
View Code

 

 

 

 

 

 

1、CURD組件開發

一、CRM程序入口 apps.App02Config,執行app02下的app.py文件中App02Config類的ready方法;

CRM要從Django的setings.py配置文件的apps.App02Config類提及,apps.App02Config類在每一個APP的apps.py文件中,該類的ready()方法會在ROOT_URLCONF = 'CRM.urls'配置未執行以前執行,實現幫咱們把表和自定義配置類註冊;

from django.apps import AppConfig


class App01Config(AppConfig):
    name = 'app01'
    def ready(self):
        pass
View Code

 

二、App02Config類中的ready方法,執行 autodiscover_modules('zhanggen'),因此去Django程序的全部app下尋找zhanggen.py並執行;

autodiscover_modules()模塊,在Django程序啓動自動發現並執行一個py文件  (啓動文件)

autodiscover_modules(xx.py)模塊自動去全部的APP下,尋找xx.py文件而且執行

from django.apps import AppConfig


class App02Config(AppConfig):
    name = 'app02'
    def ready(self):
        # 1.導入autodiscover_modules 程序啓動執行autodiscover_modules(參數)
        # 誰導入autodiscover_modules(py文件)並設置了,就會在程序啓動前執行那個pywenj
        from django.utils.module_loading import autodiscover_modules
        autodiscover_modules('zhanggen')
View Code

 

 三、找到後zhanggen.py from app02.service import v1 

先寫好註冊功能,等待調用。。(注意site是用文件實現的單例模式)

from django.shortcuts import HttpResponse,render
from django.conf.urls import url
from types import FunctionType
from django.urls import reverse
from django.utils.safestring import mark_safe

class ChangeList(object):  #封裝 table 數據傳遞給前端
    def __init__(self,data_list,list_display,model_config_obj):
        self.data_list = data_list
        self.list_display = list_display
        self.model_config_obj = model_config_obj

    def add_html(self):  #封裝add 按鈕
        app_model = self.model_config_obj.model_class._meta.app_label, self.model_config_obj.model_class._meta.model_name
        add_url = reverse("zg:%s_%s_add" % app_model)
        add_html = mark_safe('<a class="btn btn-primary" href="%s">添加</a>' % (add_url,))
        return add_html


class Modelzg(object):    # 註冊的時候沒有使用配置對象,默認使用的配置對象
    ''''用戶基礎配置類'''

    def __init__(self, model_class, site): #model_class=model.userinfo  , site=Zgsite對象
        self.model_class = model_class
        self.site = site

    list_display = []
    show_add_btn = True


    def get_show_add_btn(self):
        return self.show_add_btn


    def changelist_view(self, request, *args, **kwargs):
        #因爲組冊類調用本類的時候傳入了表名 self._registry[model]=model_zg(model,self)
        #表名 model_class=models.UserInfo ,全部就能夠根據 組冊表名獲取數據了
        data_list=self.model_class.objects.all()
        self.request = request
        # def headers():
        #     if not self.list_display:#若是用戶沒有自定義配置就返回表名
        #         yield self.model_class._meta.model_name
        #     else:
        #         for v in self.list_display:
        #         #     if isinstance(v,FunctionType):
        #         #         yield v(True)
        #         #     else:
        #         #         #獲取中文 列名稱
        #         #         # models.UserInfo._meta.get_field(email/name).verbose_name
        #         #         verbose_name= self.model_class._meta.get_field(v).verbose_name
        #         #         yield verbose_name
        #         #三元運算
        #             yield v(self, is_header=True) if isinstance(v, FunctionType) else self.model_class._meta.get_field(
        #             v).verbose_name
        # def body():
            # for row in data_list:
                # #row 是數據庫中的一行數據,對象
                # row_data=[]
                # for name in self.list_display:
                #     #list_display = ['id', 'name', 'email', xxxxx]
                #     if isinstance(name,FunctionType):
                #        row_data.append(name(self,row))
                #     else:
                #        row_data.append(getattr(row,name))
                # yield row_data
                #三元表達式 列表生成式
        #         if not self.list_display:
        #             yield [str(row), ]
        #         else:
        #             yield [name(self,obj=row) if isinstance(name, FunctionType) else  getattr(row, name) for name in
        #                    self.list_display]
        #
        # context = {'data_list': data_list,
        #            'list_display': self.list_display,
        #            'headers':headers(),
        #            'body':body(),
        #            }
        cl = ChangeList(data_list,self.list_display,self) #傳到simp_tag
        context = {
            'cl': cl
        }

        return render(request,'nb/changelist.html', context)



    def add_view(self, request, *args, **kwargs):
        return HttpResponse('添加頁面')

    def delete_view(self, request, *args, **kwargs):
        return HttpResponse('刪除頁面')

    def change_view(self, request, *args, **kwargs):
        return HttpResponse('修改頁面')

    def get_urls(self):
        app_model_name = self.model_class._meta.app_label, self.model_class._meta.model_name  #(app01,表名)
        '''
        生成url:
        /zg/app01/userinfo/ 顯示  
        /zg/app01/userinfo/add 添加
        /zg/app01/userinfo/ 刪除
        /zg/app01/userinfo/ 編輯     
        '''
        patterns = [
            url(r'^$', self.changelist_view,name="%s_%s_changelist" %app_model_name),
            url(r'^add/$', self.add_view,name="%s_%s_add" %app_model_name),
            url(r'^(.+)/delete/$', self.delete_view,name="%s_%s_delete" %app_model_name),
            url(r'^(.+)/change/$', self.change_view,name="%s_%s_change" %app_model_name),
        ]
        patterns+=self.extra_urls()
        return patterns

    def extra_urls(self):
        ''''自定義擴展url 預留的鉤子函數'''
        return []

    @property
    def urls(self):
        return self.get_urls(),None,None



class Zgsite(object):
    def __init__(self):
        self._registry={}
        self.name='zg'
        self.namespace='zg'

    def register(self,model,model_zg=None):
        if not model_zg:  #若是在register()註冊的時候,沒有傳(配置類)
            model_zg=Modelzg #就使用Modelzg本身定義的類
        self._registry[model]=model_zg(model,self)   #注意  model:表名   model_zg(model表名,self =Zgsite對象 ):是配置對象

    def login(self,request):
        return HttpResponse('登陸頁面')

    def logout(self,request):
        return HttpResponse('註銷頁面')

    def ger_urls(self):
        patterns=[]
        patterns+=[
            url(r'^login/',self.login),
            url(r'^logout/', self.logout),
        ]
        for table_name,config_obj in self._registry.items():
            '''
            table_name._meta.app_label 註冊表名所在的app名稱  例: app01
            table_name._meta.model_name 註冊表名所在的 表名小寫 例:userinfo / usergroup
            '''
            patterns += [    #'/zg/app01/userinfo/'
                # url(r'^app01/userinfo/',config_obj.urls)
                # url(r'^app01/usergroup/',config_obj.urls繼續構造([],'xx','xxx'))
                # config_obj=Modelzg()   默認對象
                #config_obj.urls  調用默認對象的 urls,繼續構造([],'xx','xxx'))
                url(r'^%s/%s/'%(table_name._meta.app_label,table_name._meta.model_name,),(config_obj.urls))
            ]
        return patterns
    @property
    def urls(self):
        return self.ger_urls(),self.name,self.namespace  #由於路由系統中的include本質就是返回一個元祖




site=Zgsite()
View Code

 

設計知識點:

文件實現單例模式:

文件導入以後該文件中定義的類被實例化成一個對象,1次執行以後,之後再調用這個文件永遠獲得同一個對象,至關於永遠執行1個對象;

四、zhanggen.py實例化了v1中Zgsite類,獲得單例對象site,site單例對象 2次(2張表) 調用了單例象的register()方法,

v1.site.register(models.UserInfo)
v1.site.register(models.UserGroup)

#一、導入v1就獲得一個單例對象 site
#二、執行site對象的regist()進行註冊
View Code

 

五、site單例對象的register()方法把site對象的self._registry屬性,填充成

{

models.UserInfo:UserInfoConfig(models.UserInfo,self,) #sel也就是,site對象因爲是單例模式userindo的site對象=slef=UserGroup的site對象
models.UserGroup:UserGroupConfig(models.UserGroup,self,)

     }

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

{

models.UserInfo,UserInfo配置對象(models.UserInfo,site對象), #配置對象是相互無關係的
models.UserGroup,UserGrou配置對(models.UserInfo,對象)

 }

class Zgsite(object):
    def __init__(self):
        self._registry={}
        self.name='zg'
        self.namespace='zg'

    def register(self,model,model_zg=None):
        if not model_zg:  #若是在register()註冊的時候,沒有傳(配置類)
            model_zg=Modelzg #就使用Modelzg本身定義的類
        self._registry[model]=model_zg(model,self)   #注意  model:表名   model_zg(model表名,self =Zgsite對象 ):是配置對象
View Code

 

 

六、Django程序的路由系統開始執行,一樣調用了單例對象 v1.py中的單例對象 site,並執行了site單例對象的urls方法;

 url(r'^zg/',v1.site.urls), 

 

七、site單例對象的urls方法,調用了site單例對象的ger_urls方法;

    def ger_urls(self):
        patterns=[]
        patterns+=[                       #基本url
            url(r'^login/',self.login),  #/app01/login/
            url(r'^logout/', self.logout),#/app01/logout/
        ]
        for table_name,config_obj in self._registry.items():
            # print(config_obj.model_class)
            '''
            table_name._meta.app_label 註冊表名所在的app名稱  例: app01
            table_name._meta.model_name 註冊表名所在的 表名小寫 例:userinfo / usergroup
            '''
            patterns += [    #'/zg/app01/userinfo/'
                # url(r'^app01/userinfo/',config_obj.urls)
                # url(r'^app01/usergroup/',config_obj.urls繼續構造([],'xx','xxx'))
                # config_obj=Modelzg()   默認對象
                #config_obj.urls  調用默認對象的 urls,繼續構造([],'xx','xxx'))
         url(r'^%s/%s/'%(table_name._meta.app_label,table_name._meta.model_name,),(config_obj.urls)) #site類注意轉接給你了基礎配置類
            ]
        return patterns
    @property
    def urls(self):
        return self.ger_urls(),self.name,self.namespace  #由於路由系統中的include本質就是返回一個元祖
View Code

 

八、ger_urls方法,for循環了單例對象的self._registry屬性,爲每張表生成基礎url映射關係;

^zg/ ^login/

^zg/ ^logout/

^zg/ ^app01/userinfo/

^zg/ ^app01/usergroup/

 

 

 九、單例對象site的ger_urls方法,並每一個調用配置對象中的get_urls方法,爲全部註冊的表生成增刪改查路由映射;

    def get_urls(self):
        app_model_name = self.model_class._meta.app_label, self.model_class._meta.model_name  #(app01,表名)
        '''
        生成url:
        /zg/app01/userinfo/ 顯示  
        /zg/app01/userinfo/add 添加
        /zg/app01/userinfo/ 刪除
        /zg/app01/userinfo/ 編輯     
        '''
        patterns = [
            #每一個配置對象執行本身的changelist_view,全部他們執行視圖雖然都叫changelist_view()可是不同
            url(r'^$', self.changelist_view,name="%s_%s_changelist" %app_model_name),
            url(r'^add/$', self.add_view,name="%s_%s_add" %app_model_name),
            url(r'^(.+)/delete/$', self.delete_view,name="%s_%s_delete" %app_model_name),
            url(r'^(.+)/change/$', self.change_view,name="%s_%s_change" %app_model_name),
        ]
        patterns+=self.extra_urls()
        return patterns
View Code

 

涉及知識點:

一、Django路由系統 includ()路由分發的本質

本質就是就是return一個元祖,([],app名稱,namespace名稱空間)或者(py文件,app名稱,namespace名稱空間)切支持一直往下嵌套;

urlpatterns = [
    # url(r'^admin/', admin.site.urls),
    url(r'^zg/',v1.site.urls),
    url(r'^app666/', ([
                  url(r'^login/', ( [url(r'^login1/',([url(r'^login2/',index)],'xxx','xxx')) ],'xx','xx')),
                  # url(r'^logout/', self.logout),
                  # url(r'^app01/userinfo/', self.logout),
                  # url(r'^app01/usergroup/', self.logout),
                 ],'nb','nb')),


]
View Code

 

二、namespace 反向生成url

2.反向生成url

視圖

def index(request):
   urls=reverse('tex',args=(8,))
   print(urls)
   return redirect(urls)

路由系統
url(r'^test/(\d+)/',test,name='tex'),


視圖
def index(request):
   urls=reverse('tex',kwargs={'a1':9})
   print(urls)
   return redirect(urls)


路由系統
 url(r'^test/(?P<a1>\d+)/',test,name='tex'),




namespace:區分urlpatterns中同名的path

若是url()中包含namespace,在 reverse(namespace:別名)時一點要加上namesp

def index(request):
   urls=reverse('xx:tex')
   print(urls)
   return redirect(urls)


url(r'^app01/',([
                url(r'^index/',index,name='inde'),
                url(r'^test/',test,name='tex'),
                            ],'xx','xx')),
View Code

 

三、model.py中類的_meta方法:

  app_name=models.UserInfo._meta.app_label     #獲取model中類(表),所在的app
    table_name=models.UserInfo._meta.model_name  #獲取model中類(表),表名
    filed_verbose=models.UserInfo._meta.get_field('email').verbose_name #獲取列的verbosename 就是描述信息
View Code

 

四、擴展、和重寫url

1、預留  url擴展和重寫

zhanggen.py
#擴展 url
    def extra_urls(self):
        patterns = [
            url(r'test/$',self.test),
        ]
        return patterns
    #重寫url
    def get_urls(self):
        patterns = [
            url(r'test/$', self.test),
        ]
        return patterns



v1.py
   def get_urls(self):
        patterns = [
            url(r'^$', self.changelist_view),
            url(r'^add/$', self.add_view),
            url(r'^(.+)/delete/$', self.delete_view),
            url(r'^(.+)/change/$', self.change_view),
        ]
        patterns+=self.extra_urls()
        return patterns

    def extra_urls(self):
        ''''自定義擴展url 預留的鉤子函數'''
        return []
View Code

 

 

 

CRM基礎框架已經搭建完成,每一個請求過來都會生成url 視圖映射關係,接下來應該設計 視圖功能了;(day88內容)

 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

一、由每張表的url路由關係,的視圖函數設置的都是配置對象的 changelist_view方法;如何區分?別忘了

{

models.UserInfo,UserInfo配置對象(models.UserInfo,site對象), #配置對象是相互無關係的
models.UserGroup,UserGrou配置對(models.UserInfo,對象)

 }

 

顯示中文標題

 

經過獲取字段的verbose_name,顯示中文標題

    def changelist_view(self, request, *args, **kwargs):
        # 因爲在註冊的時候,實例化配置對象中傳入了 model_class,site,全部能夠經過self.model_class區分不一樣的表
        data_list=self.model_class.objects.all()
        def header():
            if not self.list_display:
                yield self.model_class._meta.model_name
            else:
                for i in self.list_display:
                     yield i(self,is_header=True) if isinstance(i,FunctionType) else self.model_class._meta.get_field(i).verbose_name
                    
        context={
            'headers':header(),
            'data_list':data_list
        }
        return render(request,'test.html',context)
View Code

 

涉及知識點:

三元表達式

i(self,is_header=True) if isinstance(i,FunctionType) else self.model_class._meta.get_field(i).verbose_name
View Code

yield生成器: 在後端製做生成器,在前端for循環生成器,提升性能;

      yield [i(self, is_header=True, row=obj) if isinstance(i, FunctionType) else getattr(obj, i) for i in
                       self.list_display]
View Code

 

yileld應用場景:

應用1:xrang(100),經過yield作生成器,惰性生成,不要在內存中所有建立;

應用2:在Django若是後臺的數據須要作一些處理以後,再在模板中循環顯示出來,就能夠在後端使用yield生成;省去了後端的循環;

 

 

顯示內容

涉及知識點:

列表生成式

  def body():
            for obj in data_list:
                yield [i(self, is_header=True, row=obj) if isinstance(i, FunctionType) else getattr(obj, i) for i in
                       self.list_display]
View Code

 

在配置類自定義列標題 和自定義列內容

    def xxx(self,is_header=False,row=None):
        if is_header==True:  #若是調用該函數的時候傳入的參數是is_header==True,函數執行就返回標題
            return '列名稱'
        else:
            return '自定義列'#若是調用該函數的時候傳入的參數是is_header==Fase,函數執行就返回內容
View Code

 

 

 

二、基礎配置對象 changelist_view方法,根據每張表的list_display = []配置,把生成thead 和tbody內容,封裝到ChangeList類中cl對象中返回給nb/changelist.html

class ChangeList(object):  #因爲使用inclusion_tag,須要傳不少值,就把這個些值封裝到類裏,一對象的形式遞給前端
    def __init__(self,data_list,list_display,model_config_obj):
        self.data_list = data_list
        self.list_display = list_display
        self.model_config_obj = model_config_obj

    def add_html(self):  #封裝add 按鈕
        app_model = self.model_config_obj.model_class._meta.app_label, self.model_config_obj.model_class._meta.model_name
        add_url = reverse("zg:%s_%s_add" % app_model)
        add_html = mark_safe('<a class="btn btn-primary" href="%s">添加</a>' % (add_url,))
        return add_html
View Code

 

涉及知識點·:

面向對象封裝

def func(a1,a2,a3,a4):  #若是一個函數執行須要不少參數,就能夠吧這個函數改形成面向對象的模式
    pass

class Foo():
    def __init__(self,a1,a2,a3,a4): #優點1:一次傳入,封裝全部值到一個對象;
        self.a1=a1
        self.a2 = a2
        self.a3 = a3
        self.a4 = a4
    def a5(self):               #優點2:還能夠經過方法修改封裝的值
        pass
View Code

 

 

三、nb/changelist.html加載 result_list simp_tag,並把後端傳來c1對象傳到{% show_result_list cl%} include_tag

 

四、show_result_list include_tag,把c1對象中的數據,和change_list_table.html進行渲染,後nb/changelist.html顯示了後端全部內容

from django.template.library import Library
from types import FunctionType
register=Library()

@register.inclusion_tag('nb/change_list_table.html')
#inclusion包含_tag自動去找nb/change_list_table.html
#inclusion_tag返回什麼值,就會把這些值,渲染到nb/change_list_table.html
#其餘頁面調用show_result_list,就會獲得 nb/change_list_table.html模板和show_result_list返回的結果的渲染字符串
def show_result_list(cl):
    def headers():
        if not cl.list_display:
            yield cl.model_config_obj.model_class._meta.model_name
        else:
            for v in cl.list_display:
                yield v(cl.model_config_obj, is_header=True) if isinstance(v,
                                                                           FunctionType) else cl.model_config_obj.model_class._meta.get_field(
                    v).verbose_name
    def body():
        for row in cl.data_list:
            if not cl.list_display:
                yield [str(row), ]
            else:
                #obj=row,在Django admin定義 def xx(self,obj):  #obj =當前行的對象,方便咱們顯示 多列數據組合
                yield [name(cl.model_config_obj, obj=row) if isinstance(name, FunctionType) else  getattr(row, name) for
                       name in cl.list_display]
    return {'headers': headers(),   #
            'body': body(),}

#就是inclusion_tag(模板)相似simple_tag
View Code

 

涉及知識點:

自定義simp_tag

當Django模板語言提供的simp_tag沒法知足模板渲染需求時,能夠利用python代碼自定義本身的simp_tag

include_tag:

@register.inclusion_tag('nb/change_list_table.html')

inclusion包含_tag自動去找nb/change_list_table.html

inclusion_tag返回什麼值,就會把這些值,渲染到nb/change_list_table.html

其餘頁面調用show_result_list,就會獲得 nb/change_list_table.html模板和show_result_list返回的結果的渲染字符串

 

方法和函數

類執行類裏的函數須要加self參數,傳入一個實例對象。

對象執行類裏的函數叫方法,會自動把本身做爲參數傳遞進去,不須要加self參數。

#函數和方法

class Foo(object):
    def __init__(self,name):
        self.name=name
    def show(self):
        print('show',self.name)


obj=Foo('zhangggen')
# Foo.show(obj)           #類直接調用 類中的函數,須要加self參數


from  types import FunctionType,MethodType

print(isinstance(Foo.show,FunctionType)) #函數

print(isinstance(obj.show,FunctionType)) #方法
View Code

 

 五、前端根據權限   get_show_add_btn決定是否顯示添加按鈕,PermissionConfig()類。權限表鉤子

因爲配置類繼承了PermissionConfig也繼承了基礎配置類,權限類優先繼承,全部能夠重寫def get_show_add_btn(self):方法

class Modelzg(object):    # 註冊的時候沒有使用配置對象,默認使用的配置對象
    ''''用戶基礎配置類'''

    def __init__(self, model_class, site): #model_class=model.userinfo  , site=Zgsite對象
        self.model_class = model_class
        self.site = site

    list_display = []
    show_add_btn = True

    def get_show_add_btn(self):   #默認是True,就是顯示add按鈕,因爲廣度優先繼承PermissionConfig,PermissionConfig類重寫了get_show_add_btn
        return self.show_add_btn
View Code
class PermissionConfig(object):   #配置類重寫 get_show_add_btn,控制 add按鈕的顯示
    def get_show_add_btn(self):
        print(self.request)
        return False
View Code

 註釋:

因爲配置對象同時繼承了PermissionConfig權限配置類,v1.Modelzg默認配置類,配置對象在默認配置類寫了self.request = request,而配置對象又被封裝到了Cl對象裏,全部前端cl.model_config_obj.get_show_add_btn 能夠判斷是否顯示添加權限;

 

 六、list_display = [ ]配置了字段,就顯示 checkbox選擇和編輯選項,預留權限鉤子

 list_display = []
    show_add_btn = True
    def zhinan(self, obj=None, is_header=False):
        if is_header:
            return '選擇'
        else:
            tpl = "<input type='checkbox' value='%s' />" % (obj.pk,)
            return mark_safe(tpl)

    def option(self, obj=None, is_header=False):
        if is_header:
            return '選項'
        else:
            # edit_url = reverse('zg:app01_userinfo_change',args=(obj.pk,))
            edit_url = reverse(
                'zg:%s_%s_change' % (self.model_class._meta.app_label, self.model_class._meta.model_name,),
                args=(obj.pk,))
            tpl = "<a href='%s'>編輯</a>|<a>刪除</a>" % (edit_url,)
            return mark_safe(tpl)

    def get_list_play(self):
        res=[]  #不能直接操做self.listplay 由於刷新又會重新添加一次
        if self.list_display:  #若是用戶設置顯示的列 和數據
            res.extend(self.list_display)
            res.insert(0,Modelzg.zhinan)
            res.append(Modelzg.option)
        return res
View Code

 

預留權限鉤子

    def get_list_play(self):
        return super().get_list_play()   # UserInfoConfig對象,先來PermissionConfig,再去v1.Modelzg類找get_list_play方法
View Code

 

 七、用戶自定製 批量刪除和初始化actions

定義一個actions = []配置項,經過基礎配置類的 get_actions(self) 去獲取

   actions = []

    def get_actions(self):
        res=[]
        res.extend(self.actions)
        res.append(Modelzg.multi_del)
        res.append(Modelzg.init_action)
        return res
View Code

把在基礎配置類裏定義批量刪除 和初始化的函數

    def multi_del(self):
       id_list=self.request.POST.getlist('pk')
       # self.model_class.objects.fifter(pk__in=id_list).delete()

    multi_del.short_desc='批量刪除' #給函數定義一個屬性

    def init_action(self):
        pass
    init_action.short_desc = '初始化'  # 給函數定義一個屬性
View Code

 

把actions = []裏定義函數名封裝到 cl對象傳給前端

  def xxxff(model_config_obj):
            for i in model_config_obj.get_actions():
                yield (i.__name__,i.short_desc)
        self.actions = xxxff(model_config_obj)
View Code

 

前端把函數名做爲option的value 函數的short_desc屬性做爲內容顯示

   {% for action in cl.actions  %}
                <option value={{ action.0}}>{{ action.1}}</option>
            {% endfor %}
View Code

 

最後把選擇的checkbox和選擇的函數經過from表單提交到後臺基礎配置類 chang_list_viwe方法

  self.request = request
        if request.method=='POST':
            func = request.POST.get('action')
            print(func)
            method = getattr(self,func,None)
            # print(isinstance(action_func,FunctionType))
            # 注意從對象中獲取的是方法
            if method:
                method()  # 執行 actions中的函數
        data_list = self.model_class.objects.all()
View Code

 

後端經過反射在後端執獲取函數並執行

   def multi_del(self):
       id_list=self.request.POST.getlist('pk')
       # self.model_class.objects.fifter(pk__in=id_list).delete()
View Code

 

 

八、分頁保留原來的搜素條件

當咱們在搜素框輸入了搜素條件,點擊下一頁應當保存搜素條件!

例如:當前頁:http://127.0.0.1:8000/zg/app01/userinfo/?p=1&id=2&page=2/

點擊下一頁如和保存p=1&id=2參數

  request_get._mutable=True        #設置可編輯參數


        page=Pagination(
            #自定製分頁器須要傳入的參數
            #一、當前訪問頁碼
            current_page=model_config_obj.request.GET.get('page'),
            #二、 數據庫總條目數
            total_item_count=data_list.count(),
            #base_url 信息
            base_url=model_config_obj.request.path_info,
            request_params=request_get

        )
        #page對象產出 開始也 和結束頁
        self.data_list=data_list[page.start:page.end]
        #分頁HTML代碼
        self.page_html=page.page_html()
View Code

 

修改分頁生成的 頁碼

"""
使用方法:

from utils.page import Pagination
def users(request):
    current_page = int(request.GET.get('page',1))

    total_item_count = models.UserInfo.objects.all().count()

    page_obj = Pagination(current_page,total_item_count,'/users.html')

    user_list = models.UserInfo.objects.all()[page_obj.start:page_obj.end]

    return render(request,'users.html',{'user_list':user_list,'page_html':page_obj.page_html()})


"""


from django.utils.safestring import mark_safe

class Pagination(object):

    def __init__(self,current_page,total_item_count,base_url=None,per_page_count=10,show_pager_count=11,request_params=None):
        """
        :param current_page:  當前頁
        :param total_item_count: 數據庫數據總條數
        :param base_url: 分頁前綴URL
        :param per_page_count:   每頁顯示數據條數
        :param show_pager_count: 對多顯示的頁碼
        """
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1
        self.current_page = current_page
        self.total_item_count = total_item_count
        self.base_url = base_url
        self.per_page_count = per_page_count
        self.show_pager_count = show_pager_count
        self.request_params=request_params

        max_pager_num, b = divmod(total_item_count, per_page_count)
        if b:
            max_pager_num += 1
        self.max_pager_num = max_pager_num

    @property
    def start(self):
        """

        :return:
        """
        return (self.current_page-1)* self.per_page_count

    @property
    def end(self):
        """

        :return:
        """
        return self.current_page * self.per_page_count

    def page_html(self):
        """

        :return:
        """
        page_list = []

        if self.current_page == 1:
            prev = ' <li><a href="#">上一頁</a></li>'
        else:
            self.request_params['page']=self.current_page-1
            prev = ' <li><a href="%s?%s">上一頁</a></li>' % (self.base_url,self.request_params.urlencode(),)
        page_list.append(prev)

        half_show_pager_count = int(self.show_pager_count / 2)

        # 數據特別少,15條數據=2頁
        if self.max_pager_num < self.show_pager_count:
            # 頁碼小於11
            pager_start = 1
            pager_end = self.max_pager_num + 1
        else:
            if self.current_page <= half_show_pager_count:
                pager_start = 1
                pager_end = self.show_pager_count + 1
            else:
                if self.current_page + half_show_pager_count > self.max_pager_num:
                    pager_start = self.max_pager_num - self.show_pager_count + 1
                    pager_end = self.max_pager_num + 1
                else:
                    pager_start = self.current_page - half_show_pager_count
                    pager_end = self.current_page + half_show_pager_count + 1

        for i in range(pager_start, pager_end):
            self.request_params['page']=i
            if i == self.current_page:
                tpl = ' <li class="active"><a href="%s?%s">%s</a></li>' % (self.base_url,self.request_params.urlencode(), i,)
            else:
                tpl = ' <li><a href="%s?%s">%s</a></li>' % (self.base_url,self.request_params.urlencode(), i,)
            page_list.append(tpl)

        if self.current_page == self.max_pager_num:
            nex = ' <li><a href="#">下一頁</a></li>'
        else:
            self.request_params['page']=self.request_params+1
            nex = ' <li><a href="%s?%s">下一頁</a></li>' % (self.base_url,self.request_params.urlencode(),)
        page_list.append(nex)

        return mark_safe(''.join(page_list))

    def page_html_js(self):
        page_list = []

        if self.current_page == 1:
            prev = ' <li><a href="#">上一頁</a></li>'
        else:
            prev = ' <li><a onclick="$.changePage(%s)">上一頁</a></li>' %(self.current_page-1,)
        page_list.append(prev)

        half_show_pager_count = int(self.show_pager_count / 2)

        # 數據特別少,15條數據=2頁
        if self.max_pager_num < self.show_pager_count:
            # 頁碼小於11
            pager_start = 1
            pager_end = self.max_pager_num + 1
        else:
            if self.current_page <= half_show_pager_count:
                pager_start = 1
                pager_end = self.show_pager_count + 1
            else:
                if self.current_page + half_show_pager_count > self.max_pager_num:
                    pager_start = self.max_pager_num - self.show_pager_count + 1
                    pager_end = self.max_pager_num + 1
                else:
                    pager_start = self.current_page - half_show_pager_count
                    pager_end = self.current_page + half_show_pager_count + 1

        for i in range(pager_start, pager_end):
            if i == self.current_page:
                tpl = ' <li class="active"><a onclick="$.changePage(%s)"  >%s</a></li>' % (i,i,)
            else:
                tpl = ' <li><a onclick="$.changePage(%s)" >%s</a></li>' % (i, i,)
            page_list.append(tpl)

        if self.current_page == self.max_pager_num:
            nex = ' <li><a href="#">下一頁</a></li>'
        else:
            nex = ' <li><a onclick="$.changePage(%s)" >下一頁</a></li>' %(self.current_page+1,)
        page_list.append(nex)

        return ''.join(page_list)

    def page_html_test(self):
        page_list = []

        if self.current_page == 1:
            prev = ' <li><a href="#">上一頁</a></li>'
        else:
            prev = ' <li><a num="%s">上一頁</a></li>' %(self.current_page-1,)
        page_list.append(prev)

        half_show_pager_count = int(self.show_pager_count / 2)

        # 數據特別少,15條數據=2頁
        if self.max_pager_num < self.show_pager_count:
            # 頁碼小於11
            pager_start = 1
            pager_end = self.max_pager_num + 1
        else:
            if self.current_page <= half_show_pager_count:
                pager_start = 1
                pager_end = self.show_pager_count + 1
            else:
                if self.current_page + half_show_pager_count > self.max_pager_num:
                    pager_start = self.max_pager_num - self.show_pager_count + 1
                    pager_end = self.max_pager_num + 1
                else:
                    pager_start = self.current_page - half_show_pager_count
                    pager_end = self.current_page + half_show_pager_count + 1

        for i in range(pager_start, pager_end):
            if i == self.current_page:
                tpl = ' <li class="active"><a num="%s" >%s</a></li>' % (i,i,)
            else:
                tpl = ' <li><a num="%s" >%s</a></li>' % (i, i,)
            page_list.append(tpl)

        if self.current_page == self.max_pager_num:
            nex = ' <li><a href="#">下一頁</a></li>'
        else:
            nex = ' <li><a num="%s">下一頁</a></li>' %(self.current_page+1,)
        page_list.append(nex)

        return ''.join(page_list)
View Code

 

設計知識點:

from django.http import QueryDict

當咱們向django發送請求時,Django接收request.get()到的數據是query_dict數據類型

query_dict數據類型默認不能夠修改

request.GET.get()._mutable=True   設置該參數後便可修改

還原成urlencode類型:request.GET.urlencode()

 

 

九、單表添加功能

0、設置list顯示頁面add按鈕的跳轉的url ,保存url後面的的get參數

 def add_html(self):  #封裝add 按鈕
        app_model = self.model_config_obj.model_class._meta.app_label, self.model_config_obj.model_class._meta.model_name
        query_dict=QueryDict(mutable=True)
        query_dict['xxoo']=self.model_config_obj.request.GET.urlencode() #xxoo=p%3D1
View Code

 

一、在add_view中定義ModelForm,讓配置對象調用該方法時,能夠顯示html頁面

 self.request=request
        chang_list_url_params=request.GET.get('xxoo')
        from django.forms import ModelForm
        class AddModelForm(ModelForm):
            class Meta:
                model=self.model_class #哪一個配置調用本身的add_view方法來,就建立這個表的ModelForm
                fields='__all__'   #在前端顯示的列
        if request.method=='GET':
            form=AddModelForm()
            context={
                'form':form,
                                 }
            return render(request,'nb/add.html',context)
View Code

 

二、用戶提交數據form驗證經過,攜帶list頁面的參數,跳轉會list頁面

   elif request.method=='POST':
            form = AddModelForm(data=request.POST)
            if form.is_valid():
                form.save()
                base_url=self.revesrs_chang_list_url()+chang_list_url_params
                print(base_url)

                return redirect(base_url)
View Code
    def revesrs_chang_list_url(self):  #爲add_viwe生成 跳轉會list頁面的標籤
        list_url=reverse("%s:%s_%s_changelist" % (self.site.namespace,self.app_lable,self.model_name,) )
        return  list_url
View Code

 

三、自定製ModelForm

經過方法獲取默認配置裏面的ModelForm若是爲空就是使用默認配置,不然使用自定義配置ModelForm

    model_form=None
    def get_model_form_class(self):
        result=self.model_form
        if not result:
            class Defalut_Model_Form(ModelForm):
                class Meta:
                    model = self.model_class  # 哪一個配置調用本身的add_view方法來,就建立這個表的ModelForm
                    fields = '__all__'  # 在前端顯示的列

            result = Defalut_Model_Form

        return result
View Code
class UserInforModelFrom(ModelForm):
    www=fields.CharField(widget=widgets.Textarea)
    class Meta:
        model=models.UserInfo
        fields = '__all__'

class UserInfoConfig(PermissionConfig,v1.Modelzg):  #多繼承 既繼承v1.Modelzg又繼承PermissionConfig 優先
    def xxx(self, obj=None, is_header=False):
        if is_header:
            return '列名稱'
        return obj.name + obj.email
    # list_display = [zhinan, 'id', 'name', 'nickname', 'email', xxx, option]
    model_form=UserInforModelFrom
View Code

 

 

涉及知識點:

一、request.GET.urlencode()

獲取get請求後面攜帶的參數  

    query_dict['xxoo']=self.model_config_obj.request.GET.urlencode() #xxoo=p%3D1
View Code

 

二、ModelForm的使用

 if request.method=='GET':
            form=AddModelForm()
            context={
                'form':form,
                                 }
            return render(request,'nb/add.html',context)
        elif request.method=='POST':
            form = AddModelForm(data=request.POST)
            if form.is_valid():
                form.save()
                base_url=self.revesrs_chang_list_url()+chang_list_url_params
                print(base_url)

                return redirect(base_url)

            context = {
                'form': form,
            }
            return render(request,'nb/add.html', context)
View Code

 

在模板中使用

<form action="" method="post">
    {% csrf_token %}
{#    {{ form.as_p }}#}
    {% for item in form %}
        <p>{{ item.label }}:{{ item }}{{ item.errors.0 }} </p>
    {% endfor %}
    <input type="submit" value="提交">
</form>
View Code

 

 

十、單表編輯/刪除功能

沒時間實現

 

 

 

十一、經過popup實如今userinfo表聯動添加外鍵表usergroup的信息

 userinfo添加頁面顯示

<!DOCTYPE html>
{% load result_form %}
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.css">
    <style>
        .form-horizontal input[type='text'],input[type='checkbox'],input[type='radio'],input[type='email'], select, textarea {
            display: block;
            width: 100%;
            height: 34px;
            padding: 6px 12px;
            font-size: 14px;
            line-height: 1.42857143;
            color: #555;
            background-color: #fff;
            background-image: none;
            border: 1px solid #ccc;
            border-radius: 4px;
            -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
            box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
            -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
            -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
            transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
        }
    </style>
</head>
<body>
<h1>添加頁面</h1>
{% show_form form %}
<h1></h1>
</body>
</html>
View Code

執行inclusion_tag,完成對外鍵表聯動添

from django.template.library import Library
from types import FunctionType
from django.forms.models import ModelMultipleChoiceField
from django.urls import reverse
from django.forms.models import ModelChoiceField
from django.db.models.query import QuerySet
from app02.service.v1 import site

register=Library()


def xxxx(model_form_obj):
    for item in model_form_obj :
        tpl={'has_popup':False,'item':item,'popup_url':None}
        # 判斷userinfo表的列中是否含外鍵字段,而且外鍵的表也得已經註冊
        if isinstance(item.field,ModelChoiceField) and item.field.queryset.model in site._registry:
            tpl['has_popup']=True
            flied_class=item.field.queryset.model  #字段外鍵對應的表名
            app_label=flied_class._meta.app_label #外鍵表所在的app
            model_name=flied_class._meta.model_name#外鍵表的表名
            url= reverse('{0}:{1}_{2}_add'.format(site.namespace,app_label,model_name ))
            tpl['popup_url']=url

        # print(item.field)  #字段類型
        yield tpl


@register.inclusion_tag('nb/change_form.html')
def show_form(model_form_obj):
    return {'form':xxxx(model_form_obj)}

#inclusion_tag returm什麼數據,就在changelist.html裏面渲染什麼數據
#最後誰調用 show_form simpletag就包含了 changelist.html渲染完成頁面
View Code

inclusion_tag使用的頁面

<form class="form-horizontal" method="POST" novalidate>
    {% csrf_token %}
    {% for col in form %}
        <div class="form-group col-sm-6">
            <label class="col-sm-3 control-label">{{ col.item.label }}</label>
            <div class="col-sm-9" style="position: relative">
                {{ col.item }}
                {{ col.item.errors.0 }}
                {% if col.has_popup %}
                    <a onclick="popup('{{ col.popup_url }}')" >添加</a>
                {% endif %}
            </div>

        </div>
    {% endfor %}

    <div class="form-group">
        <div class="col-sm-offset-10 col-sm-2">
            <input type="submit" class="btn btn-primary" value="確認添加"/>
        </div>
    </div>
</form>
View Code

 

 

        elif request.method=='POST':
            form = self.get_model_form_class()(data=request.POST)
            if form.is_valid():
                obj=form.save()
                base_url=self.revesrs_chang_list_url()
                url="%s?%s" %(base_url,chang_list_url_params)
                tagId = request.GET.get('_popup', None)
                if tagId:
                    name=str(obj)  #獲取 name/title
                    val=obj.pk
                    return render(request,'nb/popuprespose.html',{ 'name':name,'tagId':tagId,'val':val })
                return redirect(url)
View Code

 

 

 

涉及知識點:

name=str(obj)         執行在model.py中定義的 __str__方法

 

查看form對象中包含列的相關屬性

def xxxx(model_form_obj):
    for item in model_form_obj :
        tpl={'has_popup':False,'item':item,'popup_url':None}
        # 判斷userinfo表的列中是否含外鍵字段,而且外鍵的表也得已經註冊
        if isinstance(item.field,ModelChoiceField) and item.field.queryset.model in site._registry:
            tpl['has_popup']=True
            flied_class=item.field.queryset.model  #字段外鍵對應的表名
            app_label=flied_class._meta.app_label #外鍵表所在的app
            model_name=flied_class._meta.model_name#外鍵表的表名
            url= reverse('{0}:{1}_{2}_add'.format(site.namespace,app_label,model_name ))
            tpl['popup_url']=url

        # print(item.field)  #字段類型
        yield tpl
View Code

 

js之popup彈窗  (3個HTML頁面)

一、p1頁面定義1個popup回調函數,p1頁面綁定1個popup彈窗時間 到 /p2/

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>p1頁面</h1>
<input type="button" value="按鈕" onclick="popfunc();">

</body>
<script>
    function  popfunc() {
        window.open('/p2/','別名',"status=1, height:500, width:600, toolbar=0, resizeable=0");
{#       點擊按鈕 出現彈窗 到p2     #}
    }
    function xxx(name) {
        alert(name)

    }
</script>
</html>
View Code

二、/p2/下的視圖添加數據庫操做,把增長的row對象模板渲染,re1個頁面popuprespose.html

def p2(request):
    if request.method=='GET':
        return render(request,'p2.html')
    elif request.method=='POST':
        from app01 import models
        city=request.POST.get('city')
        obj= models.UserGroup.objects.create(title=city)
        return render(request,'popuprespose.html',{'obj':obj})
View Code

三、popuprespose.html頁面 自執行函數執行popup發起頁面(p1)的回調函數 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>p1頁面</h1>
<input type="button" value="按鈕" onclick="popfunc();">

</body>
<script>
    function  popfunc() {
        window.open('/p2/','別名',"status=1, height:500, width:600, toolbar=0, resizeable=0");
{#       點擊按鈕 出現彈窗 到p2     #}
    }
    //回調函數
    function xxx(name) {
        alert(name)

    }
View Code

 

 

十二、組合搜素邏輯

組合搜素這個功能,其實很簡單卻浪費我大量鬧細胞;

 

一、把全部在顯示頁面須要的數據封裝進ChangeList類裏,實例化cl對象傳到模板語言經過simple-tag循環、調用生成標籤;這是前提;

 

顯示頁面模板中的2層for循環是切入點:

div class="container">
    <h1>數據列表</h1>
    {#顯示組合搜素開始#}
    <div class="row">
        {% if cl.list_filter %}
            <!-- 遍歷chang_list的gen_list_filter 方法,也就是Userinfo表中的  query_aet) -->
            {% for row_items in cl.gen_list_filter %}
                <div class="filter-item">
                    <!-- 遍歷自自定義的,具備__iter__方法的個可迭代對象  -->
                    {% for item in row_items %}
                        {{ item }}
                    {% endfor %}
                </div>

            {% endfor %}
            <!-- 遍歷自自定義的,具備__iter__方法的個可迭代對象 結束 -->

        {% endif %}
        <!-- 遍歷chang_list中的gen_list_filter 方法結束 -->
    </div>
    {#顯示組合搜素結束#}
View Code

 

1層for循環:  yelid row_items = RowItems(option, data_list, params)

其中ChangeLis中定義的gen_list_filter方法是個生成器,獲取list_filter = [‘group’,'roles ']配置項對應表的數據,for循環gen_list_filter獲得一個RowItems對象(也是個生成器);

 

   def gen_list_filter(self):
        model_class = self.model_config_obj.model_class  # userinfo 類
        params = self.model_config_obj.request.GET  # 從配置對象中獲取request參數,傳入到 RowItems類
        for option in self.list_filter:  # list_filter=['group對象','name對象','roles對象']
            fild_obj = model_class._meta.get_field(option.name)  # 獲取到列對象
            from django.db.models.fields.related import RelatedField
            if isinstance(fild_obj, RelatedField):  # 判斷是否爲外鍵字段
                field_related_class = fild_obj.rel.to  # 獲取到列對應的外鍵表
                data_list = field_related_class.objects.all()
                row_items = RowItems(option, data_list, params)  # 實例化一個RowItems對象
            else:  # 判斷若是是普通字段直接取值
                data_list = model_class.objects.all()
                row_items = RowItems(option, data_list, params)  # 實例化一個RowItems對象

            yield row_items
View Code

 

2層for循環: yield mark_safe( tmp = '<a href="?%s">%s</a>' % (url, text))

遍歷data_list,query_set[obj,objobj ],因此data_list中有幾個對象就行數據,就獲得是1個帶有herf連接url的A標籤;

 

class RowItems():
    def __init__(self, option, data_list, params):
        self.option = option
        self.data_list = data_list
        self.param = copy.deepcopy(params)  # query_dict對象
        print(self.param)  # <QueryDict: {'group': ['1'], 'roles': ['2']}>
        self.param._mutable = True

    def __iter__(self):
        current_pk = self.param.get(self.option.name)  # str類型

        self.param.pop(self.option.name)
        all_url = self.param.urlencode()
        tmp = '<a href="?%s">所有</a>' % (all_url)
        yield mark_safe(tmp)
        for obj in self.data_list:
            pk = str(obj.pk)
            text = str(obj)
            self.param[self.option.name] = pk
            url = self.param.urlencode()
            if current_pk == pk:
                tmp = '<a class="active" href="?%s">%s</a>' % (url, text)  # 設置選擇標籤顏色
            else:
                tmp = '<a href="?%s">%s</a>' % (url, text)
            yield mark_safe(tmp)
View Code

 

 

 

 

 RowItems()的__iter__ 方法,每次根據request.get攜帶的參數,yeild a標籤

 

一、先根據get請求,request.getlist(self.optionname) 獲取current_pk_or_list參數
列表 [5,6]

二、首先self.param.pop(self.option.name)清空group參數本身,留下role參數,生成所有A標籤 ;


三、循環遍歷data_list數據,根據query_set[obj,obj,obj]中有幾個obj,生成幾個A標籤;

3.1先獲得每一個obj的id 和 str(obj)中文顯示內容

3.2判斷是不是多選;

3.3 判斷當前for 循環的obj對象的pk 是否在current_pk_or_list參數列表 [5,6]?

3.4 若是不在就把當前 obj的的id 添加到current_pk_or_list [1,5,6]

3.5 重置<QueryDict: {}>,爲<QueryDict: {'group': ['1', '5', '6']}>

3.6 urlendcodeQueryDict,每次賦值A標籤的herf屬性

 


組合搜素 A標籤生成href url思想:


一、用戶首次訪問:http://127.0.0.1:8000/zg/app01/userinfo/

reques.getlist(self.option.name) #獲得 current_pk_or_list爲空 []

 

二、RowItems類的__iter__方法遍歷   data_list,query_set [ obj ,obj ]

   #生成所有右側 obj  data_list=[obj,obj,obj ]
        for obj in self.data_list:
            #每一個obj 生成 每一個 A標籤
            pk = str(obj.pk)  #1
            text = str(obj)   #阿斯蒂芬
            if self.option.is_muti:  #若是配置了多選
                if pk not in current_pk_or_list: #若是obj.pk不在【1.2】
                    tmp=[]
                    tmp.extend(current_pk_or_list)
                    tmp.append(pk)
                    self.param.setlist(self.option.name,tmp) #注意在原來不變的基礎上多加1個?group=2&group=3
View Code

若是obj.pk不在current_pk_or_list [ ] 中,就把obj.pk添加到tmp,第一次循環進來  tmp=[1 ]

 tmp=[]
 tmp.extend(current_pk_or_list)
 tmp.append(pk)  

 

三、修改 query_dict,<QueryDict: { option.name :  [1]  }

self.param.setlist(self.option.name,tmp) 

 

四、url endconde編碼      ?group=1

url = self.param.urlencode()

 

五、遍歷循環結束,根據obj個數生成A標籤的 url

 

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

一、首次生成url以後,在頁面點擊搜素項,http://127.0.0.1:8000/zg/app01/userinfo/?group=1

  reques.getlist(self.option.name) #獲得 current_pk_or_list爲  [1]

 

 

 二、RowItems類的__iter__方法遍歷   data_list,query_set [ obj ,obj, ]

若是obj.pk不在current_pk_or_list [ ] 中,就把obj.pk添加到tmp,第一次循環進來  tmp=[1,2 ] / tmp=[1,3 ] / tmp=[1,4] 

 

三、url endconde編碼,生成A標籤的url

 

 

 

補充 list_filter 配置  函數和‘數據庫字段’

class UserInfoConfig(sites.AryaConfig):
    list_display = ['name', ]
    def func(self,change_list,option):
        #option,change_list,data_list,param_dict=None
        data_list=models.UserInfo.objects.filter(id__gt=2)
        return sites.FilterRow(option,change_list,data_list,self.request.GET)
    list_filter = [                         #文本顯示的內容          url參數
        sites.FilterOption('group', True, lambda x: x.title, lambda x: x.id),
        sites.FilterOption('name', False, lambda x: x.name, lambda x: x.name),
        # sites.FilterOption(func, False, lambda x: x.name, lambda x: x.name), #自定製函數
    ]
View Code

 

 總結:根據 request.getlist()獲取的 [ ],   判斷當前循環到的obj.id是否在[],如不不存在 添加到 [  ],修改 query_dict { 'option' : [ ]  },urlencode編碼爲url格式做爲A標籤的跳轉連接;

 

 

 

2、CURD組件使用

通過漫長的開發週期,模仿DjangoAdmin開發出來的CURD組件終於可使用了,賜名arya;下面是在1個嶄新的Django項目中使用arya組件流程;

 

一、在settings.py組冊arya爲本程序app

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
    'arya.apps.AryaConfig',

]
View Code

 

二、任意APP下建立 arya.py

 

三、在arya.py中組冊model_class

from . import models
from arya.service.sites import site #導入單例模式

site.register(models.Department,)
View Code

 

四、爲已經註冊的model_class,生成路由映射關係;

from django.conf.urls import url
from django.contrib import admin
from app01 import views
from arya.service.sites import site
urlpatterns = [
    # url(r'^admin/', admin.site.urls),
    url(r'^index/',views.index),
    url(r'^arya/',site.urls),
]
View Code

 

五、修改母版板樣式layout

diango作模板渲染得時候尋找模板的順序是,從最外層templates 到app裏面的templates

因此若是想修改網頁顯示的樣式,就能夠在最外層templates,定義本身的母版,讓其餘頁面繼承;

 

六、增、刪、改、查基本操做

from . import models
from arya.service import sites  #導入單例模式

class DepartmentConfig(sites.AryaConfig):  #註冊部門表
    list_display = ['title']

sites.site.register(models.Department,DepartmentConfig)


class UserinfoConfig(sites.AryaConfig):  #註冊用戶表
    list_display = ['name','email']

sites.site.register(models.UserInfo, UserinfoConfig)


class CoursetConfig(sites.AryaConfig):  #註冊 課程表
    list_display = ['name']

sites.site.register(models.Course, CoursetConfig)

class SchoolConfig(sites.AryaConfig):  #註冊部門表
    list_display = ['title']

sites.site.register(models.School,SchoolConfig)
View Code

 

七、插件擴展

 

0、脫離插件

 

一、擴展URL,extra_url方法,重寫URL  get_urls方法,添加RBAC權限

 

1.0.添加url

class UserinfoConfig(sites.AryaConfig):  #註冊用戶表
    list_display = ['name','email']
    def extra_urls(self):
        app_model_name = self.model_class._meta.app_label, self.model_class._meta.model_name
        patterns = [
            url(r'^(.+)/detail/',self.detail_view, name="%s_%s_changelist" % app_model_name),
        ]
        return patterns

    def detail_view(self,request,pk):
        obj=self.model_class.objects.all().filter(pk=pk).first()
        print(obj.name,obj.id)
        return HttpResponse('顯示詳情')
View Code

 

#工單系統

class Worker_orderConfig(PermissionConfig,v1.AryaConfig):
    def row_graph(self, row=None, is_header=None):
        if is_header:
            return "工單狀態"

        button_list=["<button class='btn btn-danger btn-sm button' type='button'>未理中</button>",
                     "<button class='btn btn-success btn-sm button' type='button'>處理中</button>",
                     "<button class='btn disabled btn-sm button' type='button'>已關閉</button>"
                     ]
        return mark_safe(button_list[row.status] )
    def details(self, row=None, is_header=None):
        if is_header:
            return "工單流程"
        return mark_safe("<a href="">詳細</a>" )

    def fenpei(self, row=None, is_header=None):
        if is_header:
            return "分配"
        return mark_safe("<a href='/allocation/?id=%s'>分配</a>"%(row.id) )


    list_display = ['alarm_time', 'initiator', 'title', 'agent',
                    'desc',row_graph,fenpei,details,]

    def extra_url(self):
        app_model_name = self.model_class._meta.app_label, self.model_class._meta.model_name
        print(app_model_name)
        p= [
        url(r'^worker_order_out/$', self.detail_view, name="%s_%s_t" % app_model_name),
            ]
        return p

    def detail_view(self, request):
        return HttpResponse('顯示詳情')

v1.site.register(models.Worker_order,Worker_orderConfig)
擴展URL

 

1.2.arya生成url的規則 

 

/arya開頭/app名稱/model表名/擴展url的名/: 例如:/arya/cmdb/worker_order/worker_order_out/

 

1.3.在RBAC中爲角色增長該URL的訪問權限

在權限管理----》URL權限添加權限標題:測試爲標題

 

 

 

 

 

 

二、自定義添加、顯示、刪除頁面,重寫基礎配置類中的add_view 、 changelist_view 、delete_view

注意每個方法,都要執行self.request=request

class UserinfoConfig(sites.AryaConfig):  #註冊用戶表
    list_display = ['name','email']
    def add_view(self,request):
        return render(request,'test.html')
View Code

 

 

3、RBAC權限錄入

RBAC基於角色的權限管理系統回顧:詳見  Django之權限管理插件

表結構:用戶表  Many to many    角色表  Many to many 權限表   foreign key 菜單表 

中間件:判斷當前訪問用戶是否有訪問權限

登陸成功初始化:把當前用戶全部權限初始化放到session中

配置文件: 無需登陸就能使用的權限,session的key

自動生成菜單:css、JavaScript

 

一、註冊RBAC app

'rbac.apps.RbacConfig',
View Code

 

二、清空rbac插件的migration記錄

 

 

 

三、遷移rbac插件的數據

 

python manage.py makemigrations

python manage.py migrate

 

四、在rbac建立arya.py註冊rbac model_class

from arya.service import sites
from . import models



sites.site.register(models.User)
sites.site.register(models.Role)

class PermissionConfig(sites.AryaConfig):
        pass

sites.site.register(models.Permission,PermissionConfig)
sites.site.register(models.Menu)
View Code

 

五、建立權限信息arya/rbac/permission/

 

六、完善CURD插件功能;

一、添加權限自動發現URL;

(1)方式1

 定製list_play=[]

 重寫add_view

lass PermissionConfig(sites.AryaConfig):
        def dabo(self, obj=None, is_header=False):
                if is_header:
                        return '其餘'
                return obj.caption+'大波'
        list_display = ['caption','url','menu',dabo]

        def add_view(self, request, *args, **kwargs):
                from pro_crm.urls import urlpatterns
                all_url_list=get_all_url(urlpatterns,prev='/',is_first=True,)
                model_form_cls = self.get_model_form_class()
                popup_id = request.GET.get(self.popup_key)
                if request.method == 'GET':
                        form = model_form_cls()
                        return render(request, "permission_add_popup.html" if popup_id else "permission_add.html",{'form': form,'url_list':all_url_list})
                elif request.method == "POST":
                        form = model_form_cls(data=request.POST, files=request.FILES)
                        if form.is_valid():
                                obj = self.save(form, True)
                                if obj:
                                        if popup_id:
                                                context = {'pk': obj.pk, 'value': str(obj), 'popup_id': popup_id}
                                                return render(request, 'arya/popup_response.html',
                                                              {"popup_response_data": json.dumps(context)})
                                        else:
                                                return redirect(self.changelist_url_params)
                        return render(request,"permission_add_popup.html" if popup_id else "permission_add.html", {'form': form,'url_list':all_url_list})





sites.site.register(models.Permission,PermissionConfig)
View Code

 

(1)方式2

修改ModelForm組件

from arya.service import sites
from . import models
from django.shortcuts import render,redirect
import json

from django.urls.resolvers import RegexURLPattern
def get_all_url(patterns,prev,is_first=False,result=[],):
    if is_first:
        result.clear()
    for item in patterns:
        v=item._regex.strip('^$')
        if isinstance(item,RegexURLPattern):
            val=prev+v
            result.append((val,val))
        else:
            get_all_url(item.urlconf_name,prev+v)
    return result



sites.site.register(models.User)
sites.site.register(models.Role)

from django.forms import ModelForm
from django.forms import fields
from django.forms import widgets

#自定義model_form

class PermissionModelForm(ModelForm):
      #ModelForm 能夠結合Model把全部數據庫字段在頁面上生成,也能夠增長額外的字段;
    url=fields.ChoiceField()
    class Meta:
        fields = "__all__"
        model = models.Permission  #注意不是models
    def __init__(self,*args,**kwargs):   #重寫父類的 __init__方法,每次實例化實時更新 form中的數據
        super(PermissionModelForm,self).__init__(*args,**kwargs)
        from pro_crm.urls import urlpatterns
        self.fields['url'].choices=get_all_url(urlpatterns,'/', True)


class PermissionConfig(sites.AryaConfig):
        def dabo(self, obj=None, is_header=False):
                if is_header:
                        return '其餘'
                return obj.caption+'大波'
        list_display = ['caption','url','menu',dabo]
        model_form = PermissionModelForm
View Code

 

二、經過裝飾器實現每次請求保存request信息

   def warpper(self,func): #每次獲取 self.requesr=request
        @functools.wraps(func) #
        def inner(request,*args,**kwargs):
           self.request=request
           return func(request,*args,**kwargs )
        return inner
View Code
 patterns = [
            url(r'^$',self.warpper(self.changelist_view), name="%s_%s_changelist" % app_model_name),
            url(r'^add/$',self.warpper(self.add_view), name="%s_%s_add" % app_model_name),
            url(r'^(.+)/delete/$',self.warpper(self.delete_view), name="%s_%s_delete" % app_model_name),
            url(r'^(.+)/change/$',self.warpper(self.change_view), name="%s_%s_change" % app_model_name),
        ]
View Code

 

 

 

 

設計知識點:

(1)、裝飾器:

@warpper

def foo()

  pass

foo=warpprt(foo)=return的結果

 

(2)、@functools.wraps(func) 

裝飾器函數裏面的文檔介紹 徹底= 原生函數的文檔介紹

 

(3)、ModelForm 增長數據庫字段以外的field,ModelForm對象實時更新數據

Django之Form、ModelForm 組件

ModelForm 能夠結合Model把全部數據庫字段在頁面上生成,也能夠增長額外的字段;

規則:若是增長的字段和數據裏的filed重名則覆蓋,不重名則新增;

也能夠經過重寫__init__ ,每次實例化1個form對象,實時更新數據;

  View Code

 

 

 

4、Arya整合RBAC整合CRM

 

一、建立名爲crm的app,而且在setings.py裏面註冊

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'arya.apps.AryaConfig',
    'rbac.apps.RbacConfig',
    'crm.apps.CrmConfig',

]
View Code

 

二、使用RBAC,配置RBAC的表;(只能訪問本身權限的URL,登陸以後獲取本身的權限列表放入request.session)

from arya.service import sites
from . import models
from django.shortcuts import render,redirect
import json

from django.urls.resolvers import RegexURLPattern
def get_all_url(patterns,prev,is_first=False,result=[],):
    if is_first:
        result.clear()
    for item in patterns:
        v=item._regex.strip('^$')
        if isinstance(item,RegexURLPattern):
            val=prev+v
            result.append((val,val))
        else:
            get_all_url(item.urlconf_name,prev+v)
    return result



sites.site.register(models.User)
sites.site.register(models.Role)

from django.forms import ModelForm
from django.forms import fields
from django.forms import widgets


#-------------------用戶表相關配置
class UserConfig(sites.AryaConfig):
    list_display = ['username','email' ]
#--------------------角色相關配置
class RoleConfig(sites.AryaConfig):
    list_display = ['caption',]

#----------------------權限相關配置
#自定義model_form
class PermissionModelForm(ModelForm):
      #ModelForm 能夠結合Model把全部數據庫字段在頁面上生成,也能夠增長額外的字段;
    url=fields.ChoiceField()
    class Meta:
        fields = "__all__"
        model = models.Permission  #注意不是models
    def __init__(self,*args,**kwargs):   #重寫父類的 __init__方法,每次實例化實時更新 form中的數據
        super(PermissionModelForm,self).__init__(*args,**kwargs)
        from pro_crm.urls import urlpatterns
        self.fields['url'].choices=get_all_url(urlpatterns,'/', True)
class PermissionConfig(sites.AryaConfig):
        def dabo(self, obj=None, is_header=False):
                if is_header:
                        return '其餘'
                return obj.caption+'大波'
        list_display = ['caption','url','menu',dabo]
        model_form = PermissionModelForm



sites.site.register(models.User,UserConfig)
sites.site.register(models.Permission,PermissionConfig)
sites.site.register(models.Role,RoleConfig)
sites.site.register(models.Menu)
View Code

 

三、添加crm登陸頁面  

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CRM系統登陸</title>
</head>
<body>
<form action="" method="post">
    {% csrf_token %}
    <p>用戶名:<input type="text" name="username"></p>
    <p>密碼:<input type="submit" name="pwd"></p>
    <input type="submit" value="提交">
</form>


</body>
</html>
View Code

 

四、添加crm首頁

設置母版layout

<!--- 加載rbac插件中的simp-tag-->
<!--- 加載rbac插件中的css-->
//加載rbac插件中的js
{% load static %}
{% load rbac %}   <!--- 加載rbac插件中的simp-tag-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
    <link rel="stylesheet" href="{% static 'arya/plugins/bootstrap/css/bootstrap.css' %}"/>
    <link rel="stylesheet" href="{% static 'arya/css/commons.css' %}"/>
    <sytyle>
        <!--- 加載rbac插件中的css-->
        {% rbac_css %}
    </sytyle>
    {% block css %}{% endblock %}
</head>
<body>
<div class="pg-header">
    <div class="logo left" style="text-align: center;background-color: #1c5a9c;">
        <a href="#" style="color: #ffffff;font-size:22px;font-weight: bold;text-decoration: none">
            CRM系統
        </a>
    </div>

    <div class="left-menu left">
        <a class="menu-item" href="#">平臺首頁</a>
        <a class="menu-item" href="#">資產首頁</a>
    </div>

    <div class="right-menu right clearfix">

        <div class="user-info right">
            <a href="#" class="avatar">
                <img class="img-circle" src="{% static 'arya/img/default_avatar.png' %}">
            </a>

            <div class="more-info">
                <a href="#" class="more-item">我的信息</a>
                <a href="/logout.html" class="more-item">註銷</a>
            </div>
        </div>

        <a class="user-menu right">
            消息
            <i class="fa fa-commenting-o" aria-hidden="true"></i>
            <span class="badge bg-success">2</span>
        </a>

        <a class="user-menu right">
            通知
            <i class="fa fa-envelope-o" aria-hidden="true"></i>
            <span class="badge bg-success">2</span>
        </a>

        <a class="user-menu right">
            任務
            <i class="fa fa-bell-o" aria-hidden="true"></i>
            <span class="badge bg-danger">4</span>
        </a>
    </div>

</div>

<div class="pg-body">
    <div class="menu">
        {% rbac_menu request %}

    </div>
    <div class="content">
        {% block breadcrumb %} {% endblock %}
        {% block content %} {% endblock %}
    </div>
</div>


<script src="{% static 'arya/js/jquery-1.12.4.js' %}"></script>
<script src="{% static 'arya/plugins/bootstrap/js/bootstrap.js' %}"></script>

<script>
    {% rbac_js %}  //加載rbac插件中的js
</script>
{% block js %} {% endblock %}
</body>
</html>
View Code

 

設置crm首頁

    {% extends 'arya/layout.html' %}

    {% block content %}
    <h1>歡迎登陸</h1>
    {% endblock %}
View Code

 

 

五、index視圖執行RBAC插件邏輯

 

1)登陸成功以後初始化權限信息

 initial_permission(request,obj)  #初始化用戶權限信息

 

2)頁面顯示時生成 菜單HTML

 

3)setings.py配置文件 引入rbac中間件、設置rbac使用的key、simple_tag生成多級菜單

'rbac.middleware.rbac.RbacMiddleware',  #引入rbac中間件
View Code
# ############################## RBAC權限相關配置開始 ##############################
# session中保存權限信息的Key
RBAC_PERMISSION_URL_SESSION_KEY = "rbac_permission_url_session_key"

# Session中保存菜單和權限信息的Key
RBAC_MENU_PERMISSION_SESSION_KEY = "rbac_menu_permission_session_key"
RBAC_MENU_KEY = "rbac_menu_key"
RBAC_MENU_PERMISSION_KEY = "rbac_menu_permission_key"

# 匹配URL時指定規則
RBAC_MATCH_PARTTERN = "^{0}$"

# 無需權限控制的URL
RBAC_NO_AUTH_URL = [
    '/login/',
    "/index/",
]

# 無權訪問時,頁面提示信息
RBAC_PERMISSION_MSG = "無權限訪問"

# 菜單主題
RBAC_THEME = "default"
# ############################## RBAC權限相關配置結束 ##############################
View Code

 

 

 

六、在權限中間件以前,經過UserAuthMiddleware判斷用戶是否已經登陸,登陸以後纔有必要設置權限,不然直接返回登陸頁面;

權限中間件沒有必要,每次每一個用戶都要執行一次,全部在權限中間件以前新增一箇中間件,先判斷用戶是否已經登陸,沒有登陸直截了當讓他先登陸;

 

        if obj:
            request.session['user_info'] = {'nid': obj.id} #用戶登陸後 設置登陸標記
            initial_permission(request,obj)  #初始化用戶權限信息
            return redirect('/index/')    #跳轉到首頁
class UserAuthMiddleware(MiddlewareMixin):

    def process_request(self,request):
        # 若是是/login/能夠繼續執行
        if request.path_info == '/login/':
            return None
        # 設置權限以前,判斷用戶是否已經登陸?登陸以後才判斷是否有權限
        user_info = request.session.get('user_info')
        if not user_info:
            return redirect('/login/')
View Code

 

七、CRM的用戶表和RBAC的用戶表作One to One關聯

from rbac.models import User
class UserInfo(models.Model):
    """
    員工表
    """
    user = models.OneToOneField(verbose_name='用戶帳號',to=User)
    name=models.CharField(verbose_name='員工姓名',max_length=16)
    phone=models.CharField(verbose_name='員工電話',max_length=16)
    depart = models.ForeignKey(verbose_name='部門', to="Department")
View Code

 

 

八、CRM表結構 配置與頁面顯示

 

自定義函數:list_display顯示多對多關係字段

aryaCURD插件: list_display 定義函數,自定義函數返回什麼list頁面顯示什麼,外鍵關係連表查詢

from . import models
from arya.service import sites
from django.utils.safestring import mark_safe
class DepartmentConfig(sites.AryaConfig):
    list_display = ['title']

sites.site.register(models.Department,DepartmentConfig)

class UserInfoConfig(sites.AryaConfig):
    list_display = ['name','phone']

sites.site.register(models.UserInfo,UserInfoConfig)

class CourseConfig(sites.AryaConfig):
    list_display = ['name']

sites.site.register(models.Course,CourseConfig)

class SchoolConfig(sites.AryaConfig):
    list_display = ['title']
sites.site.register(models.School,SchoolConfig)

class ClassListConfig(sites.AryaConfig):
    def course_dispaly(self,obj=None,is_header=False):
        if is_header:
            return '班級'
        return '%s(%s)期'% (obj.course.name,obj.semester)
    def zhanggen(self,obj=None,is_header=False):
        if is_header:
            return '任課老師'
        teachers=[]
        for obj in obj.teachers.all():
            tpl='<span style="dispaly:inline-block;padding:3px;margin:2px;boder:1px solid #add;" >%s</span>'% (obj.name)
            teachers.append(tpl)
        return mark_safe(''.join(teachers))
    list_display = ['school',course_dispaly,'price','tutor',zhanggen]

sites.site.register(models.ClassList,ClassListConfig)
View Code

 

自定義函數:list_display顯示choices字段

利用obj的get_gender_display()特性,顯示choice字段對應的中文名稱

1   def gender_display(self,obj=None,is_header=False):  #顯示 choice 字段
2         if is_header:
3             return '性別'
4         return obj.get_gender_display()
5     list_display = ['qq', 'name', 'consultant',zhanggen,gender_display,]
6 sites.site.register(models.Customer,CustomerConfig)
View Code

 

小結:

通過CRM業務和Aaya(增、刪、改、查)插件 list_display配置項的結合配置

就能夠 把數據庫字段、外鍵字段、多對多字段、choice字段 都顯示出來了;

 

九、CRM表結構 配置組合篩選

(1)、改進list_filter  

組合搜素顯示:

foreign key

M2M

choice

 def __init__(self, option, change_list, data_list, param_dict=None,is_choices=False):
View Code
  for obj in self.data_list:
            param_dict = copy.deepcopy(self.param_dict)
            if self.is_choices:
                pk=str(obj[0])
                text=obj[1]
View Code

 

經過  Q添加篩選條件

sites.FilterOption('consultant',condtion=Q(depart_id=2)),
View Code
   def __init__(self, field_or_func, is_multi=False, text_func_name=None, val_func_name=None,condtion=None):
View Code
  @property
    def get_condtion(self):
        from django.db.models import Q
        if self.condtion:
            return self.condtion
        con=Q()
        return con
View Code

 

 

九、點擊組合篩選,顯示篩選數據;(讀取URL參數,拼接成字典,去數據庫查詢)

 @property
    def get_filter_conditon(self):
        fields2 = [obj.name for obj in self.model_class._meta._get_fields()]
        params = self.request.GET
        con = {}
        for k in params:
            if k in fields2:
                v = self.request.GET.getlist(k)
                k = '%s__in' % (k)
                con[k] = v
        return con
View Code

 

涉及知識點:

一、獲取數據庫中 外鍵字段、choice字段(注意不包含多對多字段)

 fields=[ obj.name  for obj in  self.model_class._meta.fields]  #獲取數據中 外鍵字段、choice字段(主語沒有多對多)

二、獲取數據庫中 多對多字段

fields1 = [obj.name for obj in self.model_class._meta.many_to_many]  #獲取多對多字段

 

 

三、獲取數據庫中全部字段 (包含反向關聯、多對多字段)

# fields2 = [obj.name for obj in self.model_class._meta._get_fields()]  # 獲取反向關聯的字段(包含對多多)

 

四、查詢數據去重 distinct()

data_list = self.model_class.objects.filter(**self.get_filter_conditon).filter(self.get_search_condtion).distinct() #distinct() 多選去重

 

 

 

十、CRM客戶部 配置模糊搜素功能 search_list = ['name', 'qq'],;

search_list = ['name', 'qq']

 

基礎配置類

search_list = []
    def get_search_list(self):
        search_list = []
        search_list.extend(self.search_list)
        return search_list
View Code
@property
    def get_search_condtion(self):
        con=Q()
        con.connector='OR'
        val=self.request.GET.get(self.q)
        if not val:
            return con
        fild_list=self.get_search_list()
        for field in fild_list:
            field='%s__contains'%(field)
            con.children.append((field,val))
        return con
View Code

ChangeList類
  self.search_list = model_config.get_search_list()
    def seach_attr(self):
        val=self.model_config.request.GET.get(self.model_config.q)
        print(val)
        return {'value':val,'name':self.model_config.q}
View Code

 

模板

{% if cl.list_filter %}
            <div class="comb-search">
                {% for row in cl.gen_list_filter %}
                    <div class="row">
                        {% for col in row %}
                            {{ col }}
                        {% endfor %}
                    </div>
                {% endfor %}
            </div>
        {% endif %}
        {% if cl.search_list %}
            <div class="row">
            <form method="get">
                <input value="{{cl.seach_attr.value}}" class="form-control" name="{{cl.seach_attr.name}}" style="width: 250px;display: inline-block" type="text" placeholder="請輸入關鍵字">
                <button class="btn btn-primary">
                    <span class="glyphicon glyphicon-search"></span>
                </button>
            </form>
        </div>
        {% endif %}
View Code
相關文章
相關標籤/搜索