zoukankan      html  css  js  c++  java
  • python 全栈开发,Day115(urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码)

    今日内容前戏

    静态字段和字段

    先来看下面一段代码

    class Foo:
        x = 1  # 类变量、静态字段、静态属性
        
        def __init__(self):
            y = 6 # 实例变量、字段、对象属性
        
        # 实例方法 
        def func(self):
            pass 
        
        # 静态方法
        @staticmethod
        def func():
            pass 
        
        # 类方法
        @classmethod
        def func():
            pass 
        
        @property
        def start(self)
            pass 
    View Code

    官方说法:x称之为 类变量  y称之为 实例变量

    在java和C#中,分别叫 静态字段字段

    函数名

    获取函数的名字,可通过 函数.__name__  方法获取。

    那么在django模板中,不能使用双下方法。会报错!

    urlencode

    什么是urlencode

    URL编码(URL encoding),也称作百分号编码(Percent-encoding), 是特定上下文的统一资源定位符 (URL)的编码机制。

    为什么要用urlencode

    发现现在几乎所有的网站都对url中的汉字和特殊的字符,进行了urlencode操作,也就是:

    http://hi.baidu.com/%BE%B2%D0%C4%C0%CF%C8%CB/creat/blog/

    这个样子,中间%形式的。注意:urlencode是一种编码,它不是加密方式

    url转义是为了符合url的规范,因为在标准的url规范中中文和很多的字符是不允许出现在url中的。

    URLEncode就是将URL中特殊部分进行编码。URLDecoder就是对特殊部分进行解码。

    因为当字符串数据以url的形式传递给web服务器时,字符串中是不允许出现空格和特殊字符的

    譬如:你要传的字符串数据时name=lisi&wangwu  这里的lisi&wangwu是一个字符串  但是服务器只会将lisi识别出来

    所以要用到urlencode对这个字符串进行编码

    协调开发时,所使用的编码不同。比如:A发送参数携带中文时,使用gbk。B接收时,使用utf-8解码时,就会出现乱码。

    那么A发送url的参数使用了URLEncode编码,B接收时使用URLDecoder解码,就不会出现乱码了!

    举例:

    有一个字典

    info = {'k1':'v1','k2':'v2'}

    需要转换为

    k1=v1&k2=v2

    怎么转换?

    第一种方法:使用for循环

    info = {'k1':'v1','k2':'v2'}
    list_1 = []
    for k,v in info.items():
        # print('{}={}'.format(k,v))
        list_1.append('{}={}'.format(k,v))
    
    res = '&'.join(list_1)
    print(res)
    View Code

    执行输出:k1=v1&k2=v2

    第一种方法:urlencode

    from urllib.parse import urlencode
    info = {'k1':'v1','k2':'v2'}
    print(urlencode(info))
    View Code

    以上可以看出,使用urlencode更方便!

    字典有多少个key-value,都可以方便的转换!

    QueryDict

    在django中,request.GET的值类型,是什么呢?是QueryDict

    新建一个项目untitled3,注意:django的版本为1.11

    修改urls.py,增加路径index

    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,增加index视图函数

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    def index(request):
        print(request.GET,type(request.GET))
        return HttpResponse('ok')
    View Code

    启动django项目,访问index页面

    查看Pycharm控制台输出:

    <QueryDict: {}> <class 'django.http.request.QueryDict'>

    它是一个QueryDict类型

    在url上面添加参数,访问url

    http://127.0.0.1:8000/index/?name=xiao&age=20

    查看Pycharm控制台输出:

    <QueryDict: {'age': ['20'], 'name': ['xiao']}> <class 'django.http.request.QueryDict'>

    修改views.py,导入QueryDict,查看源码

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    def index(request):
        from django.http.request import QueryDict
        print(request.GET)
    
        v = request.GET.urlencode()
        print(v)
    
        return HttpResponse('ok')
    View Code

    它内部调用了urlencode,具体位置,我找不到了...

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

    <QueryDict: {'name': ['xiao'], 'age': ['20']}>
    name=xiao&age=20

    request.GET.urlencode,可以直接调用!

    能不能在request.GET中,添加一个值呢?

    修改views.py,增加一个值

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    def index(request):
        from django.http.request import QueryDict
        print(request.GET)
        request.GET['k1'] = 666  # 增加一个值
        v = request.GET.urlencode()
        print(v)
    
        return HttpResponse('ok')
    View Code

    刷新页面,效果如下:

    提示:  这个QueReDICT实例是不可变的,这个是django安全策略做的

    如果一定要修改,需要设置一个参数

    request.GET._mutable = True

    一定要重启django,否则不生效!刷新页面,效果如下:

    查看Pycharm控制台输出:

    <QueryDict: {'age': ['20'], 'name': ['xiao']}>
    k1=666&age=20&name=xiao

    发现k1已经添加进去了!

    为了避免对后续的程序有影响,需要使用深copy

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    def index(request):
        from django.http.request import QueryDict
        import copy
        print(request.GET)
        
        params = copy.deepcopy(request.GET)  # 使用深copy
        params._mutable = True  # 允许修改
        params['k1'] = 666  # 增加一个值
        v = params.urlencode()
        print(v)
    
        return HttpResponse('ok')
    View Code

    重启django项目,刷新页面,效果同上!

    查看Pycharm控制台输出,效果同上!

    QueryDict 源码里面,提供了方法__deepcopy__ ,它也是做深copy的

    def __deepcopy__(self, memo):
        result = self.__class__('', mutable=True, encoding=self.encoding)
        memo[id(self)] = result
        for key, value in six.iterlists(self):
            result.setlist(copy.deepcopy(key, memo), copy.deepcopy(value, memo))
        return result

    查看request.GET.copy()的源码,它实际上,就是调用了__deepcopy__

    def copy(self):
        """Returns a mutable copy of this object."""
        return self.__deepcopy__({})

    修改views.py,改用request.GET.copy()

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    def index(request):
        from django.http.request import QueryDict
        import copy
        print(request.GET)
    
        params = request.GET.copy()  # 使用深copy
        params._mutable = True  # 允许修改
        params['k1'] = 666  # 增加一个值
        v = params.urlencode()
        print(v)
    
        return HttpResponse('ok')
    View Code

    重启django项目,刷新页面,效果同上!

    查看Pycharm控制台输出,效果同上!

    修改views.py,增加一个列表

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    def index(request):
        from django.http.request import QueryDict
        import copy
        print(request.GET)
    
        params = request.GET.copy()  # 使用深copy
        params._mutable = True  # 允许修改
        params['k1'] = 666  # 增加一个值
        params['k2'] = [11,12]  # 增加一个列表
        v = params.urlencode()
        print(v)
    
        return HttpResponse('ok')
    View Code

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

    <QueryDict: {'age': ['20'], 'name': ['xiao']}>
    name=xiao&k2=%5B11%2C+12%5D&k1=666&age=20

    发现k2=%5B11%2C+12%5D,这并不是我们想要的结果!

    列表需要使用setlist才行

    修改views.py,使用setlist

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    def index(request):
        from django.http.request import QueryDict
        import copy
        print(request.GET)
    
        params = request.GET.copy()  # 使用深copy
        params._mutable = True  # 允许修改
        params['k1'] = 666  # 增加一个值
        params.setlist('k4',[11,12])  # 增加一个列表
        v = params.urlencode()
        print(v)
    
        return HttpResponse('ok')
    View Code

    重启django项目,刷新页面。查看Pycharm控制台输出:

    <QueryDict: {'name': ['xiao'], 'age': ['20']}>
    k1=666&k4=11&k4=12&name=xiao&age=20

    可以看到k4=11&k4=12,它分别设置了2个值!

    那么获取k4使用getlist

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    def index(request):
        from django.http.request import QueryDict
        import copy
        print(request.GET)
    
        params = request.GET.copy()  # 使用深copy
        params._mutable = True  # 允许修改
        params['k1'] = 666  # 增加一个值
        params.setlist('k4',[11,12])  # 增加一个列表
        print(params.get('k1'))
        print(params.getlist('k4'))  # 获取列表,使用getlist
        v = params.urlencode()
        print(v)
    
    
        return HttpResponse('ok')
    View Code

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

    <QueryDict: {'name': ['xiao'], 'age': ['20']}>
    666
    [11, 12]
    k4=11&k4=12&k1=666&name=xiao&age=20

     在列表中,增加一个值,使用append

    修改views.py

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    def index(request):
        from django.http.request import QueryDict
        import copy
        print(request.GET)
    
        params = request.GET.copy()  # 使用深copy
        params._mutable = True  # 允许修改
        params['k1'] = 666  # 增加一个值
        params.setlist('k4',[11,12])  # 增加一个列表
    
        old = params.getlist('k4')  # 获取列表
        old.append('v4')  # 最加一个值
        params.setlist('k4',old)  # 重新设置
        v = params.urlencode()
        print(v)
    
    
        return HttpResponse('ok')
    View Code

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

    <QueryDict: {'name': ['xiao'], 'age': ['20']}>
    name=xiao&k1=666&age=20&k4=11&k4=12&k4=v4

    可以发现,k4有3个值

    保留原来搜索条件

    先来访问一个链接:

    http://127.0.0.1:8000/index/?name=xiao&age=20

    需要在视图函数中的request.GET中,添加一个值_filter。用来保存原来的搜索条件

    _filter = "name=xiao&age=20"

    最后变成QueryDict,如何操作?

    修改views.py

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    def index(request):
        from django.http.request import QueryDict
    
        url_params_str = request.GET.urlencode()
        # mutable=True 表示可修改
        query_dic = QueryDict(mutable=True)
        # 添加一个key为_filter
        query_dic['_filter'] = url_params_str
        print(query_dic)
    
        # 重新编码
        new_params = query_dic.urlencode()
        print(new_params)
    
    
        return HttpResponse('ok')
    View Code

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

    <QueryDict: {'_filter': ['name=xiao&age=20']}>
    _filter=name%3Dxiao%26age%3D20

    最后一个值,使用urlencode编码了

    举例:

    假设一个场景,先搜索到了一些学生。注意:此时url是有搜索条件的

    点击添加按钮,跳转到添加页面。注意:此时的url带有原搜索条件

    添加完成后,跳转到原来的页面。注意:此时的url带有原搜索条件

    修改urls.py,增加路径

    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^index/', views.index),
        url(r'^add_stu/', views.add_stu),
    ]
    View Code

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

    from django.shortcuts import render,HttpResponse,redirect
    
    # Create your views here.
    def index(request):
        from django.http.request import QueryDict
    
        url_params_str = request.GET.urlencode()
        # mutable=True 表示可修改
        query_dic = QueryDict(mutable=True)
        # 添加一个key为_filter
        query_dic['_filter'] = url_params_str
        print(query_dic)
    
        # 重新编码
        new_params = query_dic.urlencode()
        print(new_params)
    
        # 跳转地址
        target_url = "/add_stu?%s" %new_params
        print(target_url)
        # 渲染一个a标签
        return HttpResponse('<a href="%s">添加学生</a>'%target_url)
    
    def add_stu(request):
        if request.method == "GET":
            return render(request,"add_stu.html")
    
        # 接收到数据,保存到数据库...
        # 获取搜索条件
        origin_params = request.GET.get('_filter')
        # 返回地址,保留原搜索添加
        back_url = "/index/?%s" %origin_params
        # 重定向页面
        return redirect(back_url)
    View Code

     重启django程序,访问页面

    注意:这里直接访问的是带有搜索条件的

    http://127.0.0.1:8000/index/?name=xiao&age=20

    效果如下:

    点击添加学生,页面跳转

    注意:这里面的_filter,就是原来的搜索条件

    点击提交按钮,页面跳转

    此时页面:还是带有原来的搜索条件

    Q查询高级用法

     一般页面使用的搜索功能,都是使用了模糊搜索

    比如:查询name包含 "大"或者email包含 "qq"的记录

    q = Q()
    q.connecter = "OR"  # 使用OR作为连接符
    # 合并条件进行查询,__contains表示使用like查询
    q.children.append(('name__contains', ''))
    q.children.append(('email__contains', 'qq'))

    它相当于 name like '%大%' OR email like '%qq%'

    一、批量操作

    务必下载github代码:

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

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

    修改stark-->templates-->stark-->changelist.html,增加select标签

    {% extends 'stark/layout.html' %}
    
    {% block content %}
        <h1>列表页面</h1>
        <div>
            {% if add_btn %}
                <div style="margin: 5px 0;">
                    {{ add_btn }}
                </div>
            {% endif %}
            <form class="form-inline" method="post">
                {% csrf_token %}
                <div class="form-group">
                    <select name="action" class="form-control" style="min- 200px;">
                        <option>请选择功能</option>
                        <option value="1">批量删除</option>
                        <option value="2">初始化</option>
                    </select>
                    <input class="btn btn-primary" type="submit" value="执行">
                </div>
            </form>
            <table class="table table-bordered">
                <thead>
                <tr>
                    {% for item in header_list %}
                        <th>{{ item }}</th>
                    {% endfor %}
    
                </tr>
                </thead>
                <tbody>
                {% for row_list in body_list %}
                    <tr>
                        {% for col in row_list %}
                            <td>{{ col }}</td>
                        {% endfor %}
    
                    </tr>
                {% endfor %}
                </tbody>
            </table>
    
        </div>
    
    
    
    {% endblock %}
    View Code

    修改 app01-->stark.py,注释部分代码。增加复选框显示

    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,'name', 'tel', 'user']
        # model_form_class = DepartModelForm
        # 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

    访问页面: http://127.0.0.1:8000/stark/app01/userinfo/list/

    点击下拉菜单,效果如下:

    下拉框里面的元素应该是动态的,它是可配置的,每个选项包含了一个id。

    如果使用表单提交,那么表单数据就很大了

    修改stark-->server-->stark.py,添加变量action_list。用来定义要批量操作的选项

    为了防止列表来回更改,导致列表元素重复。使用extend

    添加multi_delete和multi_init方法,将action_list传给模板

    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
    
    class StarkConfig(object):
        def __init__(self,model_class,site):
            self.model_class = model_class
            self.site = site
    
        def display_checkbox(self,row=None,header=False):  # 显示复选框
            if header:
                # 输出中文
                return "选择"
            # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
            return mark_safe('<input type="checkbox" name="pk" values="%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)
    
        order_by = []  # 需要排序的字段,由用户自定义
        list_display = []  # 定义显示的列,由用户自定义
        model_form_class = None  # form组件需要的model_class
        action_list = []  # 批量操作方法
    
        def multi_delete(self):  # 批量删除
            pass
    
        def multi_init(self):  # 批量初始化
            pass
    
        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 changelist_view(self, request):
            """
            所有URL查看列表页面
            :param request:
            :return:
            """
            # 根据排序列表进行排序
            queryset = self.model_class.objects.all().order_by(*self.get_order_by())
            ### 批量操作 ###
            action_list = self.get_action_list()
            ### 添加按钮 ###
            add_btn = self.get_add_btn()  # 添加按钮返回值,不为空展示,否则不展示
    
            list_display = self.list_display  # 定义显示的列
            header_list = []  # 定义头部,用来显示verbose_name
            if list_display:
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        # 执行函数,默认显示中文
                        verbose_name = name_or_func(self,header=True)
                    else:
                        # 获取指定字段的verbose_name
                        verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
    
                    header_list.append(verbose_name)
            else:
                # 如果list_display为空,添加表名
                header_list.append(self.model_class._meta.model_name)
    
            body_list = []  # 显示内容
    
            for row in queryset:
                # 这里的row是对象,它表示表里面的一条数据
                row_list = []  # 展示每一行数据
                if not list_display:  # 如果不在list_display里面
                    # 添加对象
                    row_list.append(row)
                    body_list.append(row_list)
                    continue
    
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        val = name_or_func(self,row=row)  # 执行函数获取,传递row对象
                    else:
                        # 使用反射获取对象的值
                        val = getattr(row, name_or_func)
    
                    row_list.append(val)
    
                body_list.append(row_list)
    
            # 注意:要传入add_btn
            return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
    
        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):
            pass
    
        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),
                url(r'^add/$', self.add_view, name='%s_%s_add' % info),
                url(r'^(?P<pk>d+)/change/', self.change_view, name='%s_%s_change' % info),
                url(r'^(?P<pk>d+)/del/', 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)
            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)
            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})
            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})
            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,定义action_list

    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,'name', 'tel', 'user']
        # model_form_class = DepartModelForm
        # 批量操作
        action_list = [StarkConfig.multi_delete,StarkConfig.multi_init]
        # 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

    修改 stark-->templates-->stark-->changelist.html,使用for循环action_list

    {% extends 'stark/layout.html' %}
    
    {% block content %}
        <h1>列表页面</h1>
        <div>
            {% if add_btn %}
                <div style="margin: 5px 0;">
                    {{ add_btn }}
                </div>
            {% endif %}
            <form class="form-inline" method="post">
                {% csrf_token %}
                <div class="form-group">
                    <select name="action" class="form-control" style="min- 200px;">
                        <option>请选择功能</option>
                        {% for func in action_list %}
                            <option value="1">{{ func.__name__ }}</option>
                        {% endfor %}
                    </select>
                    <input class="btn btn-primary" type="submit" value="执行">
                </div>
            </form>
            <table class="table table-bordered">
                <thead>
                <tr>
                    {% for item in header_list %}
                        <th>{{ item }}</th>
                    {% endfor %}
    
                </tr>
                </thead>
                <tbody>
                {% for row_list in body_list %}
                    <tr>
                        {% for col in row_list %}
                            <td>{{ col }}</td>
                        {% endfor %}
    
                    </tr>
                {% endfor %}
                </tbody>
            </table>
    
        </div>
    
    
    
    {% endblock %}
    View Code

    重启django,查看页面:http://127.0.0.1:8000/stark/app01/depart/list/

    效果如下:

    提示  变量和属性不能从下划线开始

    怎么办呢?让后端把函数名传过来,使用列表生成式

    修改stark-->server-->stark.py,使用列表生成式

    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
    
    class StarkConfig(object):
        def __init__(self,model_class,site):
            self.model_class = model_class
            self.site = site
    
        def display_checkbox(self,row=None,header=False):  # 显示复选框
            if header:
                # 输出中文
                return "选择"
            # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
            return mark_safe('<input type="checkbox" name="pk" values="%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)
    
        order_by = []  # 需要排序的字段,由用户自定义
        list_display = []  # 定义显示的列,由用户自定义
        model_form_class = None  # form组件需要的model_class
        action_list = []  # 批量操作方法
    
        def multi_delete(self):  # 批量删除
            pass
    
        def multi_init(self):  # 批量初始化
            pass
    
        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 changelist_view(self, request):
            """
            所有URL查看列表页面
            :param request:
            :return:
            """
            # 根据排序列表进行排序
            queryset = self.model_class.objects.all().order_by(*self.get_order_by())
            ### 批量操作 ###
            action_list = self.get_action_list()
            action_list = [ func.__name__ for func in action_list ]
            ### 添加按钮 ###
            add_btn = self.get_add_btn()  # 添加按钮返回值,不为空展示,否则不展示
    
    
            list_display = self.list_display  # 定义显示的列
            header_list = []  # 定义头部,用来显示verbose_name
            if list_display:
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        # 执行函数,默认显示中文
                        verbose_name = name_or_func(self,header=True)
                    else:
                        # 获取指定字段的verbose_name
                        verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
    
                    header_list.append(verbose_name)
            else:
                # 如果list_display为空,添加表名
                header_list.append(self.model_class._meta.model_name)
    
            body_list = []  # 显示内容
    
            for row in queryset:
                # 这里的row是对象,它表示表里面的一条数据
                row_list = []  # 展示每一行数据
                if not list_display:  # 如果不在list_display里面
                    # 添加对象
                    row_list.append(row)
                    body_list.append(row_list)
                    continue
    
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        val = name_or_func(self,row=row)  # 执行函数获取,传递row对象
                    else:
                        # 使用反射获取对象的值
                        val = getattr(row, name_or_func)
    
                    row_list.append(val)
    
                body_list.append(row_list)
    
            # 注意:要传入add_btn
            return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
    
        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):
            pass
    
        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),
                url(r'^add/$', self.add_view, name='%s_%s_add' % info),
                url(r'^(?P<pk>d+)/change/', self.change_view, name='%s_%s_change' % info),
                url(r'^(?P<pk>d+)/del/', 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)
            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)
            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})
            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})
            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,修改变量名

    {% extends 'stark/layout.html' %}
    
    {% block content %}
        <h1>列表页面</h1>
        <div>
            {% if add_btn %}
                <div style="margin: 5px 0;">
                    {{ add_btn }}
                </div>
            {% endif %}
            <form class="form-inline" method="post">
                {% csrf_token %}
                <div class="form-group">
                    <select name="action" class="form-control" style="min- 200px;">
                        <option>请选择功能</option>
                        {% for name in action_list %}
                            <option value="1">{{ name }}</option>
                        {% endfor %}
                    </select>
                    <input class="btn btn-primary" type="submit" value="执行">
                </div>
            </form>
            <table class="table table-bordered">
                <thead>
                <tr>
                    {% for item in header_list %}
                        <th>{{ item }}</th>
                    {% endfor %}
    
                </tr>
                </thead>
                <tbody>
                {% for row_list in body_list %}
                    <tr>
                        {% for col in row_list %}
                            <td>{{ col }}</td>
                        {% endfor %}
    
                    </tr>
                {% endfor %}
                </tbody>
            </table>
    
        </div>
    
    
    
    {% endblock %}
    View Code

    刷新页面,效果如下:

    它是英文的,要中文展示,怎么搞?

    python一切皆对象,可以为函数添加一个属性。属性名无所谓!

    新建一个a.py文件,内容如下:

    def func():
        print(123)
    
    func.aa = '批量删除'
    
    print(func.aa)
    View Code

    执行输出:批量删除

    同样的道理,给函数multi_delete和multi_init,添加属性text

    在列表生成式中,获取text属性

    修改stark-->server-->stark.py,

    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
    
    class StarkConfig(object):
        def __init__(self,model_class,site):
            self.model_class = model_class
            self.site = site
    
        def display_checkbox(self,row=None,header=False):  # 显示复选框
            if header:
                # 输出中文
                return "选择"
            # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
            return mark_safe('<input type="checkbox" name="pk" values="%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)
    
        order_by = []  # 需要排序的字段,由用户自定义
        list_display = []  # 定义显示的列,由用户自定义
        model_form_class = None  # form组件需要的model_class
        action_list = []  # 批量操作方法
    
        def multi_delete(self):  # 批量删除
            pass
    
        multi_delete.text = "批量删除"  # 添加自定义属性text
    
        def multi_init(self):  # 批量初始化
            pass
    
        multi_init.text = "批量初始化"  # 添加自定义属性text
    
        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 changelist_view(self, request):
            """
            所有URL查看列表页面
            :param request:
            :return:
            """
            # 根据排序列表进行排序
            queryset = self.model_class.objects.all().order_by(*self.get_order_by())
            ### 批量操作 ###
            action_list = self.get_action_list()
            # 获取text属性
            action_list = [ func.text for func in action_list ]
            ### 添加按钮 ###
            add_btn = self.get_add_btn()  # 添加按钮返回值,不为空展示,否则不展示
    
    
            list_display = self.list_display  # 定义显示的列
            header_list = []  # 定义头部,用来显示verbose_name
            if list_display:
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        # 执行函数,默认显示中文
                        verbose_name = name_or_func(self,header=True)
                    else:
                        # 获取指定字段的verbose_name
                        verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
    
                    header_list.append(verbose_name)
            else:
                # 如果list_display为空,添加表名
                header_list.append(self.model_class._meta.model_name)
    
            body_list = []  # 显示内容
    
            for row in queryset:
                # 这里的row是对象,它表示表里面的一条数据
                row_list = []  # 展示每一行数据
                if not list_display:  # 如果不在list_display里面
                    # 添加对象
                    row_list.append(row)
                    body_list.append(row_list)
                    continue
    
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        val = name_or_func(self,row=row)  # 执行函数获取,传递row对象
                    else:
                        # 使用反射获取对象的值
                        val = getattr(row, name_or_func)
    
                    row_list.append(val)
    
                body_list.append(row_list)
    
            # 注意:要传入add_btn
            return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
    
        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):
            pass
    
        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),
                url(r'^add/$', self.add_view, name='%s_%s_add' % info),
                url(r'^(?P<pk>d+)/change/', self.change_view, name='%s_%s_change' % info),
                url(r'^(?P<pk>d+)/del/', 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)
            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)
            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})
            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})
            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

    重启django,刷新页面,效果如下:

    光有中文,无法知道它对应的是哪个函数?value值都是1

    修改stark-->server-->stark.py,更改列表生成式的元素为字典

    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
    
    class StarkConfig(object):
        def __init__(self,model_class,site):
            self.model_class = model_class
            self.site = site
    
        def display_checkbox(self,row=None,header=False):  # 显示复选框
            if header:
                # 输出中文
                return "选择"
            # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
            return mark_safe('<input type="checkbox" name="pk" values="%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)
    
        order_by = []  # 需要排序的字段,由用户自定义
        list_display = []  # 定义显示的列,由用户自定义
        model_form_class = None  # form组件需要的model_class
        action_list = []  # 批量操作方法
    
        def multi_delete(self):  # 批量删除
            pass
    
        multi_delete.text = "批量删除"  # 添加自定义属性text
    
        def multi_init(self):  # 批量初始化
            pass
    
        multi_init.text = "批量初始化"  # 添加自定义属性text
    
        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 changelist_view(self, request):
            """
            所有URL查看列表页面
            :param request:
            :return:
            """
            # 根据排序列表进行排序
            queryset = self.model_class.objects.all().order_by(*self.get_order_by())
            ### 批量操作 ###
            action_list = self.get_action_list()
            # 获取函数名以及text属性
            action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
            ### 添加按钮 ###
            add_btn = self.get_add_btn()  # 添加按钮返回值,不为空展示,否则不展示
    
    
            list_display = self.list_display  # 定义显示的列
            header_list = []  # 定义头部,用来显示verbose_name
            if list_display:
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        # 执行函数,默认显示中文
                        verbose_name = name_or_func(self,header=True)
                    else:
                        # 获取指定字段的verbose_name
                        verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
    
                    header_list.append(verbose_name)
            else:
                # 如果list_display为空,添加表名
                header_list.append(self.model_class._meta.model_name)
    
            body_list = []  # 显示内容
    
            for row in queryset:
                # 这里的row是对象,它表示表里面的一条数据
                row_list = []  # 展示每一行数据
                if not list_display:  # 如果不在list_display里面
                    # 添加对象
                    row_list.append(row)
                    body_list.append(row_list)
                    continue
    
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        val = name_or_func(self,row=row)  # 执行函数获取,传递row对象
                    else:
                        # 使用反射获取对象的值
                        val = getattr(row, name_or_func)
    
                    row_list.append(val)
    
                body_list.append(row_list)
    
            # 注意:要传入add_btn
            return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
    
        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):
            pass
    
        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),
                url(r'^add/$', self.add_view, name='%s_%s_add' % info),
                url(r'^(?P<pk>d+)/change/', self.change_view, name='%s_%s_change' % info),
                url(r'^(?P<pk>d+)/del/', 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)
            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)
            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})
            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})
            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,value值为函数名

    {% extends 'stark/layout.html' %}
    
    {% block content %}
        <h1>列表页面</h1>
        <div>
            {% if add_btn %}
                <div style="margin: 5px 0;">
                    {{ add_btn }}
                </div>
            {% endif %}
            <form class="form-inline" method="post">
                {% csrf_token %}
                <div class="form-group">
                    <select name="action" class="form-control" style="min- 200px;">
                        <option>请选择功能</option>
                        {% for item in action_list %}
                            <option value="{{ item.name }}">{{ item.text }}</option>
                        {% endfor %}
                    </select>
                    <input class="btn btn-primary" type="submit" value="执行">
                </div>
            </form>
            <table class="table table-bordered">
                <thead>
                <tr>
                    {% for item in header_list %}
                        <th>{{ item }}</th>
                    {% endfor %}
    
                </tr>
                </thead>
                <tbody>
                {% for row_list in body_list %}
                    <tr>
                        {% for col in row_list %}
                            <td>{{ col }}</td>
                        {% endfor %}
    
                    </tr>
                {% endfor %}
                </tbody>
            </table>
    
        </div>
    
    
    
    {% endblock %}
    View Code

    刷新页面,效果如下:

    可以看到value值已经渲染出来了!

    那么问题来了,table表格的数据,没有包含到form表单里面

    修改 stark-->templates-->stark-->changelist.html,更改form表单

    {% extends 'stark/layout.html' %}
    
    {% block content %}
        <h1>列表页面</h1>
        <div>
            {% if add_btn %}
                <div style="margin: 5px 0;">
                    {{ add_btn }}
                </div>
            {% endif %}
            <form class="form-inline" method="post">
                {% csrf_token %}
                <div class="form-group">
                    <select name="action" class="form-control" style="min- 200px;">
                        <option>请选择功能</option>
                        {% for item in action_list %}
                            <option value="{{ item.name }}">{{ item.text }}</option>
                        {% endfor %}
                    </select>
                    <input class="btn btn-primary" type="submit" value="执行">
                </div>
    
                <table class="table table-bordered">
                    <thead>
                    <tr>
                        {% for item in header_list %}
                            <th>{{ item }}</th>
                        {% endfor %}
    
                    </tr>
                    </thead>
                    <tbody>
                    {% for row_list in body_list %}
                        <tr>
                            {% for col in row_list %}
                                <td>{{ col }}</td>
                            {% endfor %}
    
                        </tr>
                    {% endfor %}
                    </tbody>
                </table>
            </form>
        </div>
    
    
    
    {% endblock %}
    View Code

    修改stark-->server-->stark.py,修改changelist_view方法,接收post数据

    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
    
    class StarkConfig(object):
        def __init__(self,model_class,site):
            self.model_class = model_class
            self.site = site
    
        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)
    
        order_by = []  # 需要排序的字段,由用户自定义
        list_display = []  # 定义显示的列,由用户自定义
        model_form_class = None  # form组件需要的model_class
        action_list = []  # 批量操作方法
    
        def multi_delete(self):  # 批量删除
            pass
    
        multi_delete.text = "批量删除"  # 添加自定义属性text
    
        def multi_init(self):  # 批量初始化
            pass
    
        multi_init.text = "批量初始化"  # 添加自定义属性text
    
        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 changelist_view(self, request):
            """
            所有URL查看列表页面
            :param request:
            :return:
            """
            if request.method == "POST":
                print(request.POST)  # 获取post数据
    
    
            # 根据排序列表进行排序
            queryset = self.model_class.objects.all().order_by(*self.get_order_by())
            ### 批量操作 ###
            action_list = self.get_action_list()
            # 获取函数名以及text属性
            action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
            # print(action_list)
            ### 添加按钮 ###
            add_btn = self.get_add_btn()  # 添加按钮返回值,不为空展示,否则不展示
    
    
            list_display = self.list_display  # 定义显示的列
            header_list = []  # 定义头部,用来显示verbose_name
            if list_display:
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        # 执行函数,默认显示中文
                        verbose_name = name_or_func(self,header=True)
                    else:
                        # 获取指定字段的verbose_name
                        verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
    
                    header_list.append(verbose_name)
            else:
                # 如果list_display为空,添加表名
                header_list.append(self.model_class._meta.model_name)
    
            body_list = []  # 显示内容
    
            for row in queryset:
                # 这里的row是对象,它表示表里面的一条数据
                row_list = []  # 展示每一行数据
                if not list_display:  # 如果不在list_display里面
                    # 添加对象
                    row_list.append(row)
                    body_list.append(row_list)
                    continue
    
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        val = name_or_func(self,row=row)  # 执行函数获取,传递row对象
                    else:
                        # 使用反射获取对象的值
                        val = getattr(row, name_or_func)
    
                    row_list.append(val)
    
                body_list.append(row_list)
    
            # 注意:要传入add_btn
            return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
    
        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):
            pass
    
        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),
                url(r'^add/$', self.add_view, name='%s_%s_add' % info),
                url(r'^(?P<pk>d+)/change/', self.change_view, name='%s_%s_change' % info),
                url(r'^(?P<pk>d+)/del/', 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)
            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)
            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})
            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})
            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

    刷新页面,选择批量删除,勾选几条数据。点击提交

    查看Pycharm控制台输出:

    <QueryDict: {'csrfmiddlewaretoken': ['FtQPfzOYfkiOxGx80QYcIFxqT9wqSpgwWYO1M9rR9lUzkLwDgO6ud260AfIUkJnK'], 'pk': ['1', '2', '3'], 'action': ['multi_delete']}>

    获取得到数据了!

    接下来需要根据获取的方法名来执行方法。怎么判断呢?使用if?如果有多个方法呢?

    这个时候,应该使用反射

    修改stark-->server-->stark.py,使用反射执行对应的方法。修改那2个方法

    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
    
    class StarkConfig(object):
        def __init__(self,model_class,site):
            self.model_class = model_class
            self.site = site
    
        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)
    
        order_by = []  # 需要排序的字段,由用户自定义
        list_display = []  # 定义显示的列,由用户自定义
        model_form_class = None  # form组件需要的model_class
        action_list = []  # 批量操作方法
    
        def multi_delete(self,request):  # 批量删除
            print('批量删除')
    
        multi_delete.text = "批量删除"  # 添加自定义属性text
    
        def multi_init(self,request):  # 批量初始化
            print('批量初始化')
    
        multi_init.text = "批量初始化"  # 添加自定义属性text
    
        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 changelist_view(self, request):
            """
            所有URL查看列表页面
            :param request:
            :return:
            """
            if request.method == "POST":
                action_name = request.POST.get('action')
                getattr(self,action_name)(request)
    
    
            # 根据排序列表进行排序
            queryset = self.model_class.objects.all().order_by(*self.get_order_by())
            ### 批量操作 ###
            action_list = self.get_action_list()
            # 获取函数名以及text属性
            action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
            # print(action_list)
            ### 添加按钮 ###
            add_btn = self.get_add_btn()  # 添加按钮返回值,不为空展示,否则不展示
    
    
            list_display = self.list_display  # 定义显示的列
            header_list = []  # 定义头部,用来显示verbose_name
            if list_display:
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        # 执行函数,默认显示中文
                        verbose_name = name_or_func(self,header=True)
                    else:
                        # 获取指定字段的verbose_name
                        verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
    
                    header_list.append(verbose_name)
            else:
                # 如果list_display为空,添加表名
                header_list.append(self.model_class._meta.model_name)
    
            body_list = []  # 显示内容
    
            for row in queryset:
                # 这里的row是对象,它表示表里面的一条数据
                row_list = []  # 展示每一行数据
                if not list_display:  # 如果不在list_display里面
                    # 添加对象
                    row_list.append(row)
                    body_list.append(row_list)
                    continue
    
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        val = name_or_func(self,row=row)  # 执行函数获取,传递row对象
                    else:
                        # 使用反射获取对象的值
                        val = getattr(row, name_or_func)
    
                    row_list.append(val)
    
                body_list.append(row_list)
    
            # 注意:要传入add_btn
            return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
    
        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):
            pass
    
        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),
                url(r'^add/$', self.add_view, name='%s_%s_add' % info),
                url(r'^(?P<pk>d+)/change/', self.change_view, name='%s_%s_change' % info),
                url(r'^(?P<pk>d+)/del/', 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)
            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)
            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})
            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})
            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

    刷新页面,再次选择批量删除,勾选几条数据,点击提交!

    查看Pycharm控制台输出: 批量删除

    假如有不法分子,修改html呢?

    为了避免攻击,使用字典判断,因为字典查询快

    修改stark-->server-->stark.py,增加方法get_action_dict

    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
    
    class StarkConfig(object):
        def __init__(self,model_class,site):
            self.model_class = model_class
            self.site = site
    
        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)
    
        order_by = []  # 需要排序的字段,由用户自定义
        list_display = []  # 定义显示的列,由用户自定义
        model_form_class = None  # form组件需要的model_class
        action_list = []  # 批量操作方法
    
        def multi_delete(self,request):  # 批量删除
            print('批量删除')
            return HttpResponse('批量删除')
    
        multi_delete.text = "批量删除"  # 添加自定义属性text
    
        def multi_init(self,request):  # 批量初始化
            print('批量初始化')
    
        multi_init.text = "批量初始化"  # 添加自定义属性text
    
        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 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
    
    
            # 根据排序列表进行排序
            queryset = self.model_class.objects.all().order_by(*self.get_order_by())
            ### 批量操作 ###
            action_list = self.get_action_list()
            # 获取函数名以及text属性
            action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
            # print(action_list)
            ### 添加按钮 ###
            add_btn = self.get_add_btn()  # 添加按钮返回值,不为空展示,否则不展示
    
    
            list_display = self.list_display  # 定义显示的列
            header_list = []  # 定义头部,用来显示verbose_name
            if list_display:
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        # 执行函数,默认显示中文
                        verbose_name = name_or_func(self,header=True)
                    else:
                        # 获取指定字段的verbose_name
                        verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
    
                    header_list.append(verbose_name)
            else:
                # 如果list_display为空,添加表名
                header_list.append(self.model_class._meta.model_name)
    
            body_list = []  # 显示内容
    
            for row in queryset:
                # 这里的row是对象,它表示表里面的一条数据
                row_list = []  # 展示每一行数据
                if not list_display:  # 如果不在list_display里面
                    # 添加对象
                    row_list.append(row)
                    body_list.append(row_list)
                    continue
    
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        val = name_or_func(self,row=row)  # 执行函数获取,传递row对象
                    else:
                        # 使用反射获取对象的值
                        val = getattr(row, name_or_func)
    
                    row_list.append(val)
    
                body_list.append(row_list)
    
            # 注意:要传入add_btn
            return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
    
        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):
            pass
    
        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),
                url(r'^add/$', self.add_view, name='%s_%s_add' % info),
                url(r'^(?P<pk>d+)/change/', self.change_view, name='%s_%s_change' % info),
                url(r'^(?P<pk>d+)/del/', 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)
            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)
            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})
            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})
            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-->server-->stark.py,修改multi_delete方法

    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
    
    class StarkConfig(object):
        def __init__(self,model_class,site):
            self.model_class = model_class
            self.site = site
    
        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)
    
        order_by = []  # 需要排序的字段,由用户自定义
        list_display = []  # 定义显示的列,由用户自定义
        model_form_class = None  # form组件需要的model_class
        action_list = []  # 批量操作方法
    
        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
    
        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 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
    
    
            # 根据排序列表进行排序
            queryset = self.model_class.objects.all().order_by(*self.get_order_by())
            ### 批量操作 ###
            action_list = self.get_action_list()
            # 获取函数名以及text属性
            action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
            # print(action_list)
            ### 添加按钮 ###
            add_btn = self.get_add_btn()  # 添加按钮返回值,不为空展示,否则不展示
    
    
            list_display = self.list_display  # 定义显示的列
            header_list = []  # 定义头部,用来显示verbose_name
            if list_display:
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        # 执行函数,默认显示中文
                        verbose_name = name_or_func(self,header=True)
                    else:
                        # 获取指定字段的verbose_name
                        verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
    
                    header_list.append(verbose_name)
            else:
                # 如果list_display为空,添加表名
                header_list.append(self.model_class._meta.model_name)
    
            body_list = []  # 显示内容
    
            for row in queryset:
                # 这里的row是对象,它表示表里面的一条数据
                row_list = []  # 展示每一行数据
                if not list_display:  # 如果不在list_display里面
                    # 添加对象
                    row_list.append(row)
                    body_list.append(row_list)
                    continue
    
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        val = name_or_func(self,row=row)  # 执行函数获取,传递row对象
                    else:
                        # 使用反射获取对象的值
                        val = getattr(row, name_or_func)
    
                    row_list.append(val)
    
                body_list.append(row_list)
    
            # 注意:要传入add_btn
            return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
    
        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):
            pass
    
        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),
                url(r'^add/$', self.add_view, name='%s_%s_add' % info),
                url(r'^(?P<pk>d+)/change/', self.change_view, name='%s_%s_change' % info),
                url(r'^(?P<pk>d+)/del/', 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)
            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)
            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})
            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})
            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

    刷新页面,选中一条数据,点击提交

     

    发现少了一条数据

    访问其它页面,比如: http://127.0.0.1:8000/stark/app01/userinfo/list/

    发现下拉框是空的,那么它就不应该显示!

    修改stark-->templates-->stark-->changelist.html,添加if判断

    {% extends 'stark/layout.html' %}
    
    {% block content %}
        <h1>列表页面</h1>
        <div>
            {% if add_btn %}
                <div style="margin: 5px 0;">
                    {{ add_btn }}
                </div>
            {% endif %}
            <form class="form-inline" method="post">
                {% csrf_token %}
                {% if action_list %}
                    <div class="form-group">
                        <select name="action" class="form-control" style="min- 200px;">
                            <option>请选择功能</option>
                            {% for item in action_list %}
                                <option value="{{ item.name }}">{{ item.text }}</option>
                            {% endfor %}
                        </select>
                        <input class="btn btn-primary" type="submit" value="执行">
                    </div>
                {% endif %}
    
                <table class="table table-bordered">
                    <thead>
                    <tr>
                        {% for item in header_list %}
                            <th>{{ item }}</th>
                        {% endfor %}
    
                    </tr>
                    </thead>
                    <tbody>
                    {% for row_list in body_list %}
                        <tr>
                            {% for col in row_list %}
                                <td>{{ col }}</td>
                            {% endfor %}
    
                        </tr>
                    {% endfor %}
                    </tbody>
                </table>
            </form>
        </div>
    
    
    
    {% endblock %}
    View Code

    刷新页面,效果如下:

    多选框,没有了!

    其他页面,想使用批量操作,定义action_list变量,就可以了!

    二、快速搜索

     搜索什么内容,取哪个字段搜,都是可以定制的!

    修改stark-->server-->stark.py,增加search_list变量。定义name和tel可以搜索!

    增加方法get_search_list方法

    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
    
    class StarkConfig(object):
        def __init__(self,model_class,site):
            self.model_class = model_class
            self.site = site
    
        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 = []  # 批量操作方法
        search_list = ['name','tel']  # 固定搜索字段
    
        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 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
    
            ### 处理搜索 ###
            from django.db.models import Q
            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))
    
    
            # 根据排序列表进行排序
            queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
            ### 批量操作 ###
            action_list = self.get_action_list()
            # 获取函数名以及text属性
            action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
            # print(action_list)
            ### 添加按钮 ###
            add_btn = self.get_add_btn()  # 添加按钮返回值,不为空展示,否则不展示
    
    
            list_display = self.list_display  # 定义显示的列
            header_list = []  # 定义头部,用来显示verbose_name
            if list_display:
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        # 执行函数,默认显示中文
                        verbose_name = name_or_func(self,header=True)
                    else:
                        # 获取指定字段的verbose_name
                        verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
    
                    header_list.append(verbose_name)
            else:
                # 如果list_display为空,添加表名
                header_list.append(self.model_class._meta.model_name)
    
            body_list = []  # 显示内容
    
            for row in queryset:
                # 这里的row是对象,它表示表里面的一条数据
                row_list = []  # 展示每一行数据
                if not list_display:  # 如果不在list_display里面
                    # 添加对象
                    row_list.append(row)
                    body_list.append(row_list)
                    continue
    
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        val = name_or_func(self,row=row)  # 执行函数获取,传递row对象
                    else:
                        # 使用反射获取对象的值
                        val = getattr(row, name_or_func)
    
                    row_list.append(val)
    
                body_list.append(row_list)
    
            # 注意:要传入add_btn
            return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
    
        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):
            pass
    
        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),
                url(r'^add/$', self.add_view, name='%s_%s_add' % info),
                url(r'^(?P<pk>d+)/change/', self.change_view, name='%s_%s_change' % info),
                url(r'^(?P<pk>d+)/del/', 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)
            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)
            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})
            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})
            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

    访问url,要带上q参数

    http://127.0.0.1:8000/stark/app01/depart/list/?q=总

    效果如下:

    去掉参数,有2条数据

    这样体验不好,要用户输入才行!

    修改stark-->server-->stark.py,给changelist.html传入参数q

    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
    
    class StarkConfig(object):
        def __init__(self,model_class,site):
            self.model_class = model_class
            self.site = site
    
        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 = []  # 批量操作方法
        search_list = ['name','tel']  # 固定搜索字段
    
        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 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
    
            ### 处理搜索 ###
            from django.db.models import Q
            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))
    
    
            # 根据排序列表进行排序
            queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
            ### 批量操作 ###
            action_list = self.get_action_list()
            # 获取函数名以及text属性
            action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
            # print(action_list)
            ### 添加按钮 ###
            add_btn = self.get_add_btn()  # 添加按钮返回值,不为空展示,否则不展示
    
    
            list_display = self.list_display  # 定义显示的列
            header_list = []  # 定义头部,用来显示verbose_name
            if list_display:
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        # 执行函数,默认显示中文
                        verbose_name = name_or_func(self,header=True)
                    else:
                        # 获取指定字段的verbose_name
                        verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
    
                    header_list.append(verbose_name)
            else:
                # 如果list_display为空,添加表名
                header_list.append(self.model_class._meta.model_name)
    
            body_list = []  # 显示内容
    
            for row in queryset:
                # 这里的row是对象,它表示表里面的一条数据
                row_list = []  # 展示每一行数据
                if not list_display:  # 如果不在list_display里面
                    # 添加对象
                    row_list.append(row)
                    body_list.append(row_list)
                    continue
    
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        val = name_or_func(self,row=row)  # 执行函数获取,传递row对象
                    else:
                        # 使用反射获取对象的值
                        val = getattr(row, name_or_func)
    
                    row_list.append(val)
    
                body_list.append(row_list)
    
            # 注意:要传入参数
            return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q})
    
        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):
            pass
    
        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),
                url(r'^add/$', self.add_view, name='%s_%s_add' % info),
                url(r'^(?P<pk>d+)/change/', self.change_view, name='%s_%s_change' % info),
                url(r'^(?P<pk>d+)/del/', 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)
            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)
            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})
            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})
            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,增加输入框

    即使页面刷新,input输入框还会保留搜索的关键字!

    {% extends 'stark/layout.html' %}
    
    {% block content %}
        <h1>列表页面</h1>
        <div>
            {#添加按钮#}
            {% if add_btn %}
                <div style="margin: 5px 0;">
                    {{ add_btn }}
                </div>
            {% endif %}
            {#搜索框#}
            <div style="float: right;">
                <form method="GET" class="form-inline">
                    <div class="form-group">
                        <input class="form-control" type="text" name="q" value="{{ q }}" placeholder="关键字搜索">
                        <button class="btn btn-primary" type="submit">
                            <i class="fa fa-search" aria-hidden="true"></i>
                        </button>
                    </div>
                </form>
            </div>
    
            <form class="form-inline" method="post">
                {% csrf_token %}
                {#批量操作#}
                {% if action_list %}
                    <div class="form-group">
                        <select name="action" class="form-control" style="min- 200px;">
                            <option>请选择功能</option>
                            {% for item in action_list %}
                                <option value="{{ item.name }}">{{ item.text }}</option>
                            {% endfor %}
                        </select>
                        <input class="btn btn-primary" type="submit" value="执行">
                    </div>
                {% endif %}
                {#使用table展示数据#}
                <table class="table table-bordered">
                    <thead>
                    <tr>
                        {% for item in header_list %}
                            <th>{{ item }}</th>
                        {% endfor %}
    
                    </tr>
                    </thead>
                    <tbody>
                    {% for row_list in body_list %}
                        <tr>
                            {% for col in row_list %}
                                <td>{{ col }}</td>
                            {% endfor %}
    
                        </tr>
                    {% endfor %}
                    </tbody>
                </table>
            </form>
        </div>
    
    
    
    {% endblock %}
    View Code

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

    效果如下:

    输入关键字534,点击搜索按钮。那么url会自动带上参数,这个是GET本来就有的功能。因为GET参数在url里面!

    同时结果只有一条

    如果要搜索负责人呢?注意:这个字段涉及到跨表了。那么填写search_list参数时,要带上下划线

    修改stark-->server-->stark.py,修改search_list变量

    # 固定搜索字段,如果是跨表字段,要按照ORM语法来
    search_list = ['name','tel','user__username']

    刷新页面,重新制定关键字

    上面是为了方便调试,把搜索字段给固定死了。它应该是可以定制的!

    修改stark-->server-->stark.py,将search_list变量设置为空

    # 搜索字段,如果是跨表字段,要按照ORM语法来
    search_list = []

    修改 app01-->stark.py,指定变量search_list

    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,'name', 'tel', 'user']
        # model_form_class = DepartModelForm
        # 批量操作
        action_list = [StarkConfig.multi_delete,StarkConfig.multi_init]
        # 搜索关键字
        # 固定搜索字段,如果是跨表字段,要按照ORM语法来
        search_list = ['name', 'tel', 'user__username']
    
        # 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

    重启django,刷新页面,效果同上!

    如果没有定义search_list变量,那么search_list默认为空,它不应该展示!

    修改stark-->server-->stark.py,给changelist.html传入参数search_list

    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
    
    class StarkConfig(object):
        def __init__(self,model_class,site):
            self.model_class = model_class
            self.site = site
    
        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 = []
    
        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 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
    
            ### 处理搜索 ###
            from django.db.models import Q
            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))
    
    
            # 根据排序列表进行排序
            queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
            ### 批量操作 ###
            action_list = self.get_action_list()
            # 获取函数名以及text属性
            action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
            # print(action_list)
            ### 添加按钮 ###
            add_btn = self.get_add_btn()  # 添加按钮返回值,不为空展示,否则不展示
    
    
            list_display = self.list_display  # 定义显示的列
            header_list = []  # 定义头部,用来显示verbose_name
            if list_display:
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        # 执行函数,默认显示中文
                        verbose_name = name_or_func(self,header=True)
                    else:
                        # 获取指定字段的verbose_name
                        verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
    
                    header_list.append(verbose_name)
            else:
                # 如果list_display为空,添加表名
                header_list.append(self.model_class._meta.model_name)
    
            body_list = []  # 显示内容
    
            for row in queryset:
                # 这里的row是对象,它表示表里面的一条数据
                row_list = []  # 展示每一行数据
                if not list_display:  # 如果不在list_display里面
                    # 添加对象
                    row_list.append(row)
                    body_list.append(row_list)
                    continue
    
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        val = name_or_func(self,row=row)  # 执行函数获取,传递row对象
                    else:
                        # 使用反射获取对象的值
                        val = getattr(row, name_or_func)
    
                    row_list.append(val)
    
                body_list.append(row_list)
    
            # 注意:要传入参数
            return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list})
    
        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):
            pass
    
        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),
                url(r'^add/$', self.add_view, name='%s_%s_add' % info),
                url(r'^(?P<pk>d+)/change/', self.change_view, name='%s_%s_change' % info),
                url(r'^(?P<pk>d+)/del/', 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)
            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)
            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})
            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})
            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,增加if 判断

    {% extends 'stark/layout.html' %}
    
    {% block content %}
        <h1>列表页面</h1>
        <div>
            {#添加按钮#}
            {% if add_btn %}
                <div style="margin: 5px 0;">
                    {{ add_btn }}
                </div>
            {% endif %}
            {#搜索框#}
            {% if 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="{{ 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 action_list %}
                    <div class="form-group">
                        <select name="action" class="form-control" style="min- 200px;">
                            <option>请选择功能</option>
                            {% for item in action_list %}
                                <option value="{{ item.name }}">{{ item.text }}</option>
                            {% endfor %}
                        </select>
                        <input class="btn btn-primary" type="submit" value="执行">
                    </div>
                {% endif %}
                {#使用table展示数据#}
                <table class="table table-bordered">
                    <thead>
                    <tr>
                        {% for item in header_list %}
                            <th>{{ item }}</th>
                        {% endfor %}
    
                    </tr>
                    </thead>
                    <tbody>
                    {% for row_list in body_list %}
                        <tr>
                            {% for col in row_list %}
                                <td>{{ col }}</td>
                            {% endfor %}
    
                        </tr>
                    {% endfor %}
                    </tbody>
                </table>
            </form>
        </div>
    
    
    
    {% endblock %}
    View Code

    修改app01-->stark.py,注释掉search_list变量

    # 固定搜索字段,如果是跨表字段,要按照ORM语法来
    # search_list = ['name', 'tel', 'user__username']

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

    搜索框就没有了!

    在changelist_view中,处理搜索的代码,可以封装成方法。

    修改stark-->server-->stark.py,增加get_search_condition方法

    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
    
    class StarkConfig(object):
        def __init__(self,model_class,site):
            self.model_class = model_class
            self.site = site
    
        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 = []
    
        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 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)
    
            # 根据排序列表进行排序
            queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
            ### 批量操作 ###
            action_list = self.get_action_list()
            # 获取函数名以及text属性
            action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
            # print(action_list)
            ### 添加按钮 ###
            add_btn = self.get_add_btn()  # 添加按钮返回值,不为空展示,否则不展示
    
    
            list_display = self.list_display  # 定义显示的列
            header_list = []  # 定义头部,用来显示verbose_name
            if list_display:
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        # 执行函数,默认显示中文
                        verbose_name = name_or_func(self,header=True)
                    else:
                        # 获取指定字段的verbose_name
                        verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
    
                    header_list.append(verbose_name)
            else:
                # 如果list_display为空,添加表名
                header_list.append(self.model_class._meta.model_name)
    
            body_list = []  # 显示内容
    
            for row in queryset:
                # 这里的row是对象,它表示表里面的一条数据
                row_list = []  # 展示每一行数据
                if not list_display:  # 如果不在list_display里面
                    # 添加对象
                    row_list.append(row)
                    body_list.append(row_list)
                    continue
    
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        val = name_or_func(self,row=row)  # 执行函数获取,传递row对象
                    else:
                        # 使用反射获取对象的值
                        val = getattr(row, name_or_func)
    
                    row_list.append(val)
    
                body_list.append(row_list)
    
            # 注意:要传入参数
            return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list})
    
        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):
            pass
    
        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),
                url(r'^add/$', self.add_view, name='%s_%s_add' % info),
                url(r'^(?P<pk>d+)/change/', self.change_view, name='%s_%s_change' % info),
                url(r'^(?P<pk>d+)/del/', 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)
            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)
            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})
            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})
            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,开启search_list变量

    # 搜索字段,如果是跨表字段,要按照ORM语法来
    search_list = ['name', 'tel', 'user__username']

    重启django,刷新页面。测试搜索功能,效果同上!

    三、保留原搜索条件

    在内容前戏部分,讲到了 保留原搜索条件。关键点在于:搜索到结果后,跳转其他页面时,url要带上原搜索条件。

    比如页面默认展示的是20条数据。搜索了2个用户,并编辑保存。跳转的页面应该也还是之前2个客户的数据,而不是20数据(返回列表首页)

    页面上的a标签,button按钮。这些url是反向生成的,能不能加条件?加上原来的搜索条件?

    答案是可以的!

    装饰器

    查看 stark-->server-->stark.py,StarkConfig类里面的get_urls方法。里面定义了4个视图函数,它们是有request变量的。除此之外,其他函数也需要使用request变量,怎么办?一个一个传?

    太麻烦了,使用装饰器就可以解决!

    修改 stark-->server-->stark.py,修改StarkConfig类的__init__方法,添加变量request。

    增加装饰器wrapper。并定义self.request = request,对request重新赋值!

    修改get_urls方法,应用装饰器

    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 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 = []
    
        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 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)
    
            # 根据排序列表进行排序
            queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
            ### 批量操作 ###
            action_list = self.get_action_list()
            # 获取函数名以及text属性
            action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
            # print(action_list)
            ### 添加按钮 ###
            add_btn = self.get_add_btn()  # 添加按钮返回值,不为空展示,否则不展示
    
    
            list_display = self.list_display  # 定义显示的列
            header_list = []  # 定义头部,用来显示verbose_name
            if list_display:
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        # 执行函数,默认显示中文
                        verbose_name = name_or_func(self,header=True)
                    else:
                        # 获取指定字段的verbose_name
                        verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
    
                    header_list.append(verbose_name)
            else:
                # 如果list_display为空,添加表名
                header_list.append(self.model_class._meta.model_name)
    
            body_list = []  # 显示内容
    
            for row in queryset:
                # 这里的row是对象,它表示表里面的一条数据
                row_list = []  # 展示每一行数据
                if not list_display:  # 如果不在list_display里面
                    # 添加对象
                    row_list.append(row)
                    body_list.append(row_list)
                    continue
    
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        val = name_or_func(self,row=row)  # 执行函数获取,传递row对象
                    else:
                        # 使用反射获取对象的值
                        val = getattr(row, name_or_func)
    
                    row_list.append(val)
    
                body_list.append(row_list)
    
            # 注意:要传入参数
            return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list})
    
        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)
            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:
                return add_url
            param_str = self.request.GET.urlencode()  # q=嘉瑞&page=2
            new_query_dict = QueryDict(mutable=True)
            new_query_dict[self.back_condition_key] = param_str
            add_url = "%s?%s" % (add_url, new_query_dict.urlencode(),)
    
            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})
            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})
            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

    重启django,刷新页面,效果同上!

    修改 stark-->server-->stark.py,修改reverse_add_url方法,增加搜索条件

    其它3个方法reverse_del_url,reverse_edit_url,reverse_list_url也同样需要添加

    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 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 = []
    
        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 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)
    
            # 根据排序列表进行排序
            queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
            ### 批量操作 ###
            action_list = self.get_action_list()
            # 获取函数名以及text属性
            action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
            # print(action_list)
            ### 添加按钮 ###
            add_btn = self.get_add_btn()  # 添加按钮返回值,不为空展示,否则不展示
    
    
            list_display = self.list_display  # 定义显示的列
            header_list = []  # 定义头部,用来显示verbose_name
            if list_display:
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        # 执行函数,默认显示中文
                        verbose_name = name_or_func(self,header=True)
                    else:
                        # 获取指定字段的verbose_name
                        verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
    
                    header_list.append(verbose_name)
            else:
                # 如果list_display为空,添加表名
                header_list.append(self.model_class._meta.model_name)
    
            body_list = []  # 显示内容
    
            for row in queryset:
                # 这里的row是对象,它表示表里面的一条数据
                row_list = []  # 展示每一行数据
                if not list_display:  # 如果不在list_display里面
                    # 添加对象
                    row_list.append(row)
                    body_list.append(row_list)
                    continue
    
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        val = name_or_func(self,row=row)  # 执行函数获取,传递row对象
                    else:
                        # 使用反射获取对象的值
                        val = getattr(row, name_or_func)
    
                    row_list.append(val)
    
                body_list.append(row_list)
    
            # 注意:要传入参数
            return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list})
    
        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

    重启django项目,访问页面: http://127.0.0.1:8000/stark/app01/depart/list/

    输入搜索条件 xiao,点击搜索按钮

    查看添加按钮的跳转地址,发下已经添加了搜索条件

    修改app01-->stark.py,增加编辑和删除选项

    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,'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']
    
        # 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

    刷新页面,查看编辑和删除的herf属性

    发送链接地址,也加上了搜索条件。

    点击编辑按钮,此时的url地址为:

    http://127.0.0.1:8000/stark/app01/depart/1/change/?_filter=q%3Dxiao

    修改一条数据,点击提交

    页面跳转地址,此时之前的搜索条件还在!

    http://127.0.0.1:8000/stark/app01/depart/list/?q=xiao

    效果如下:

    如果有分页功能,这个搜索条件,还要不要?当然要啊!

    四、自定义分页

    展示数据页面数据过多,一般会采用分页处理。

    这里使用的分页,不是django自带的分页器(paginator) ,使用的是自定义分页类。为什么?虽然使用paginator也可以完成。但是我们要做的是,跨框架的组件。即使在flask框架中,也依然能使用!

     进入stark应用目录,创建文件夹utils,它表示工具类。在此文件夹下创建文件pagination.py

    注意:分页跳转时,它是带有搜索条件的

    这里没有使用request.GET.urlencode(),为了做到通用性。其他框架也可以使用!

    """
    分页组件
    """
    from urllib.parse import urlencode
    
    class Pagination(object):
        def __init__(self, current_page, all_count, base_url,query_params, per_page=10, pager_page_count=11):
            """
            分页初始化
            :param current_page: 当前页码
            :param per_page: 每页显示数据条数
            :param all_count: 数据库中总条数
            :param base_url: 基础URL
            :param query_params: QueryDict对象,内部含所有当前URL的原条件
            :param pager_page_count: 页面上最多显示的页码数量
            """
            self.base_url = base_url
            try:
                self.current_page = int(current_page)
                if self.current_page <= 0:  # 当前页码数不能小于等于0
                    raise Exception()
            except Exception as e:
                self.current_page = 1
            self.query_params = query_params
            self.per_page = per_page
            self.all_count = all_count
            self.pager_page_count = pager_page_count
            pager_count, b = divmod(all_count, per_page)
            if b != 0:
                pager_count += 1
            self.pager_count = pager_count
    
            half_pager_page_count = int(pager_page_count / 2)
            self.half_pager_page_count = half_pager_page_count
    
        @property
        def start(self):
            """
            数据获取值起始索引
            :return:
            """
            return (self.current_page - 1) * self.per_page
    
        @property
        def end(self):
            """
            数据获取值结束索引
            :return:
            """
            return self.current_page * self.per_page
    
        def page_html(self):
            """
            生成HTML页码
            :return:
            """
            # 如果数据总页码pager_count<11 pager_page_count
            if self.pager_count < self.pager_page_count:
                pager_start = 1
                pager_end = self.pager_count
            else:
                # 数据页码已经超过11
                # 判断: 如果当前页 <= 5 half_pager_page_count
                if self.current_page <= self.half_pager_page_count:
                    pager_start = 1
                    pager_end = self.pager_page_count
                else:
                    # 如果: 当前页+5 > 总页码
                    if (self.current_page + self.half_pager_page_count) > self.pager_count:
                        pager_end = self.pager_count
                        pager_start = self.pager_count - self.pager_page_count + 1
                    else:
                        pager_start = self.current_page - self.half_pager_page_count
                        pager_end = self.current_page + self.half_pager_page_count
    
            page_list = []
    
            if self.current_page <= 1:
                prev = '<li><a href="#">上一页</a></li>'
            else:
                self.query_params['page'] = self.current_page - 1
                prev = '<li><a href="%s?%s">上一页</a></li>' % (self.base_url,self.query_params.urlencode())
            page_list.append(prev)
            for i in range(pager_start, pager_end + 1):
                self.query_params['page'] = i
                if self.current_page == i:
                    tpl = '<li class="active"><a href="%s?%s">%s</a></li>' % (
                        self.base_url, self.query_params.urlencode(), i,)
                else:
                    tpl = '<li><a href="%s?%s">%s</a></li>' % (self.base_url, self.query_params.urlencode(), i,)
                page_list.append(tpl)
    
            if self.current_page >= self.pager_count:
                nex = '<li><a href="#">下一页</a></li>'
            else:
                self.query_params['page'] = self.current_page + 1
                nex = '<li><a href="%s?%s">下一页</a></li>' % (self.base_url, self.query_params.urlencode(),)
            page_list.append(nex)
            page_str = "".join(page_list)
            return page_str
    View Code

    打开表app01_depart,添加几条数据

    修改app01-->stark.py,增加id显示

    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']
    
        # 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

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

    效果如下:

    修改 stark-->server-->stark.py,处理分页,并传入参数page给changelist.html

    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 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 = []
    
        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 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]
            ### 批量操作 ###
            action_list = self.get_action_list()
            # 获取函数名以及text属性
            action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
            # print(action_list)
            ### 添加按钮 ###
            add_btn = self.get_add_btn()  # 添加按钮返回值,不为空展示,否则不展示
    
    
            list_display = self.list_display  # 定义显示的列
            header_list = []  # 定义头部,用来显示verbose_name
            if list_display:
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        # 执行函数,默认显示中文
                        verbose_name = name_or_func(self,header=True)
                    else:
                        # 获取指定字段的verbose_name
                        verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
    
                    header_list.append(verbose_name)
            else:
                # 如果list_display为空,添加表名
                header_list.append(self.model_class._meta.model_name)
    
            body_list = []  # 显示内容
    
            for row in queryset:
                # 这里的row是对象,它表示表里面的一条数据
                row_list = []  # 展示每一行数据
                if not list_display:  # 如果不在list_display里面
                    # 添加对象
                    row_list.append(row)
                    body_list.append(row_list)
                    continue
    
                for name_or_func in list_display:
                    if isinstance(name_or_func,FunctionType):
                        val = name_or_func(self,row=row)  # 执行函数获取,传递row对象
                    else:
                        # 使用反射获取对象的值
                        val = getattr(row, name_or_func)
    
                    row_list.append(val)
    
                body_list.append(row_list)
    
            # 注意:要传入参数
            return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list,'page':page})
    
        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,增加分页标签

    {% extends 'stark/layout.html' %}
    
    {% block content %}
        <h1>列表页面</h1>
        <div>
            {#添加按钮#}
            {% if add_btn %}
                <div style="margin: 5px 0;">
                    {{ add_btn }}
                </div>
            {% endif %}
            {#搜索框#}
            {% if 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="{{ 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 action_list %}
                    <div class="form-group">
                        <select name="action" class="form-control" style="min- 200px;">
                            <option>请选择功能</option>
                            {% for item in action_list %}
                                <option value="{{ item.name }}">{{ item.text }}</option>
                            {% endfor %}
                        </select>
                        <input class="btn btn-primary" type="submit" value="执行">
                    </div>
                {% endif %}
                {#使用table展示数据#}
                <table class="table table-bordered">
                    <thead>
                    <tr>
                        {% for item in header_list %}
                            <th>{{ item }}</th>
                        {% endfor %}
    
                    </tr>
                    </thead>
                    <tbody>
                    {% for row_list in body_list %}
                        <tr>
                            {% for col in row_list %}
                                <td>{{ col }}</td>
                            {% endfor %}
    
                        </tr>
                    {% endfor %}
                    </tbody>
                </table>
                {#分页展示#}
                <nav aria-label="Page navigation">
                    <ul class="pagination">
                        {{ page.page_html|safe }}
                    </ul>
                </nav>
            </form>
        </div>
    
    
    
    {% endblock %}
    View Code

    基本测试

    重启django,刷新页面,效果如下:

    测试搜索条件

    五、拆分代码

    上面的 stark-->server-->stark.py,代码太冗长。不方便扩展功能!

    要用面向对象的封装特性,来做代码拆分。

    首先拆分changelist_view方法的render,它传了很多参数!代码太长!

    修改stark-->server-->stark.py,添加ChangeList类。将changelist_view中的相关变量移植过来

    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 = []
    
        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 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)
            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

    inclusion_tag+yield

    列表页面中的table表格数据,应该使用inclusion_tag+yield

    进入stark应用目录,创建目录templatetags,目录名必须是这个!在此目录新建文件stark.py

    from django.template import Library
    from types import FunctionType
    
    
    register = Library()
    
    def header_list(cl):
        """
        表头
        :param cl:
        :return:
        """
        if cl.list_display:
            for name_or_func in cl.list_display:
                if isinstance(name_or_func, FunctionType):
                    verbose_name = name_or_func(cl.config, header=True)
                else:
                    verbose_name = cl.config.model_class._meta.get_field(name_or_func).verbose_name
                yield verbose_name
        else:
            yield cl.config.model_class._meta.model_name
    
    def body_list(cl):
        """
        表格内容
        :param cl:
        :return:
        """
        for row in cl.queryset:
            row_list = []
            if not cl.list_display:
                row_list.append(row)
                yield row_list
                continue
            for name_or_func in cl.list_display:
                if isinstance(name_or_func, FunctionType):
                    val = name_or_func(cl.config, row=row)
                else:
                    val = getattr(row, name_or_func)
                row_list.append(val)
            yield row_list
    
    @register.inclusion_tag('stark/table.html')
    def table(cl):
    
        return {'header_list':header_list(cl),'body_list':body_list(cl)}
    View Code

    修改 stark-->templates-->stark-->custom_list.html,使用inclusion_tag

    {% extends 'stark/layout.html' %}
    {% load stark %}
    
    {% block content %}
        <h1>列表页面</h1>
        <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

    务必重启django,因为必须重启,inclusion_tag才会生效!

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

    效果如下:

    总结:

    1. 批量操作[扩展]
        - 反射 
        - __name__ 
        - 一切皆对象
            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 = "批量删除"
    
    2. 搜索[扩展]
        - Q 
        - __contains
    
    
    3. 保留原搜索条件 
        - QueryDict,request.GET/request.POST 
            - urlencode()
            - _mutable = True 
            - 深拷贝 
            - urllib.parse.urlencode
    
    4. 分页 
        - 分页组件
        - 保留原条件
        
    5. 拆分 
        - ChangeList类封装 
        - inclusion_tag
        - 生成器 
    View Code

    完整代码,请参数github:

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

  • 相关阅读:
    各种推荐资料汇总。。。
    不错的blog,做计算广告学的,还有机器学习的
    大数据建模,eBay的一个牛人
    factor graph和sum product和TrueSkill相关的两个blog,相当不错
    词云制作工具。。。
    CSS:nthchild选择器用法练习
    CSS控制表格隔行变色:nthchild()选择器
    CSS3calc()函数练习(制作响应式布局)
    CSS3boxsizing属性练习(borderbox设置padding和border不会改变width和height的值)
    CSS3clippath练习
  • 原文地址:https://www.cnblogs.com/xiao987334176/p/9568195.html
Copyright © 2011-2022 走看看