• python 全栈开发,Day116(可迭代对象,type创建动态类,偏函数,面向对象的封装,获取外键数据,组合搜索,领域驱动设计(DDD))


     

    昨日内容回顾

    1. 三个类 
        ChangeList,封装列表页面需要的所有数据。
        
        StarkConfig,生成URL和视图对应关系 + 默认配置 
        
        AdminSite,用于保存 数据库类 和 处理该类的对象 的对应关系 + 路由分发
                  
                  _registry = {
                    
                  }
    2. 知识点 
        
        inclusion_tag
            yield
        
        urlencode
        
        _meta.model_name
        _meta.app_label
        
        深浅拷贝 
        
        QueryDict对象默认不可改 _mutable=True 
        
        生成器 
        
        路由分发:
            - include
            - ([],None,None)
            
        函数和方法的区别?
            
        Q的作用?构造复杂的查询条件
        
            models.User.object.filter(name__contains='')
            models.User.object.filter(name__contains='',email__contains='')
            
            构造 or 
            c1 = Q()
            c1.connector = 'OR'
            c1.children.append( ('name__contains','') )
            c1.children.append( ('email__contains','') )
            
            c2 = Q()
            c2.connector = 'ADN'
            c2.children.append( ('id__gt',2) )
            c3.children.append( ('age__lte',5) )
            
            c3 = Q()
            c3.connector = 'ADN'
            
            c3.add(c1,"ADN")
            c3.add(c2,"ADN")
            等同于
            (name=li or email = li)  AND ( id>2 and age<=5)
            
            models.User.object.filter(con)
        
        反射 
            有2个地方用到了反射
            list_display:  # 显示指定的列
                row.name 
                getattr(row,'name')
                  
            # 批量操作
            response = getattr(self, action_name)(request)
                pass
                
        继承 
            
            class RoleConfig(StarkConfig):
                pass 
                
            self到底是谁?
            
        反向生成URL
            reverse('xxx')
            reverse('namespace:xxx')
            多个namespace,也是以冒号分隔
        
        分页(保留原搜索条件) 
            跳转的url中保留_filter=原搜索条件
        
        ModelForm组件
            添加和修改
            
            
        functools
            - wraps,用于保留原函数的元信息(函数名/函数注释等信息)
            - partial,偏函数为函数默认传参。
                import functools
    
                def func(a1,a2):
                    print(a1+a2)
    
                new_func = functools.partial(func,8)
    
                new_func(7)
                new_func(2)
                new_func(8)
        
        预留可扩展位置
            
        request.GET
            request.GET.get('x')
            request.GET['x']
            request.GET.getlist('xxx')
            request.GET._mutable = True 
            request.GET.copy()
            request.GET.urlencode()
            
        mark_safe 
            
        xss攻击是什么?
            跨站脚本攻击
            一般用js代码,进行攻击。
            获取cookie,模拟登录,做非法操作!
            
        单例模式
            - 多个模块之间导入
            - 共享同一个数据时
            
        获取函数名
            __name__
            
        autodiscover_module
            自动发现模块
        
        装饰器 
            添加request参数,方便非视图函数调用request
            
            
        order_by
            排序展示
            
        __str__
            在models.py中使用
            
    3. QueryDict对象 
    
        params = request.GET.copy()  # 使用深copy
        params._mutable = True # 允许修改
        
        params['k1'] = 'v1'  # 添加单个
        params.setlist('k2',[11,22])  # 添加多个
    View Code

    一、可迭代对象

    什么是可迭代对象

    在之前学过的容器中,许多容器都是可迭代对象,可以直接用于for…in…循环的而对象都是可迭代对象,比如:list,tuple,dict,set,str等等。

    可迭代对象满足条件:实现了__iter__方法

    注意:__iter__方法必须返回一个迭代器(iter)

    可迭代对象并不是一种具体的数据类型,比如list是可迭代对象,dict也是可迭代对象。

    如何判断一个对象是否是可迭代对象? 使用isinstance()函数

    from collections import Iterable
    
    a = isinstance("123",Iterable)
    b = isinstance(1,Iterable)
    print(a) #字符串是不是可迭代对象 返回True
    print(b) #数字是不是可迭代对象  返回False

    举例1

    看下面一段代码

    class Foo(object):
        pass
    
    obj = Foo()
    for item in obj:
        print(item)

    执行报错:

    TypeError: 'Foo' object is not iterable

    提示不可迭代,怎么让它可以迭代?

    先来看可迭代对象满足条件:实现了__iter__方法

    那么在类中,定义一个__iter__方法,返回迭代器就可以了!

    class Foo(object):
        def __iter__(self):
            # return iter([11,22,33,44]) # 返回迭代器
            yield 11  # 返回生成器(迭代器的一种)
    
    obj = Foo()
    for item in obj:
        print(item)

    执行输出: 11

    举例2

    这是汽车之家的筛选页面,每一个行的数据是循环展示的!如何构造这些数据?

    方法一:使用字典

    data_list= [
        ['1.0以下','1.1-1.6'],
        ['汽油','柴油','混合动力','电动'],
    ]
    
    for row in data_list:
        for field in row:
            print(field)

    执行输出:

    1.0以下
    1.1-1.6
    汽油
    柴油
    混合动力
    电动
    View Code

    方法二:使用对象+yield

    class Row(object):
        def __init__(self,data):
            self.data = data
    
        def __iter__(self):
            for item in self.data:
                yield item
    
    data_list= [
        Row(['1.0以下','1.1-1.6']),
        Row(['汽油','柴油','混合动力','电动']),
    ]
    
    for row in data_list:
        for field in row:
            print(field)
    View Code

    执行输出,效果同上!

    为什么要用对象构造数据?因为要构造更复杂的数据结构!

    class Row(object):
        def __init__(self,data):
            self.data = data
    
        def __iter__(self):
            yield "<div>"
            yield '全部'
            for item in self.data:
                yield "<a href='/index/?p1=1.0'>%s</a>" %item
            yield "</div>"
    
    data_list= [
        Row(['1.0以下','1.1-1.6']),
        Row(['汽油','柴油','混合动力','电动']),
    ]
    
    for row in data_list:
        for field in row:
            print(field)
    View Code

    执行输出:

    <div>
    全部
    <a href='/index/?p1=1.0'>1.0以下</a>
    <a href='/index/?p1=1.0'>1.1-1.6</a>
    </div>
    <div>
    全部
    <a href='/index/?p1=1.0'>汽油</a>
    <a href='/index/?p1=1.0'>柴油</a>
    <a href='/index/?p1=1.0'>混合动力</a>
    <a href='/index/?p1=1.0'>电动</a>
    </div>
    View Code

    可以看出,这段数据,比上面的复杂!

    如果用列表,那么有很多if判断!使用类,2层for循环就出来了!

    为什么要输出a标签?因为前端渲染麻烦,后端生成a标签。前端容易展示,还可以做一些复杂的需求!

    二、type创建动态类

    前戏

    举例1

    def gen_cls():
        class Foo(object):
            pass
    
        return Foo
    
    cls = gen_cls()
    
    print(cls)
    View Code

    执行输出:

    <class '__main__.gen_cls.<locals>.Foo'>

    对象是由类创建的。那么类是由谁创建的?先带着这个疑问

    举例2

    name = "Foo"
    country = "中国"
    detail = lambda self, x: x + 1

    根据以上三个参数创建一个类,类中有两个成员。实现的效果如下:

    class Foo(object):
        country = '中国'
        def detail(self,x):
            return x + 1 

    如何用代码实现呢?

    不能这么写

    class name(object):
        country = '中国'
        ...

    那么class名就是name了,需要使用type

    type创建动态类

    type还有一种完全不同的功能,动态的创建类。

    type可以接受一个类的描述作为参数,然后返回一个类。(要知道,根据传入参数的不同,同一个函数拥有两种完全不同的用法是一件很傻的事情,但这在Python中是为了保持向后兼容性)

    type可以像这样工作:

    type(类名,由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值)) 

    根据上面的3个条件,使用type创建一个类

    name = "Foo"
    country = "中国"
    detail = lambda self, x: x + 1
    # 使用type创建动态类
    cls = type(name, (object,), {'country': '中国', 'detail': lambda self, x: x + 1})
    
    obj = cls()
    print(obj)
    print(obj.country)
    print(obj.detail(100))
    View Code

    执行输出:

    <__main__.Foo object at 0x000001E0FBF2DA20>
    中国
    101

    根据条件,由type动态创建了一个类。类的基本操作,比如实例化,获取静态字段,传参,都没有问题!

    函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。type就是Python的内建元类

    总结:对象是由类创建的。那么类默认是由type创建的

    三、偏函数

    什么是偏函数

    偏函数是2.5版本以后引进来的东西。属于函数式编程的一部分,使用偏函数可以通过有效地"冻结"那些预先确定的参数,来缓存函数参数,然后在运行时,当获得需要的剩余参数后,可以将他们解冻,传递到最终的参数中,从而使用最终确定的所有参数去调用函数。

    举例

    示例函数

    def func(a1,a2):
        return a1+a2
    
    res = func(1,3)
    print(res)

    执行输出:4

    使用偏函数

    import functools
    def func(a1,a2):
        return a1+a2
    
    # 相当于给第一个参数,设置默认参数8
    new_func = functools.partial(func,8)
    # 传入第二个参数
    res = new_func(7)
    print(res)

    执行输出:15

    func有2个参数,怎么确定是给第一个参数,设置了默认参数?

    import functools
    def func(a1,a2=22):
        return a1+a2
    
    # 相当于给第一个参数,设置默认参数8
    new_func = functools.partial(func,8)
    # 传入第二个参数
    res = new_func(7)
    print(res)

    执行输出:15

    注意:a1直接设置默认参数,是会报错的!默认参数必须在位置参数的后面!

    在flask中,会用到偏函数

    四、面向对象的封装

    看下面一段数据

    list_filter = ['全智贤','高圆圆','胡歌',]

    如何用代码区分性别?

    第一种方法:使用字典

    list_filter = [
        {'name':'全智贤','sex':''},
        {'name':'高圆圆','sex':''},
        {'name':'胡歌','sex':''},
    ]

    再区分衣服的颜色呢?

    list_filter = [
        {'name':'全智贤','sex':'','color':'pink'},
        {'name':'高圆圆','sex':'','color':'red'},
        {'name':'胡歌','sex':'','color':'black'},
    ]

    如果还有其它属性呢?继续加?

    使用字典也是一个封装思想

    第二种方法:使用类(推荐)

    class Option(object):
        def __init__(self,name,sex,color):
            self.name = name
            self.sex = sex
            self.color = color
    
    list_filter = [
        # 字典对象做封装
        Option(name='全智贤',sex='',color = 'pink'),
        Option(name='高圆圆',sex='',color = 'red'),
        Option(name='胡歌',sex='',color = 'black'),
    ]
    
    for item in list_filter:
        print(item.__dict__)
    View Code

    执行输出:

    {'name': '全智贤', 'color': 'pink', 'sex': ''}
    {'name': '高圆圆', 'color': 'red', 'sex': ''}
    {'name': '胡歌', 'color': 'black', 'sex': ''}

    通过这2种方法对比,使用类更好一点!

    如果使用字典,那么第一条数据,加了一个参数。而另外2条数据,却没有加!

    那么页面展示就会错乱!

    而使用类,则不会这个情况!另外一点,使用类,可以构造更加复杂的数据结构!

    使用类,是新的 封装思想。可扩展功能比较方便

    五、获取外键数据

    外键分为3种: FK/M2M/O2O,分别对应一对多,多对多,一对一

    给定2个参数

    model_class = Depart  # 模型表类
    _field = "user"  # 外键字段

    要获取外键字段对应的所有数据,如何获取?

    新建项目untitled4,注意:django版本为1.11

    修改models.py,新建2个表

    from django.db import models
    
    # Create your models here.
    class UserInfo(models.Model):
        title = models.CharField(verbose_name='标题',max_length=32)
    
        def __str__(self):
            return self.title
    
    
    class Depart(models.Model):
        name = models.CharField(verbose_name='部门名称',max_length=32)
        tel = models.CharField(verbose_name='联系电话',max_length=32)
        user = models.ForeignKey(verbose_name='负责人',to='UserInfo')
    
        def __str__(self):
            return self.name
    View Code

    使用2个命令生成表

    python manage.py makemigrations
    python manage.py migrate

    增加数据,使用Navicat打开sqlite3数据库,执行sql语句

    INSERT INTO "app01_depart" ("name", "tel", "user_id") VALUES ('总经理', 23456342, 1);
    INSERT INTO "app01_depart" ("name", "tel", "user_id") VALUES ('技术部', 34565422, 1);
    INSERT INTO "app01_depart" ("name", "tel", "user_id") VALUES ('运营部', 34344523, 2);
    
    INSERT INTO "app01_userinfo" ("title") VALUES ('xiao');
    INSERT INTO "app01_userinfo" ("title") VALUES ('zhang');
    View Code

    修改urls.py,增加路径

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

    修改views.py,增加视图函数

    from django.shortcuts import render,HttpResponse
    from app01 import models
    # Create your views here.
    def index(request):
        # 外键对象,通过get_field获取
        fk_obj = models.Depart._meta.get_field("user")
        print(fk_obj,type(fk_obj))
        # .all表示获取所有数据
        user_info_queryset = fk_obj.rel.model.objects.all()
        print(user_info_queryset)
    
        for i in user_info_queryset:
            print('row:',i)  # 打印每一个行的数据
    
        return HttpResponse('...')
    View Code

    启动django项目,访问首页

    查看Pycharm控制台输出:

    app01.Depart.user <class 'django.db.models.fields.related.ForeignKey'>
    <QuerySet [<UserInfo: xiao>, <UserInfo: zhang>]>
    row: xiao
    row: zhang

    注意:FK/M2M/O2O都是通过get_field来获取的

    六、组合搜索

    展示页面

    务必下载github代码:

    https://github.com/987334176/luffy_stark/archive/v1.2.zip

    因为下面的内容,都是这份代码来修改的!

    修改 stark-->templates-->stark-->changelist.html

    {% extends 'stark/layout.html' %}
    {% load stark %}
    
    {% block css %}
        <style>
            .comb-search {
                padding: 5px 20px;
            }
    
            .comb-search .row .whole {
                 60px;
                float: left;
    
            }
    
            .comb-search .row .others {
                padding-left: 60px;
            }
    
            .comb-search .row a {
                display: inline-block;
                padding: 5px 8px;
                margin: 3px;
                border: 1px solid #d4d4d4;
    
            }
    
            .comb-search .row a {
                display: inline-block;
                padding: 5px 8px;
                margin: 3px;
                border: 1px solid #d4d4d4;
            }
    
            .comb-search a.active {
                color: #fff;
                background-color: #337ab7;
                border-color: #2e6da4;
            }
        </style>
    {% endblock %}
    {% block content %}
        <div>
            {#组合搜索#}
            <div class="comb-search">
                <div class="row">
                    <div class="whole">
                        <a href="#">全部</a>
                    </div>
                    <div class="others">
                        <a href="#">条件1</a>
                        <a href="#">条件2</a>
                        <a href="#">条件3</a>
                    </div>
                </div>
    
            </div>
    
    
            {#添加按钮#}
            {% if cl.add_btn %}
                <div style="margin: 5px 0;">
                    {{ cl.add_btn }}
                </div>
            {% endif %}
            {#搜索框#}
            {% if cl.search_list %}
                <div style="float: right;">
                    <form method="GET" class="form-inline">
                        <div class="form-group">
                            <input class="form-control" type="text" name="q" value="{{ cl.q }}" placeholder="关键字搜索">
                            <button class="btn btn-primary" type="submit">
                                <i class="fa fa-search" aria-hidden="true"></i>
                            </button>
                        </div>
                    </form>
                </div>
            {% endif %}
    
            <form class="form-inline" method="post">
                {% csrf_token %}
                {#批量操作#}
                {% if cl.action_list %}
                    <div class="form-group">
                        <select name="action" class="form-control" style="min- 200px;">
                            <option>请选择功能</option>
                            {% for item in cl.action_list %}
                                <option value="{{ item.name }}">{{ item.text }}</option>
                            {% endfor %}
                        </select>
                        <input class="btn btn-primary" type="submit" value="执行">
                    </div>
                {% endif %}
                {#使用table展示数据#}
                {% table cl %}
                {#分页展示#}
                <nav aria-label="Page navigation">
                    <ul class="pagination">
                        {{ cl.page.page_html|safe }}
                    </ul>
                </nav>
            </form>
        </div>
    
    
    
    {% endblock %}
    View Code

    访问url: http://127.0.0.1:8000/stark/app01/depart/list/

    效果如下:

    修改 stark-->server-->stark.py,增加变量 list_filter,增加一个钩子函数 get_list_filter

    使用get_field获取字段

    import functools
    from django.conf.urls import url
    from django.shortcuts import HttpResponse,render,redirect
    from types import FunctionType
    from django.utils.safestring import mark_safe
    from django.urls import reverse
    from django import forms
    from django.db.models import Q
    from django.http import QueryDict
    
    class ChangeList(object):
        """
        封装列表页面需要的所有功能
        """
        def __init__(self,config,queryset,q,search_list,page):
            ### 处理搜索 ###
            self.q = q  # 搜索条件
            self.search_list = search_list  # 搜索字段
            self.page = page  # 分页
            # 配置参数
            self.config = config
            # 批量操作
            self.action_list = [{'name': func.__name__, 'text': func.text} for func in config.get_action_list()]
            # 添加按钮
            self.add_btn = config.get_add_btn()
            # ORM执行结果
            self.queryset = queryset
            # 显示的列
            self.list_display = config.get_list_display()
    
    class StarkConfig(object):
        def __init__(self,model_class,site):
            self.model_class = model_class
            self.site = site
            # 定义request变量,用于非视图函数使用。
            # 在wrapper装饰器中,对这个值重新赋值!
            self.request = None
            # url中的搜索条件,存在字典中。key为_filter
            self.back_condition_key = "_filter"
    
        def display_checkbox(self,row=None,header=False):  # 显示复选框
            if header:
                # 输出中文
                return "选择"
            # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
            return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)
    
        def display_edit(self, row=None, header=False):
            if header:
                return "编辑"
    
            return mark_safe(
                '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))
    
        def display_del(self, row=None, header=False):
            if header:
                return "删除"
    
            return mark_safe(
                '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))
    
        def display_edit_del(self, row=None, header=False):
            if header:
                return "操作"
            tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
            <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
            """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
            return mark_safe(tpl)
    
        def multi_delete(self, request):  # 批量删除
            """
            批量删除的action
            :param request:
            :return:
            """
            pk_list = request.POST.getlist('pk')
            self.model_class.objects.filter(pk__in=pk_list).delete()
            # return HttpResponse('删除成功')
    
        multi_delete.text = "批量删除"  # 添加自定义属性text
    
        def multi_init(self,request):  # 批量初始化
            print('批量初始化')
    
        multi_init.text = "批量初始化"  # 添加自定义属性text
    
        order_by = []  # 需要排序的字段,由用户自定义
        list_display = []  # 定义显示的列,由用户自定义
        model_form_class = None  # form组件需要的model_class
        action_list = []  # 批量操作方法
        # 搜索字段,如果是跨表字段,要按照ORM语法来
        search_list = []
        list_filter = []  # 组合搜索
    
        def get_order_by(self):  # 获取排序列表
            return self.order_by
    
        def get_list_display(self):  # 获取显示的列
            return self.list_display
    
        def get_add_btn(self):  # 显示添加按钮
            return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())
    
        def get_model_form_class(self):
            """
            获取ModelForm类
            :return:
            """
            if self.model_form_class:
                return self.model_form_class
    
            class AddModelForm(forms.ModelForm):
                class Meta:
                    model = self.model_class
                    fields = "__all__"
    
            return AddModelForm
    
        def get_action_list(self):  # 获取批量操作方法
            val = []  # 空列表
            # 扩展列表的元素
            val.extend(self.action_list)
            return val
    
        def get_action_dict(self):  # 获取匹配操作字典
            val = {}
            for item in self.action_list:
                # 以方法名为key
                val[item.__name__] = item
            return val
    
        def get_search_list(self):  # 获取搜索字段
            val = []
            val.extend(self.search_list)
            return val
    
        def get_search_condition(self, request):  # 根据关键字,组合ORM查询语句
            search_list = self.get_search_list()  # ['name','tel']
            q = request.GET.get('q', "")  # 搜索条件
            con = Q()
            con.connector = "OR"  # 以OR作为连接符
            if q:  # 判断条件不为空
                for field in search_list:
                    # 合并条件进行查询, __contains表示使用like查询
                    con.children.append(('%s__contains' % field, q))
    
            return search_list, q, con
    
        def get_list_filter(self):  # 获取组合搜索条件
            val = []
            val.extend(self.list_filter)
            return val
    
        def changelist_view(self, request):
            """
            所有URL查看列表页面
            :param request:
            :return:
            """
            if request.method == 'POST':
                action_name = request.POST.get('action')
                action_dict = self.get_action_dict()
                if action_name not in action_dict:
                    return HttpResponse('非法请求')
    
                response = getattr(self, action_name)(request)
                if response:
                    return response
    
            ### 处理搜索 ###
            search_list, q, con = self.get_search_condition(request)
            # ##### 处理分页 #####
            from stark.utils.pagination import Pagination
            # 总条数
            total_count = self.model_class.objects.filter(con).count()
            # 复制GET参数
            query_params = request.GET.copy()
            # 允许编辑
            query_params._mutable = True
            # 使用分页类Pagination,传入参数。每页显示3条
            page = Pagination(request.GET.get('page'), total_count, request.path_info, query_params, per_page=3)
    
            # 根据排序列表进行排序,以及分页功能
            queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())[page.start:page.end]
    
            cl = ChangeList(self, queryset, q, search_list, page)
    
            # ######## 组合搜索 #########
            # list_filter = ['name','user']
            list_filter = self.get_list_filter()
            for field in list_filter:
                # 如果field = "name" --> 查Depart所有数据
                # 如果field = "user" --> 查UserInfo所有数据
                _field = self.model_class._meta.get_field(field)
                print(_field,type(_field))  # 打印字段类型
    
            context = {
                'cl': cl
            }
    
            # 注意:要传入参数
            return render(request,'stark/changelist.html',context)
    
        def add_view(self, request):
            """
            所有的添加页面,都在此方法处理
            使用ModelForm实现
            :param request:
            :return:
            """
            # 添加数据,使用ModelForm
            AddModelForm = self.get_model_form_class()
    
            if request.method == "GET":
                form = AddModelForm()
                return render(request,'stark/change.html',{'form':form})
    
            form = AddModelForm(request.POST)  # 接收POST数据
            if form.is_valid():  # 验证数据
                form.save()  # 自动保存数据
                # 反向生成url,跳转到列表页面
                return redirect(self.reverse_list_url())
            # 渲染页面,此时会保存表单数据
            return render(request, 'stark/change.html', {'form': form})
    
        def change_view(self, request, pk):
            """
            所有编辑页面
            :param request:
            :param pk:
            :return:
            """
            # 查看单条数据
            obj = self.model_class.objects.filter(pk=pk).first()
            if not obj:
                return HttpResponse('数据不存在')
            # 获取model_form类
            ModelFormClass = self.get_model_form_class()
            if request.method == 'GET':
                # instance表示生成默认值
                form = ModelFormClass(instance=obj)
                # 渲染页面,添加和修改可以共用一个一个模板文件
                return render(request, 'stark/change.html', {'form': form})
            # instance = obj 表示指定给谁做修改
            form = ModelFormClass(data=request.POST, instance=obj)
            if form.is_valid():
                form.save()  # 修改数据
                # 跳转到列表页面
                return redirect(self.reverse_list_url())
            return render(request, 'stark/change.html', {'form': form})
    
        def delete_view(self, request, pk):
            """
            所有删除页面
            :param request:
            :param pk:
            :return:
            """
            if request.method == "GET":
                # cancel_url表示用户点击取消时,跳转到列表页面
                return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
            # 定位单条数据,并删除!
            self.model_class.objects.filter(pk=pk).delete()
            return redirect(self.reverse_list_url())
    
        def wrapper(self, func):
            @functools.wraps(func)
            def inner(request, *args, **kwargs):
                self.request = request
                return func(request, *args, **kwargs)
    
            return inner
    
        def get_urls(self):
            info = self.model_class._meta.app_label, self.model_class._meta.model_name
            urlpatterns = [
                url(r'^list/$', self.wrapper(self.changelist_view), name='%s_%s_changelist' % info),
                url(r'^add/$', self.wrapper(self.add_view), name='%s_%s_add' % info),
                url(r'^(?P<pk>d+)/change/', self.wrapper(self.change_view), name='%s_%s_change' % info),
                url(r'^(?P<pk>d+)/del/', self.wrapper(self.delete_view), name='%s_%s_del' % info),
            ]
    
            extra = self.extra_url()
            if extra:  # 判断变量不为空
                # 扩展路由
                urlpatterns.extend(extra)
    
            # print(urlpatterns)
            return urlpatterns
    
        def extra_url(self):  # 额外的路由,由调用者重构
            pass
    
        def reverse_list_url(self):  # 反向生成访问列表的url
            app_label = self.model_class._meta.app_label
            model_name = self.model_class._meta.model_name
            namespace = self.site.namespace
            name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
            list_url = reverse(name)
    
            # 获取当前请求的_filter参数,也就是跳转之前的搜索条件
            origin_condition = self.request.GET.get(self.back_condition_key)
            if not origin_condition:  # 如果没有获取到
                return list_url  # 返回列表页面
    
            # 列表地址和搜索条件拼接
            list_url = "%s?%s" % (list_url, origin_condition,)
    
            return list_url
    
        def reverse_add_url(self):  # 反向生成添加url
            app_label = self.model_class._meta.app_label
            model_name = self.model_class._meta.model_name
            namespace = self.site.namespace
            name = '%s:%s_%s_add' % (namespace, app_label, model_name)
            add_url = reverse(name)
    
            if not self.request.GET:  # 判断get参数为空
                return add_url  # 返回原url
            # request.GET的数据类型为QueryDict
            # 对QueryDict做urlencode编码
            param_str = self.request.GET.urlencode() # 比如q=xiao&age=20
            # 允许对QueryDict做修改
            new_query_dict = QueryDict(mutable=True)
            # 添加键值对. _filter = param_str
            new_query_dict[self.back_condition_key] = param_str
            # 添加url和搜索条件做拼接
            add_url = "%s?%s" % (add_url, new_query_dict.urlencode(),)
            # 返回最终url
            return add_url
    
        def reverse_edit_url(self, row):  # 反向生成编辑行内容的url
            app_label = self.model_class._meta.app_label  # app名
            model_name = self.model_class._meta.model_name  # 表名
            namespace = self.site.namespace  # 命名空间
            # 拼接字符串,这里为change
            name = '%s:%s_%s_change' % (namespace, app_label, model_name)
            # 反向生成url,传入参数pk=row.pk
            edit_url = reverse(name, kwargs={'pk': row.pk})
    
            if not self.request.GET:
                return edit_url
            param_str = self.request.GET.urlencode()
            new_query_dict = QueryDict(mutable=True)
            new_query_dict[self.back_condition_key] = param_str
            edit_url = "%s?%s" % (edit_url, new_query_dict.urlencode(),)
    
            return edit_url
    
        def reverse_del_url(self, row):  # 反向生成删除行内容的url
            app_label = self.model_class._meta.app_label
            model_name = self.model_class._meta.model_name
            namespace = self.site.namespace
            # 注意:这里为del
            name = '%s:%s_%s_del' % (namespace, app_label, model_name)
            del_url = reverse(name, kwargs={'pk': row.pk})
    
            if not self.request.GET:
                return del_url
            param_str = self.request.GET.urlencode()
            new_query_dict = QueryDict(mutable=True)
            new_query_dict[self.back_condition_key] = param_str
            del_url = "%s?%s" % (del_url, new_query_dict.urlencode(),)
    
            return del_url
    
        @property
        def urls(self):
            return self.get_urls()
    
    class AdminSite(object):
        def __init__(self):
            self._registry = {}
            self.app_name = 'stark'
            self.namespace = 'stark'
    
        def register(self,model_class,stark_config=None):
            # not None的结果为Ture
            if not stark_config:
                # 也就是说,当其他应用调用register时,如果不指定stark_config参数
                # 那么必然执行下面这段代码!
                # stark_config和StarkConfig是等值的!都能实例化
                stark_config = StarkConfig
    
            # 添加键值对,实例化类StarkConfig,传入参数model_class
            # self指的是AdminSite类
            self._registry[model_class] = stark_config(model_class,self)
    
            # print(self._registry)  # 打印字典
            """
            {
                app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
                app02.models.Role:RoleConfig(app02.models.Role)
            }
            """
    
            # for k, v in self._registry.items():
            #     print(k,v)
    
        def get_urls(self):
            urlpatterns = []
    
            for k, v in self._registry.items():
                # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
                # k=modes.Role,v=RoleConfig(models.Role)           # 封装:model_class=Role,site=site对象
                app_label = k._meta.app_label
                model_name = k._meta.model_name
                urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))
    
            return urlpatterns
    
        @property
        def urls(self):
            # 调用get_urls方法
            # self.app_name和self.namespace值是一样的,都是stark
            return self.get_urls(), self.app_name, self.namespace
    
    site = AdminSite()  # 实例化类
    View Code

    修改 app01-->stark.py,增加list_filter属性

    from stark.server.stark import site, StarkConfig
    from app01 import models
    from django import forms
    from django.shortcuts import render
    from django.conf.urls import url
    
    class UserInfoConfig(StarkConfig):
        list_display = ['id', 'username']
    
    
    class DepartModelForm(forms.ModelForm):
        class Meta:
            model = models.Depart
            fields = "__all__"
    
        def clean_name(self):  # 定义钩子
            # print(self.cleaned_data['name'])
            return self.cleaned_data['name']
    
    class DepartConfig(StarkConfig):
        list_display = [StarkConfig.display_checkbox,'id','name', 'tel', 'user',StarkConfig.display_edit_del]
        # model_form_class = DepartModelForm
        # 批量操作
        action_list = [StarkConfig.multi_delete,StarkConfig.multi_init]
        # 搜索字段,如果是跨表字段,要按照ORM语法来
        search_list = ['name', 'tel', 'user__username']
        list_filter = ["name","user"]  # 组合搜索
        # def get_add_btn(self):  # 返回None,表示不显示添加按钮
        #     pass
        # def changelist_view(self, request):  # 重写changelist_view方法
        #     # 渲染自定义的列表页面
        #     return render(request,'stark/custom_list.html')
        # def get_urls(self):  # 自定义路由
        #     info = self.model_class._meta.app_label, self.model_class._meta.model_name
        #
        #     urlpatterns = [
        #         url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
        #     ]
        #     return urlpatterns
    
    site.register(models.UserInfo, UserInfoConfig)
    site.register(models.Depart, DepartConfig)
    View Code

    刷新页面,查看Pycharm控制台输出:

    app01.Depart.name <class 'django.db.models.fields.CharField'>
    app01.Depart.user <class 'django.db.models.fields.related.ForeignKey'>

    ForeignKey表示一对多

    修改 stark-->server-->stark.py,导入模块ForeignKey,判断类型。如果是FK,就跨表查询。

    添加list_filter_rows列表,并传给模板

    import functools
    from django.conf.urls import url
    from django.shortcuts import HttpResponse,render,redirect
    from types import FunctionType
    from django.utils.safestring import mark_safe
    from django.urls import reverse
    from django import forms
    from django.db.models import Q
    from django.http import QueryDict
    from django.db.models.fields.related import ForeignKey
    
    class ChangeList(object):
        """
        封装列表页面需要的所有功能
        """
        def __init__(self,config,queryset,q,search_list,page):
            ### 处理搜索 ###
            self.q = q  # 搜索条件
            self.search_list = search_list  # 搜索字段
            self.page = page  # 分页
            # 配置参数
            self.config = config
            # 批量操作
            self.action_list = [{'name': func.__name__, 'text': func.text} for func in config.get_action_list()]
            # 添加按钮
            self.add_btn = config.get_add_btn()
            # ORM执行结果
            self.queryset = queryset
            # 显示的列
            self.list_display = config.get_list_display()
    
    class StarkConfig(object):
        def __init__(self,model_class,site):
            self.model_class = model_class
            self.site = site
            # 定义request变量,用于非视图函数使用。
            # 在wrapper装饰器中,对这个值重新赋值!
            self.request = None
            # url中的搜索条件,存在字典中。key为_filter
            self.back_condition_key = "_filter"
    
        def display_checkbox(self,row=None,header=False):  # 显示复选框
            if header:
                # 输出中文
                return "选择"
            # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
            return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)
    
        def display_edit(self, row=None, header=False):
            if header:
                return "编辑"
    
            return mark_safe(
                '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))
    
        def display_del(self, row=None, header=False):
            if header:
                return "删除"
    
            return mark_safe(
                '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))
    
        def display_edit_del(self, row=None, header=False):
            if header:
                return "操作"
            tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
            <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
            """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
            return mark_safe(tpl)
    
        def multi_delete(self, request):  # 批量删除
            """
            批量删除的action
            :param request:
            :return:
            """
            pk_list = request.POST.getlist('pk')
            self.model_class.objects.filter(pk__in=pk_list).delete()
            # return HttpResponse('删除成功')
    
        multi_delete.text = "批量删除"  # 添加自定义属性text
    
        def multi_init(self,request):  # 批量初始化
            print('批量初始化')
    
        multi_init.text = "批量初始化"  # 添加自定义属性text
    
        order_by = []  # 需要排序的字段,由用户自定义
        list_display = []  # 定义显示的列,由用户自定义
        model_form_class = None  # form组件需要的model_class
        action_list = []  # 批量操作方法
        # 搜索字段,如果是跨表字段,要按照ORM语法来
        search_list = []
        list_filter = []  # 组合搜索
    
        def get_order_by(self):  # 获取排序列表
            return self.order_by
    
        def get_list_display(self):  # 获取显示的列
            return self.list_display
    
        def get_add_btn(self):  # 显示添加按钮
            return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())
    
        def get_model_form_class(self):
            """
            获取ModelForm类
            :return:
            """
            if self.model_form_class:
                return self.model_form_class
    
            class AddModelForm(forms.ModelForm):
                class Meta:
                    model = self.model_class
                    fields = "__all__"
    
            return AddModelForm
    
        def get_action_list(self):  # 获取批量操作方法
            val = []  # 空列表
            # 扩展列表的元素
            val.extend(self.action_list)
            return val
    
        def get_action_dict(self):  # 获取匹配操作字典
            val = {}
            for item in self.action_list:
                # 以方法名为key
                val[item.__name__] = item
            return val
    
        def get_search_list(self):  # 获取搜索字段
            val = []
            val.extend(self.search_list)
            return val
    
        def get_search_condition(self, request):  # 根据关键字,组合ORM查询语句
            search_list = self.get_search_list()  # ['name','tel']
            q = request.GET.get('q', "")  # 搜索条件
            con = Q()
            con.connector = "OR"  # 以OR作为连接符
            if q:  # 判断条件不为空
                for field in search_list:
                    # 合并条件进行查询, __contains表示使用like查询
                    con.children.append(('%s__contains' % field, q))
    
            return search_list, q, con
    
        def get_list_filter(self):  # 获取组合搜索条件
            val = []
            val.extend(self.list_filter)
            return val
    
        def changelist_view(self, request):
            """
            所有URL查看列表页面
            :param request:
            :return:
            """
            if request.method == 'POST':
                action_name = request.POST.get('action')
                action_dict = self.get_action_dict()
                if action_name not in action_dict:
                    return HttpResponse('非法请求')
    
                response = getattr(self, action_name)(request)
                if response:
                    return response
    
            ### 处理搜索 ###
            search_list, q, con = self.get_search_condition(request)
            # ##### 处理分页 #####
            from stark.utils.pagination import Pagination
            # 总条数
            total_count = self.model_class.objects.filter(con).count()
            # 复制GET参数
            query_params = request.GET.copy()
            # 允许编辑
            query_params._mutable = True
            # 使用分页类Pagination,传入参数。每页显示3条
            page = Pagination(request.GET.get('page'), total_count, request.path_info, query_params, per_page=3)
    
            # 根据排序列表进行排序,以及分页功能
            queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())[page.start:page.end]
    
            cl = ChangeList(self, queryset, q, search_list, page)
    
            # ######## 组合搜索 #########
            # list_filter = ['name','user']
            list_filter = self.get_list_filter()
            list_filter_rows = []
            for field in list_filter:
                # 如果field = "name" --> 查Depart所有数据
                # 如果field = "user" --> 查UserInfo所有数据
                _field = self.model_class._meta.get_field(field)
                # print(_field,type(_field))  # 打印字段类型
                if isinstance(_field,ForeignKey):
                    row = _field.rel.model.objects.all()
                else:
                    row = self.model_class.objects.all()
                list_filter_rows.append(row)
    
            context = {
                'cl': cl,
                'list_filter_rows':list_filter_rows
            }
    
            # 注意:要传入参数
            return render(request,'stark/changelist.html',context)
    
        def add_view(self, request):
            """
            所有的添加页面,都在此方法处理
            使用ModelForm实现
            :param request:
            :return:
            """
            # 添加数据,使用ModelForm
            AddModelForm = self.get_model_form_class()
    
            if request.method == "GET":
                form = AddModelForm()
                return render(request,'stark/change.html',{'form':form})
    
            form = AddModelForm(request.POST)  # 接收POST数据
            if form.is_valid():  # 验证数据
                form.save()  # 自动保存数据
                # 反向生成url,跳转到列表页面
                return redirect(self.reverse_list_url())
            # 渲染页面,此时会保存表单数据
            return render(request, 'stark/change.html', {'form': form})
    
        def change_view(self, request, pk):
            """
            所有编辑页面
            :param request:
            :param pk:
            :return:
            """
            # 查看单条数据
            obj = self.model_class.objects.filter(pk=pk).first()
            if not obj:
                return HttpResponse('数据不存在')
            # 获取model_form类
            ModelFormClass = self.get_model_form_class()
            if request.method == 'GET':
                # instance表示生成默认值
                form = ModelFormClass(instance=obj)
                # 渲染页面,添加和修改可以共用一个一个模板文件
                return render(request, 'stark/change.html', {'form': form})
            # instance = obj 表示指定给谁做修改
            form = ModelFormClass(data=request.POST, instance=obj)
            if form.is_valid():
                form.save()  # 修改数据
                # 跳转到列表页面
                return redirect(self.reverse_list_url())
            return render(request, 'stark/change.html', {'form': form})
    
        def delete_view(self, request, pk):
            """
            所有删除页面
            :param request:
            :param pk:
            :return:
            """
            if request.method == "GET":
                # cancel_url表示用户点击取消时,跳转到列表页面
                return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
            # 定位单条数据,并删除!
            self.model_class.objects.filter(pk=pk).delete()
            return redirect(self.reverse_list_url())
    
        def wrapper(self, func):
            @functools.wraps(func)
            def inner(request, *args, **kwargs):
                self.request = request
                return func(request, *args, **kwargs)
    
            return inner
    
        def get_urls(self):
            info = self.model_class._meta.app_label, self.model_class._meta.model_name
            urlpatterns = [
                url(r'^list/$', self.wrapper(self.changelist_view), name='%s_%s_changelist' % info),
                url(r'^add/$', self.wrapper(self.add_view), name='%s_%s_add' % info),
                url(r'^(?P<pk>d+)/change/', self.wrapper(self.change_view), name='%s_%s_change' % info),
                url(r'^(?P<pk>d+)/del/', self.wrapper(self.delete_view), name='%s_%s_del' % info),
            ]
    
            extra = self.extra_url()
            if extra:  # 判断变量不为空
                # 扩展路由
                urlpatterns.extend(extra)
    
            # print(urlpatterns)
            return urlpatterns
    
        def extra_url(self):  # 额外的路由,由调用者重构
            pass
    
        def reverse_list_url(self):  # 反向生成访问列表的url
            app_label = self.model_class._meta.app_label
            model_name = self.model_class._meta.model_name
            namespace = self.site.namespace
            name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
            list_url = reverse(name)
    
            # 获取当前请求的_filter参数,也就是跳转之前的搜索条件
            origin_condition = self.request.GET.get(self.back_condition_key)
            if not origin_condition:  # 如果没有获取到
                return list_url  # 返回列表页面
    
            # 列表地址和搜索条件拼接
            list_url = "%s?%s" % (list_url, origin_condition,)
    
            return list_url
    
        def reverse_add_url(self):  # 反向生成添加url
            app_label = self.model_class._meta.app_label
            model_name = self.model_class._meta.model_name
            namespace = self.site.namespace
            name = '%s:%s_%s_add' % (namespace, app_label, model_name)
            add_url = reverse(name)
    
            if not self.request.GET:  # 判断get参数为空
                return add_url  # 返回原url
            # request.GET的数据类型为QueryDict
            # 对QueryDict做urlencode编码
            param_str = self.request.GET.urlencode() # 比如q=xiao&age=20
            # 允许对QueryDict做修改
            new_query_dict = QueryDict(mutable=True)
            # 添加键值对. _filter = param_str
            new_query_dict[self.back_condition_key] = param_str
            # 添加url和搜索条件做拼接
            add_url = "%s?%s" % (add_url, new_query_dict.urlencode(),)
            # 返回最终url
            return add_url
    
        def reverse_edit_url(self, row):  # 反向生成编辑行内容的url
            app_label = self.model_class._meta.app_label  # app名
            model_name = self.model_class._meta.model_name  # 表名
            namespace = self.site.namespace  # 命名空间
            # 拼接字符串,这里为change
            name = '%s:%s_%s_change' % (namespace, app_label, model_name)
            # 反向生成url,传入参数pk=row.pk
            edit_url = reverse(name, kwargs={'pk': row.pk})
    
            if not self.request.GET:
                return edit_url
            param_str = self.request.GET.urlencode()
            new_query_dict = QueryDict(mutable=True)
            new_query_dict[self.back_condition_key] = param_str
            edit_url = "%s?%s" % (edit_url, new_query_dict.urlencode(),)
    
            return edit_url
    
        def reverse_del_url(self, row):  # 反向生成删除行内容的url
            app_label = self.model_class._meta.app_label
            model_name = self.model_class._meta.model_name
            namespace = self.site.namespace
            # 注意:这里为del
            name = '%s:%s_%s_del' % (namespace, app_label, model_name)
            del_url = reverse(name, kwargs={'pk': row.pk})
    
            if not self.request.GET:
                return del_url
            param_str = self.request.GET.urlencode()
            new_query_dict = QueryDict(mutable=True)
            new_query_dict[self.back_condition_key] = param_str
            del_url = "%s?%s" % (del_url, new_query_dict.urlencode(),)
    
            return del_url
    
        @property
        def urls(self):
            return self.get_urls()
    
    class AdminSite(object):
        def __init__(self):
            self._registry = {}
            self.app_name = 'stark'
            self.namespace = 'stark'
    
        def register(self,model_class,stark_config=None):
            # not None的结果为Ture
            if not stark_config:
                # 也就是说,当其他应用调用register时,如果不指定stark_config参数
                # 那么必然执行下面这段代码!
                # stark_config和StarkConfig是等值的!都能实例化
                stark_config = StarkConfig
    
            # 添加键值对,实例化类StarkConfig,传入参数model_class
            # self指的是AdminSite类
            self._registry[model_class] = stark_config(model_class,self)
    
            # print(self._registry)  # 打印字典
            """
            {
                app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
                app02.models.Role:RoleConfig(app02.models.Role)
            }
            """
    
            # for k, v in self._registry.items():
            #     print(k,v)
    
        def get_urls(self):
            urlpatterns = []
    
            for k, v in self._registry.items():
                # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
                # k=modes.Role,v=RoleConfig(models.Role)           # 封装:model_class=Role,site=site对象
                app_label = k._meta.app_label
                model_name = k._meta.model_name
                urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))
    
            return urlpatterns
    
        @property
        def urls(self):
            # 调用get_urls方法
            # self.app_name和self.namespace值是一样的,都是stark
            return self.get_urls(), self.app_name, self.namespace
    
    site = AdminSite()  # 实例化类
    View Code

    修改 stark-->templates-->stark-->changelist.html,for循环list_filter_rows列表

    {% extends 'stark/layout.html' %}
    {% load stark %}
    
    {% block css %}
        <style>
            .comb-search {
                padding: 5px 20px;
            }
    
            .comb-search .row .whole {
                 60px;
                float: left;
    
            }
    
            .comb-search .row .others {
                padding-left: 60px;
            }
    
            .comb-search .row a {
                display: inline-block;
                padding: 5px 8px;
                margin: 3px;
                border: 1px solid #d4d4d4;
    
            }
    
            .comb-search .row a {
                display: inline-block;
                padding: 5px 8px;
                margin: 3px;
                border: 1px solid #d4d4d4;
            }
    
            .comb-search a.active {
                color: #fff;
                background-color: #337ab7;
                border-color: #2e6da4;
            }
        </style>
    {% endblock %}
    {% block content %}
        <div>
            {#组合搜索#}
            <div class="comb-search">
                {% for row in list_filter_rows %}
                    <div class="row">
                        <div class="whole">
                            <a href="#">全部</a>
                        </div>
                        <div class="others">
                            {% for obj in row %}
                                <a href="#">{{ obj }}</a>
                            {% endfor %}
    
                        </div>
                    </div>
                {% endfor %}
    
            </div>
    
    
            {#添加按钮#}
            {% if cl.add_btn %}
                <div style="margin: 5px 0;">
                    {{ cl.add_btn }}
                </div>
            {% endif %}
            {#搜索框#}
            {% if cl.search_list %}
                <div style="float: right;">
                    <form method="GET" class="form-inline">
                        <div class="form-group">
                            <input class="form-control" type="text" name="q" value="{{ cl.q }}" placeholder="关键字搜索">
                            <button class="btn btn-primary" type="submit">
                                <i class="fa fa-search" aria-hidden="true"></i>
                            </button>
                        </div>
                    </form>
                </div>
            {% endif %}
    
            <form class="form-inline" method="post">
                {% csrf_token %}
                {#批量操作#}
                {% if cl.action_list %}
                    <div class="form-group">
                        <select name="action" class="form-control" style="min- 200px;">
                            <option>请选择功能</option>
                            {% for item in cl.action_list %}
                                <option value="{{ item.name }}">{{ item.text }}</option>
                            {% endfor %}
                        </select>
                        <input class="btn btn-primary" type="submit" value="执行">
                    </div>
                {% endif %}
                {#使用table展示数据#}
                {% table cl %}
                {#分页展示#}
                <nav aria-label="Page navigation">
                    <ul class="pagination">
                        {{ cl.page.page_html|safe }}
                    </ul>
                </nav>
            </form>
        </div>
    
    
    
    {% endblock %}
    View Code

    刷新页面,效果如下:

    后台搜索

    关于后面的详细步骤,没有时间写了。附上完整代码:

    链接:https://pan.baidu.com/s/1fLOGH_3G7hPTvCYKX84UdQ 密码:m8rh

    七、领域驱动设计(DDD)

    什么是领域驱动设计(DDD)

    2004年著名建模专家Eric Evans发表了他最具影响力的书籍:《Domain-Driven Design –Tackling Complexity in the Heart of Software》(中文译名:领域驱动设计—软件核心复杂性应对之道),书中提出了“领域驱动设计(简称 DDD)”的概念。

      领域驱动设计事实上是针对OOAD的一个扩展和延伸,DDD基于面向对象分析与设计技术,对技术架构进行了分层规划,同时对每个类进行了策略和类型的划分。

      领域模型是领域驱动的核心。采用DDD的设计思想,业务逻辑不再集中在几个大型的类上,而是由大量相对小的领域对象(类)组成,这些类具备自己的状态和行为,每个类是相对完整的独立体,并与现实领域的业务对象映射。领域模型就是由这样许多的细粒度的类组成。基于领域驱动的设计,保证了系统的可维护性、扩展性和复用性,在处理复杂业务逻辑方面有着先天的优势。

    领域驱动设计的特点

    领域驱动的核心应用场景就是解决复杂业务的设计问题,其特点与这一核心主题息息相关:

    1. 分层架构与职责划分:领域驱动设计很好的遵循了关注点分离的原则,提出了成熟、清晰的分层架构。同时对领域对象进行了明确的策略和职责划分,让领域对象和现实世界中的业务形成良好的映射关系,为领域专家与开发人员搭建了沟通的桥梁。
    2. 复用:在领域驱动设计中,领域对象是核心,每个领域对象都是一个相对完整的内聚的业务对象描述,所以可以形成直接的复用。同时设计过程是基于领域对象而不是基于数据库的Schema,所以整个设计也是可以复用的。
    3. 使用场景:适合具备复杂业务逻辑的软件系统,对软件的可维护性和扩展性要求比较高。不适用简单的增删改查业务。

    举例:商品价格

    一个商品,有商品名(name),原价(price),折扣价(discount)。如何用类来表示呢?

    常规类

    class Goods(object):
        def __init__(self,name,price,discount):
            self.name = name
            self.price = price
            self.discount = discount

    如果要增加优惠券(满减,立减,折扣),怎么办?

    领域驱动设计

    class BaseCoupon(object):
        """
        优惠券基础类
        """
        pass
    
    class Coupon1(BaseCoupon):
        """
        满减
        """
        pass
    
    class Coupon2(BaseCoupon):
        """
        立减
        """
        pass
    
    class Coupon3(BaseCoupon):
        """
        折扣
        """
        pass
    
    class Price(object):
        """
        商品价格
        """
        def __init__(self,price,discount):
            self.price = price
            self.discount = discount
    
        def pay(self):  # 交易价格
            pass
    
    class Goods(object):
        def __init__(self,name):
            self.name = name
    View Code

    重点就是建模
    一般做3年开发,就可以领悟 领域驱动设计。具体还得看个人的领悟能力!

    其他更多信息,请参考链接:

    https://www.cnblogs.com/yihaha/p/3977496.html

    关于python方面领域驱动设计的相关书籍,暂时还没有。

    主流的是JAVA,C#,PHP

    领域驱动设计它是一种编程思想,重点就是建模!对于开发一个大型项目,尤为重要!

    在python源代码中,就利用这种思想。类中层层嵌套类!

    对于个人编程能力的提升,可以看一下相关书籍!

    未完待续...

  • 相关阅读:
    Vue核心之数据劫持
    Flex 布局教程
    Grid布局
    我们都在深夜,参差不齐地入眠
    一个十分好用的动画工具:Velocity.js
    前端知识点总结——jQuery(下)
    前端知识点总结——jQuery(上)
    虫师Selenium2+Python_2、测试环境搭建
    虫师Selenium2+Python_11、自动化测试项目实战
    虫师Selenium2+Python_12、BDD框架之Lettuce入门
  • 原文地址:https://www.cnblogs.com/xiao987334176/p/9575783.html
Copyright © 2020-2023  润新知