一、装饰器

装饰器本质上就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。

装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。

基本装饰器

不应用装饰器

  1. def wrapper(func):
  2. def inner(*args,**kwargs):
  3. return func(*args,**kwargs)
  4. return inner
  5. def f1():
  6. print('f1')
  7. print(f1.__name__)

执行输出:

  1. f1

使用装饰器

  1. def wrapper(func):
  2. def inner(*args,**kwargs):
  3. return func(*args,**kwargs)
  4. return inner
  5. @wrapper
  6. def f1():
  7. print('f1')
  8. print(f1.__name__)

执行输出:

  1. inner

咦?为什么输出了inner,我要的是f1啊。因为函数装饰之后,相当于执行了inner函数,所以输出inner

为了解决这个问题,需要调用一个模块wraps

高级装饰器

wraps将 被修饰的函数(wrapped) 的一些属性值赋值给 修饰器函数(wrapper) ,最终让属性的显示更符合我们的直觉!

导入模块functools,并使用@functools.wraps,就可进行高级伪装

  1. import functools
  2. def wrapper(func):
  3. @functools.wraps(func)
  4. def inner(*args,**kwargs):
  5. return func(*args,**kwargs)
  6. return inner
  7. @wrapper
  8. def f1():
  9. print('f1')
  10. print(f1.__name__)

执行输出:

  1. f1

虽然结果看起来是原来的,但真正执行的是inner

这个@functools.wraps装饰器,保留原函数信息,比如函数名

如果以后写装饰器,一定要写@functools.wraps!

后续flask会用到

二、排序规则

务必下载github代码:

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

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

修改stark—>server—>stark.py

注意:order_by是空列表,它需要用户自定义

  1. from django.conf.urls import url
  2. from django.shortcuts import HttpResponse
  3. class StarkConfig(object):
  4. def __init__(self,model_class,site):
  5. self.model_class = model_class
  6. self.site = site
  7. order_by = [] # 需要排序的字段,由用户自定义
  8. def get_order_by(self):
  9. return self.order_by # 获取排序列表
  10. def changelist_view(self, request):
  11. """
  12. 所有URL查看列表页面
  13. :param request:
  14. :return:
  15. """
  16. # 根据排序列表进行排序
  17. print(self.model_class)
  18. queryset = self.model_class.objects.all().order_by(*self.get_order_by())
  19. print(queryset)
  20. return HttpResponse('stark list')
  21. def add_view(self, request):
  22. return HttpResponse('stark add')
  23. def change_view(self, request, pk):
  24. return HttpResponse('stark change')
  25. def delete_view(self, request, pk):
  26. return HttpResponse('stark delete')
  27. def wrapper(self,func):
  28. pass
  29. def get_urls(self):
  30. info = self.model_class._meta.app_label, self.model_class._meta.model_name
  31. # print(info)
  32. urlpatterns = [
  33. url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
  34. url(r'^add/$', self.add_view, name='%s_%s_add' % info),
  35. url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
  36. url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
  37. ]
  38. extra = self.extra_url()
  39. if extra: # 判断变量不为空
  40. # 扩展路由
  41. urlpatterns.extend(extra)
  42. # print(urlpatterns)
  43. return urlpatterns
  44. def extra_url(self): # 额外的路由,由调用者重构
  45. pass
  46. @property
  47. def urls(self):
  48. return self.get_urls()
  49. class AdminSite(object):
  50. def __init__(self):
  51. self._registry = {}
  52. self.app_name = 'stark'
  53. self.namespace = 'stark'
  54. def register(self,model_class,stark_config=None):
  55. # not None的结果为Ture
  56. if not stark_config:
  57. # 也就是说,当其他应用调用register时,如果不指定stark_config参数
  58. # 那么必然执行下面这段代码!
  59. # stark_config和StarkConfig是等值的!都能实例化
  60. stark_config = StarkConfig
  61. # 添加键值对,实例化类StarkConfig,传入参数model_class
  62. # self指的是AdminSite类
  63. self._registry[model_class] = stark_config(model_class,self)
  64. # print(self._registry) # 打印字典
  65. """
  66. {
  67. app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
  68. app02.models.Role:RoleConfig(app02.models.Role)
  69. }
  70. """
  71. # for k, v in self._registry.items():
  72. # print(k,v)
  73. def get_urls(self):
  74. urlpatterns = []
  75. for k, v in self._registry.items():
  76. # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
  77. # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
  78. app_label = k._meta.app_label
  79. model_name = k._meta.model_name
  80. urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))
  81. return urlpatterns
  82. @property
  83. def urls(self):
  84. # 调用get_urls方法
  85. # self.app_name和self.namespace值是一样的,都是stark
  86. return self.get_urls(), self.app_name, self.namespace
  87. site = AdminSite() # 实例化类

修改app02—>stark.py,定义order_by

  1. # 这里的site,指的是实例化后的变量名
  2. # StarkConfig表示类
  3. from stark.server.stark import site,StarkConfig
  4. from app02 import models
  5. from django.conf.urls import url
  6. from django.shortcuts import HttpResponse
  7. class RoleConfig(StarkConfig):
  8. order_by = ['-id'] # 有负号,表示降序
  9. def sk2(self, request):
  10. return HttpResponse('sk2神仙水')
  11. def extra_url(self):
  12. data = [
  13. url(r'^sk2/$', self.sk2),
  14. ]
  15. return data
  16. site.register(models.Role,RoleConfig) # 注册表

添加数据

使用navicat打开app02_role表,添加2条数据

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图1

打开app01_userinfo表,添加2条数据

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图2

访问页面

  1. http://127.0.0.1:8000/stark/app02/role/list/

效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图3

查看Pycharm控制台输出:

  1. <class 'app02.models.Role'>
  2. <QuerySet [<Role: Role object>, <Role: Role object>]>

应该有一个页面,要展示一下数据。那么页面,应该写在哪里?

要写在stark里面,因为它是组件,它是能够应用到其他django项目的!

前端展示数据

进入stark应用目录,创建目录templates,在此目录下创建stark目录。在此目录创建layout.html。这个是母版文件!

  1. {% load staticfiles %}
  2. <!DOCTYPE html>
  3. <html lang="en">
  4. <head>
  5. <meta charset="UTF-8">
  6. <title>路飞学城</title>
  7. <link rel="shortcut icon" href="{% static 'stark/imgs/luffy-study-logo.png' %} ">
  8. <link rel="stylesheet" href="{% static 'stark/plugins/bootstrap/css/bootstrap.css' %} "/>
  9. <link rel="stylesheet" href="{% static 'stark/plugins/font-awesome/css/font-awesome.css' %} "/>
  10. <link rel="stylesheet" href="{% static 'stark/css/commons.css' %} "/>
  11. <link rel="stylesheet" href="{% static 'stark/css/nav.css' %} "/>
  12. <style>
  13. body {
  14. margin: 0;
  15. }
  16. .no-radius {
  17. border-radius: 0;
  18. }
  19. .no-margin {
  20. margin: 0;
  21. }
  22. .pg-body > .left-menu {
  23. background-color: #EAEDF1;
  24. position: absolute;
  25. left: 0;
  26. top: 48px;
  27. bottom: 0;
  28. width: 220px;
  29. border: 1px solid #EAEDF1;
  30. overflow: auto;
  31. }
  32. .pg-body > .right-body {
  33. position: absolute;
  34. left: 225px;
  35. right: 0;
  36. top: 48px;
  37. bottom: 0;
  38. overflow: scroll;
  39. border: 1px solid #ddd;
  40. border-top: 0;
  41. font-size: 13px;
  42. min-width: 755px;
  43. }
  44. .navbar-right {
  45. float: right !important;
  46. margin-right: -15px;
  47. }
  48. .luffy-container {
  49. padding: 15px;
  50. }
  51. </style>
  52. {% block css %}{% endblock %}
  53. </head>
  54. <body>
  55. <div class="pg-header">
  56. <div class="nav">
  57. <div class="logo-area left">
  58. <a href="#">
  59. <img class="logo" src="{% static 'stark/imgs/logo.svg' %}">
  60. <span style="font-size: 18px;">路飞学城 </span>
  61. </a>
  62. </div>
  63. <div class="left-menu left">
  64. <a class="menu-item">资产管理</a>
  65. <a class="menu-item">用户信息</a>
  66. <a class="menu-item">路飞管理</a>
  67. <div class="menu-item">
  68. <span>使用说明</span>
  69. <i class="fa fa-caret-down" aria-hidden="true"></i>
  70. <div class="more-info">
  71. <a href="#" class="more-item">管他什么菜单</a>
  72. <a href="#" class="more-item">实在是编不了</a>
  73. </div>
  74. </div>
  75. </div>
  76. <div class="right-menu right clearfix">
  77. <div class="user-info right">
  78. <a href="#" class="avatar">
  79. <img class="img-circle" src="{% static 'stark/imgs/default.png' %}">
  80. </a>
  81. <div class="more-info">
  82. <a href="#" class="more-item">个人信息</a>
  83. <a href="/logout/" class="more-item">注销</a>
  84. </div>
  85. </div>
  86. <a class="user-menu right">
  87. 消息
  88. <i class="fa fa-commenting-o" aria-hidden="true"></i>
  89. <span class="badge bg-success">2</span>
  90. </a>
  91. <a class="user-menu right">
  92. 通知
  93. <i class="fa fa-envelope-o" aria-hidden="true"></i>
  94. <span class="badge bg-success">2</span>
  95. </a>
  96. <a class="user-menu right">
  97. 任务
  98. <i class="fa fa-bell-o" aria-hidden="true"></i>
  99. <span class="badge bg-danger">4</span>
  100. </a>
  101. </div>
  102. </div>
  103. </div>
  104. <div class="pg-body">
  105. <div class="left-menu">
  106. <div class="menu-body">
  107. </div>
  108. </div>
  109. <div class="right-body">
  110. <div>
  111. </div>
  112. {% block content %} {% endblock %}
  113. </div>
  114. </div>
  115. <script src="{% static 'stark/js/jquery-3.3.1.min.js' %} "></script>
  116. <script src="{% static 'stark/plugins/bootstrap/js/bootstrap.js' %} "></script>
  117. {% block js %} {% endblock %}
  118. </body>
  119. </html>

进入目录stark—>templates—>stark,创建文件changelist.html

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

{% block content %}
    <h1>列表页面</h1>
    <div>
        <table class="table table-bordered">
            <thead>
                <tr>
                    <th>第一列</th>
                    <th>第二列</th>
                    <th>第三列</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>11</td>
                    <td>22</td>
                    <td>33</td>
                </tr>
            </tbody>
        </table>

    </div>



{% endblock %}

进入stark应用目录,创建目录static,再创建stark目录

下载文件:

https://github.com/987334176/luffy_permission/archive/v1.5.zip

解压文件,进入目录web—>static,将里面的css,imgs,js,plugins复制过来

项目结构如下:

这里面删除了css,js相关文件,否则结构长了!

luffy_stark/
├── app01
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── models.py
│   ├── stark.py
│   ├── tests.py
│   └── views.py
├── app02
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── models.py
│   ├── stark.py
│   ├── tests.py
│   └── views.py
├── db.sqlite3
├── luffy_stark
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
├── README.md
└── stark
    ├── admin.py
    ├── apps.py
    ├── __init__.py
    ├── models.py
    ├── server
    │   └── stark.py
    ├── static
    │   └── stark
    │       ├── css
    │       ├── imgs
    │       ├── js
    │       └── plugins
    ├── templates
    │   └── stark
    │       ├── changelist.html
    │       └── layout.html
    ├── tests.py
    └── views.py

注意:changelist.html是在stark—>templates—>stark目录里面!

修改stark—>server—>stark.py,渲染changelist.html

from django.conf.urls import url
from django.shortcuts import HttpResponse,render

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    order_by = []  # 需要排序的字段,由用户自定义

    def get_order_by(self):
        return self.order_by  # 获取排序列表

    def changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        return render(request,'stark/changelist.html')

    def add_view(self, request):
        return HttpResponse('stark add')

    def change_view(self, request, pk):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        # print(info)
        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

    @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()  # 实例化类

重启django项目,访问url:

http://127.0.0.1:8000/stark/app02/role/list/

效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图4

显示列

如果需要显示自定义的列,怎么做?

定义header_list和list_display,分别表示头部和显示的列。

修改stark—>server—>stark.py

from django.conf.urls import url
from django.shortcuts import HttpResponse,render

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    order_by = []  # 需要排序的字段,由用户自定义

    def get_order_by(self):
        return self.order_by  # 获取排序列表

    def changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        list_display = ['id','username']  # 定义显示的列
        header_list = []  # 定义头部,用来显示verbose_name
        if list_display:
            for name in list_display:
                # 获取指定字段的verbose_name
                verbose_name = self.model_class._meta.get_field(name).verbose_name
                header_list.append(verbose_name)

        body_list = []  # 显示内容

        for row in queryset:
            # 这里的row是对象,它表示表里面的一条数据
            row_list = []  # 展示每一行数据

            for name in list_display:
                # 使用反射获取对象的值
                val = getattr(row, name)
                row_list.append(val)
            body_list.append(row_list)

        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list})

    def add_view(self, request):
        return HttpResponse('stark add')

    def change_view(self, request, pk):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        # print(info)
        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

    @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()  # 实例化类

修改changelist.html,使用for循环

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

{% block content %}
    <h1>列表页面</h1>
    <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>

    </div>



{% endblock %}

刷新页面,效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图5

去掉id

修改stark—>server—>stark.py,修改list_display

list_display = ['username']

刷新页面,效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图6

这个变量,定死了。应该要动态生成才行。

修改stark—>server—>stark.py,定义静态变量,定义方法get_list_display

from django.conf.urls import url
from django.shortcuts import HttpResponse,render

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    order_by = []  # 需要排序的字段,由用户自定义
    list_display = []  # 定义显示的列,由用户自定义

    def get_order_by(self):  # 获取排序列表
        return self.order_by

    def get_list_display(self):  # 获取显示的列
        return self.list_display

    def changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        list_display = self.list_display  # 定义显示的列
        header_list = []  # 定义头部,用来显示verbose_name
        if list_display:
            for name in list_display:
                # 获取指定字段的verbose_name
                verbose_name = self.model_class._meta.get_field(name).verbose_name
                header_list.append(verbose_name)

        body_list = []  # 显示内容

        for row in queryset:
            # 这里的row是对象,它表示表里面的一条数据
            row_list = []  # 展示每一行数据

            for name in list_display:
                # 使用反射获取对象的值
                val = getattr(row, name)
                row_list.append(val)
            body_list.append(row_list)

        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list})

    def add_view(self, request):
        return HttpResponse('stark add')

    def change_view(self, request, pk):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        # print(info)
        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

    @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()  # 实例化类

修改app02—>stark.py,设置list_display,只显示title

# 这里的site,指的是实例化后的变量名
# StarkConfig表示类
from stark.server.stark import site,StarkConfig
from app02 import models
from django.conf.urls import url
from django.shortcuts import HttpResponse

class RoleConfig(StarkConfig):
    order_by = ['-id']  # 有负号,表示降序
    list_display = ['title']  # 定义显示的列

    def sk2(self, request):
        return HttpResponse('sk2神仙水')

    def extra_url(self):
        data = [
            url(r'^sk2/$', self.sk2),
        ]
        return data

site.register(models.Role,RoleConfig)  # 注册表

刷新页面,效果同上!

修改app01—>stark.py,增加自定义方法

from stark.server.stark import site,StarkConfig
from app01 import models

class UserInfoConfig(StarkConfig):
    list_display = ['id','username']

site.register(models.UserInfo,UserInfoConfig)

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

效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图7

如果用户没有指定list_display,那改如何显示?直接显示表名和对象

修改stark—>server—>stark.py,做判断。header_list显示表名,body_list显示对象

from django.conf.urls import url
from django.shortcuts import HttpResponse,render

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    order_by = []  # 需要排序的字段,由用户自定义
    list_display = []  # 定义显示的列,由用户自定义

    def get_order_by(self):  # 获取排序列表
        return self.order_by

    def get_list_display(self):  # 获取显示的列
        return self.list_display

    def changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        list_display = self.list_display  # 定义显示的列
        header_list = []  # 定义头部,用来显示verbose_name
        if list_display:
            for name in list_display:
                # 获取指定字段的verbose_name
                verbose_name = self.model_class._meta.get_field(name).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 in list_display:
                # 使用反射获取对象的值
                val = getattr(row, name)
                row_list.append(val)
            body_list.append(row_list)

        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list})

    def add_view(self, request):
        return HttpResponse('stark add')

    def change_view(self, request, pk):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        # print(info)
        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

    @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()  # 实例化类

修改 app02—>stark.py,注释掉list_display变量

访问url: http://127.0.0.1:8000/stark/app02/role/list/

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图8

但是显示object,用户会不爽,怎么搞?

修改app02—>models.py,增加str方法,用来显示默认字段

from django.db import models

# Create your models here.
class Role(models.Model):
    title = models.CharField(verbose_name="名称", max_length=32)

    def __str__(self):
        return self.title

修改app01—>models.py,增加str方法

from django.db import models

# Create your models here.
class UserInfo(models.Model):
    username = models.CharField(verbose_name="用户名",max_length=32)

    def __str__(self):
        return self.username

刷新页面,效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图9

注意:在stark—>server—>stark.py 中定义的get_list_display方法,是一个预留的钩子。它可以做定制化

在app01—>stark.py里面定义了list_display,默认是所有用户,都显示这些列。

如果需要根据用户角色不同来展示不同的数据呢?那么这个钩子,就可以派上用场了!

可以直接在app01—>stark.py里面,重构get_list_display方法。

比如:

修改app02—>stark.py

注意:get_list_display里面的if 1 == 1,只是为了做演示而已。实际上,应该是你的业务代码!

# 这里的site,指的是实例化后的变量名
# StarkConfig表示类
from stark.server.stark import site,StarkConfig
from app02 import models
from django.conf.urls import url
from django.shortcuts import HttpResponse

class RoleConfig(StarkConfig):
    order_by = ['-id']  # 有负号,表示降序
    # list_display = ['id','title']  # 定义显示的列
    def get_list_display(self):
        if 1 == 1:
            return ['id']
        else:
            return ['id','title']

    def sk2(self, request):
        return HttpResponse('sk2神仙水')

    def extra_url(self):
        data = [
            url(r'^sk2/$', self.sk2),
        ]
        return data

site.register(models.Role,RoleConfig)  # 注册表

注意:list_display和get_list_display不能同时存在。因为list_display的优先级比get_list_display高

定义了list_display,那么get_list_display就没有效果了!

访问url: http://127.0.0.1:8000/stark/app02/role/list/

效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图10

修改app01—>models.py,增加部门表

from django.db import models

# Create your models here.
class UserInfo(models.Model):
    username = models.CharField(verbose_name="用户名",max_length=32)

    def __str__(self):
        return self.username

class Depart(models.Model):

    name = models.CharField(verbose_name='部门名称',max_length=32)
    tel = models.CharField(verbose_name='联系电话',max_length=31)
    user = models.ForeignKey(verbose_name='负责人',to='UserInfo')

    def __str__(self):
        return self.name

使用2个命令生成表

python manage.py makemigrations
python manage.py migrate

注意:如果无法生成表depart,使用命令

python manage.py makemigrations app01
python manage.py migrate

添加2条记录

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图11

修改app01—>stark.py,注册表Depart

from stark.server.stark import site,StarkConfig
from app01 import models

class UserInfoConfig(StarkConfig):
    list_display = ['id','username']

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

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

效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图12

修改app01—>stark.py,为depart增加list_display

from stark.server.stark import site,StarkConfig
from app01 import models

class UserInfoConfig(StarkConfig):
    list_display = ['id','username']

class DepartConfig(StarkConfig):
    list_display = ['name','tel','user']

site.register(models.UserInfo,UserInfoConfig)
site.register(models.Depart,DepartConfig)

千万要记得在site.register,添加自定义类!否则下面的效果出不来!

刷新页面,效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图13

查看stark—>server—>stark.py,查看changelist_view,发现代码都写在这里了。可读性不好!

如果要增加其他功能呢?比如下面的效果:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图14

那么就需要拆分代码,不同的功能,放在不同的方法里面!

四、添加按钮

CheckBox

以部门表为例,加一个checkbox,也就是复选框

修改 app01—>stark.py,增加一个方法,添加到列表中

from stark.server.stark import site,StarkConfig
from app01 import models

class UserInfoConfig(StarkConfig):
    list_display = ['id','username']

class DepartConfig(StarkConfig):
    def f1(self):  # 测试函数
        pass

    list_display = [f1,'name', 'tel', 'user']

site.register(models.UserInfo,UserInfoConfig)
site.register(models.Depart,DepartConfig)

修改 stark—>server—>stark.py,对list_display每一个元素做判断。

如果是字符串,去数据库取。如果是函数,去用户自定义的方法中获取!使用FunctionType判断

from django.conf.urls import url
from django.shortcuts import HttpResponse,render
from types import FunctionType

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    order_by = []  # 需要排序的字段,由用户自定义
    list_display = []  # 定义显示的列,由用户自定义

    def get_order_by(self):  # 获取排序列表
        return self.order_by

    def get_list_display(self):  # 获取显示的列
        return self.list_display

    def changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        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)  # 执行函数获取
                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})

    def add_view(self, request):
        return HttpResponse('stark add')

    def change_view(self, request, pk):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        # print(info)
        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

    @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()  # 实例化类

修改app01—>stark.py,增加方法f1

from stark.server.stark import site,StarkConfig
from app01 import models

class UserInfoConfig(StarkConfig):
    list_display = ['id','username']

class DepartConfig(StarkConfig):
    def f1(self,header=False):  # 测试函数
        if header:
            # 输出中文
            return "选择"

        return "checkbox"  # 标签名

    list_display = [f1,'name', 'tel', 'user']

site.register(models.UserInfo,UserInfoConfig)
site.register(models.Depart,DepartConfig)

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

效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图15

这个checkbox看着不爽,要换成html标签。需要使用mark_safe渲染html标签

修改app01—>stark.py,返回一个input标签

from stark.server.stark import site,StarkConfig
from app01 import models
from django.utils.safestring import mark_safe

class UserInfoConfig(StarkConfig):
    list_display = ['id','username']

class DepartConfig(StarkConfig):
    def f1(self,header=False):  # 测试函数
        if header:
            # 输出标签
            return mark_safe('<input type="checkbox" name="pk" />')

        return mark_safe('<input type="checkbox" name="pk" />')  # 标签名

    list_display = [f1,'name', 'tel', 'user']

site.register(models.UserInfo,UserInfoConfig)
site.register(models.Depart,DepartConfig)

刷新页面,效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图16

但是有一个问题,input标签的值和行数据的id不匹配。怎么动态获取呢?

在stark—>server—>stark.py里面,给它传一个row对象,就可以获取了!

修改stark—>server—>stark.py,传递row参数

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

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    order_by = []  # 需要排序的字段,由用户自定义
    list_display = []  # 定义显示的列,由用户自定义

    def get_order_by(self):  # 获取排序列表
        return self.order_by

    def get_list_display(self):  # 获取显示的列
        return self.list_display

    def changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        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})

    def add_view(self, request):
        return HttpResponse('stark add')

    def change_view(self, request, pk):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        # print(info)
        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

    @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()  # 实例化类

修改app01—>stark.py,接收row参数

f1的名字不好,要改成display_checkbox

from stark.server.stark import site,StarkConfig
from app01 import models
from django.utils.safestring import mark_safe

class UserInfoConfig(StarkConfig):
    list_display = ['id','username']

class DepartConfig(StarkConfig):
    def f1(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)

    list_display = [f1,'name', 'tel', 'user']

site.register(models.UserInfo,UserInfoConfig)
site.register(models.Depart,DepartConfig)

刷新页面,效果如下:

id就动态生成了!

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图17

以后有form表单,后台就可以批量操作了

现在role表也想有checkbox功能,怎么办?代码copy一遍?

这样不好,如果有10个表呢?需要写在StarkConfig类里面!

为什么呢?因为在app01—>stark.py里面的自定义类,都是继承了StarkConfig类

修改stark—>server—>stark.py,在StarkConfig类增加display_checkbox方法

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

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)

    order_by = []  # 需要排序的字段,由用户自定义
    list_display = []  # 定义显示的列,由用户自定义

    def get_order_by(self):  # 获取排序列表
        return self.order_by

    def get_list_display(self):  # 获取显示的列
        return self.list_display

    def changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        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})

    def add_view(self, request):
        return HttpResponse('stark add')

    def change_view(self, request, pk):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        # print(info)
        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

    @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()  # 实例化类

修改app01—>stark.py,使用StarkConfig类的display_checkbox方法

from stark.server.stark import site,StarkConfig
from app01 import models

class UserInfoConfig(StarkConfig):
    list_display = ['id','username']

class DepartConfig(StarkConfig):
    list_display = [StarkConfig.display_checkbox,'name', 'tel', 'user']

site.register(models.UserInfo,UserInfoConfig)
site.register(models.Depart,DepartConfig)

刷新页面,效果同上!

修改app02—>stark.py,使用display_checkbox方法

# 这里的site,指的是实例化后的变量名
# StarkConfig表示类
from stark.server.stark import site,StarkConfig
from app02 import models
from django.conf.urls import url
from django.shortcuts import HttpResponse

class RoleConfig(StarkConfig):
    order_by = ['-id']  # 有负号,表示降序
    # 定义显示的列
    list_display = [StarkConfig.display_checkbox,'id','title']
    # def get_list_display(self):  # 钩子函数,用来做定制显示
    #     if 1 == 1:
    #         return ['id']
    #     else:
    #         return ['id','title']

    def sk2(self, request):
        return HttpResponse('sk2神仙水')

    def extra_url(self):
        data = [
            url(r'^sk2/$', self.sk2),
        ]
        return data

site.register(models.Role,RoleConfig)  # 注册表

访问url: http://127.0.0.1:8000/stark/app02/role/list/

效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图18

编辑和删除

修改stark—>server—>stark.py,增加编辑和删除方法

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

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="/edit/%s">编辑</a>' % row.pk)

    def display_del(self, row=None, header=False):
        if header:
            return "删除"
        return mark_safe('<a href="/del/%s">删除</a>' % row.pk)

    order_by = []  # 需要排序的字段,由用户自定义
    list_display = []  # 定义显示的列,由用户自定义

    def get_order_by(self):  # 获取排序列表
        return self.order_by

    def get_list_display(self):  # 获取显示的列
        return self.list_display

    def changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        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})

    def add_view(self, request):
        return HttpResponse('stark add')

    def change_view(self, request, pk):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        # print(info)
        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

    @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()  # 实例化类

修改app01—>stark.py,应用编辑和删除

from stark.server.stark import site, StarkConfig
from app01 import models


class UserInfoConfig(StarkConfig):
    list_display = ['id', 'username']

class DepartConfig(StarkConfig):
    list_display = [StarkConfig.display_checkbox, 'name', 'tel', 'user', StarkConfig.display_edit,
                    StarkConfig.display_del]

site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)

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

效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图19

文字看着不爽,改成图标

修改stark—>server—>stark.py

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

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="/edit/%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % row.pk)

    def display_del(self, row=None, header=False):
        if header:
            return "删除"
        return mark_safe('<a href="/del/%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % row.pk)

    order_by = []  # 需要排序的字段,由用户自定义
    list_display = []  # 定义显示的列,由用户自定义

    def get_order_by(self):  # 获取排序列表
        return self.order_by

    def get_list_display(self):  # 获取显示的列
        return self.list_display

    def changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        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})

    def add_view(self, request):
        return HttpResponse('stark add')

    def change_view(self, request, pk):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        # print(info)
        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

    @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()  # 实例化类

刷新页面,效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图20

编辑和删除,占用太宽了。合并成一列!

修改stark—>server—>stark.py,增加方法display_edit_del

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

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="/edit/%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % row.pk)

    def display_del(self, row=None, header=False):
        if header:
            return "删除"
        return mark_safe('<a href="/del/%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % row.pk)

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操作"
        tpl = """<a href="/edit/%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="/del/%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (row.pk, row.pk,)
        return mark_safe(tpl)

    order_by = []  # 需要排序的字段,由用户自定义
    list_display = []  # 定义显示的列,由用户自定义

    def get_order_by(self):  # 获取排序列表
        return self.order_by

    def get_list_display(self):  # 获取显示的列
        return self.list_display

    def changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        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})

    def add_view(self, request):
        return HttpResponse('stark add')

    def change_view(self, request, pk):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        # print(info)
        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

    @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()  # 实例化类

修改app01—>stark.py,应用方法display_edit_del

from stark.server.stark import site, StarkConfig
from app01 import models

class UserInfoConfig(StarkConfig):
    list_display = ['id', 'username']

class DepartConfig(StarkConfig):
    list_display = [StarkConfig.display_checkbox, 'name', 'tel', 'user', StarkConfig.display_edit_del]

site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)

刷新页面,效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图21

通过这样,想显示 编辑、删除、编辑和删除。都可以任意选择!

但是有一个问题,url不对。怎么办?应该使用url别名反向生成。

看这一段代码

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),
    ...
]

别名需要app名和表名,以及命令空间。再拼接_changelist,就可以反向生成url了

修改stark—>server—>stark.py,增加方法reverse_edit_url方法。并在编辑的方法中,使用此方法获取url

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

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="/del/%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % row.pk)

    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="/del/%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), row.pk,)
        return mark_safe(tpl)

    order_by = []  # 需要排序的字段,由用户自定义
    list_display = []  # 定义显示的列,由用户自定义

    def get_order_by(self):  # 获取排序列表
        return self.order_by

    def get_list_display(self):  # 获取显示的列
        return self.list_display

    def changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        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})

    def add_view(self, request):
        return HttpResponse('stark add')

    def change_view(self, request, pk):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    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_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  # 命名空间
        # 拼接字符串
        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

    @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()  # 实例化类

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

可以发现url生成了,效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图22

点击编辑按钮,会自动跳转页面

http://127.0.0.1:8000/stark/app01/depart/1/change/

效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图23

那么删除也是同样的,再定义一个方法reverse_del_url方法。并在删除的方法中,使用此方法获取url

修改stark—>server—>stark.py

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

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 = []  # 定义显示的列,由用户自定义

    def get_order_by(self):  # 获取排序列表
        return self.order_by

    def get_list_display(self):  # 获取显示的列
        return self.list_display

    def changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        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})

    def add_view(self, request):
        return HttpResponse('stark add')

    def change_view(self, request, pk):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    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_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()  # 实例化类

刷新页面,效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图24

点击删除按钮,会自动跳转页面

http://127.0.0.1:8000/stark/app01/depart/1/del/

效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图25

那么其他页面,也是同样的效果!

添加按钮

修改stark—>templates—>stark—>changelist.html ,先写一个添加按钮

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

{% block content %}
    <h1>列表页面</h1>
    <div>
        <div style="margin: 5px 0;">
            <a href="#" class="btn btn-success">添加</a>
        </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>

    </div>



{% endblock %}

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

效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图26

但是,并不是每个页面,都需要显示添加按钮。如何控制页面显示呢?

修改stark—>server—>stark.py,增加get_add_btn方法

注意:在渲染stark/changelist.html页面时,要传入参数add_btn

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

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 = []  # 定义显示的列,由用户自定义

    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="#" class="btn btn-success">添加</a>')

    def changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())

        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})

    def add_view(self, request):
        return HttpResponse('stark add')

    def change_view(self, request, pk):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    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_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()  # 实例化类

修改app01—>stark.py,重构get_add_btn方法,返回None

from stark.server.stark import site, StarkConfig
from app01 import models

class UserInfoConfig(StarkConfig):
    list_display = ['id', 'username']

class DepartConfig(StarkConfig):
    list_display = [StarkConfig.display_checkbox, 'name', 'tel', 'user', StarkConfig.display_edit_del]

    def get_add_btn(self):  # 返回None,表示不显示添加按钮
        pass

site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)

修改stark—>templates—>stark—>changelist.html,做一个if判断。如果get_add_btn返回结果不为空,则展示。否则不显示!

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

{% block content %}
    <h1>列表页面</h1>
    <div>
        {% if add_btn %}
        <div style="margin: 5px 0;">
            {{ add_btn }}
        </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>

    </div>



{% endblock %}

刷新页面,就没有添加按钮了,效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图27

那么其他页面,是有的,访问页面:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图28

但是链接不对,得生成url

修改stark—>server—>stark.py,增加reverse_add_url方法

并修改get_add_btn,执行reverse_add_url方法

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

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 = []  # 定义显示的列,由用户自定义

    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 changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())

        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})

    def add_view(self, request):
        return HttpResponse('stark add')

    def change_view(self, request, pk):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    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_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()  # 实例化类

访问url: http://127.0.0.1:8000/stark/app01/userinfo/list/Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图29

点击添加按钮,页面跳转

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图30

那么页面的添加按钮,链接是不一样的。

五、定制ModelForm

添加数据

问题来了,如果是添加,这个页面应该有几个input框?

每个页面,都需要做表单验证,还有默认选中状态

这就需要用到ModelForm

要为每一个model生成类,还得需要实例化。比如这样

class xxxModelForm(ModelForm):
    class Meta:
        model = xxx
        field = ['id','xx']

修改stark—>server—>stark.py,增加类 AddModelForm,并渲染页面change.html

from django.conf.urls import url
from django.shortcuts import HttpResponse,render
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 = []  # 定义显示的列,由用户自定义

    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 changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())

        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})

    def add_view(self, request):
        """
        所有的添加页面,都在此方法处理
        使用ModelForm实现
        :param request:
        :return:
        """

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

    def change_view(self, request, pk):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    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_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()  # 实例化类

进入目录 stark—>templates—>stark,创建文件change.html

写一个form表单,使用for循环

{% extends 'stark/layout.html' %}
{% block css %}
    <style>
        .change input,select {
            display: block;
            width: 100%;
            height: 34px;
            padding: 6px 12px;
            font-size: 14px;
            line-height: 1.42857143;
            color: #555;
            background-color: #fff;
            background-image: none;
            border: 1px solid #ccc;
            border-radius: 4px;
            -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
            box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
            -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
            -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
            transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
        }
    </style>

{% endblock %}
{% block content %}
    <div style="width: 680px;margin: 0 auto;">
        <form class="change" method="post">
            {% csrf_token %}
            {% for filed in form %}
                <div class="form-group">
                    <label>{{ filed.label }}</label>
                    {{ filed }}
                    {{ filed.errors.0 }}
                </div>
            {% endfor %}

            <button type="submit" class="btn btn-default">Submit</button>
        </form>
    </div>
{% endblock %}

修改app01—>stark.py,注释get_add_btn方法。让它显示添加按钮

from stark.server.stark import site, StarkConfig
from app01 import models


class UserInfoConfig(StarkConfig):
    list_display = ['id', 'username']


class DepartConfig(StarkConfig):
    list_display = [StarkConfig.display_checkbox, 'name', 'tel', 'user', StarkConfig.display_edit_del]

    # def get_add_btn(self):  # 返回None,表示不显示添加按钮
    #     pass

site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)

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

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图31

点击添加按钮,效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图32

点击提交,没有任何效果。

修改stark—>server—>stark.py,修改add_view方法。为post时,保存数据

添加reverse_list_url方法,用来做保存之后的跳转

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 = []  # 定义显示的列,由用户自定义

    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 changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())

        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})

    def add_view(self, request):
        """
        所有的添加页面,都在此方法处理
        使用ModelForm实现
        :param request:
        :return:
        """

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        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):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    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()  # 实例化类

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

添加一条数据,点击提交

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图33

页面会自动跳转,添加的数据,就可以看到了!

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图34

这样不够好,其他方法要使用ModelForm呢?比如编辑

修改stark—>server—>stark.py,定义get_model_form_class方法,将add_view方法中的AddModelForm类抽离出来

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

    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 changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())

        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})

    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):
        return HttpResponse('stark change')

    def delete_view(self, request, pk):
        return HttpResponse('stark delete')

    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()  # 实例化类

测试添加功能,访问以下url:

http://127.0.0.1:8000/stark/app01/userinfo/list/

点击添加

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图35

添加一条数据

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图36

发现数据有了

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图37

修改 app01—>stark.py,自定义form类,添加DepartModelForm\

from stark.server.stark import site, StarkConfig
from app01 import models
from django import forms

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
    # def get_add_btn(self):  # 返回None,表示不显示添加按钮
    #     pass

site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)

访问url:

http://127.0.0.1:8000/stark/app01/depart/list/

点击添加按钮,添加一条数据

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图38

提交之后,页面跳转,数据就出来了

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图39

查看Pycharm控制台输出:

运营部

说明它,走到了clean_name钩子。那么表示,app01—>stark.py自定义的ModelForm类生效了!

编辑数据

修改stark—>server—>stark.py,修改change_view方法

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

    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 changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())

        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})

    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):
        return HttpResponse('stark delete')

    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()  # 实例化类

注意:添加和编辑,可以共用一个模板文件。因为它们都是用的ModelForm组件

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

修改一条数据,比如人事部

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图40

点击提交,数据就修改过来了

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图41

删除数据

删除,得弹出一个模态框,要用户提示才行

如果是一个GET请求,让用户看到一个页面

修改stark—>server—>stark.py,修改delete_view方法

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

    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 changelist_view(self, request):
        """
        所有URL查看列表页面
        :param request:
        :return:
        """
        # 根据排序列表进行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())

        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})

    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()  # 实例化类

进入目录stark—>templates—>stark,创建文件delete.html

{% extends 'stark/layout.html' %}
{% block css %}

{% endblock %}
{% block content %}
    <div >
        <form method="post">
            {% csrf_token %}
            <p>是否确定要删除?</p>
            <a href="{{ cancel_url }}" class="btn btn-default">取消</a>
            <button type="submit" class="btn btn-danger">确 认</button>
        </form>
    </div>
{% endblock %}

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

点击最后一条数据的删除

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图42

提示,是否删除

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图43

点击取消,就会跳转到列表页面

点击确认,还是会跳转到列表页面,但是数据删除了

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图44

六、自定义列表页面

默认的列表页面,它是table表格展示的。但是我们可以定制列表页面,比如使用面板

进入目录stark—>templates—>stark,创建文件custom_list.html

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

{% block content %}
    <h1>自定义列表页面</h1>
    <div class="bs-example" data-example-id="contextual-panels">
    <div class="panel panel-primary">
      <div class="panel-heading">
        <h3 class="panel-title">Panel title</h3>
      </div>
      <div class="panel-body">
        Panel content
      </div>
    </div>
    <div class="panel panel-success">
      <div class="panel-heading">
        <h3 class="panel-title">Panel title</h3>
      </div>
      <div class="panel-body">
        Panel content
      </div>
    </div>
    <div class="panel panel-info">
      <div class="panel-heading">
        <h3 class="panel-title">Panel title</h3>
      </div>
      <div class="panel-body">
        Panel content
      </div>
    </div>
    <div class="panel panel-warning">
      <div class="panel-heading">
        <h3 class="panel-title">Panel title</h3>
      </div>
      <div class="panel-body">
        Panel content
      </div>
    </div>
    <div class="panel panel-danger">
      <div class="panel-heading">
        <h3 class="panel-title">Panel title</h3>
      </div>
      <div class="panel-body">
        Panel content
      </div>
    </div>
  </div>

{% endblock %}

修改app01—>stark.py,重写changelist_view方法

from stark.server.stark import site, StarkConfig
from app01 import models
from django import forms
from django.shortcuts import render

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
    # def get_add_btn(self):  # 返回None,表示不显示添加按钮
    #     pass
    def changelist_view(self, request):  # 重写changelist_view方法
        # 渲染自定义的列表页面
        return render(request,'stark/custom_list.html')

site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)

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

效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图45

那么就可以自定义列表页面了!如果没有重写changelist_view方法,它显示默认页面

修改app01—>stark.py,注释掉changelist_view方法

from stark.server.stark import site, StarkConfig
from app01 import models
from django import forms
from django.shortcuts import render

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
    # def get_add_btn(self):  # 返回None,表示不显示添加按钮
    #     pass
    # def changelist_view(self, request):  # 重写changelist_view方法
    #     # 渲染自定义的列表页面
    #     return render(request,'stark/custom_list.html')

site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)

刷新页面,效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图46

七、自定制URL

假设有一个表,不要增删改功能,只要一个列表页面,怎么办?

重写get_urls方法,就可以了

修改app01—>stark.py,重写get_urls方法

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

注意:

1. list_display里面的字段,不能写其他的函数,比如编辑和删除。因为它们依赖一些东西

2. 必须要定义get_add_btn方法。因为在changelist_view方法中,调用了get_add_btn方法

上面2个注意事项,必须要遵守,否则启动报错,或者页面报错!

重启django项目,访问页面:

http://127.0.0.1:8000/stark/app01/depart/list/

效果如下:

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图47

访问添加页面: http://127.0.0.1:8000/stark/app01/depart/add/

会报错!

Day114 装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL - 图48

如果需要增肌除增删改查以外的url,定义extra_url方法,就可以了!

参考app02—>stark.py里面的extra_url方法

总结:

1. 排序规则
    第一种方法:
        class UserInfoConfig(StarkConfig):
            order_by = ['-id']
            list_display = ['id','title',StarkConfig.display_edit,StarkConfig.display_del]

        site.register(models.UserInfo,UserInfoConfig)
    第二种方法:
        class UserInfoConfig(StarkConfig):
            list_display = ['id','title',StarkConfig.display_edit,StarkConfig.display_del]

            def get_order_by(self):
                return ['-id']
        site.register(models.UserInfo,UserInfoConfig)
2. 显示列
    第一种方法:
        class UserInfoConfig(StarkConfig):
            list_display = ['id','title',StarkConfig.display_edit,StarkConfig.display_del]
        site.register(models.UserInfo,UserInfoConfig)
    第二种方法:
        class UserInfoConfig(StarkConfig):
            order_by = ['-id']

            def get_list_display(self):
                return  ['id','title',StarkConfig.display_edit,StarkConfig.display_del]

3. 添加按钮
        class UserInfoConfig(StarkConfig):
            list_display = ['id','title',StarkConfig.display_edit,StarkConfig.display_del]
            def get_add_btn(self):
                # 显示 
                # return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

                # 不显示
                return None 

        site.register(models.UserInfo,UserInfoConfig)

4. 定制ModelForm
    第一种方法:    
        class DepartModelForm(forms.ModelForm):
            class Meta:
                model = models.Depart
                fields = "__all__"

            def clean_name(self):
                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
    第二种方法:    
        class DepartModelForm(forms.ModelForm):
            class Meta:
                model = models.Depart
                fields = "__all__"

            def clean_name(self):
                return self.cleaned_data['name']

        class DepartConfig(StarkConfig):
            list_display = [StarkConfig.display_checkbox,'id', 'name', 'tel', 'user',StarkConfig.display_edit_del]

            def get_model_form_class(self):

                return DepartModelForm

5. 自定义列表页面
    class DepartConfig(StarkConfig):
        list_display = [StarkConfig.display_checkbox,'id', 'name', 'tel', 'user',StarkConfig.display_edit_del]
        model_form_class = DepartModelForm


        def changelist_view(self, request):
            return HttpResponse('自定义列表页面')


    site.register(models.Depart, DepartConfig)

6. 自定制URL

    class RoleConfig(StarkConfig):

        order_by = ['-id', ]
        list_display = [StarkConfig.display_checkbox,'id','title']


        def get_add_btn(self):
            return False

        def extra_url(self):
            data = [
                url(r'^xxxxxxx/$', self.xxxxxx),
            ]

            return data

        def xxxxxx(self,request):
            print('....')

            return HttpResponse('xxxxx')


        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),
            ]

            extra = self.extra_url()
            if extra:
                urlpatterns.extend(extra)

            return urlpatterns

    site.register(models.Role,RoleConfig)

7. 增加URL(除了增删改查以外的url)
    class RoleConfig(StarkConfig):
        order_by = ['-id']  # 有负号,表示降序
        # 定义显示的列
        list_display = [StarkConfig.display_checkbox,'id','title']

        def sk2(self, request):
            return HttpResponse('sk2神仙水')

        def extra_url(self):
            data = [
                url(r'^sk2/$', self.sk2),
            ]
            return data

    site.register(models.Role,RoleConfig)  # 注册表

完整代码,请参考github:

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