随笔 - 241  文章 - 1  评论 - 58  阅读 - 85万 

前言

如何提升自己的开发效率?

每个新项目都是自己经做过的项目(经验所致),在项目开发过程中不断总结、封装属于自己的组件,

例如:每个web项目大部分都涉及增删改查,分页显示,搜素,CRM就是这样的组件,是一件很有必要的事情;

 

CURD组件(arya):模仿DjangoAdmin编写增删改查插件;

组件功能:通过在后台注册表名配置自定制类,操作、显示数据库中的内容;

组件设计目标:把CRM模块嵌套到不同具有增删改查功能的Django程序上,实现快速开发基于web的后台管理系统;

 

组件配置项:

list_display=[ '字段',‘函数’] : 定制显示页面显示的列名,和内容;

action                                   : 定制action,批量操作;

show_add_btn=True            :定制是否显示添加按钮

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类:组合搜素 配置项  

1
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
复制代码

 

 

 

 

 

 

一、CURD组件开发

1、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
复制代码

 

2、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
复制代码

 

 3、找到后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个对象;

4、zhanggen.py实例化了v1中Zgsite类,得到单例对象site,site单例对象 2次(2张表) 调用了单例象的register()方法,

复制代码
v1.site.register(models.UserInfo)
v1.site.register(models.UserGroup)

#1、导入v1就得到一个单例对象 site
#2、执行site对象的regist()进行注册
View Code
复制代码

 

5、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
复制代码

 

 

6、Django程序的路由系统开始执行,同样调用了单例对象 v1.py中的单例对象 site,并执行了site单例对象的urls方法;

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

 

7、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
复制代码

 

8、ger_urls方法,for循环了单例对象的self._registry属性,为每张表生成基础url映射关系;

^zg/ ^login/

^zg/ ^logout/

^zg/ ^app01/userinfo/

^zg/ ^app01/usergroup/

 

 

 9、单例对象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
复制代码

 

涉及知识点:

1、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
复制代码

 

2、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
复制代码

 

3、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

 

4、扩展、和重写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内容)

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

 

1、由每张表的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
复制代码

 

 

 

2、基础配置对象 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
复制代码

 

 

3、nb/changelist.html加载 result_list simp_tag,并把后端传来c1对象传到{% show_result_list cl%} include_tag

 

4、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
复制代码

 

 5、前端根据权限   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 可以判断是否显示添加权限;

 

 6、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

 

 7、用户自定制 批量删除和初始化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

 

 

8、分页保留原来的搜素条件

当我们在搜素框输入了搜素条件,点击下一页应当保存搜素条件!

例如:当前页: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(
            #自定制分页器需要传入的参数
            #1、当前访问页码
            current_page=model_config_obj.request.GET.get('page'),
            #2、 数据库总条目数
            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()

 

 

9、单表添加功能

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
复制代码

 

1、在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
复制代码

 

2、用户提交数据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

 

3、自定制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
复制代码

 

 

涉及知识点:

1、request.GET.urlencode()

获取get请求后面携带的参数  

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

 

2、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
复制代码

 

 

10、单表编辑/删除功能

没时间实现

 

 

 

11、通过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页面)

1、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
复制代码

2、/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
复制代码

3、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
复制代码

 

 

12、组合搜素逻辑

组合搜素这个功能,其实很简单却浪费我大量闹细胞;

 

1、把所有在显示页面需要的数据封装进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标签

 

1、先根据get请求,request.getlist(self.optionname) 获取current_pk_or_list参数
列表 [5,6]

2、首先self.param.pop(self.option.name)清空group参数自己,留下role参数,生成全部A标签 ;


3、循环遍历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思想:


1、用户首次访问:http://127.0.0.1:8000/zg/app01/userinfo/

reques.getlist(self.option.name) #得到 current_pk_or_list为空 []

 

2、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)  

 

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

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

 

4、url endconde编码      ?group=1

url = self.param.urlencode()

 

5、遍历循环结束,根据obj个数生成A标签的 url

 

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

 

 

1、首次生成url之后,在页面点击搜素项,http://127.0.0.1:8000/zg/app01/userinfo/?group=1

  reques.getlist(self.option.name) #得到 current_pk_or_list为  [1]

 

 

 2、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] 

 

3、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标签的跳转链接;

 

 

 

二、CURD组件使用

经过漫长的开发周期,模仿DjangoAdmin开发出来的CURD组件终于可以使用了,赐名arya;下面是在1个崭新的Django项目中使用arya组件流程;

 

1、在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
复制代码

 

2、任意APP下创建 arya.py

 

3、在arya.py中组册model_class

复制代码
from . import models
from arya.service.sites import site #导入单例模式

site.register(models.Department,)
View Code
复制代码

 

4、为已经注册的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
复制代码

 

5、修改母版板样式layout

diango做模板渲染得时候寻找模板的顺序是,从最外层templates 到app里面的templates

所以如果想修改网页显示的样式,就可以在最外层templates,定义自己的母版,让其他页面继承;

 

6、增、删、改、查基本操作

复制代码
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
复制代码

 

7、插件扩展

 

0、脱离插件

 

1、扩展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权限添加权限标题:测试为标题

 

 

 

 

 

 

2、自定义添加、显示、删除页面,重写基础配置类中的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
复制代码

 

 

三、RBAC权限录入

RBAC基于角色的权限管理系统回顾:详见  Django之权限管理插件

表结构:用户表  Many to many    角色表  Many to many 权限表   foreign key 菜单表 

中间件:判断当前访问用户是否有访问权限

登录成功初始化:把当前用户所有权限初始化放到session中

配置文件: 无需登录就能使用的权限,session的key

自动生成菜单:css、JavaScript

 

1、注册RBAC app

'rbac.apps.RbacConfig',
View Code

 

2、清空rbac插件的migration记录

 

 

 

3、迁移rbac插件的数据

 

python manage.py makemigrations

python manage.py migrate

 

4、在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
复制代码

 

5、创建权限信息arya/rbac/permission/

 

6、完善CURD插件功能;

1、添加权限自动发现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
复制代码

 

2、通过装饰器实现每次请求保存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

 

 

 

四、Arya整合RBAC整合CRM

 

1、创建名为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
复制代码

 

2、使用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
复制代码

 

3、添加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
复制代码

 

4、添加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
复制代码

 

 

5、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
复制代码

 

 

 

6、在权限中间件之前,通过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
复制代码

 

7、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
复制代码

 

 

8、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字段 都显示出来了;

 

9、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
复制代码

 

 

9、点击组合筛选,显示筛选数据;(读取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
复制代码

 

涉及知识点:

1、获取数据库中 外键字段、choice字段(注意不包含多对多字段)

 fields=[ obj.name  for obj in  self.model_class._meta.fields]  #获取数据中 外键字段、choice字段(主语没有多对多)

2、获取数据库中 多对多字段

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

 

 

3、获取数据库中所有字段 (包含反向关联、多对多字段)

# fields2 = [obj.name for obj in self.model_class._meta._get_fields()]  # 获取反向关联的字段(包含对多多)

 

4、查询数据去重 distinct()

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

 

 

 

10、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
复制代码

 

posted on   Martin8866  阅读(738)  评论(1编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示