今日内容前戏

静态字段和字段

先来看下面一段代码

  1. class Foo:
  2. x = 1 # 类变量、静态字段、静态属性
  3.  
  4. def __init__(self):
  5. y = 6 # 实例变量、字段、对象属性
  6.  
  7. # 实例方法
  8. def func(self):
  9. pass
  10.  
  11. # 静态方法
  12. @staticmethod
  13. def func():
  14. pass
  15.  
  16. # 类方法
  17. @classmethod
  18. def func():
  19. pass
  20.  
  21. @property
  22. def start(self)
  23. pass

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

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

函数名

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

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

urlencode

什么是urlencode

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

为什么要用urlencode

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

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

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

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

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

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

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

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

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

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

举例:

有一个字典

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

需要转换为

  1. k1=v1&k2=v2

怎么转换?

第一种方法:使用for循环

  1. info = {'k1':'v1','k2':'v2'}
  2. list_1 = []
  3. for k,v in info.items():
  4. # print('{}={}'.format(k,v))
  5. list_1.append('{}={}'.format(k,v))
  6.  
  7. res = '&'.join(list_1)
  8. print(res)

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

第一种方法:urlencode

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

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

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

QueryDict

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

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

修改urls.py,增加路径index

  1. from django.conf.urls import url
  2. from django.contrib import admin
  3. from app01 import views
  4. urlpatterns = [
  5. url(r'^admin/', admin.site.urls),
  6. url(r'^index/', views.index),
  7. ]

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

  1. from django.shortcuts import render,HttpResponse
  2.  
  3. # Create your views here.
  4. def index(request):
  5. print(request.GET,type(request.GET))
  6. return HttpResponse('ok')

启动django项目,访问index页面

查看Pycharm控制台输出:

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

它是一个QueryDict类型

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

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

查看Pycharm控制台输出:

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

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

  1. from django.shortcuts import render,HttpResponse
  2.  
  3. # Create your views here.
  4. def index(request):
  5. from django.http.request import QueryDict
  6. print(request.GET)
  7.  
  8. v = request.GET.urlencode()
  9. print(v)
  10.  
  11. return HttpResponse('ok')

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

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

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

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

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

修改views.py,增加一个值

  1. from django.shortcuts import render,HttpResponse
  2.  
  3. # Create your views here.
  4. def index(request):
  5. from django.http.request import QueryDict
  6. print(request.GET)
  7. request.GET['k1'] = 666 # 增加一个值
  8. v = request.GET.urlencode()
  9. print(v)
  10.  
  11. return HttpResponse('ok')

刷新页面,效果如下:

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

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

  1. request.GET._mutable = True

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

查看Pycharm控制台输出:

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

发现k1已经添加进去了!

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

  1. from django.shortcuts import render,HttpResponse
  2.  
  3. # Create your views here.
  4. def index(request):
  5. from django.http.request import QueryDict
  6. import copy
  7. print(request.GET)
  8.  
  9. params = copy.deepcopy(request.GET) # 使用深copy
  10. params._mutable = True # 允许修改
  11. params['k1'] = 666 # 增加一个值
  12. v = params.urlencode()
  13. print(v)
  14.  
  15. return HttpResponse('ok')

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

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

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

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

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

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

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

  1. from django.shortcuts import render,HttpResponse
  2.  
  3. # Create your views here.
  4. def index(request):
  5. from django.http.request import QueryDict
  6. import copy
  7. print(request.GET)
  8.  
  9. params = request.GET.copy() # 使用深copy
  10. params._mutable = True # 允许修改
  11. params['k1'] = 666 # 增加一个值
  12. v = params.urlencode()
  13. print(v)
  14.  
  15. return HttpResponse('ok')

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

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

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

  1. from django.shortcuts import render,HttpResponse
  2.  
  3. # Create your views here.
  4. def index(request):
  5. from django.http.request import QueryDict
  6. import copy
  7. print(request.GET)
  8.  
  9. params = request.GET.copy() # 使用深copy
  10. params._mutable = True # 允许修改
  11. params['k1'] = 666 # 增加一个值
  12. params['k2'] = [11,12] # 增加一个列表
  13. v = params.urlencode()
  14. print(v)
  15.  
  16. return HttpResponse('ok')

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

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

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

列表需要使用setlist才行

修改views.py,使用setlist

  1. from django.shortcuts import render,HttpResponse
  2.  
  3. # Create your views here.
  4. def index(request):
  5. from django.http.request import QueryDict
  6. import copy
  7. print(request.GET)
  8.  
  9. params = request.GET.copy() # 使用深copy
  10. params._mutable = True # 允许修改
  11. params['k1'] = 666 # 增加一个值
  12. params.setlist('k4',[11,12]) # 增加一个列表
  13. v = params.urlencode()
  14. print(v)
  15.  
  16. return HttpResponse('ok')

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

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

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

那么获取k4使用getlist

  1. from django.shortcuts import render,HttpResponse
  2.  
  3. # Create your views here.
  4. def index(request):
  5. from django.http.request import QueryDict
  6. import copy
  7. print(request.GET)
  8.  
  9. params = request.GET.copy() # 使用深copy
  10. params._mutable = True # 允许修改
  11. params['k1'] = 666 # 增加一个值
  12. params.setlist('k4',[11,12]) # 增加一个列表
  13. print(params.get('k1'))
  14. print(params.getlist('k4')) # 获取列表,使用getlist
  15. v = params.urlencode()
  16. print(v)
  17.  
  18. return HttpResponse('ok')

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

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

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

修改views.py

  1. from django.shortcuts import render,HttpResponse
  2.  
  3. # Create your views here.
  4. def index(request):
  5. from django.http.request import QueryDict
  6. import copy
  7. print(request.GET)
  8.  
  9. params = request.GET.copy() # 使用深copy
  10. params._mutable = True # 允许修改
  11. params['k1'] = 666 # 增加一个值
  12. params.setlist('k4',[11,12]) # 增加一个列表
  13.  
  14. old = params.getlist('k4') # 获取列表
  15. old.append('v4') # 最加一个值
  16. params.setlist('k4',old) # 重新设置
  17. v = params.urlencode()
  18. print(v)
  19.  
  20. return HttpResponse('ok')

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

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

可以发现,k4有3个值

保留原来搜索条件

先来访问一个链接:

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

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

  1. _filter = "name=xiao&age=20"

最后变成QueryDict,如何操作?

修改views.py

  1. from django.shortcuts import render,HttpResponse
  2.  
  3. # Create your views here.
  4. def index(request):
  5. from django.http.request import QueryDict
  6.  
  7. url_params_str = request.GET.urlencode()
  8. # mutable=True 表示可修改
  9. query_dic = QueryDict(mutable=True)
  10. # 添加一个key为_filter
  11. query_dic['_filter'] = url_params_str
  12. print(query_dic)
  13.  
  14. # 重新编码
  15. new_params = query_dic.urlencode()
  16. print(new_params)
  17.  
  18. return HttpResponse('ok')

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

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

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

举例:

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

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

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

修改urls.py,增加路径

  1. urlpatterns = [
  2. url(r'^admin/', admin.site.urls),
  3. url(r'^index/', views.index),
  4. url(r'^add_stu/', views.add_stu),
  5. ]

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

  1. from django.shortcuts import render,HttpResponse,redirect
  2.  
  3. # Create your views here.
  4. def index(request):
  5. from django.http.request import QueryDict
  6.  
  7. url_params_str = request.GET.urlencode()
  8. # mutable=True 表示可修改
  9. query_dic = QueryDict(mutable=True)
  10. # 添加一个key为_filter
  11. query_dic['_filter'] = url_params_str
  12. print(query_dic)
  13.  
  14. # 重新编码
  15. new_params = query_dic.urlencode()
  16. print(new_params)
  17.  
  18. # 跳转地址
  19. target_url = "/add_stu?%s" %new_params
  20. print(target_url)
  21. # 渲染一个a标签
  22. return HttpResponse('<a href="%s">添加学生</a>'%target_url)
  23.  
  24. def add_stu(request):
  25. if request.method == "GET":
  26. return render(request,"add_stu.html")
  27.  
  28. # 接收到数据,保存到数据库...
  29. # 获取搜索条件
  30. origin_params = request.GET.get('_filter')
  31. # 返回地址,保留原搜索添加
  32. back_url = "/index/?%s" %origin_params
  33. # 重定向页面
  34. return redirect(back_url)

重启django程序,访问页面

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

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

效果如下:

点击添加学生,页面跳转

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

点击提交按钮,页面跳转

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

Q查询高级用法

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

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

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

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

一、批量操作

务必下载github代码:

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

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

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

  1. {% extends 'stark/layout.html' %}
  2.  
  3. {% block content %}
  4. <h1>列表页面</h1>
  5. <div>
  6. {% if add_btn %}
  7. <div style="margin: 5px 0;">
  8. {{ add_btn }}
  9. </div>
  10. {% endif %}
  11. <form class="form-inline" method="post">
  12. {% csrf_token %}
  13. <div class="form-group">
  14. <select name="action" class="form-control" style="min-width: 200px;">
  15. <option>请选择功能</option>
  16. <option value="">批量删除</option>
  17. <option value="">初始化</option>
  18. </select>
  19. <input class="btn btn-primary" type="submit" value="执行">
  20. </div>
  21. </form>
  22. <table class="table table-bordered">
  23. <thead>
  24. <tr>
  25. {% for item in header_list %}
  26. <th>{{ item }}</th>
  27. {% endfor %}
  28.  
  29. </tr>
  30. </thead>
  31. <tbody>
  32. {% for row_list in body_list %}
  33. <tr>
  34. {% for col in row_list %}
  35. <td>{{ col }}</td>
  36. {% endfor %}
  37.  
  38. </tr>
  39. {% endfor %}
  40. </tbody>
  41. </table>
  42.  
  43. </div>
  44.  
  45. {% endblock %}

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

  1. from stark.server.stark import site, StarkConfig
  2. from app01 import models
  3. from django import forms
  4. from django.shortcuts import render
  5. from django.conf.urls import url
  6.  
  7. class UserInfoConfig(StarkConfig):
  8. list_display = ['id', 'username']
  9.  
  10. class DepartModelForm(forms.ModelForm):
  11. class Meta:
  12. model = models.Depart
  13. fields = "__all__"
  14.  
  15. def clean_name(self): # 定义钩子
  16. # print(self.cleaned_data['name'])
  17. return self.cleaned_data['name']
  18.  
  19. class DepartConfig(StarkConfig):
  20. list_display = [StarkConfig.display_checkbox,'name', 'tel', 'user']
  21. # model_form_class = DepartModelForm
  22. # def get_add_btn(self): # 返回None,表示不显示添加按钮
  23. # pass
  24. # def changelist_view(self, request): # 重写changelist_view方法
  25. # # 渲染自定义的列表页面
  26. # return render(request,'stark/custom_list.html')
  27. # def get_urls(self): # 自定义路由
  28. # info = self.model_class._meta.app_label, self.model_class._meta.model_name
  29. #
  30. # urlpatterns = [
  31. # url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
  32. # ]
  33. # return urlpatterns
  34.  
  35. site.register(models.UserInfo, UserInfoConfig)
  36. site.register(models.Depart, DepartConfig)

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

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

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

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

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

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

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

  1. from django.conf.urls import url
  2. from django.shortcuts import HttpResponse,render,redirect
  3. from types import FunctionType
  4. from django.utils.safestring import mark_safe
  5. from django.urls import reverse
  6. from django import forms
  7.  
  8. class StarkConfig(object):
  9. def __init__(self,model_class,site):
  10. self.model_class = model_class
  11. self.site = site
  12.  
  13. def display_checkbox(self,row=None,header=False): # 显示复选框
  14. if header:
  15. # 输出中文
  16. return "选择"
  17. # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
  18. return mark_safe('<input type="checkbox" name="pk" values="%s"/>' %row.pk)
  19.  
  20. def display_edit(self, row=None, header=False):
  21. if header:
  22. return "编辑"
  23.  
  24. return mark_safe(
  25. '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))
  26.  
  27. def display_del(self, row=None, header=False):
  28. if header:
  29. return "删除"
  30.  
  31. return mark_safe(
  32. '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))
  33.  
  34. def display_edit_del(self, row=None, header=False):
  35. if header:
  36. return "操作"
  37. tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
  38. <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
  39. """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
  40. return mark_safe(tpl)
  41.  
  42. order_by = [] # 需要排序的字段,由用户自定义
  43. list_display = [] # 定义显示的列,由用户自定义
  44. model_form_class = None # form组件需要的model_class
  45. action_list = [] # 批量操作方法
  46.  
  47. def multi_delete(self): # 批量删除
  48. pass
  49.  
  50. def multi_init(self): # 批量初始化
  51. pass
  52.  
  53. def get_order_by(self): # 获取排序列表
  54. return self.order_by
  55.  
  56. def get_list_display(self): # 获取显示的列
  57. return self.list_display
  58.  
  59. def get_add_btn(self): # 显示添加按钮
  60. return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())
  61.  
  62. def get_model_form_class(self):
  63. """
  64. 获取ModelForm类
  65. :return:
  66. """
  67. if self.model_form_class:
  68. return self.model_form_class
  69.  
  70. class AddModelForm(forms.ModelForm):
  71. class Meta:
  72. model = self.model_class
  73. fields = "__all__"
  74.  
  75. return AddModelForm
  76.  
  77. def get_action_list(self): # 获取批量操作方法
  78. val = [] # 空列表
  79. # 扩展列表的元素
  80. val.extend(self.action_list)
  81. return val
  82.  
  83. def changelist_view(self, request):
  84. """
  85. 所有URL查看列表页面
  86. :param request:
  87. :return:
  88. """
  89. # 根据排序列表进行排序
  90. queryset = self.model_class.objects.all().order_by(*self.get_order_by())
  91. ### 批量操作 ###
  92. action_list = self.get_action_list()
  93. ### 添加按钮 ###
  94. add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示
  95.  
  96. list_display = self.list_display # 定义显示的列
  97. header_list = [] # 定义头部,用来显示verbose_name
  98. if list_display:
  99. for name_or_func in list_display:
  100. if isinstance(name_or_func,FunctionType):
  101. # 执行函数,默认显示中文
  102. verbose_name = name_or_func(self,header=True)
  103. else:
  104. # 获取指定字段的verbose_name
  105. verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
  106.  
  107. header_list.append(verbose_name)
  108. else:
  109. # 如果list_display为空,添加表名
  110. header_list.append(self.model_class._meta.model_name)
  111.  
  112. body_list = [] # 显示内容
  113.  
  114. for row in queryset:
  115. # 这里的row是对象,它表示表里面的一条数据
  116. row_list = [] # 展示每一行数据
  117. if not list_display: # 如果不在list_display里面
  118. # 添加对象
  119. row_list.append(row)
  120. body_list.append(row_list)
  121. continue
  122.  
  123. for name_or_func in list_display:
  124. if isinstance(name_or_func,FunctionType):
  125. val = name_or_func(self,row=row) # 执行函数获取,传递row对象
  126. else:
  127. # 使用反射获取对象的值
  128. val = getattr(row, name_or_func)
  129.  
  130. row_list.append(val)
  131.  
  132. body_list.append(row_list)
  133.  
  134. # 注意:要传入add_btn
  135. return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
  136.  
  137. def add_view(self, request):
  138. """
  139. 所有的添加页面,都在此方法处理
  140. 使用ModelForm实现
  141. :param request:
  142. :return:
  143. """
  144. # 添加数据,使用ModelForm
  145. AddModelForm = self.get_model_form_class()
  146.  
  147. if request.method == "GET":
  148. form = AddModelForm()
  149. return render(request,'stark/change.html',{'form':form})
  150.  
  151. form = AddModelForm(request.POST) # 接收POST数据
  152. if form.is_valid(): # 验证数据
  153. form.save() # 自动保存数据
  154. # 反向生成url,跳转到列表页面
  155. return redirect(self.reverse_list_url())
  156. # 渲染页面,此时会保存表单数据
  157. return render(request, 'stark/change.html', {'form': form})
  158.  
  159. def change_view(self, request, pk):
  160. """
  161. 所有编辑页面
  162. :param request:
  163. :param pk:
  164. :return:
  165. """
  166. # 查看单条数据
  167. obj = self.model_class.objects.filter(pk=pk).first()
  168. if not obj:
  169. return HttpResponse('数据不存在')
  170. # 获取model_form类
  171. ModelFormClass = self.get_model_form_class()
  172. if request.method == 'GET':
  173. # instance表示生成默认值
  174. form = ModelFormClass(instance=obj)
  175. # 渲染页面,添加和修改可以共用一个一个模板文件
  176. return render(request, 'stark/change.html', {'form': form})
  177. # instance = obj 表示指定给谁做修改
  178. form = ModelFormClass(data=request.POST, instance=obj)
  179. if form.is_valid():
  180. form.save() # 修改数据
  181. # 跳转到列表页面
  182. return redirect(self.reverse_list_url())
  183. return render(request, 'stark/change.html', {'form': form})
  184.  
  185. def delete_view(self, request, pk):
  186. """
  187. 所有删除页面
  188. :param request:
  189. :param pk:
  190. :return:
  191. """
  192. if request.method == "GET":
  193. # cancel_url表示用户点击取消时,跳转到列表页面
  194. return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
  195. # 定位单条数据,并删除!
  196. self.model_class.objects.filter(pk=pk).delete()
  197. return redirect(self.reverse_list_url())
  198.  
  199. def wrapper(self,func):
  200. pass
  201.  
  202. def get_urls(self):
  203. info = self.model_class._meta.app_label, self.model_class._meta.model_name
  204. urlpatterns = [
  205. url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
  206. url(r'^add/$', self.add_view, name='%s_%s_add' % info),
  207. url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
  208. url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
  209. ]
  210.  
  211. extra = self.extra_url()
  212. if extra: # 判断变量不为空
  213. # 扩展路由
  214. urlpatterns.extend(extra)
  215.  
  216. # print(urlpatterns)
  217. return urlpatterns
  218.  
  219. def extra_url(self): # 额外的路由,由调用者重构
  220. pass
  221.  
  222. def reverse_list_url(self): # 反向生成访问列表的url
  223. app_label = self.model_class._meta.app_label
  224. model_name = self.model_class._meta.model_name
  225. namespace = self.site.namespace
  226. name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
  227. list_url = reverse(name)
  228. return list_url
  229.  
  230. def reverse_add_url(self): # 反向生成添加url
  231. app_label = self.model_class._meta.app_label
  232. model_name = self.model_class._meta.model_name
  233. namespace = self.site.namespace
  234. name = '%s:%s_%s_add' % (namespace, app_label, model_name)
  235. add_url = reverse(name)
  236. return add_url
  237.  
  238. def reverse_edit_url(self, row): # 反向生成编辑行内容的url
  239. app_label = self.model_class._meta.app_label # app名
  240. model_name = self.model_class._meta.model_name # 表名
  241. namespace = self.site.namespace # 命名空间
  242. # 拼接字符串,这里为change
  243. name = '%s:%s_%s_change' % (namespace, app_label, model_name)
  244. # 反向生成url,传入参数pk=row.pk
  245. edit_url = reverse(name, kwargs={'pk': row.pk})
  246. return edit_url
  247.  
  248. def reverse_del_url(self, row): # 反向生成删除行内容的url
  249. app_label = self.model_class._meta.app_label
  250. model_name = self.model_class._meta.model_name
  251. namespace = self.site.namespace
  252. # 注意:这里为del
  253. name = '%s:%s_%s_del' % (namespace, app_label, model_name)
  254. del_url = reverse(name, kwargs={'pk': row.pk})
  255. return del_url
  256.  
  257. @property
  258. def urls(self):
  259. return self.get_urls()
  260.  
  261. class AdminSite(object):
  262. def __init__(self):
  263. self._registry = {}
  264. self.app_name = 'stark'
  265. self.namespace = 'stark'
  266.  
  267. def register(self,model_class,stark_config=None):
  268. # not None的结果为Ture
  269. if not stark_config:
  270. # 也就是说,当其他应用调用register时,如果不指定stark_config参数
  271. # 那么必然执行下面这段代码!
  272. # stark_config和StarkConfig是等值的!都能实例化
  273. stark_config = StarkConfig
  274.  
  275. # 添加键值对,实例化类StarkConfig,传入参数model_class
  276. # self指的是AdminSite类
  277. self._registry[model_class] = stark_config(model_class,self)
  278.  
  279. # print(self._registry) # 打印字典
  280. """
  281. {
  282. app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
  283. app02.models.Role:RoleConfig(app02.models.Role)
  284. }
  285. """
  286.  
  287. # for k, v in self._registry.items():
  288. # print(k,v)
  289.  
  290. def get_urls(self):
  291. urlpatterns = []
  292.  
  293. for k, v in self._registry.items():
  294. # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
  295. # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
  296. app_label = k._meta.app_label
  297. model_name = k._meta.model_name
  298. urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))
  299.  
  300. return urlpatterns
  301.  
  302. @property
  303. def urls(self):
  304. # 调用get_urls方法
  305. # self.app_name和self.namespace值是一样的,都是stark
  306. return self.get_urls(), self.app_name, self.namespace
  307.  
  308. site = AdminSite() # 实例化类

修改app01-->stark.py,定义action_list

  1. from stark.server.stark import site, StarkConfig
  2. from app01 import models
  3. from django import forms
  4. from django.shortcuts import render
  5. from django.conf.urls import url
  6.  
  7. class UserInfoConfig(StarkConfig):
  8. list_display = ['id', 'username']
  9.  
  10. class DepartModelForm(forms.ModelForm):
  11. class Meta:
  12. model = models.Depart
  13. fields = "__all__"
  14.  
  15. def clean_name(self): # 定义钩子
  16. # print(self.cleaned_data['name'])
  17. return self.cleaned_data['name']
  18.  
  19. class DepartConfig(StarkConfig):
  20. list_display = [StarkConfig.display_checkbox,'name', 'tel', 'user']
  21. # model_form_class = DepartModelForm
  22. # 批量操作
  23. action_list = [StarkConfig.multi_delete,StarkConfig.multi_init]
  24. # def get_add_btn(self): # 返回None,表示不显示添加按钮
  25. # pass
  26. # def changelist_view(self, request): # 重写changelist_view方法
  27. # # 渲染自定义的列表页面
  28. # return render(request,'stark/custom_list.html')
  29. # def get_urls(self): # 自定义路由
  30. # info = self.model_class._meta.app_label, self.model_class._meta.model_name
  31. #
  32. # urlpatterns = [
  33. # url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
  34. # ]
  35. # return urlpatterns
  36.  
  37. site.register(models.UserInfo, UserInfoConfig)
  38. site.register(models.Depart, DepartConfig)

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

  1. {% extends 'stark/layout.html' %}
  2.  
  3. {% block content %}
  4. <h1>列表页面</h1>
  5. <div>
  6. {% if add_btn %}
  7. <div style="margin: 5px 0;">
  8. {{ add_btn }}
  9. </div>
  10. {% endif %}
  11. <form class="form-inline" method="post">
  12. {% csrf_token %}
  13. <div class="form-group">
  14. <select name="action" class="form-control" style="min-width: 200px;">
  15. <option>请选择功能</option>
  16. {% for func in action_list %}
  17. <option value="">{{ func.__name__ }}</option>
  18. {% endfor %}
  19. </select>
  20. <input class="btn btn-primary" type="submit" value="执行">
  21. </div>
  22. </form>
  23. <table class="table table-bordered">
  24. <thead>
  25. <tr>
  26. {% for item in header_list %}
  27. <th>{{ item }}</th>
  28. {% endfor %}
  29.  
  30. </tr>
  31. </thead>
  32. <tbody>
  33. {% for row_list in body_list %}
  34. <tr>
  35. {% for col in row_list %}
  36. <td>{{ col }}</td>
  37. {% endfor %}
  38.  
  39. </tr>
  40. {% endfor %}
  41. </tbody>
  42. </table>
  43.  
  44. </div>
  45.  
  46. {% endblock %}

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

效果如下:

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

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

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

  1. from django.conf.urls import url
  2. from django.shortcuts import HttpResponse,render,redirect
  3. from types import FunctionType
  4. from django.utils.safestring import mark_safe
  5. from django.urls import reverse
  6. from django import forms
  7.  
  8. class StarkConfig(object):
  9. def __init__(self,model_class,site):
  10. self.model_class = model_class
  11. self.site = site
  12.  
  13. def display_checkbox(self,row=None,header=False): # 显示复选框
  14. if header:
  15. # 输出中文
  16. return "选择"
  17. # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
  18. return mark_safe('<input type="checkbox" name="pk" values="%s"/>' %row.pk)
  19.  
  20. def display_edit(self, row=None, header=False):
  21. if header:
  22. return "编辑"
  23.  
  24. return mark_safe(
  25. '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))
  26.  
  27. def display_del(self, row=None, header=False):
  28. if header:
  29. return "删除"
  30.  
  31. return mark_safe(
  32. '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))
  33.  
  34. def display_edit_del(self, row=None, header=False):
  35. if header:
  36. return "操作"
  37. tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
  38. <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
  39. """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
  40. return mark_safe(tpl)
  41.  
  42. order_by = [] # 需要排序的字段,由用户自定义
  43. list_display = [] # 定义显示的列,由用户自定义
  44. model_form_class = None # form组件需要的model_class
  45. action_list = [] # 批量操作方法
  46.  
  47. def multi_delete(self): # 批量删除
  48. pass
  49.  
  50. def multi_init(self): # 批量初始化
  51. pass
  52.  
  53. def get_order_by(self): # 获取排序列表
  54. return self.order_by
  55.  
  56. def get_list_display(self): # 获取显示的列
  57. return self.list_display
  58.  
  59. def get_add_btn(self): # 显示添加按钮
  60. return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())
  61.  
  62. def get_model_form_class(self):
  63. """
  64. 获取ModelForm类
  65. :return:
  66. """
  67. if self.model_form_class:
  68. return self.model_form_class
  69.  
  70. class AddModelForm(forms.ModelForm):
  71. class Meta:
  72. model = self.model_class
  73. fields = "__all__"
  74.  
  75. return AddModelForm
  76.  
  77. def get_action_list(self): # 获取批量操作方法
  78. val = [] # 空列表
  79. # 扩展列表的元素
  80. val.extend(self.action_list)
  81. return val
  82.  
  83. def changelist_view(self, request):
  84. """
  85. 所有URL查看列表页面
  86. :param request:
  87. :return:
  88. """
  89. # 根据排序列表进行排序
  90. queryset = self.model_class.objects.all().order_by(*self.get_order_by())
  91. ### 批量操作 ###
  92. action_list = self.get_action_list()
  93. action_list = [ func.__name__ for func in action_list ]
  94. ### 添加按钮 ###
  95. add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示
  96.  
  97. list_display = self.list_display # 定义显示的列
  98. header_list = [] # 定义头部,用来显示verbose_name
  99. if list_display:
  100. for name_or_func in list_display:
  101. if isinstance(name_or_func,FunctionType):
  102. # 执行函数,默认显示中文
  103. verbose_name = name_or_func(self,header=True)
  104. else:
  105. # 获取指定字段的verbose_name
  106. verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
  107.  
  108. header_list.append(verbose_name)
  109. else:
  110. # 如果list_display为空,添加表名
  111. header_list.append(self.model_class._meta.model_name)
  112.  
  113. body_list = [] # 显示内容
  114.  
  115. for row in queryset:
  116. # 这里的row是对象,它表示表里面的一条数据
  117. row_list = [] # 展示每一行数据
  118. if not list_display: # 如果不在list_display里面
  119. # 添加对象
  120. row_list.append(row)
  121. body_list.append(row_list)
  122. continue
  123.  
  124. for name_or_func in list_display:
  125. if isinstance(name_or_func,FunctionType):
  126. val = name_or_func(self,row=row) # 执行函数获取,传递row对象
  127. else:
  128. # 使用反射获取对象的值
  129. val = getattr(row, name_or_func)
  130.  
  131. row_list.append(val)
  132.  
  133. body_list.append(row_list)
  134.  
  135. # 注意:要传入add_btn
  136. return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
  137.  
  138. def add_view(self, request):
  139. """
  140. 所有的添加页面,都在此方法处理
  141. 使用ModelForm实现
  142. :param request:
  143. :return:
  144. """
  145. # 添加数据,使用ModelForm
  146. AddModelForm = self.get_model_form_class()
  147.  
  148. if request.method == "GET":
  149. form = AddModelForm()
  150. return render(request,'stark/change.html',{'form':form})
  151.  
  152. form = AddModelForm(request.POST) # 接收POST数据
  153. if form.is_valid(): # 验证数据
  154. form.save() # 自动保存数据
  155. # 反向生成url,跳转到列表页面
  156. return redirect(self.reverse_list_url())
  157. # 渲染页面,此时会保存表单数据
  158. return render(request, 'stark/change.html', {'form': form})
  159.  
  160. def change_view(self, request, pk):
  161. """
  162. 所有编辑页面
  163. :param request:
  164. :param pk:
  165. :return:
  166. """
  167. # 查看单条数据
  168. obj = self.model_class.objects.filter(pk=pk).first()
  169. if not obj:
  170. return HttpResponse('数据不存在')
  171. # 获取model_form类
  172. ModelFormClass = self.get_model_form_class()
  173. if request.method == 'GET':
  174. # instance表示生成默认值
  175. form = ModelFormClass(instance=obj)
  176. # 渲染页面,添加和修改可以共用一个一个模板文件
  177. return render(request, 'stark/change.html', {'form': form})
  178. # instance = obj 表示指定给谁做修改
  179. form = ModelFormClass(data=request.POST, instance=obj)
  180. if form.is_valid():
  181. form.save() # 修改数据
  182. # 跳转到列表页面
  183. return redirect(self.reverse_list_url())
  184. return render(request, 'stark/change.html', {'form': form})
  185.  
  186. def delete_view(self, request, pk):
  187. """
  188. 所有删除页面
  189. :param request:
  190. :param pk:
  191. :return:
  192. """
  193. if request.method == "GET":
  194. # cancel_url表示用户点击取消时,跳转到列表页面
  195. return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
  196. # 定位单条数据,并删除!
  197. self.model_class.objects.filter(pk=pk).delete()
  198. return redirect(self.reverse_list_url())
  199.  
  200. def wrapper(self,func):
  201. pass
  202.  
  203. def get_urls(self):
  204. info = self.model_class._meta.app_label, self.model_class._meta.model_name
  205. urlpatterns = [
  206. url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
  207. url(r'^add/$', self.add_view, name='%s_%s_add' % info),
  208. url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
  209. url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
  210. ]
  211.  
  212. extra = self.extra_url()
  213. if extra: # 判断变量不为空
  214. # 扩展路由
  215. urlpatterns.extend(extra)
  216.  
  217. # print(urlpatterns)
  218. return urlpatterns
  219.  
  220. def extra_url(self): # 额外的路由,由调用者重构
  221. pass
  222.  
  223. def reverse_list_url(self): # 反向生成访问列表的url
  224. app_label = self.model_class._meta.app_label
  225. model_name = self.model_class._meta.model_name
  226. namespace = self.site.namespace
  227. name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
  228. list_url = reverse(name)
  229. return list_url
  230.  
  231. def reverse_add_url(self): # 反向生成添加url
  232. app_label = self.model_class._meta.app_label
  233. model_name = self.model_class._meta.model_name
  234. namespace = self.site.namespace
  235. name = '%s:%s_%s_add' % (namespace, app_label, model_name)
  236. add_url = reverse(name)
  237. return add_url
  238.  
  239. def reverse_edit_url(self, row): # 反向生成编辑行内容的url
  240. app_label = self.model_class._meta.app_label # app名
  241. model_name = self.model_class._meta.model_name # 表名
  242. namespace = self.site.namespace # 命名空间
  243. # 拼接字符串,这里为change
  244. name = '%s:%s_%s_change' % (namespace, app_label, model_name)
  245. # 反向生成url,传入参数pk=row.pk
  246. edit_url = reverse(name, kwargs={'pk': row.pk})
  247. return edit_url
  248.  
  249. def reverse_del_url(self, row): # 反向生成删除行内容的url
  250. app_label = self.model_class._meta.app_label
  251. model_name = self.model_class._meta.model_name
  252. namespace = self.site.namespace
  253. # 注意:这里为del
  254. name = '%s:%s_%s_del' % (namespace, app_label, model_name)
  255. del_url = reverse(name, kwargs={'pk': row.pk})
  256. return del_url
  257.  
  258. @property
  259. def urls(self):
  260. return self.get_urls()
  261.  
  262. class AdminSite(object):
  263. def __init__(self):
  264. self._registry = {}
  265. self.app_name = 'stark'
  266. self.namespace = 'stark'
  267.  
  268. def register(self,model_class,stark_config=None):
  269. # not None的结果为Ture
  270. if not stark_config:
  271. # 也就是说,当其他应用调用register时,如果不指定stark_config参数
  272. # 那么必然执行下面这段代码!
  273. # stark_config和StarkConfig是等值的!都能实例化
  274. stark_config = StarkConfig
  275.  
  276. # 添加键值对,实例化类StarkConfig,传入参数model_class
  277. # self指的是AdminSite类
  278. self._registry[model_class] = stark_config(model_class,self)
  279.  
  280. # print(self._registry) # 打印字典
  281. """
  282. {
  283. app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
  284. app02.models.Role:RoleConfig(app02.models.Role)
  285. }
  286. """
  287.  
  288. # for k, v in self._registry.items():
  289. # print(k,v)
  290.  
  291. def get_urls(self):
  292. urlpatterns = []
  293.  
  294. for k, v in self._registry.items():
  295. # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
  296. # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
  297. app_label = k._meta.app_label
  298. model_name = k._meta.model_name
  299. urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))
  300.  
  301. return urlpatterns
  302.  
  303. @property
  304. def urls(self):
  305. # 调用get_urls方法
  306. # self.app_name和self.namespace值是一样的,都是stark
  307. return self.get_urls(), self.app_name, self.namespace
  308.  
  309. site = AdminSite() # 实例化类

修改 stark-->templates-->stark-->changelist.html,修改变量名

  1. {% extends 'stark/layout.html' %}
  2.  
  3. {% block content %}
  4. <h1>列表页面</h1>
  5. <div>
  6. {% if add_btn %}
  7. <div style="margin: 5px 0;">
  8. {{ add_btn }}
  9. </div>
  10. {% endif %}
  11. <form class="form-inline" method="post">
  12. {% csrf_token %}
  13. <div class="form-group">
  14. <select name="action" class="form-control" style="min-width: 200px;">
  15. <option>请选择功能</option>
  16. {% for name in action_list %}
  17. <option value="">{{ name }}</option>
  18. {% endfor %}
  19. </select>
  20. <input class="btn btn-primary" type="submit" value="执行">
  21. </div>
  22. </form>
  23. <table class="table table-bordered">
  24. <thead>
  25. <tr>
  26. {% for item in header_list %}
  27. <th>{{ item }}</th>
  28. {% endfor %}
  29.  
  30. </tr>
  31. </thead>
  32. <tbody>
  33. {% for row_list in body_list %}
  34. <tr>
  35. {% for col in row_list %}
  36. <td>{{ col }}</td>
  37. {% endfor %}
  38.  
  39. </tr>
  40. {% endfor %}
  41. </tbody>
  42. </table>
  43.  
  44. </div>
  45.  
  46. {% endblock %}

刷新页面,效果如下:

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

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

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

  1. def func():
  2. print(123)
  3.  
  4. func.aa = '批量删除'
  5.  
  6. print(func.aa)

执行输出:批量删除

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

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

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

  1. from django.conf.urls import url
  2. from django.shortcuts import HttpResponse,render,redirect
  3. from types import FunctionType
  4. from django.utils.safestring import mark_safe
  5. from django.urls import reverse
  6. from django import forms
  7.  
  8. class StarkConfig(object):
  9. def __init__(self,model_class,site):
  10. self.model_class = model_class
  11. self.site = site
  12.  
  13. def display_checkbox(self,row=None,header=False): # 显示复选框
  14. if header:
  15. # 输出中文
  16. return "选择"
  17. # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
  18. return mark_safe('<input type="checkbox" name="pk" values="%s"/>' %row.pk)
  19.  
  20. def display_edit(self, row=None, header=False):
  21. if header:
  22. return "编辑"
  23.  
  24. return mark_safe(
  25. '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))
  26.  
  27. def display_del(self, row=None, header=False):
  28. if header:
  29. return "删除"
  30.  
  31. return mark_safe(
  32. '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))
  33.  
  34. def display_edit_del(self, row=None, header=False):
  35. if header:
  36. return "操作"
  37. tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
  38. <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
  39. """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
  40. return mark_safe(tpl)
  41.  
  42. order_by = [] # 需要排序的字段,由用户自定义
  43. list_display = [] # 定义显示的列,由用户自定义
  44. model_form_class = None # form组件需要的model_class
  45. action_list = [] # 批量操作方法
  46.  
  47. def multi_delete(self): # 批量删除
  48. pass
  49.  
  50. multi_delete.text = "批量删除" # 添加自定义属性text
  51.  
  52. def multi_init(self): # 批量初始化
  53. pass
  54.  
  55. multi_init.text = "批量初始化" # 添加自定义属性text
  56.  
  57. def get_order_by(self): # 获取排序列表
  58. return self.order_by
  59.  
  60. def get_list_display(self): # 获取显示的列
  61. return self.list_display
  62.  
  63. def get_add_btn(self): # 显示添加按钮
  64. return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())
  65.  
  66. def get_model_form_class(self):
  67. """
  68. 获取ModelForm类
  69. :return:
  70. """
  71. if self.model_form_class:
  72. return self.model_form_class
  73.  
  74. class AddModelForm(forms.ModelForm):
  75. class Meta:
  76. model = self.model_class
  77. fields = "__all__"
  78.  
  79. return AddModelForm
  80.  
  81. def get_action_list(self): # 获取批量操作方法
  82. val = [] # 空列表
  83. # 扩展列表的元素
  84. val.extend(self.action_list)
  85. return val
  86.  
  87. def changelist_view(self, request):
  88. """
  89. 所有URL查看列表页面
  90. :param request:
  91. :return:
  92. """
  93. # 根据排序列表进行排序
  94. queryset = self.model_class.objects.all().order_by(*self.get_order_by())
  95. ### 批量操作 ###
  96. action_list = self.get_action_list()
  97. # 获取text属性
  98. action_list = [ func.text for func in action_list ]
  99. ### 添加按钮 ###
  100. add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示
  101.  
  102. list_display = self.list_display # 定义显示的列
  103. header_list = [] # 定义头部,用来显示verbose_name
  104. if list_display:
  105. for name_or_func in list_display:
  106. if isinstance(name_or_func,FunctionType):
  107. # 执行函数,默认显示中文
  108. verbose_name = name_or_func(self,header=True)
  109. else:
  110. # 获取指定字段的verbose_name
  111. verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
  112.  
  113. header_list.append(verbose_name)
  114. else:
  115. # 如果list_display为空,添加表名
  116. header_list.append(self.model_class._meta.model_name)
  117.  
  118. body_list = [] # 显示内容
  119.  
  120. for row in queryset:
  121. # 这里的row是对象,它表示表里面的一条数据
  122. row_list = [] # 展示每一行数据
  123. if not list_display: # 如果不在list_display里面
  124. # 添加对象
  125. row_list.append(row)
  126. body_list.append(row_list)
  127. continue
  128.  
  129. for name_or_func in list_display:
  130. if isinstance(name_or_func,FunctionType):
  131. val = name_or_func(self,row=row) # 执行函数获取,传递row对象
  132. else:
  133. # 使用反射获取对象的值
  134. val = getattr(row, name_or_func)
  135.  
  136. row_list.append(val)
  137.  
  138. body_list.append(row_list)
  139.  
  140. # 注意:要传入add_btn
  141. return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
  142.  
  143. def add_view(self, request):
  144. """
  145. 所有的添加页面,都在此方法处理
  146. 使用ModelForm实现
  147. :param request:
  148. :return:
  149. """
  150. # 添加数据,使用ModelForm
  151. AddModelForm = self.get_model_form_class()
  152.  
  153. if request.method == "GET":
  154. form = AddModelForm()
  155. return render(request,'stark/change.html',{'form':form})
  156.  
  157. form = AddModelForm(request.POST) # 接收POST数据
  158. if form.is_valid(): # 验证数据
  159. form.save() # 自动保存数据
  160. # 反向生成url,跳转到列表页面
  161. return redirect(self.reverse_list_url())
  162. # 渲染页面,此时会保存表单数据
  163. return render(request, 'stark/change.html', {'form': form})
  164.  
  165. def change_view(self, request, pk):
  166. """
  167. 所有编辑页面
  168. :param request:
  169. :param pk:
  170. :return:
  171. """
  172. # 查看单条数据
  173. obj = self.model_class.objects.filter(pk=pk).first()
  174. if not obj:
  175. return HttpResponse('数据不存在')
  176. # 获取model_form类
  177. ModelFormClass = self.get_model_form_class()
  178. if request.method == 'GET':
  179. # instance表示生成默认值
  180. form = ModelFormClass(instance=obj)
  181. # 渲染页面,添加和修改可以共用一个一个模板文件
  182. return render(request, 'stark/change.html', {'form': form})
  183. # instance = obj 表示指定给谁做修改
  184. form = ModelFormClass(data=request.POST, instance=obj)
  185. if form.is_valid():
  186. form.save() # 修改数据
  187. # 跳转到列表页面
  188. return redirect(self.reverse_list_url())
  189. return render(request, 'stark/change.html', {'form': form})
  190.  
  191. def delete_view(self, request, pk):
  192. """
  193. 所有删除页面
  194. :param request:
  195. :param pk:
  196. :return:
  197. """
  198. if request.method == "GET":
  199. # cancel_url表示用户点击取消时,跳转到列表页面
  200. return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
  201. # 定位单条数据,并删除!
  202. self.model_class.objects.filter(pk=pk).delete()
  203. return redirect(self.reverse_list_url())
  204.  
  205. def wrapper(self,func):
  206. pass
  207.  
  208. def get_urls(self):
  209. info = self.model_class._meta.app_label, self.model_class._meta.model_name
  210. urlpatterns = [
  211. url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
  212. url(r'^add/$', self.add_view, name='%s_%s_add' % info),
  213. url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
  214. url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
  215. ]
  216.  
  217. extra = self.extra_url()
  218. if extra: # 判断变量不为空
  219. # 扩展路由
  220. urlpatterns.extend(extra)
  221.  
  222. # print(urlpatterns)
  223. return urlpatterns
  224.  
  225. def extra_url(self): # 额外的路由,由调用者重构
  226. pass
  227.  
  228. def reverse_list_url(self): # 反向生成访问列表的url
  229. app_label = self.model_class._meta.app_label
  230. model_name = self.model_class._meta.model_name
  231. namespace = self.site.namespace
  232. name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
  233. list_url = reverse(name)
  234. return list_url
  235.  
  236. def reverse_add_url(self): # 反向生成添加url
  237. app_label = self.model_class._meta.app_label
  238. model_name = self.model_class._meta.model_name
  239. namespace = self.site.namespace
  240. name = '%s:%s_%s_add' % (namespace, app_label, model_name)
  241. add_url = reverse(name)
  242. return add_url
  243.  
  244. def reverse_edit_url(self, row): # 反向生成编辑行内容的url
  245. app_label = self.model_class._meta.app_label # app名
  246. model_name = self.model_class._meta.model_name # 表名
  247. namespace = self.site.namespace # 命名空间
  248. # 拼接字符串,这里为change
  249. name = '%s:%s_%s_change' % (namespace, app_label, model_name)
  250. # 反向生成url,传入参数pk=row.pk
  251. edit_url = reverse(name, kwargs={'pk': row.pk})
  252. return edit_url
  253.  
  254. def reverse_del_url(self, row): # 反向生成删除行内容的url
  255. app_label = self.model_class._meta.app_label
  256. model_name = self.model_class._meta.model_name
  257. namespace = self.site.namespace
  258. # 注意:这里为del
  259. name = '%s:%s_%s_del' % (namespace, app_label, model_name)
  260. del_url = reverse(name, kwargs={'pk': row.pk})
  261. return del_url
  262.  
  263. @property
  264. def urls(self):
  265. return self.get_urls()
  266.  
  267. class AdminSite(object):
  268. def __init__(self):
  269. self._registry = {}
  270. self.app_name = 'stark'
  271. self.namespace = 'stark'
  272.  
  273. def register(self,model_class,stark_config=None):
  274. # not None的结果为Ture
  275. if not stark_config:
  276. # 也就是说,当其他应用调用register时,如果不指定stark_config参数
  277. # 那么必然执行下面这段代码!
  278. # stark_config和StarkConfig是等值的!都能实例化
  279. stark_config = StarkConfig
  280.  
  281. # 添加键值对,实例化类StarkConfig,传入参数model_class
  282. # self指的是AdminSite类
  283. self._registry[model_class] = stark_config(model_class,self)
  284.  
  285. # print(self._registry) # 打印字典
  286. """
  287. {
  288. app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
  289. app02.models.Role:RoleConfig(app02.models.Role)
  290. }
  291. """
  292.  
  293. # for k, v in self._registry.items():
  294. # print(k,v)
  295.  
  296. def get_urls(self):
  297. urlpatterns = []
  298.  
  299. for k, v in self._registry.items():
  300. # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
  301. # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
  302. app_label = k._meta.app_label
  303. model_name = k._meta.model_name
  304. urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))
  305.  
  306. return urlpatterns
  307.  
  308. @property
  309. def urls(self):
  310. # 调用get_urls方法
  311. # self.app_name和self.namespace值是一样的,都是stark
  312. return self.get_urls(), self.app_name, self.namespace
  313.  
  314. site = AdminSite() # 实例化类

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

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

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

  1. from django.conf.urls import url
  2. from django.shortcuts import HttpResponse,render,redirect
  3. from types import FunctionType
  4. from django.utils.safestring import mark_safe
  5. from django.urls import reverse
  6. from django import forms
  7.  
  8. class StarkConfig(object):
  9. def __init__(self,model_class,site):
  10. self.model_class = model_class
  11. self.site = site
  12.  
  13. def display_checkbox(self,row=None,header=False): # 显示复选框
  14. if header:
  15. # 输出中文
  16. return "选择"
  17. # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
  18. return mark_safe('<input type="checkbox" name="pk" values="%s"/>' %row.pk)
  19.  
  20. def display_edit(self, row=None, header=False):
  21. if header:
  22. return "编辑"
  23.  
  24. return mark_safe(
  25. '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))
  26.  
  27. def display_del(self, row=None, header=False):
  28. if header:
  29. return "删除"
  30.  
  31. return mark_safe(
  32. '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))
  33.  
  34. def display_edit_del(self, row=None, header=False):
  35. if header:
  36. return "操作"
  37. tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
  38. <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
  39. """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
  40. return mark_safe(tpl)
  41.  
  42. order_by = [] # 需要排序的字段,由用户自定义
  43. list_display = [] # 定义显示的列,由用户自定义
  44. model_form_class = None # form组件需要的model_class
  45. action_list = [] # 批量操作方法
  46.  
  47. def multi_delete(self): # 批量删除
  48. pass
  49.  
  50. multi_delete.text = "批量删除" # 添加自定义属性text
  51.  
  52. def multi_init(self): # 批量初始化
  53. pass
  54.  
  55. multi_init.text = "批量初始化" # 添加自定义属性text
  56.  
  57. def get_order_by(self): # 获取排序列表
  58. return self.order_by
  59.  
  60. def get_list_display(self): # 获取显示的列
  61. return self.list_display
  62.  
  63. def get_add_btn(self): # 显示添加按钮
  64. return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())
  65.  
  66. def get_model_form_class(self):
  67. """
  68. 获取ModelForm类
  69. :return:
  70. """
  71. if self.model_form_class:
  72. return self.model_form_class
  73.  
  74. class AddModelForm(forms.ModelForm):
  75. class Meta:
  76. model = self.model_class
  77. fields = "__all__"
  78.  
  79. return AddModelForm
  80.  
  81. def get_action_list(self): # 获取批量操作方法
  82. val = [] # 空列表
  83. # 扩展列表的元素
  84. val.extend(self.action_list)
  85. return val
  86.  
  87. def changelist_view(self, request):
  88. """
  89. 所有URL查看列表页面
  90. :param request:
  91. :return:
  92. """
  93. # 根据排序列表进行排序
  94. queryset = self.model_class.objects.all().order_by(*self.get_order_by())
  95. ### 批量操作 ###
  96. action_list = self.get_action_list()
  97. # 获取函数名以及text属性
  98. action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
  99. ### 添加按钮 ###
  100. add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示
  101.  
  102. list_display = self.list_display # 定义显示的列
  103. header_list = [] # 定义头部,用来显示verbose_name
  104. if list_display:
  105. for name_or_func in list_display:
  106. if isinstance(name_or_func,FunctionType):
  107. # 执行函数,默认显示中文
  108. verbose_name = name_or_func(self,header=True)
  109. else:
  110. # 获取指定字段的verbose_name
  111. verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
  112.  
  113. header_list.append(verbose_name)
  114. else:
  115. # 如果list_display为空,添加表名
  116. header_list.append(self.model_class._meta.model_name)
  117.  
  118. body_list = [] # 显示内容
  119.  
  120. for row in queryset:
  121. # 这里的row是对象,它表示表里面的一条数据
  122. row_list = [] # 展示每一行数据
  123. if not list_display: # 如果不在list_display里面
  124. # 添加对象
  125. row_list.append(row)
  126. body_list.append(row_list)
  127. continue
  128.  
  129. for name_or_func in list_display:
  130. if isinstance(name_or_func,FunctionType):
  131. val = name_or_func(self,row=row) # 执行函数获取,传递row对象
  132. else:
  133. # 使用反射获取对象的值
  134. val = getattr(row, name_or_func)
  135.  
  136. row_list.append(val)
  137.  
  138. body_list.append(row_list)
  139.  
  140. # 注意:要传入add_btn
  141. return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
  142.  
  143. def add_view(self, request):
  144. """
  145. 所有的添加页面,都在此方法处理
  146. 使用ModelForm实现
  147. :param request:
  148. :return:
  149. """
  150. # 添加数据,使用ModelForm
  151. AddModelForm = self.get_model_form_class()
  152.  
  153. if request.method == "GET":
  154. form = AddModelForm()
  155. return render(request,'stark/change.html',{'form':form})
  156.  
  157. form = AddModelForm(request.POST) # 接收POST数据
  158. if form.is_valid(): # 验证数据
  159. form.save() # 自动保存数据
  160. # 反向生成url,跳转到列表页面
  161. return redirect(self.reverse_list_url())
  162. # 渲染页面,此时会保存表单数据
  163. return render(request, 'stark/change.html', {'form': form})
  164.  
  165. def change_view(self, request, pk):
  166. """
  167. 所有编辑页面
  168. :param request:
  169. :param pk:
  170. :return:
  171. """
  172. # 查看单条数据
  173. obj = self.model_class.objects.filter(pk=pk).first()
  174. if not obj:
  175. return HttpResponse('数据不存在')
  176. # 获取model_form类
  177. ModelFormClass = self.get_model_form_class()
  178. if request.method == 'GET':
  179. # instance表示生成默认值
  180. form = ModelFormClass(instance=obj)
  181. # 渲染页面,添加和修改可以共用一个一个模板文件
  182. return render(request, 'stark/change.html', {'form': form})
  183. # instance = obj 表示指定给谁做修改
  184. form = ModelFormClass(data=request.POST, instance=obj)
  185. if form.is_valid():
  186. form.save() # 修改数据
  187. # 跳转到列表页面
  188. return redirect(self.reverse_list_url())
  189. return render(request, 'stark/change.html', {'form': form})
  190.  
  191. def delete_view(self, request, pk):
  192. """
  193. 所有删除页面
  194. :param request:
  195. :param pk:
  196. :return:
  197. """
  198. if request.method == "GET":
  199. # cancel_url表示用户点击取消时,跳转到列表页面
  200. return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
  201. # 定位单条数据,并删除!
  202. self.model_class.objects.filter(pk=pk).delete()
  203. return redirect(self.reverse_list_url())
  204.  
  205. def wrapper(self,func):
  206. pass
  207.  
  208. def get_urls(self):
  209. info = self.model_class._meta.app_label, self.model_class._meta.model_name
  210. urlpatterns = [
  211. url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
  212. url(r'^add/$', self.add_view, name='%s_%s_add' % info),
  213. url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
  214. url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
  215. ]
  216.  
  217. extra = self.extra_url()
  218. if extra: # 判断变量不为空
  219. # 扩展路由
  220. urlpatterns.extend(extra)
  221.  
  222. # print(urlpatterns)
  223. return urlpatterns
  224.  
  225. def extra_url(self): # 额外的路由,由调用者重构
  226. pass
  227.  
  228. def reverse_list_url(self): # 反向生成访问列表的url
  229. app_label = self.model_class._meta.app_label
  230. model_name = self.model_class._meta.model_name
  231. namespace = self.site.namespace
  232. name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
  233. list_url = reverse(name)
  234. return list_url
  235.  
  236. def reverse_add_url(self): # 反向生成添加url
  237. app_label = self.model_class._meta.app_label
  238. model_name = self.model_class._meta.model_name
  239. namespace = self.site.namespace
  240. name = '%s:%s_%s_add' % (namespace, app_label, model_name)
  241. add_url = reverse(name)
  242. return add_url
  243.  
  244. def reverse_edit_url(self, row): # 反向生成编辑行内容的url
  245. app_label = self.model_class._meta.app_label # app名
  246. model_name = self.model_class._meta.model_name # 表名
  247. namespace = self.site.namespace # 命名空间
  248. # 拼接字符串,这里为change
  249. name = '%s:%s_%s_change' % (namespace, app_label, model_name)
  250. # 反向生成url,传入参数pk=row.pk
  251. edit_url = reverse(name, kwargs={'pk': row.pk})
  252. return edit_url
  253.  
  254. def reverse_del_url(self, row): # 反向生成删除行内容的url
  255. app_label = self.model_class._meta.app_label
  256. model_name = self.model_class._meta.model_name
  257. namespace = self.site.namespace
  258. # 注意:这里为del
  259. name = '%s:%s_%s_del' % (namespace, app_label, model_name)
  260. del_url = reverse(name, kwargs={'pk': row.pk})
  261. return del_url
  262.  
  263. @property
  264. def urls(self):
  265. return self.get_urls()
  266.  
  267. class AdminSite(object):
  268. def __init__(self):
  269. self._registry = {}
  270. self.app_name = 'stark'
  271. self.namespace = 'stark'
  272.  
  273. def register(self,model_class,stark_config=None):
  274. # not None的结果为Ture
  275. if not stark_config:
  276. # 也就是说,当其他应用调用register时,如果不指定stark_config参数
  277. # 那么必然执行下面这段代码!
  278. # stark_config和StarkConfig是等值的!都能实例化
  279. stark_config = StarkConfig
  280.  
  281. # 添加键值对,实例化类StarkConfig,传入参数model_class
  282. # self指的是AdminSite类
  283. self._registry[model_class] = stark_config(model_class,self)
  284.  
  285. # print(self._registry) # 打印字典
  286. """
  287. {
  288. app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
  289. app02.models.Role:RoleConfig(app02.models.Role)
  290. }
  291. """
  292.  
  293. # for k, v in self._registry.items():
  294. # print(k,v)
  295.  
  296. def get_urls(self):
  297. urlpatterns = []
  298.  
  299. for k, v in self._registry.items():
  300. # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
  301. # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
  302. app_label = k._meta.app_label
  303. model_name = k._meta.model_name
  304. urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))
  305.  
  306. return urlpatterns
  307.  
  308. @property
  309. def urls(self):
  310. # 调用get_urls方法
  311. # self.app_name和self.namespace值是一样的,都是stark
  312. return self.get_urls(), self.app_name, self.namespace
  313.  
  314. site = AdminSite() # 实例化类

修改 stark-->templates-->stark-->changelist.html,value值为函数名

  1. {% extends 'stark/layout.html' %}
  2.  
  3. {% block content %}
  4. <h1>列表页面</h1>
  5. <div>
  6. {% if add_btn %}
  7. <div style="margin: 5px 0;">
  8. {{ add_btn }}
  9. </div>
  10. {% endif %}
  11. <form class="form-inline" method="post">
  12. {% csrf_token %}
  13. <div class="form-group">
  14. <select name="action" class="form-control" style="min-width: 200px;">
  15. <option>请选择功能</option>
  16. {% for item in action_list %}
  17. <option value="{{ item.name }}">{{ item.text }}</option>
  18. {% endfor %}
  19. </select>
  20. <input class="btn btn-primary" type="submit" value="执行">
  21. </div>
  22. </form>
  23. <table class="table table-bordered">
  24. <thead>
  25. <tr>
  26. {% for item in header_list %}
  27. <th>{{ item }}</th>
  28. {% endfor %}
  29.  
  30. </tr>
  31. </thead>
  32. <tbody>
  33. {% for row_list in body_list %}
  34. <tr>
  35. {% for col in row_list %}
  36. <td>{{ col }}</td>
  37. {% endfor %}
  38.  
  39. </tr>
  40. {% endfor %}
  41. </tbody>
  42. </table>
  43.  
  44. </div>
  45.  
  46. {% endblock %}

刷新页面,效果如下:

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

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

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

  1. {% extends 'stark/layout.html' %}
  2.  
  3. {% block content %}
  4. <h1>列表页面</h1>
  5. <div>
  6. {% if add_btn %}
  7. <div style="margin: 5px 0;">
  8. {{ add_btn }}
  9. </div>
  10. {% endif %}
  11. <form class="form-inline" method="post">
  12. {% csrf_token %}
  13. <div class="form-group">
  14. <select name="action" class="form-control" style="min-width: 200px;">
  15. <option>请选择功能</option>
  16. {% for item in action_list %}
  17. <option value="{{ item.name }}">{{ item.text }}</option>
  18. {% endfor %}
  19. </select>
  20. <input class="btn btn-primary" type="submit" value="执行">
  21. </div>
  22.  
  23. <table class="table table-bordered">
  24. <thead>
  25. <tr>
  26. {% for item in header_list %}
  27. <th>{{ item }}</th>
  28. {% endfor %}
  29.  
  30. </tr>
  31. </thead>
  32. <tbody>
  33. {% for row_list in body_list %}
  34. <tr>
  35. {% for col in row_list %}
  36. <td>{{ col }}</td>
  37. {% endfor %}
  38.  
  39. </tr>
  40. {% endfor %}
  41. </tbody>
  42. </table>
  43. </form>
  44. </div>
  45.  
  46. {% endblock %}

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

  1. from django.conf.urls import url
  2. from django.shortcuts import HttpResponse,render,redirect
  3. from types import FunctionType
  4. from django.utils.safestring import mark_safe
  5. from django.urls import reverse
  6. from django import forms
  7.  
  8. class StarkConfig(object):
  9. def __init__(self,model_class,site):
  10. self.model_class = model_class
  11. self.site = site
  12.  
  13. def display_checkbox(self,row=None,header=False): # 显示复选框
  14. if header:
  15. # 输出中文
  16. return "选择"
  17. # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
  18. return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)
  19.  
  20. def display_edit(self, row=None, header=False):
  21. if header:
  22. return "编辑"
  23.  
  24. return mark_safe(
  25. '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))
  26.  
  27. def display_del(self, row=None, header=False):
  28. if header:
  29. return "删除"
  30.  
  31. return mark_safe(
  32. '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))
  33.  
  34. def display_edit_del(self, row=None, header=False):
  35. if header:
  36. return "操作"
  37. tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
  38. <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
  39. """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
  40. return mark_safe(tpl)
  41.  
  42. order_by = [] # 需要排序的字段,由用户自定义
  43. list_display = [] # 定义显示的列,由用户自定义
  44. model_form_class = None # form组件需要的model_class
  45. action_list = [] # 批量操作方法
  46.  
  47. def multi_delete(self): # 批量删除
  48. pass
  49.  
  50. multi_delete.text = "批量删除" # 添加自定义属性text
  51.  
  52. def multi_init(self): # 批量初始化
  53. pass
  54.  
  55. multi_init.text = "批量初始化" # 添加自定义属性text
  56.  
  57. def get_order_by(self): # 获取排序列表
  58. return self.order_by
  59.  
  60. def get_list_display(self): # 获取显示的列
  61. return self.list_display
  62.  
  63. def get_add_btn(self): # 显示添加按钮
  64. return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())
  65.  
  66. def get_model_form_class(self):
  67. """
  68. 获取ModelForm类
  69. :return:
  70. """
  71. if self.model_form_class:
  72. return self.model_form_class
  73.  
  74. class AddModelForm(forms.ModelForm):
  75. class Meta:
  76. model = self.model_class
  77. fields = "__all__"
  78.  
  79. return AddModelForm
  80.  
  81. def get_action_list(self): # 获取批量操作方法
  82. val = [] # 空列表
  83. # 扩展列表的元素
  84. val.extend(self.action_list)
  85. return val
  86.  
  87. def changelist_view(self, request):
  88. """
  89. 所有URL查看列表页面
  90. :param request:
  91. :return:
  92. """
  93. if request.method == "POST":
  94. print(request.POST) # 获取post数据
  95.  
  96. # 根据排序列表进行排序
  97. queryset = self.model_class.objects.all().order_by(*self.get_order_by())
  98. ### 批量操作 ###
  99. action_list = self.get_action_list()
  100. # 获取函数名以及text属性
  101. action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
  102. # print(action_list)
  103. ### 添加按钮 ###
  104. add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示
  105.  
  106. list_display = self.list_display # 定义显示的列
  107. header_list = [] # 定义头部,用来显示verbose_name
  108. if list_display:
  109. for name_or_func in list_display:
  110. if isinstance(name_or_func,FunctionType):
  111. # 执行函数,默认显示中文
  112. verbose_name = name_or_func(self,header=True)
  113. else:
  114. # 获取指定字段的verbose_name
  115. verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
  116.  
  117. header_list.append(verbose_name)
  118. else:
  119. # 如果list_display为空,添加表名
  120. header_list.append(self.model_class._meta.model_name)
  121.  
  122. body_list = [] # 显示内容
  123.  
  124. for row in queryset:
  125. # 这里的row是对象,它表示表里面的一条数据
  126. row_list = [] # 展示每一行数据
  127. if not list_display: # 如果不在list_display里面
  128. # 添加对象
  129. row_list.append(row)
  130. body_list.append(row_list)
  131. continue
  132.  
  133. for name_or_func in list_display:
  134. if isinstance(name_or_func,FunctionType):
  135. val = name_or_func(self,row=row) # 执行函数获取,传递row对象
  136. else:
  137. # 使用反射获取对象的值
  138. val = getattr(row, name_or_func)
  139.  
  140. row_list.append(val)
  141.  
  142. body_list.append(row_list)
  143.  
  144. # 注意:要传入add_btn
  145. return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
  146.  
  147. def add_view(self, request):
  148. """
  149. 所有的添加页面,都在此方法处理
  150. 使用ModelForm实现
  151. :param request:
  152. :return:
  153. """
  154. # 添加数据,使用ModelForm
  155. AddModelForm = self.get_model_form_class()
  156.  
  157. if request.method == "GET":
  158. form = AddModelForm()
  159. return render(request,'stark/change.html',{'form':form})
  160.  
  161. form = AddModelForm(request.POST) # 接收POST数据
  162. if form.is_valid(): # 验证数据
  163. form.save() # 自动保存数据
  164. # 反向生成url,跳转到列表页面
  165. return redirect(self.reverse_list_url())
  166. # 渲染页面,此时会保存表单数据
  167. return render(request, 'stark/change.html', {'form': form})
  168.  
  169. def change_view(self, request, pk):
  170. """
  171. 所有编辑页面
  172. :param request:
  173. :param pk:
  174. :return:
  175. """
  176. # 查看单条数据
  177. obj = self.model_class.objects.filter(pk=pk).first()
  178. if not obj:
  179. return HttpResponse('数据不存在')
  180. # 获取model_form类
  181. ModelFormClass = self.get_model_form_class()
  182. if request.method == 'GET':
  183. # instance表示生成默认值
  184. form = ModelFormClass(instance=obj)
  185. # 渲染页面,添加和修改可以共用一个一个模板文件
  186. return render(request, 'stark/change.html', {'form': form})
  187. # instance = obj 表示指定给谁做修改
  188. form = ModelFormClass(data=request.POST, instance=obj)
  189. if form.is_valid():
  190. form.save() # 修改数据
  191. # 跳转到列表页面
  192. return redirect(self.reverse_list_url())
  193. return render(request, 'stark/change.html', {'form': form})
  194.  
  195. def delete_view(self, request, pk):
  196. """
  197. 所有删除页面
  198. :param request:
  199. :param pk:
  200. :return:
  201. """
  202. if request.method == "GET":
  203. # cancel_url表示用户点击取消时,跳转到列表页面
  204. return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
  205. # 定位单条数据,并删除!
  206. self.model_class.objects.filter(pk=pk).delete()
  207. return redirect(self.reverse_list_url())
  208.  
  209. def wrapper(self,func):
  210. pass
  211.  
  212. def get_urls(self):
  213. info = self.model_class._meta.app_label, self.model_class._meta.model_name
  214. urlpatterns = [
  215. url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
  216. url(r'^add/$', self.add_view, name='%s_%s_add' % info),
  217. url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
  218. url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
  219. ]
  220.  
  221. extra = self.extra_url()
  222. if extra: # 判断变量不为空
  223. # 扩展路由
  224. urlpatterns.extend(extra)
  225.  
  226. # print(urlpatterns)
  227. return urlpatterns
  228.  
  229. def extra_url(self): # 额外的路由,由调用者重构
  230. pass
  231.  
  232. def reverse_list_url(self): # 反向生成访问列表的url
  233. app_label = self.model_class._meta.app_label
  234. model_name = self.model_class._meta.model_name
  235. namespace = self.site.namespace
  236. name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
  237. list_url = reverse(name)
  238. return list_url
  239.  
  240. def reverse_add_url(self): # 反向生成添加url
  241. app_label = self.model_class._meta.app_label
  242. model_name = self.model_class._meta.model_name
  243. namespace = self.site.namespace
  244. name = '%s:%s_%s_add' % (namespace, app_label, model_name)
  245. add_url = reverse(name)
  246. return add_url
  247.  
  248. def reverse_edit_url(self, row): # 反向生成编辑行内容的url
  249. app_label = self.model_class._meta.app_label # app名
  250. model_name = self.model_class._meta.model_name # 表名
  251. namespace = self.site.namespace # 命名空间
  252. # 拼接字符串,这里为change
  253. name = '%s:%s_%s_change' % (namespace, app_label, model_name)
  254. # 反向生成url,传入参数pk=row.pk
  255. edit_url = reverse(name, kwargs={'pk': row.pk})
  256. return edit_url
  257.  
  258. def reverse_del_url(self, row): # 反向生成删除行内容的url
  259. app_label = self.model_class._meta.app_label
  260. model_name = self.model_class._meta.model_name
  261. namespace = self.site.namespace
  262. # 注意:这里为del
  263. name = '%s:%s_%s_del' % (namespace, app_label, model_name)
  264. del_url = reverse(name, kwargs={'pk': row.pk})
  265. return del_url
  266.  
  267. @property
  268. def urls(self):
  269. return self.get_urls()
  270.  
  271. class AdminSite(object):
  272. def __init__(self):
  273. self._registry = {}
  274. self.app_name = 'stark'
  275. self.namespace = 'stark'
  276.  
  277. def register(self,model_class,stark_config=None):
  278. # not None的结果为Ture
  279. if not stark_config:
  280. # 也就是说,当其他应用调用register时,如果不指定stark_config参数
  281. # 那么必然执行下面这段代码!
  282. # stark_config和StarkConfig是等值的!都能实例化
  283. stark_config = StarkConfig
  284.  
  285. # 添加键值对,实例化类StarkConfig,传入参数model_class
  286. # self指的是AdminSite类
  287. self._registry[model_class] = stark_config(model_class,self)
  288.  
  289. # print(self._registry) # 打印字典
  290. """
  291. {
  292. app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
  293. app02.models.Role:RoleConfig(app02.models.Role)
  294. }
  295. """
  296.  
  297. # for k, v in self._registry.items():
  298. # print(k,v)
  299.  
  300. def get_urls(self):
  301. urlpatterns = []
  302.  
  303. for k, v in self._registry.items():
  304. # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
  305. # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
  306. app_label = k._meta.app_label
  307. model_name = k._meta.model_name
  308. urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))
  309.  
  310. return urlpatterns
  311.  
  312. @property
  313. def urls(self):
  314. # 调用get_urls方法
  315. # self.app_name和self.namespace值是一样的,都是stark
  316. return self.get_urls(), self.app_name, self.namespace
  317.  
  318. site = AdminSite() # 实例化类

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

查看Pycharm控制台输出:

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

获取得到数据了!

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

这个时候,应该使用反射

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

  1. from django.conf.urls import url
  2. from django.shortcuts import HttpResponse,render,redirect
  3. from types import FunctionType
  4. from django.utils.safestring import mark_safe
  5. from django.urls import reverse
  6. from django import forms
  7.  
  8. class StarkConfig(object):
  9. def __init__(self,model_class,site):
  10. self.model_class = model_class
  11. self.site = site
  12.  
  13. def display_checkbox(self,row=None,header=False): # 显示复选框
  14. if header:
  15. # 输出中文
  16. return "选择"
  17. # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
  18. return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)
  19.  
  20. def display_edit(self, row=None, header=False):
  21. if header:
  22. return "编辑"
  23.  
  24. return mark_safe(
  25. '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))
  26.  
  27. def display_del(self, row=None, header=False):
  28. if header:
  29. return "删除"
  30.  
  31. return mark_safe(
  32. '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))
  33.  
  34. def display_edit_del(self, row=None, header=False):
  35. if header:
  36. return "操作"
  37. tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
  38. <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
  39. """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
  40. return mark_safe(tpl)
  41.  
  42. order_by = [] # 需要排序的字段,由用户自定义
  43. list_display = [] # 定义显示的列,由用户自定义
  44. model_form_class = None # form组件需要的model_class
  45. action_list = [] # 批量操作方法
  46.  
  47. def multi_delete(self,request): # 批量删除
  48. print('批量删除')
  49.  
  50. multi_delete.text = "批量删除" # 添加自定义属性text
  51.  
  52. def multi_init(self,request): # 批量初始化
  53. print('批量初始化')
  54.  
  55. multi_init.text = "批量初始化" # 添加自定义属性text
  56.  
  57. def get_order_by(self): # 获取排序列表
  58. return self.order_by
  59.  
  60. def get_list_display(self): # 获取显示的列
  61. return self.list_display
  62.  
  63. def get_add_btn(self): # 显示添加按钮
  64. return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())
  65.  
  66. def get_model_form_class(self):
  67. """
  68. 获取ModelForm类
  69. :return:
  70. """
  71. if self.model_form_class:
  72. return self.model_form_class
  73.  
  74. class AddModelForm(forms.ModelForm):
  75. class Meta:
  76. model = self.model_class
  77. fields = "__all__"
  78.  
  79. return AddModelForm
  80.  
  81. def get_action_list(self): # 获取批量操作方法
  82. val = [] # 空列表
  83. # 扩展列表的元素
  84. val.extend(self.action_list)
  85. return val
  86.  
  87. def changelist_view(self, request):
  88. """
  89. 所有URL查看列表页面
  90. :param request:
  91. :return:
  92. """
  93. if request.method == "POST":
  94. action_name = request.POST.get('action')
  95. getattr(self,action_name)(request)
  96.  
  97. # 根据排序列表进行排序
  98. queryset = self.model_class.objects.all().order_by(*self.get_order_by())
  99. ### 批量操作 ###
  100. action_list = self.get_action_list()
  101. # 获取函数名以及text属性
  102. action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
  103. # print(action_list)
  104. ### 添加按钮 ###
  105. add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示
  106.  
  107. list_display = self.list_display # 定义显示的列
  108. header_list = [] # 定义头部,用来显示verbose_name
  109. if list_display:
  110. for name_or_func in list_display:
  111. if isinstance(name_or_func,FunctionType):
  112. # 执行函数,默认显示中文
  113. verbose_name = name_or_func(self,header=True)
  114. else:
  115. # 获取指定字段的verbose_name
  116. verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
  117.  
  118. header_list.append(verbose_name)
  119. else:
  120. # 如果list_display为空,添加表名
  121. header_list.append(self.model_class._meta.model_name)
  122.  
  123. body_list = [] # 显示内容
  124.  
  125. for row in queryset:
  126. # 这里的row是对象,它表示表里面的一条数据
  127. row_list = [] # 展示每一行数据
  128. if not list_display: # 如果不在list_display里面
  129. # 添加对象
  130. row_list.append(row)
  131. body_list.append(row_list)
  132. continue
  133.  
  134. for name_or_func in list_display:
  135. if isinstance(name_or_func,FunctionType):
  136. val = name_or_func(self,row=row) # 执行函数获取,传递row对象
  137. else:
  138. # 使用反射获取对象的值
  139. val = getattr(row, name_or_func)
  140.  
  141. row_list.append(val)
  142.  
  143. body_list.append(row_list)
  144.  
  145. # 注意:要传入add_btn
  146. return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
  147.  
  148. def add_view(self, request):
  149. """
  150. 所有的添加页面,都在此方法处理
  151. 使用ModelForm实现
  152. :param request:
  153. :return:
  154. """
  155. # 添加数据,使用ModelForm
  156. AddModelForm = self.get_model_form_class()
  157.  
  158. if request.method == "GET":
  159. form = AddModelForm()
  160. return render(request,'stark/change.html',{'form':form})
  161.  
  162. form = AddModelForm(request.POST) # 接收POST数据
  163. if form.is_valid(): # 验证数据
  164. form.save() # 自动保存数据
  165. # 反向生成url,跳转到列表页面
  166. return redirect(self.reverse_list_url())
  167. # 渲染页面,此时会保存表单数据
  168. return render(request, 'stark/change.html', {'form': form})
  169.  
  170. def change_view(self, request, pk):
  171. """
  172. 所有编辑页面
  173. :param request:
  174. :param pk:
  175. :return:
  176. """
  177. # 查看单条数据
  178. obj = self.model_class.objects.filter(pk=pk).first()
  179. if not obj:
  180. return HttpResponse('数据不存在')
  181. # 获取model_form类
  182. ModelFormClass = self.get_model_form_class()
  183. if request.method == 'GET':
  184. # instance表示生成默认值
  185. form = ModelFormClass(instance=obj)
  186. # 渲染页面,添加和修改可以共用一个一个模板文件
  187. return render(request, 'stark/change.html', {'form': form})
  188. # instance = obj 表示指定给谁做修改
  189. form = ModelFormClass(data=request.POST, instance=obj)
  190. if form.is_valid():
  191. form.save() # 修改数据
  192. # 跳转到列表页面
  193. return redirect(self.reverse_list_url())
  194. return render(request, 'stark/change.html', {'form': form})
  195.  
  196. def delete_view(self, request, pk):
  197. """
  198. 所有删除页面
  199. :param request:
  200. :param pk:
  201. :return:
  202. """
  203. if request.method == "GET":
  204. # cancel_url表示用户点击取消时,跳转到列表页面
  205. return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
  206. # 定位单条数据,并删除!
  207. self.model_class.objects.filter(pk=pk).delete()
  208. return redirect(self.reverse_list_url())
  209.  
  210. def wrapper(self,func):
  211. pass
  212.  
  213. def get_urls(self):
  214. info = self.model_class._meta.app_label, self.model_class._meta.model_name
  215. urlpatterns = [
  216. url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
  217. url(r'^add/$', self.add_view, name='%s_%s_add' % info),
  218. url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
  219. url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
  220. ]
  221.  
  222. extra = self.extra_url()
  223. if extra: # 判断变量不为空
  224. # 扩展路由
  225. urlpatterns.extend(extra)
  226.  
  227. # print(urlpatterns)
  228. return urlpatterns
  229.  
  230. def extra_url(self): # 额外的路由,由调用者重构
  231. pass
  232.  
  233. def reverse_list_url(self): # 反向生成访问列表的url
  234. app_label = self.model_class._meta.app_label
  235. model_name = self.model_class._meta.model_name
  236. namespace = self.site.namespace
  237. name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
  238. list_url = reverse(name)
  239. return list_url
  240.  
  241. def reverse_add_url(self): # 反向生成添加url
  242. app_label = self.model_class._meta.app_label
  243. model_name = self.model_class._meta.model_name
  244. namespace = self.site.namespace
  245. name = '%s:%s_%s_add' % (namespace, app_label, model_name)
  246. add_url = reverse(name)
  247. return add_url
  248.  
  249. def reverse_edit_url(self, row): # 反向生成编辑行内容的url
  250. app_label = self.model_class._meta.app_label # app名
  251. model_name = self.model_class._meta.model_name # 表名
  252. namespace = self.site.namespace # 命名空间
  253. # 拼接字符串,这里为change
  254. name = '%s:%s_%s_change' % (namespace, app_label, model_name)
  255. # 反向生成url,传入参数pk=row.pk
  256. edit_url = reverse(name, kwargs={'pk': row.pk})
  257. return edit_url
  258.  
  259. def reverse_del_url(self, row): # 反向生成删除行内容的url
  260. app_label = self.model_class._meta.app_label
  261. model_name = self.model_class._meta.model_name
  262. namespace = self.site.namespace
  263. # 注意:这里为del
  264. name = '%s:%s_%s_del' % (namespace, app_label, model_name)
  265. del_url = reverse(name, kwargs={'pk': row.pk})
  266. return del_url
  267.  
  268. @property
  269. def urls(self):
  270. return self.get_urls()
  271.  
  272. class AdminSite(object):
  273. def __init__(self):
  274. self._registry = {}
  275. self.app_name = 'stark'
  276. self.namespace = 'stark'
  277.  
  278. def register(self,model_class,stark_config=None):
  279. # not None的结果为Ture
  280. if not stark_config:
  281. # 也就是说,当其他应用调用register时,如果不指定stark_config参数
  282. # 那么必然执行下面这段代码!
  283. # stark_config和StarkConfig是等值的!都能实例化
  284. stark_config = StarkConfig
  285.  
  286. # 添加键值对,实例化类StarkConfig,传入参数model_class
  287. # self指的是AdminSite类
  288. self._registry[model_class] = stark_config(model_class,self)
  289.  
  290. # print(self._registry) # 打印字典
  291. """
  292. {
  293. app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
  294. app02.models.Role:RoleConfig(app02.models.Role)
  295. }
  296. """
  297.  
  298. # for k, v in self._registry.items():
  299. # print(k,v)
  300.  
  301. def get_urls(self):
  302. urlpatterns = []
  303.  
  304. for k, v in self._registry.items():
  305. # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
  306. # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
  307. app_label = k._meta.app_label
  308. model_name = k._meta.model_name
  309. urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))
  310.  
  311. return urlpatterns
  312.  
  313. @property
  314. def urls(self):
  315. # 调用get_urls方法
  316. # self.app_name和self.namespace值是一样的,都是stark
  317. return self.get_urls(), self.app_name, self.namespace
  318.  
  319. site = AdminSite() # 实例化类

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

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

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

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

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

  1. from django.conf.urls import url
  2. from django.shortcuts import HttpResponse,render,redirect
  3. from types import FunctionType
  4. from django.utils.safestring import mark_safe
  5. from django.urls import reverse
  6. from django import forms
  7.  
  8. class StarkConfig(object):
  9. def __init__(self,model_class,site):
  10. self.model_class = model_class
  11. self.site = site
  12.  
  13. def display_checkbox(self,row=None,header=False): # 显示复选框
  14. if header:
  15. # 输出中文
  16. return "选择"
  17. # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
  18. return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)
  19.  
  20. def display_edit(self, row=None, header=False):
  21. if header:
  22. return "编辑"
  23.  
  24. return mark_safe(
  25. '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))
  26.  
  27. def display_del(self, row=None, header=False):
  28. if header:
  29. return "删除"
  30.  
  31. return mark_safe(
  32. '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))
  33.  
  34. def display_edit_del(self, row=None, header=False):
  35. if header:
  36. return "操作"
  37. tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
  38. <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
  39. """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
  40. return mark_safe(tpl)
  41.  
  42. order_by = [] # 需要排序的字段,由用户自定义
  43. list_display = [] # 定义显示的列,由用户自定义
  44. model_form_class = None # form组件需要的model_class
  45. action_list = [] # 批量操作方法
  46.  
  47. def multi_delete(self,request): # 批量删除
  48. print('批量删除')
  49. return HttpResponse('批量删除')
  50.  
  51. multi_delete.text = "批量删除" # 添加自定义属性text
  52.  
  53. def multi_init(self,request): # 批量初始化
  54. print('批量初始化')
  55.  
  56. multi_init.text = "批量初始化" # 添加自定义属性text
  57.  
  58. def get_order_by(self): # 获取排序列表
  59. return self.order_by
  60.  
  61. def get_list_display(self): # 获取显示的列
  62. return self.list_display
  63.  
  64. def get_add_btn(self): # 显示添加按钮
  65. return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())
  66.  
  67. def get_model_form_class(self):
  68. """
  69. 获取ModelForm类
  70. :return:
  71. """
  72. if self.model_form_class:
  73. return self.model_form_class
  74.  
  75. class AddModelForm(forms.ModelForm):
  76. class Meta:
  77. model = self.model_class
  78. fields = "__all__"
  79.  
  80. return AddModelForm
  81.  
  82. def get_action_list(self): # 获取批量操作方法
  83. val = [] # 空列表
  84. # 扩展列表的元素
  85. val.extend(self.action_list)
  86. return val
  87.  
  88. def get_action_dict(self): # 获取匹配操作字典
  89. val = {}
  90. for item in self.action_list:
  91. # 以方法名为key
  92. val[item.__name__] = item
  93. return val
  94.  
  95. def changelist_view(self, request):
  96. """
  97. 所有URL查看列表页面
  98. :param request:
  99. :return:
  100. """
  101. if request.method == 'POST':
  102. action_name = request.POST.get('action')
  103. action_dict = self.get_action_dict()
  104. if action_name not in action_dict:
  105. return HttpResponse('非法请求')
  106.  
  107. response = getattr(self, action_name)(request)
  108. if response:
  109. return response
  110.  
  111. # 根据排序列表进行排序
  112. queryset = self.model_class.objects.all().order_by(*self.get_order_by())
  113. ### 批量操作 ###
  114. action_list = self.get_action_list()
  115. # 获取函数名以及text属性
  116. action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
  117. # print(action_list)
  118. ### 添加按钮 ###
  119. add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示
  120.  
  121. list_display = self.list_display # 定义显示的列
  122. header_list = [] # 定义头部,用来显示verbose_name
  123. if list_display:
  124. for name_or_func in list_display:
  125. if isinstance(name_or_func,FunctionType):
  126. # 执行函数,默认显示中文
  127. verbose_name = name_or_func(self,header=True)
  128. else:
  129. # 获取指定字段的verbose_name
  130. verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
  131.  
  132. header_list.append(verbose_name)
  133. else:
  134. # 如果list_display为空,添加表名
  135. header_list.append(self.model_class._meta.model_name)
  136.  
  137. body_list = [] # 显示内容
  138.  
  139. for row in queryset:
  140. # 这里的row是对象,它表示表里面的一条数据
  141. row_list = [] # 展示每一行数据
  142. if not list_display: # 如果不在list_display里面
  143. # 添加对象
  144. row_list.append(row)
  145. body_list.append(row_list)
  146. continue
  147.  
  148. for name_or_func in list_display:
  149. if isinstance(name_or_func,FunctionType):
  150. val = name_or_func(self,row=row) # 执行函数获取,传递row对象
  151. else:
  152. # 使用反射获取对象的值
  153. val = getattr(row, name_or_func)
  154.  
  155. row_list.append(val)
  156.  
  157. body_list.append(row_list)
  158.  
  159. # 注意:要传入add_btn
  160. return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
  161.  
  162. def add_view(self, request):
  163. """
  164. 所有的添加页面,都在此方法处理
  165. 使用ModelForm实现
  166. :param request:
  167. :return:
  168. """
  169. # 添加数据,使用ModelForm
  170. AddModelForm = self.get_model_form_class()
  171.  
  172. if request.method == "GET":
  173. form = AddModelForm()
  174. return render(request,'stark/change.html',{'form':form})
  175.  
  176. form = AddModelForm(request.POST) # 接收POST数据
  177. if form.is_valid(): # 验证数据
  178. form.save() # 自动保存数据
  179. # 反向生成url,跳转到列表页面
  180. return redirect(self.reverse_list_url())
  181. # 渲染页面,此时会保存表单数据
  182. return render(request, 'stark/change.html', {'form': form})
  183.  
  184. def change_view(self, request, pk):
  185. """
  186. 所有编辑页面
  187. :param request:
  188. :param pk:
  189. :return:
  190. """
  191. # 查看单条数据
  192. obj = self.model_class.objects.filter(pk=pk).first()
  193. if not obj:
  194. return HttpResponse('数据不存在')
  195. # 获取model_form类
  196. ModelFormClass = self.get_model_form_class()
  197. if request.method == 'GET':
  198. # instance表示生成默认值
  199. form = ModelFormClass(instance=obj)
  200. # 渲染页面,添加和修改可以共用一个一个模板文件
  201. return render(request, 'stark/change.html', {'form': form})
  202. # instance = obj 表示指定给谁做修改
  203. form = ModelFormClass(data=request.POST, instance=obj)
  204. if form.is_valid():
  205. form.save() # 修改数据
  206. # 跳转到列表页面
  207. return redirect(self.reverse_list_url())
  208. return render(request, 'stark/change.html', {'form': form})
  209.  
  210. def delete_view(self, request, pk):
  211. """
  212. 所有删除页面
  213. :param request:
  214. :param pk:
  215. :return:
  216. """
  217. if request.method == "GET":
  218. # cancel_url表示用户点击取消时,跳转到列表页面
  219. return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
  220. # 定位单条数据,并删除!
  221. self.model_class.objects.filter(pk=pk).delete()
  222. return redirect(self.reverse_list_url())
  223.  
  224. def wrapper(self,func):
  225. pass
  226.  
  227. def get_urls(self):
  228. info = self.model_class._meta.app_label, self.model_class._meta.model_name
  229. urlpatterns = [
  230. url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
  231. url(r'^add/$', self.add_view, name='%s_%s_add' % info),
  232. url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
  233. url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
  234. ]
  235.  
  236. extra = self.extra_url()
  237. if extra: # 判断变量不为空
  238. # 扩展路由
  239. urlpatterns.extend(extra)
  240.  
  241. # print(urlpatterns)
  242. return urlpatterns
  243.  
  244. def extra_url(self): # 额外的路由,由调用者重构
  245. pass
  246.  
  247. def reverse_list_url(self): # 反向生成访问列表的url
  248. app_label = self.model_class._meta.app_label
  249. model_name = self.model_class._meta.model_name
  250. namespace = self.site.namespace
  251. name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
  252. list_url = reverse(name)
  253. return list_url
  254.  
  255. def reverse_add_url(self): # 反向生成添加url
  256. app_label = self.model_class._meta.app_label
  257. model_name = self.model_class._meta.model_name
  258. namespace = self.site.namespace
  259. name = '%s:%s_%s_add' % (namespace, app_label, model_name)
  260. add_url = reverse(name)
  261. return add_url
  262.  
  263. def reverse_edit_url(self, row): # 反向生成编辑行内容的url
  264. app_label = self.model_class._meta.app_label # app名
  265. model_name = self.model_class._meta.model_name # 表名
  266. namespace = self.site.namespace # 命名空间
  267. # 拼接字符串,这里为change
  268. name = '%s:%s_%s_change' % (namespace, app_label, model_name)
  269. # 反向生成url,传入参数pk=row.pk
  270. edit_url = reverse(name, kwargs={'pk': row.pk})
  271. return edit_url
  272.  
  273. def reverse_del_url(self, row): # 反向生成删除行内容的url
  274. app_label = self.model_class._meta.app_label
  275. model_name = self.model_class._meta.model_name
  276. namespace = self.site.namespace
  277. # 注意:这里为del
  278. name = '%s:%s_%s_del' % (namespace, app_label, model_name)
  279. del_url = reverse(name, kwargs={'pk': row.pk})
  280. return del_url
  281.  
  282. @property
  283. def urls(self):
  284. return self.get_urls()
  285.  
  286. class AdminSite(object):
  287. def __init__(self):
  288. self._registry = {}
  289. self.app_name = 'stark'
  290. self.namespace = 'stark'
  291.  
  292. def register(self,model_class,stark_config=None):
  293. # not None的结果为Ture
  294. if not stark_config:
  295. # 也就是说,当其他应用调用register时,如果不指定stark_config参数
  296. # 那么必然执行下面这段代码!
  297. # stark_config和StarkConfig是等值的!都能实例化
  298. stark_config = StarkConfig
  299.  
  300. # 添加键值对,实例化类StarkConfig,传入参数model_class
  301. # self指的是AdminSite类
  302. self._registry[model_class] = stark_config(model_class,self)
  303.  
  304. # print(self._registry) # 打印字典
  305. """
  306. {
  307. app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
  308. app02.models.Role:RoleConfig(app02.models.Role)
  309. }
  310. """
  311.  
  312. # for k, v in self._registry.items():
  313. # print(k,v)
  314.  
  315. def get_urls(self):
  316. urlpatterns = []
  317.  
  318. for k, v in self._registry.items():
  319. # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
  320. # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
  321. app_label = k._meta.app_label
  322. model_name = k._meta.model_name
  323. urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))
  324.  
  325. return urlpatterns
  326.  
  327. @property
  328. def urls(self):
  329. # 调用get_urls方法
  330. # self.app_name和self.namespace值是一样的,都是stark
  331. return self.get_urls(), self.app_name, self.namespace
  332.  
  333. site = AdminSite() # 实例化类

刷新页面,点击匹配初始化,还是会跳转到当前页面。因为此方法,没有返回值。

点击批量删除,页面效果如下:

修改stark-->server-->stark.py,修改multi_delete方法

  1. from django.conf.urls import url
  2. from django.shortcuts import HttpResponse,render,redirect
  3. from types import FunctionType
  4. from django.utils.safestring import mark_safe
  5. from django.urls import reverse
  6. from django import forms
  7.  
  8. class StarkConfig(object):
  9. def __init__(self,model_class,site):
  10. self.model_class = model_class
  11. self.site = site
  12.  
  13. def display_checkbox(self,row=None,header=False): # 显示复选框
  14. if header:
  15. # 输出中文
  16. return "选择"
  17. # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
  18. return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)
  19.  
  20. def display_edit(self, row=None, header=False):
  21. if header:
  22. return "编辑"
  23.  
  24. return mark_safe(
  25. '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))
  26.  
  27. def display_del(self, row=None, header=False):
  28. if header:
  29. return "删除"
  30.  
  31. return mark_safe(
  32. '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))
  33.  
  34. def display_edit_del(self, row=None, header=False):
  35. if header:
  36. return "操作"
  37. tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
  38. <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
  39. """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
  40. return mark_safe(tpl)
  41.  
  42. order_by = [] # 需要排序的字段,由用户自定义
  43. list_display = [] # 定义显示的列,由用户自定义
  44. model_form_class = None # form组件需要的model_class
  45. action_list = [] # 批量操作方法
  46.  
  47. def multi_delete(self, request): # 批量删除
  48. """
  49. 批量删除的action
  50. :param request:
  51. :return:
  52. """
  53. pk_list = request.POST.getlist('pk')
  54. self.model_class.objects.filter(pk__in=pk_list).delete()
  55. # return HttpResponse('删除成功')
  56.  
  57. multi_delete.text = "批量删除" # 添加自定义属性text
  58.  
  59. def multi_init(self,request): # 批量初始化
  60. print('批量初始化')
  61.  
  62. multi_init.text = "批量初始化" # 添加自定义属性text
  63.  
  64. def get_order_by(self): # 获取排序列表
  65. return self.order_by
  66.  
  67. def get_list_display(self): # 获取显示的列
  68. return self.list_display
  69.  
  70. def get_add_btn(self): # 显示添加按钮
  71. return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())
  72.  
  73. def get_model_form_class(self):
  74. """
  75. 获取ModelForm类
  76. :return:
  77. """
  78. if self.model_form_class:
  79. return self.model_form_class
  80.  
  81. class AddModelForm(forms.ModelForm):
  82. class Meta:
  83. model = self.model_class
  84. fields = "__all__"
  85.  
  86. return AddModelForm
  87.  
  88. def get_action_list(self): # 获取批量操作方法
  89. val = [] # 空列表
  90. # 扩展列表的元素
  91. val.extend(self.action_list)
  92. return val
  93.  
  94. def get_action_dict(self): # 获取匹配操作字典
  95. val = {}
  96. for item in self.action_list:
  97. # 以方法名为key
  98. val[item.__name__] = item
  99. return val
  100.  
  101. def changelist_view(self, request):
  102. """
  103. 所有URL查看列表页面
  104. :param request:
  105. :return:
  106. """
  107. if request.method == 'POST':
  108. action_name = request.POST.get('action')
  109. action_dict = self.get_action_dict()
  110. if action_name not in action_dict:
  111. return HttpResponse('非法请求')
  112.  
  113. response = getattr(self, action_name)(request)
  114. if response:
  115. return response
  116.  
  117. # 根据排序列表进行排序
  118. queryset = self.model_class.objects.all().order_by(*self.get_order_by())
  119. ### 批量操作 ###
  120. action_list = self.get_action_list()
  121. # 获取函数名以及text属性
  122. action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
  123. # print(action_list)
  124. ### 添加按钮 ###
  125. add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示
  126.  
  127. list_display = self.list_display # 定义显示的列
  128. header_list = [] # 定义头部,用来显示verbose_name
  129. if list_display:
  130. for name_or_func in list_display:
  131. if isinstance(name_or_func,FunctionType):
  132. # 执行函数,默认显示中文
  133. verbose_name = name_or_func(self,header=True)
  134. else:
  135. # 获取指定字段的verbose_name
  136. verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
  137.  
  138. header_list.append(verbose_name)
  139. else:
  140. # 如果list_display为空,添加表名
  141. header_list.append(self.model_class._meta.model_name)
  142.  
  143. body_list = [] # 显示内容
  144.  
  145. for row in queryset:
  146. # 这里的row是对象,它表示表里面的一条数据
  147. row_list = [] # 展示每一行数据
  148. if not list_display: # 如果不在list_display里面
  149. # 添加对象
  150. row_list.append(row)
  151. body_list.append(row_list)
  152. continue
  153.  
  154. for name_or_func in list_display:
  155. if isinstance(name_or_func,FunctionType):
  156. val = name_or_func(self,row=row) # 执行函数获取,传递row对象
  157. else:
  158. # 使用反射获取对象的值
  159. val = getattr(row, name_or_func)
  160.  
  161. row_list.append(val)
  162.  
  163. body_list.append(row_list)
  164.  
  165. # 注意:要传入add_btn
  166. return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
  167.  
  168. def add_view(self, request):
  169. """
  170. 所有的添加页面,都在此方法处理
  171. 使用ModelForm实现
  172. :param request:
  173. :return:
  174. """
  175. # 添加数据,使用ModelForm
  176. AddModelForm = self.get_model_form_class()
  177.  
  178. if request.method == "GET":
  179. form = AddModelForm()
  180. return render(request,'stark/change.html',{'form':form})
  181.  
  182. form = AddModelForm(request.POST) # 接收POST数据
  183. if form.is_valid(): # 验证数据
  184. form.save() # 自动保存数据
  185. # 反向生成url,跳转到列表页面
  186. return redirect(self.reverse_list_url())
  187. # 渲染页面,此时会保存表单数据
  188. return render(request, 'stark/change.html', {'form': form})
  189.  
  190. def change_view(self, request, pk):
  191. """
  192. 所有编辑页面
  193. :param request:
  194. :param pk:
  195. :return:
  196. """
  197. # 查看单条数据
  198. obj = self.model_class.objects.filter(pk=pk).first()
  199. if not obj:
  200. return HttpResponse('数据不存在')
  201. # 获取model_form类
  202. ModelFormClass = self.get_model_form_class()
  203. if request.method == 'GET':
  204. # instance表示生成默认值
  205. form = ModelFormClass(instance=obj)
  206. # 渲染页面,添加和修改可以共用一个一个模板文件
  207. return render(request, 'stark/change.html', {'form': form})
  208. # instance = obj 表示指定给谁做修改
  209. form = ModelFormClass(data=request.POST, instance=obj)
  210. if form.is_valid():
  211. form.save() # 修改数据
  212. # 跳转到列表页面
  213. return redirect(self.reverse_list_url())
  214. return render(request, 'stark/change.html', {'form': form})
  215.  
  216. def delete_view(self, request, pk):
  217. """
  218. 所有删除页面
  219. :param request:
  220. :param pk:
  221. :return:
  222. """
  223. if request.method == "GET":
  224. # cancel_url表示用户点击取消时,跳转到列表页面
  225. return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
  226. # 定位单条数据,并删除!
  227. self.model_class.objects.filter(pk=pk).delete()
  228. return redirect(self.reverse_list_url())
  229.  
  230. def wrapper(self,func):
  231. pass
  232.  
  233. def get_urls(self):
  234. info = self.model_class._meta.app_label, self.model_class._meta.model_name
  235. urlpatterns = [
  236. url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
  237. url(r'^add/$', self.add_view, name='%s_%s_add' % info),
  238. url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
  239. url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
  240. ]
  241.  
  242. extra = self.extra_url()
  243. if extra: # 判断变量不为空
  244. # 扩展路由
  245. urlpatterns.extend(extra)
  246.  
  247. # print(urlpatterns)
  248. return urlpatterns
  249.  
  250. def extra_url(self): # 额外的路由,由调用者重构
  251. pass
  252.  
  253. def reverse_list_url(self): # 反向生成访问列表的url
  254. app_label = self.model_class._meta.app_label
  255. model_name = self.model_class._meta.model_name
  256. namespace = self.site.namespace
  257. name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
  258. list_url = reverse(name)
  259. return list_url
  260.  
  261. def reverse_add_url(self): # 反向生成添加url
  262. app_label = self.model_class._meta.app_label
  263. model_name = self.model_class._meta.model_name
  264. namespace = self.site.namespace
  265. name = '%s:%s_%s_add' % (namespace, app_label, model_name)
  266. add_url = reverse(name)
  267. return add_url
  268.  
  269. def reverse_edit_url(self, row): # 反向生成编辑行内容的url
  270. app_label = self.model_class._meta.app_label # app名
  271. model_name = self.model_class._meta.model_name # 表名
  272. namespace = self.site.namespace # 命名空间
  273. # 拼接字符串,这里为change
  274. name = '%s:%s_%s_change' % (namespace, app_label, model_name)
  275. # 反向生成url,传入参数pk=row.pk
  276. edit_url = reverse(name, kwargs={'pk': row.pk})
  277. return edit_url
  278.  
  279. def reverse_del_url(self, row): # 反向生成删除行内容的url
  280. app_label = self.model_class._meta.app_label
  281. model_name = self.model_class._meta.model_name
  282. namespace = self.site.namespace
  283. # 注意:这里为del
  284. name = '%s:%s_%s_del' % (namespace, app_label, model_name)
  285. del_url = reverse(name, kwargs={'pk': row.pk})
  286. return del_url
  287.  
  288. @property
  289. def urls(self):
  290. return self.get_urls()
  291.  
  292. class AdminSite(object):
  293. def __init__(self):
  294. self._registry = {}
  295. self.app_name = 'stark'
  296. self.namespace = 'stark'
  297.  
  298. def register(self,model_class,stark_config=None):
  299. # not None的结果为Ture
  300. if not stark_config:
  301. # 也就是说,当其他应用调用register时,如果不指定stark_config参数
  302. # 那么必然执行下面这段代码!
  303. # stark_config和StarkConfig是等值的!都能实例化
  304. stark_config = StarkConfig
  305.  
  306. # 添加键值对,实例化类StarkConfig,传入参数model_class
  307. # self指的是AdminSite类
  308. self._registry[model_class] = stark_config(model_class,self)
  309.  
  310. # print(self._registry) # 打印字典
  311. """
  312. {
  313. app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
  314. app02.models.Role:RoleConfig(app02.models.Role)
  315. }
  316. """
  317.  
  318. # for k, v in self._registry.items():
  319. # print(k,v)
  320.  
  321. def get_urls(self):
  322. urlpatterns = []
  323.  
  324. for k, v in self._registry.items():
  325. # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
  326. # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
  327. app_label = k._meta.app_label
  328. model_name = k._meta.model_name
  329. urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))
  330.  
  331. return urlpatterns
  332.  
  333. @property
  334. def urls(self):
  335. # 调用get_urls方法
  336. # self.app_name和self.namespace值是一样的,都是stark
  337. return self.get_urls(), self.app_name, self.namespace
  338.  
  339. site = AdminSite() # 实例化类

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

发现少了一条数据

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

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

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

  1. {% extends 'stark/layout.html' %}
  2.  
  3. {% block content %}
  4. <h1>列表页面</h1>
  5. <div>
  6. {% if add_btn %}
  7. <div style="margin: 5px 0;">
  8. {{ add_btn }}
  9. </div>
  10. {% endif %}
  11. <form class="form-inline" method="post">
  12. {% csrf_token %}
  13. {% if action_list %}
  14. <div class="form-group">
  15. <select name="action" class="form-control" style="min-width: 200px;">
  16. <option>请选择功能</option>
  17. {% for item in action_list %}
  18. <option value="{{ item.name }}">{{ item.text }}</option>
  19. {% endfor %}
  20. </select>
  21. <input class="btn btn-primary" type="submit" value="执行">
  22. </div>
  23. {% endif %}
  24.  
  25. <table class="table table-bordered">
  26. <thead>
  27. <tr>
  28. {% for item in header_list %}
  29. <th>{{ item }}</th>
  30. {% endfor %}
  31.  
  32. </tr>
  33. </thead>
  34. <tbody>
  35. {% for row_list in body_list %}
  36. <tr>
  37. {% for col in row_list %}
  38. <td>{{ col }}</td>
  39. {% endfor %}
  40.  
  41. </tr>
  42. {% endfor %}
  43. </tbody>
  44. </table>
  45. </form>
  46. </div>
  47.  
  48. {% endblock %}

刷新页面,效果如下:

多选框,没有了!

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

二、快速搜索

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

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

增加方法get_search_list方法

  1. from django.conf.urls import url
  2. from django.shortcuts import HttpResponse,render,redirect
  3. from types import FunctionType
  4. from django.utils.safestring import mark_safe
  5. from django.urls import reverse
  6. from django import forms
  7.  
  8. class StarkConfig(object):
  9. def __init__(self,model_class,site):
  10. self.model_class = model_class
  11. self.site = site
  12.  
  13. def display_checkbox(self,row=None,header=False): # 显示复选框
  14. if header:
  15. # 输出中文
  16. return "选择"
  17. # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
  18. return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)
  19.  
  20. def display_edit(self, row=None, header=False):
  21. if header:
  22. return "编辑"
  23.  
  24. return mark_safe(
  25. '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))
  26.  
  27. def display_del(self, row=None, header=False):
  28. if header:
  29. return "删除"
  30.  
  31. return mark_safe(
  32. '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))
  33.  
  34. def display_edit_del(self, row=None, header=False):
  35. if header:
  36. return "操作"
  37. tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
  38. <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
  39. """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
  40. return mark_safe(tpl)
  41.  
  42. def multi_delete(self, request): # 批量删除
  43. """
  44. 批量删除的action
  45. :param request:
  46. :return:
  47. """
  48. pk_list = request.POST.getlist('pk')
  49. self.model_class.objects.filter(pk__in=pk_list).delete()
  50. # return HttpResponse('删除成功')
  51.  
  52. multi_delete.text = "批量删除" # 添加自定义属性text
  53.  
  54. def multi_init(self,request): # 批量初始化
  55. print('批量初始化')
  56.  
  57. multi_init.text = "批量初始化" # 添加自定义属性text
  58.  
  59. order_by = [] # 需要排序的字段,由用户自定义
  60. list_display = [] # 定义显示的列,由用户自定义
  61. model_form_class = None # form组件需要的model_class
  62. action_list = [] # 批量操作方法
  63. search_list = ['name','tel'] # 固定搜索字段
  64.  
  65. def get_order_by(self): # 获取排序列表
  66. return self.order_by
  67.  
  68. def get_list_display(self): # 获取显示的列
  69. return self.list_display
  70.  
  71. def get_add_btn(self): # 显示添加按钮
  72. return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())
  73.  
  74. def get_model_form_class(self):
  75. """
  76. 获取ModelForm类
  77. :return:
  78. """
  79. if self.model_form_class:
  80. return self.model_form_class
  81.  
  82. class AddModelForm(forms.ModelForm):
  83. class Meta:
  84. model = self.model_class
  85. fields = "__all__"
  86.  
  87. return AddModelForm
  88.  
  89. def get_action_list(self): # 获取批量操作方法
  90. val = [] # 空列表
  91. # 扩展列表的元素
  92. val.extend(self.action_list)
  93. return val
  94.  
  95. def get_action_dict(self): # 获取匹配操作字典
  96. val = {}
  97. for item in self.action_list:
  98. # 以方法名为key
  99. val[item.__name__] = item
  100. return val
  101.  
  102. def get_search_list(self): # 获取搜索字段
  103. val = []
  104. val.extend(self.search_list)
  105. return val
  106.  
  107. def changelist_view(self, request):
  108. """
  109. 所有URL查看列表页面
  110. :param request:
  111. :return:
  112. """
  113. if request.method == 'POST':
  114. action_name = request.POST.get('action')
  115. action_dict = self.get_action_dict()
  116. if action_name not in action_dict:
  117. return HttpResponse('非法请求')
  118.  
  119. response = getattr(self, action_name)(request)
  120. if response:
  121. return response
  122.  
  123. ### 处理搜索 ###
  124. from django.db.models import Q
  125. search_list = self.get_search_list() # ['name','tel']
  126. q = request.GET.get('q', "") # 搜索条件
  127. con = Q()
  128. con.connector = "OR" # 以OR作为连接符
  129. if q: # 判断条件不为空
  130. for field in search_list:
  131. # 合并条件进行查询, __contains表示使用like查询
  132. con.children.append(('%s__contains' % field, q))
  133.  
  134. # 根据排序列表进行排序
  135. queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
  136. ### 批量操作 ###
  137. action_list = self.get_action_list()
  138. # 获取函数名以及text属性
  139. action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
  140. # print(action_list)
  141. ### 添加按钮 ###
  142. add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示
  143.  
  144. list_display = self.list_display # 定义显示的列
  145. header_list = [] # 定义头部,用来显示verbose_name
  146. if list_display:
  147. for name_or_func in list_display:
  148. if isinstance(name_or_func,FunctionType):
  149. # 执行函数,默认显示中文
  150. verbose_name = name_or_func(self,header=True)
  151. else:
  152. # 获取指定字段的verbose_name
  153. verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
  154.  
  155. header_list.append(verbose_name)
  156. else:
  157. # 如果list_display为空,添加表名
  158. header_list.append(self.model_class._meta.model_name)
  159.  
  160. body_list = [] # 显示内容
  161.  
  162. for row in queryset:
  163. # 这里的row是对象,它表示表里面的一条数据
  164. row_list = [] # 展示每一行数据
  165. if not list_display: # 如果不在list_display里面
  166. # 添加对象
  167. row_list.append(row)
  168. body_list.append(row_list)
  169. continue
  170.  
  171. for name_or_func in list_display:
  172. if isinstance(name_or_func,FunctionType):
  173. val = name_or_func(self,row=row) # 执行函数获取,传递row对象
  174. else:
  175. # 使用反射获取对象的值
  176. val = getattr(row, name_or_func)
  177.  
  178. row_list.append(val)
  179.  
  180. body_list.append(row_list)
  181.  
  182. # 注意:要传入add_btn
  183. return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})
  184.  
  185. def add_view(self, request):
  186. """
  187. 所有的添加页面,都在此方法处理
  188. 使用ModelForm实现
  189. :param request:
  190. :return:
  191. """
  192. # 添加数据,使用ModelForm
  193. AddModelForm = self.get_model_form_class()
  194.  
  195. if request.method == "GET":
  196. form = AddModelForm()
  197. return render(request,'stark/change.html',{'form':form})
  198.  
  199. form = AddModelForm(request.POST) # 接收POST数据
  200. if form.is_valid(): # 验证数据
  201. form.save() # 自动保存数据
  202. # 反向生成url,跳转到列表页面
  203. return redirect(self.reverse_list_url())
  204. # 渲染页面,此时会保存表单数据
  205. return render(request, 'stark/change.html', {'form': form})
  206.  
  207. def change_view(self, request, pk):
  208. """
  209. 所有编辑页面
  210. :param request:
  211. :param pk:
  212. :return:
  213. """
  214. # 查看单条数据
  215. obj = self.model_class.objects.filter(pk=pk).first()
  216. if not obj:
  217. return HttpResponse('数据不存在')
  218. # 获取model_form类
  219. ModelFormClass = self.get_model_form_class()
  220. if request.method == 'GET':
  221. # instance表示生成默认值
  222. form = ModelFormClass(instance=obj)
  223. # 渲染页面,添加和修改可以共用一个一个模板文件
  224. return render(request, 'stark/change.html', {'form': form})
  225. # instance = obj 表示指定给谁做修改
  226. form = ModelFormClass(data=request.POST, instance=obj)
  227. if form.is_valid():
  228. form.save() # 修改数据
  229. # 跳转到列表页面
  230. return redirect(self.reverse_list_url())
  231. return render(request, 'stark/change.html', {'form': form})
  232.  
  233. def delete_view(self, request, pk):
  234. """
  235. 所有删除页面
  236. :param request:
  237. :param pk:
  238. :return:
  239. """
  240. if request.method == "GET":
  241. # cancel_url表示用户点击取消时,跳转到列表页面
  242. return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
  243. # 定位单条数据,并删除!
  244. self.model_class.objects.filter(pk=pk).delete()
  245. return redirect(self.reverse_list_url())
  246.  
  247. def wrapper(self,func):
  248. pass
  249.  
  250. def get_urls(self):
  251. info = self.model_class._meta.app_label, self.model_class._meta.model_name
  252. urlpatterns = [
  253. url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
  254. url(r'^add/$', self.add_view, name='%s_%s_add' % info),
  255. url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
  256. url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
  257. ]
  258.  
  259. extra = self.extra_url()
  260. if extra: # 判断变量不为空
  261. # 扩展路由
  262. urlpatterns.extend(extra)
  263.  
  264. # print(urlpatterns)
  265. return urlpatterns
  266.  
  267. def extra_url(self): # 额外的路由,由调用者重构
  268. pass
  269.  
  270. def reverse_list_url(self): # 反向生成访问列表的url
  271. app_label = self.model_class._meta.app_label
  272. model_name = self.model_class._meta.model_name
  273. namespace = self.site.namespace
  274. name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
  275. list_url = reverse(name)
  276. return list_url
  277.  
  278. def reverse_add_url(self): # 反向生成添加url
  279. app_label = self.model_class._meta.app_label
  280. model_name = self.model_class._meta.model_name
  281. namespace = self.site.namespace
  282. name = '%s:%s_%s_add' % (namespace, app_label, model_name)
  283. add_url = reverse(name)
  284. return add_url
  285.  
  286. def reverse_edit_url(self, row): # 反向生成编辑行内容的url
  287. app_label = self.model_class._meta.app_label # app名
  288. model_name = self.model_class._meta.model_name # 表名
  289. namespace = self.site.namespace # 命名空间
  290. # 拼接字符串,这里为change
  291. name = '%s:%s_%s_change' % (namespace, app_label, model_name)
  292. # 反向生成url,传入参数pk=row.pk
  293. edit_url = reverse(name, kwargs={'pk': row.pk})
  294. return edit_url
  295.  
  296. def reverse_del_url(self, row): # 反向生成删除行内容的url
  297. app_label = self.model_class._meta.app_label
  298. model_name = self.model_class._meta.model_name
  299. namespace = self.site.namespace
  300. # 注意:这里为del
  301. name = '%s:%s_%s_del' % (namespace, app_label, model_name)
  302. del_url = reverse(name, kwargs={'pk': row.pk})
  303. return del_url
  304.  
  305. @property
  306. def urls(self):
  307. return self.get_urls()
  308.  
  309. class AdminSite(object):
  310. def __init__(self):
  311. self._registry = {}
  312. self.app_name = 'stark'
  313. self.namespace = 'stark'
  314.  
  315. def register(self,model_class,stark_config=None):
  316. # not None的结果为Ture
  317. if not stark_config:
  318. # 也就是说,当其他应用调用register时,如果不指定stark_config参数
  319. # 那么必然执行下面这段代码!
  320. # stark_config和StarkConfig是等值的!都能实例化
  321. stark_config = StarkConfig
  322.  
  323. # 添加键值对,实例化类StarkConfig,传入参数model_class
  324. # self指的是AdminSite类
  325. self._registry[model_class] = stark_config(model_class,self)
  326.  
  327. # print(self._registry) # 打印字典
  328. """
  329. {
  330. app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
  331. app02.models.Role:RoleConfig(app02.models.Role)
  332. }
  333. """
  334.  
  335. # for k, v in self._registry.items():
  336. # print(k,v)
  337.  
  338. def get_urls(self):
  339. urlpatterns = []
  340.  
  341. for k, v in self._registry.items():
  342. # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
  343. # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
  344. app_label = k._meta.app_label
  345. model_name = k._meta.model_name
  346. urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))
  347.  
  348. return urlpatterns
  349.  
  350. @property
  351. def urls(self):
  352. # 调用get_urls方法
  353. # self.app_name和self.namespace值是一样的,都是stark
  354. return self.get_urls(), self.app_name, self.namespace
  355.  
  356. site = AdminSite() # 实例化类

访问url,要带上q参数

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

效果如下:

去掉参数,有2条数据

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

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

  1. from django.conf.urls import url
  2. from django.shortcuts import HttpResponse,render,redirect
  3. from types import FunctionType
  4. from django.utils.safestring import mark_safe
  5. from django.urls import reverse
  6. from django import forms
  7.  
  8. class StarkConfig(object):
  9. def __init__(self,model_class,site):
  10. self.model_class = model_class
  11. self.site = site
  12.  
  13. def display_checkbox(self,row=None,header=False): # 显示复选框
  14. if header:
  15. # 输出中文
  16. return "选择"
  17. # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
  18. return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)
  19.  
  20. def display_edit(self, row=None, header=False):
  21. if header:
  22. return "编辑"
  23.  
  24. return mark_safe(
  25. '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))
  26.  
  27. def display_del(self, row=None, header=False):
  28. if header:
  29. return "删除"
  30.  
  31. return mark_safe(
  32. '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))
  33.  
  34. def display_edit_del(self, row=None, header=False):
  35. if header:
  36. return "操作"
  37. tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
  38. <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
  39. """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
  40. return mark_safe(tpl)
  41.  
  42. def multi_delete(self, request): # 批量删除
  43. """
  44. 批量删除的action
  45. :param request:
  46. :return:
  47. """
  48. pk_list = request.POST.getlist('pk')
  49. self.model_class.objects.filter(pk__in=pk_list).delete()
  50. # return HttpResponse('删除成功')
  51.  
  52. multi_delete.text = "批量删除" # 添加自定义属性text
  53.  
  54. def multi_init(self,request): # 批量初始化
  55. print('批量初始化')
  56.  
  57. multi_init.text = "批量初始化" # 添加自定义属性text
  58.  
  59. order_by = [] # 需要排序的字段,由用户自定义
  60. list_display = [] # 定义显示的列,由用户自定义
  61. model_form_class = None # form组件需要的model_class
  62. action_list = [] # 批量操作方法
  63. search_list = ['name','tel'] # 固定搜索字段
  64.  
  65. def get_order_by(self): # 获取排序列表
  66. return self.order_by
  67.  
  68. def get_list_display(self): # 获取显示的列
  69. return self.list_display
  70.  
  71. def get_add_btn(self): # 显示添加按钮
  72. return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())
  73.  
  74. def get_model_form_class(self):
  75. """
  76. 获取ModelForm类
  77. :return:
  78. """
  79. if self.model_form_class:
  80. return self.model_form_class
  81.  
  82. class AddModelForm(forms.ModelForm):
  83. class Meta:
  84. model = self.model_class
  85. fields = "__all__"
  86.  
  87. return AddModelForm
  88.  
  89. def get_action_list(self): # 获取批量操作方法
  90. val = [] # 空列表
  91. # 扩展列表的元素
  92. val.extend(self.action_list)
  93. return val
  94.  
  95. def get_action_dict(self): # 获取匹配操作字典
  96. val = {}
  97. for item in self.action_list:
  98. # 以方法名为key
  99. val[item.__name__] = item
  100. return val
  101.  
  102. def get_search_list(self): # 获取搜索字段
  103. val = []
  104. val.extend(self.search_list)
  105. return val
  106.  
  107. def changelist_view(self, request):
  108. """
  109. 所有URL查看列表页面
  110. :param request:
  111. :return:
  112. """
  113. if request.method == 'POST':
  114. action_name = request.POST.get('action')
  115. action_dict = self.get_action_dict()
  116. if action_name not in action_dict:
  117. return HttpResponse('非法请求')
  118.  
  119. response = getattr(self, action_name)(request)
  120. if response:
  121. return response
  122.  
  123. ### 处理搜索 ###
  124. from django.db.models import Q
  125. search_list = self.get_search_list() # ['name','tel']
  126. q = request.GET.get('q', "") # 搜索条件
  127. con = Q()
  128. con.connector = "OR" # 以OR作为连接符
  129. if q: # 判断条件不为空
  130. for field in search_list:
  131. # 合并条件进行查询, __contains表示使用like查询
  132. con.children.append(('%s__contains' % field, q))
  133.  
  134. # 根据排序列表进行排序
  135. queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
  136. ### 批量操作 ###
  137. action_list = self.get_action_list()
  138. # 获取函数名以及text属性
  139. action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
  140. # print(action_list)
  141. ### 添加按钮 ###
  142. add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示
  143.  
  144. list_display = self.list_display # 定义显示的列
  145. header_list = [] # 定义头部,用来显示verbose_name
  146. if list_display:
  147. for name_or_func in list_display:
  148. if isinstance(name_or_func,FunctionType):
  149. # 执行函数,默认显示中文
  150. verbose_name = name_or_func(self,header=True)
  151. else:
  152. # 获取指定字段的verbose_name
  153. verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
  154.  
  155. header_list.append(verbose_name)
  156. else:
  157. # 如果list_display为空,添加表名
  158. header_list.append(self.model_class._meta.model_name)
  159.  
  160. body_list = [] # 显示内容
  161.  
  162. for row in queryset:
  163. # 这里的row是对象,它表示表里面的一条数据
  164. row_list = [] # 展示每一行数据
  165. if not list_display: # 如果不在list_display里面
  166. # 添加对象
  167. row_list.append(row)
  168. body_list.append(row_list)
  169. continue
  170.  
  171. for name_or_func in list_display:
  172. if isinstance(name_or_func,FunctionType):
  173. val = name_or_func(self,row=row) # 执行函数获取,传递row对象
  174. else:
  175. # 使用反射获取对象的值
  176. val = getattr(row, name_or_func)
  177.  
  178. row_list.append(val)
  179.  
  180. body_list.append(row_list)
  181.  
  182. # 注意:要传入参数
  183. return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q})
  184.  
  185. def add_view(self, request):
  186. """
  187. 所有的添加页面,都在此方法处理
  188. 使用ModelForm实现
  189. :param request:
  190. :return:
  191. """
  192. # 添加数据,使用ModelForm
  193. AddModelForm = self.get_model_form_class()
  194.  
  195. if request.method == "GET":
  196. form = AddModelForm()
  197. return render(request,'stark/change.html',{'form':form})
  198.  
  199. form = AddModelForm(request.POST) # 接收POST数据
  200. if form.is_valid(): # 验证数据
  201. form.save() # 自动保存数据
  202. # 反向生成url,跳转到列表页面
  203. return redirect(self.reverse_list_url())
  204. # 渲染页面,此时会保存表单数据
  205. return render(request, 'stark/change.html', {'form': form})
  206.  
  207. def change_view(self, request, pk):
  208. """
  209. 所有编辑页面
  210. :param request:
  211. :param pk:
  212. :return:
  213. """
  214. # 查看单条数据
  215. obj = self.model_class.objects.filter(pk=pk).first()
  216. if not obj:
  217. return HttpResponse('数据不存在')
  218. # 获取model_form类
  219. ModelFormClass = self.get_model_form_class()
  220. if request.method == 'GET':
  221. # instance表示生成默认值
  222. form = ModelFormClass(instance=obj)
  223. # 渲染页面,添加和修改可以共用一个一个模板文件
  224. return render(request, 'stark/change.html', {'form': form})
  225. # instance = obj 表示指定给谁做修改
  226. form = ModelFormClass(data=request.POST, instance=obj)
  227. if form.is_valid():
  228. form.save() # 修改数据
  229. # 跳转到列表页面
  230. return redirect(self.reverse_list_url())
  231. return render(request, 'stark/change.html', {'form': form})
  232.  
  233. def delete_view(self, request, pk):
  234. """
  235. 所有删除页面
  236. :param request:
  237. :param pk:
  238. :return:
  239. """
  240. if request.method == "GET":
  241. # cancel_url表示用户点击取消时,跳转到列表页面
  242. return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
  243. # 定位单条数据,并删除!
  244. self.model_class.objects.filter(pk=pk).delete()
  245. return redirect(self.reverse_list_url())
  246.  
  247. def wrapper(self,func):
  248. pass
  249.  
  250. def get_urls(self):
  251. info = self.model_class._meta.app_label, self.model_class._meta.model_name
  252. urlpatterns = [
  253. url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
  254. url(r'^add/$', self.add_view, name='%s_%s_add' % info),
  255. url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
  256. url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
  257. ]
  258.  
  259. extra = self.extra_url()
  260. if extra: # 判断变量不为空
  261. # 扩展路由
  262. urlpatterns.extend(extra)
  263.  
  264. # print(urlpatterns)
  265. return urlpatterns
  266.  
  267. def extra_url(self): # 额外的路由,由调用者重构
  268. pass
  269.  
  270. def reverse_list_url(self): # 反向生成访问列表的url
  271. app_label = self.model_class._meta.app_label
  272. model_name = self.model_class._meta.model_name
  273. namespace = self.site.namespace
  274. name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
  275. list_url = reverse(name)
  276. return list_url
  277.  
  278. def reverse_add_url(self): # 反向生成添加url
  279. app_label = self.model_class._meta.app_label
  280. model_name = self.model_class._meta.model_name
  281. namespace = self.site.namespace
  282. name = '%s:%s_%s_add' % (namespace, app_label, model_name)
  283. add_url = reverse(name)
  284. return add_url
  285.  
  286. def reverse_edit_url(self, row): # 反向生成编辑行内容的url
  287. app_label = self.model_class._meta.app_label # app名
  288. model_name = self.model_class._meta.model_name # 表名
  289. namespace = self.site.namespace # 命名空间
  290. # 拼接字符串,这里为change
  291. name = '%s:%s_%s_change' % (namespace, app_label, model_name)
  292. # 反向生成url,传入参数pk=row.pk
  293. edit_url = reverse(name, kwargs={'pk': row.pk})
  294. return edit_url
  295.  
  296. def reverse_del_url(self, row): # 反向生成删除行内容的url
  297. app_label = self.model_class._meta.app_label
  298. model_name = self.model_class._meta.model_name
  299. namespace = self.site.namespace
  300. # 注意:这里为del
  301. name = '%s:%s_%s_del' % (namespace, app_label, model_name)
  302. del_url = reverse(name, kwargs={'pk': row.pk})
  303. return del_url
  304.  
  305. @property
  306. def urls(self):
  307. return self.get_urls()
  308.  
  309. class AdminSite(object):
  310. def __init__(self):
  311. self._registry = {}
  312. self.app_name = 'stark'
  313. self.namespace = 'stark'
  314.  
  315. def register(self,model_class,stark_config=None):
  316. # not None的结果为Ture
  317. if not stark_config:
  318. # 也就是说,当其他应用调用register时,如果不指定stark_config参数
  319. # 那么必然执行下面这段代码!
  320. # stark_config和StarkConfig是等值的!都能实例化
  321. stark_config = StarkConfig
  322.  
  323. # 添加键值对,实例化类StarkConfig,传入参数model_class
  324. # self指的是AdminSite类
  325. self._registry[model_class] = stark_config(model_class,self)
  326.  
  327. # print(self._registry) # 打印字典
  328. """
  329. {
  330. app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
  331. app02.models.Role:RoleConfig(app02.models.Role)
  332. }
  333. """
  334.  
  335. # for k, v in self._registry.items():
  336. # print(k,v)
  337.  
  338. def get_urls(self):
  339. urlpatterns = []
  340.  
  341. for k, v in self._registry.items():
  342. # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
  343. # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
  344. app_label = k._meta.app_label
  345. model_name = k._meta.model_name
  346. urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))
  347.  
  348. return urlpatterns
  349.  
  350. @property
  351. def urls(self):
  352. # 调用get_urls方法
  353. # self.app_name和self.namespace值是一样的,都是stark
  354. return self.get_urls(), self.app_name, self.namespace
  355.  
  356. site = AdminSite() # 实例化类

修改 stark-->templates-->stark-->changelist.html,增加输入框

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

  1. {% extends 'stark/layout.html' %}
  2.  
  3. {% block content %}
  4. <h1>列表页面</h1>
  5. <div>
  6. {#添加按钮#}
  7. {% if add_btn %}
  8. <div style="margin: 5px 0;">
  9. {{ add_btn }}
  10. </div>
  11. {% endif %}
  12. {#搜索框#}
  13. <div style="float: right;">
  14. <form method="GET" class="form-inline">
  15. <div class="form-group">
  16. <input class="form-control" type="text" name="q" value="{{ q }}" placeholder="关键字搜索">
  17. <button class="btn btn-primary" type="submit">
  18. <i class="fa fa-search" aria-hidden="true"></i>
  19. </button>
  20. </div>
  21. </form>
  22. </div>
  23.  
  24. <form class="form-inline" method="post">
  25. {% csrf_token %}
  26. {#批量操作#}
  27. {% if action_list %}
  28. <div class="form-group">
  29. <select name="action" class="form-control" style="min-width: 200px;">
  30. <option>请选择功能</option>
  31. {% for item in action_list %}
  32. <option value="{{ item.name }}">{{ item.text }}</option>
  33. {% endfor %}
  34. </select>
  35. <input class="btn btn-primary" type="submit" value="执行">
  36. </div>
  37. {% endif %}
  38. {#使用table展示数据#}
  39. <table class="table table-bordered">
  40. <thead>
  41. <tr>
  42. {% for item in header_list %}
  43. <th>{{ item }}</th>
  44. {% endfor %}
  45.  
  46. </tr>
  47. </thead>
  48. <tbody>
  49. {% for row_list in body_list %}
  50. <tr>
  51. {% for col in row_list %}
  52. <td>{{ col }}</td>
  53. {% endfor %}
  54.  
  55. </tr>
  56. {% endfor %}
  57. </tbody>
  58. </table>
  59. </form>
  60. </div>
  61.  
  62. {% endblock %}

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

效果如下:

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

同时结果只有一条

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

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

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

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

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

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

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

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

  1. from stark.server.stark import site, StarkConfig
  2. from app01 import models
  3. from django import forms
  4. from django.shortcuts import render
  5. from django.conf.urls import url
  6.  
  7. class UserInfoConfig(StarkConfig):
  8. list_display = ['id', 'username']
  9.  
  10. class DepartModelForm(forms.ModelForm):
  11. class Meta:
  12. model = models.Depart
  13. fields = "__all__"
  14.  
  15. def clean_name(self): # 定义钩子
  16. # print(self.cleaned_data['name'])
  17. return self.cleaned_data['name']
  18.  
  19. class DepartConfig(StarkConfig):
  20. list_display = [StarkConfig.display_checkbox,'name', 'tel', 'user']
  21. # model_form_class = DepartModelForm
  22. # 批量操作
  23. action_list = [StarkConfig.multi_delete,StarkConfig.multi_init]
  24. # 搜索关键字
  25. # 固定搜索字段,如果是跨表字段,要按照ORM语法来
  26. search_list = ['name', 'tel', 'user__username']
  27.  
  28. # def get_add_btn(self): # 返回None,表示不显示添加按钮
  29. # pass
  30. # def changelist_view(self, request): # 重写changelist_view方法
  31. # # 渲染自定义的列表页面
  32. # return render(request,'stark/custom_list.html')
  33. # def get_urls(self): # 自定义路由
  34. # info = self.model_class._meta.app_label, self.model_class._meta.model_name
  35. #
  36. # urlpatterns = [
  37. # url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
  38. # ]
  39. # return urlpatterns
  40.  
  41. site.register(models.UserInfo, UserInfoConfig)
  42. site.register(models.Depart, DepartConfig)

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

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

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

  1. from django.conf.urls import url
  2. from django.shortcuts import HttpResponse,render,redirect
  3. from types import FunctionType
  4. from django.utils.safestring import mark_safe
  5. from django.urls import reverse
  6. from django import forms
  7.  
  8. class StarkConfig(object):
  9. def __init__(self,model_class,site):
  10. self.model_class = model_class
  11. self.site = site
  12.  
  13. def display_checkbox(self,row=None,header=False): # 显示复选框
  14. if header:
  15. # 输出中文
  16. return "选择"
  17. # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
  18. return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)
  19.  
  20. def display_edit(self, row=None, header=False):
  21. if header:
  22. return "编辑"
  23.  
  24. return mark_safe(
  25. '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))
  26.  
  27. def display_del(self, row=None, header=False):
  28. if header:
  29. return "删除"
  30.  
  31. return mark_safe(
  32. '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))
  33.  
  34. def display_edit_del(self, row=None, header=False):
  35. if header:
  36. return "操作"
  37. tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
  38. <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
  39. """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
  40. return mark_safe(tpl)
  41.  
  42. def multi_delete(self, request): # 批量删除
  43. """
  44. 批量删除的action
  45. :param request:
  46. :return:
  47. """
  48. pk_list = request.POST.getlist('pk')
  49. self.model_class.objects.filter(pk__in=pk_list).delete()
  50. # return HttpResponse('删除成功')
  51.  
  52. multi_delete.text = "批量删除" # 添加自定义属性text
  53.  
  54. def multi_init(self,request): # 批量初始化
  55. print('批量初始化')
  56.  
  57. multi_init.text = "批量初始化" # 添加自定义属性text
  58.  
  59. order_by = [] # 需要排序的字段,由用户自定义
  60. list_display = [] # 定义显示的列,由用户自定义
  61. model_form_class = None # form组件需要的model_class
  62. action_list = [] # 批量操作方法
  63. # 搜索字段,如果是跨表字段,要按照ORM语法来
  64. search_list = []
  65.  
  66. def get_order_by(self): # 获取排序列表
  67. return self.order_by
  68.  
  69. def get_list_display(self): # 获取显示的列
  70. return self.list_display
  71.  
  72. def get_add_btn(self): # 显示添加按钮
  73. return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())
  74.  
  75. def get_model_form_class(self):
  76. """
  77. 获取ModelForm类
  78. :return:
  79. """
  80. if self.model_form_class:
  81. return self.model_form_class
  82.  
  83. class AddModelForm(forms.ModelForm):
  84. class Meta:
  85. model = self.model_class
  86. fields = "__all__"
  87.  
  88. return AddModelForm
  89.  
  90. def get_action_list(self): # 获取批量操作方法
  91. val = [] # 空列表
  92. # 扩展列表的元素
  93. val.extend(self.action_list)
  94. return val
  95.  
  96. def get_action_dict(self): # 获取匹配操作字典
  97. val = {}
  98. for item in self.action_list:
  99. # 以方法名为key
  100. val[item.__name__] = item
  101. return val
  102.  
  103. def get_search_list(self): # 获取搜索字段
  104. val = []
  105. val.extend(self.search_list)
  106. return val
  107.  
  108. def changelist_view(self, request):
  109. """
  110. 所有URL查看列表页面
  111. :param request:
  112. :return:
  113. """
  114. if request.method == 'POST':
  115. action_name = request.POST.get('action')
  116. action_dict = self.get_action_dict()
  117. if action_name not in action_dict:
  118. return HttpResponse('非法请求')
  119.  
  120. response = getattr(self, action_name)(request)
  121. if response:
  122. return response
  123.  
  124. ### 处理搜索 ###
  125. from django.db.models import Q
  126. search_list = self.get_search_list() # ['name','tel']
  127. q = request.GET.get('q', "") # 搜索条件
  128. con = Q()
  129. con.connector = "OR" # 以OR作为连接符
  130. if q: # 判断条件不为空
  131. for field in search_list:
  132. # 合并条件进行查询, __contains表示使用like查询
  133. con.children.append(('%s__contains' % field, q))
  134.  
  135. # 根据排序列表进行排序
  136. queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
  137. ### 批量操作 ###
  138. action_list = self.get_action_list()
  139. # 获取函数名以及text属性
  140. action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
  141. # print(action_list)
  142. ### 添加按钮 ###
  143. add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示
  144.  
  145. list_display = self.list_display # 定义显示的列
  146. header_list = [] # 定义头部,用来显示verbose_name
  147. if list_display:
  148. for name_or_func in list_display:
  149. if isinstance(name_or_func,FunctionType):
  150. # 执行函数,默认显示中文
  151. verbose_name = name_or_func(self,header=True)
  152. else:
  153. # 获取指定字段的verbose_name
  154. verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
  155.  
  156. header_list.append(verbose_name)
  157. else:
  158. # 如果list_display为空,添加表名
  159. header_list.append(self.model_class._meta.model_name)
  160.  
  161. body_list = [] # 显示内容
  162.  
  163. for row in queryset:
  164. # 这里的row是对象,它表示表里面的一条数据
  165. row_list = [] # 展示每一行数据
  166. if not list_display: # 如果不在list_display里面
  167. # 添加对象
  168. row_list.append(row)
  169. body_list.append(row_list)
  170. continue
  171.  
  172. for name_or_func in list_display:
  173. if isinstance(name_or_func,FunctionType):
  174. val = name_or_func(self,row=row) # 执行函数获取,传递row对象
  175. else:
  176. # 使用反射获取对象的值
  177. val = getattr(row, name_or_func)
  178.  
  179. row_list.append(val)
  180.  
  181. body_list.append(row_list)
  182.  
  183. # 注意:要传入参数
  184. return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list})
  185.  
  186. def add_view(self, request):
  187. """
  188. 所有的添加页面,都在此方法处理
  189. 使用ModelForm实现
  190. :param request:
  191. :return:
  192. """
  193. # 添加数据,使用ModelForm
  194. AddModelForm = self.get_model_form_class()
  195.  
  196. if request.method == "GET":
  197. form = AddModelForm()
  198. return render(request,'stark/change.html',{'form':form})
  199.  
  200. form = AddModelForm(request.POST) # 接收POST数据
  201. if form.is_valid(): # 验证数据
  202. form.save() # 自动保存数据
  203. # 反向生成url,跳转到列表页面
  204. return redirect(self.reverse_list_url())
  205. # 渲染页面,此时会保存表单数据
  206. return render(request, 'stark/change.html', {'form': form})
  207.  
  208. def change_view(self, request, pk):
  209. """
  210. 所有编辑页面
  211. :param request:
  212. :param pk:
  213. :return:
  214. """
  215. # 查看单条数据
  216. obj = self.model_class.objects.filter(pk=pk).first()
  217. if not obj:
  218. return HttpResponse('数据不存在')
  219. # 获取model_form类
  220. ModelFormClass = self.get_model_form_class()
  221. if request.method == 'GET':
  222. # instance表示生成默认值
  223. form = ModelFormClass(instance=obj)
  224. # 渲染页面,添加和修改可以共用一个一个模板文件
  225. return render(request, 'stark/change.html', {'form': form})
  226. # instance = obj 表示指定给谁做修改
  227. form = ModelFormClass(data=request.POST, instance=obj)
  228. if form.is_valid():
  229. form.save() # 修改数据
  230. # 跳转到列表页面
  231. return redirect(self.reverse_list_url())
  232. return render(request, 'stark/change.html', {'form': form})
  233.  
  234. def delete_view(self, request, pk):
  235. """
  236. 所有删除页面
  237. :param request:
  238. :param pk:
  239. :return:
  240. """
  241. if request.method == "GET":
  242. # cancel_url表示用户点击取消时,跳转到列表页面
  243. return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
  244. # 定位单条数据,并删除!
  245. self.model_class.objects.filter(pk=pk).delete()
  246. return redirect(self.reverse_list_url())
  247.  
  248. def wrapper(self,func):
  249. pass
  250.  
  251. def get_urls(self):
  252. info = self.model_class._meta.app_label, self.model_class._meta.model_name
  253. urlpatterns = [
  254. url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
  255. url(r'^add/$', self.add_view, name='%s_%s_add' % info),
  256. url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
  257. url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
  258. ]
  259.  
  260. extra = self.extra_url()
  261. if extra: # 判断变量不为空
  262. # 扩展路由
  263. urlpatterns.extend(extra)
  264.  
  265. # print(urlpatterns)
  266. return urlpatterns
  267.  
  268. def extra_url(self): # 额外的路由,由调用者重构
  269. pass
  270.  
  271. def reverse_list_url(self): # 反向生成访问列表的url
  272. app_label = self.model_class._meta.app_label
  273. model_name = self.model_class._meta.model_name
  274. namespace = self.site.namespace
  275. name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
  276. list_url = reverse(name)
  277. return list_url
  278.  
  279. def reverse_add_url(self): # 反向生成添加url
  280. app_label = self.model_class._meta.app_label
  281. model_name = self.model_class._meta.model_name
  282. namespace = self.site.namespace
  283. name = '%s:%s_%s_add' % (namespace, app_label, model_name)
  284. add_url = reverse(name)
  285. return add_url
  286.  
  287. def reverse_edit_url(self, row): # 反向生成编辑行内容的url
  288. app_label = self.model_class._meta.app_label # app名
  289. model_name = self.model_class._meta.model_name # 表名
  290. namespace = self.site.namespace # 命名空间
  291. # 拼接字符串,这里为change
  292. name = '%s:%s_%s_change' % (namespace, app_label, model_name)
  293. # 反向生成url,传入参数pk=row.pk
  294. edit_url = reverse(name, kwargs={'pk': row.pk})
  295. return edit_url
  296.  
  297. def reverse_del_url(self, row): # 反向生成删除行内容的url
  298. app_label = self.model_class._meta.app_label
  299. model_name = self.model_class._meta.model_name
  300. namespace = self.site.namespace
  301. # 注意:这里为del
  302. name = '%s:%s_%s_del' % (namespace, app_label, model_name)
  303. del_url = reverse(name, kwargs={'pk': row.pk})
  304. return del_url
  305.  
  306. @property
  307. def urls(self):
  308. return self.get_urls()
  309.  
  310. class AdminSite(object):
  311. def __init__(self):
  312. self._registry = {}
  313. self.app_name = 'stark'
  314. self.namespace = 'stark'
  315.  
  316. def register(self,model_class,stark_config=None):
  317. # not None的结果为Ture
  318. if not stark_config:
  319. # 也就是说,当其他应用调用register时,如果不指定stark_config参数
  320. # 那么必然执行下面这段代码!
  321. # stark_config和StarkConfig是等值的!都能实例化
  322. stark_config = StarkConfig
  323.  
  324. # 添加键值对,实例化类StarkConfig,传入参数model_class
  325. # self指的是AdminSite类
  326. self._registry[model_class] = stark_config(model_class,self)
  327.  
  328. # print(self._registry) # 打印字典
  329. """
  330. {
  331. app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
  332. app02.models.Role:RoleConfig(app02.models.Role)
  333. }
  334. """
  335.  
  336. # for k, v in self._registry.items():
  337. # print(k,v)
  338.  
  339. def get_urls(self):
  340. urlpatterns = []
  341.  
  342. for k, v in self._registry.items():
  343. # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
  344. # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
  345. app_label = k._meta.app_label
  346. model_name = k._meta.model_name
  347. urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))
  348.  
  349. return urlpatterns
  350.  
  351. @property
  352. def urls(self):
  353. # 调用get_urls方法
  354. # self.app_name和self.namespace值是一样的,都是stark
  355. return self.get_urls(), self.app_name, self.namespace
  356.  
  357. site = AdminSite() # 实例化类

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

  1. {% extends 'stark/layout.html' %}
  2.  
  3. {% block content %}
  4. <h1>列表页面</h1>
  5. <div>
  6. {#添加按钮#}
  7. {% if add_btn %}
  8. <div style="margin: 5px 0;">
  9. {{ add_btn }}
  10. </div>
  11. {% endif %}
  12. {#搜索框#}
  13. {% if search_list %}
  14. <div style="float: right;">
  15. <form method="GET" class="form-inline">
  16. <div class="form-group">
  17. <input class="form-control" type="text" name="q" value="{{ q }}" placeholder="关键字搜索">
  18. <button class="btn btn-primary" type="submit">
  19. <i class="fa fa-search" aria-hidden="true"></i>
  20. </button>
  21. </div>
  22. </form>
  23. </div>
  24. {% endif %}
  25.  
  26. <form class="form-inline" method="post">
  27. {% csrf_token %}
  28. {#批量操作#}
  29. {% if action_list %}
  30. <div class="form-group">
  31. <select name="action" class="form-control" style="min-width: 200px;">
  32. <option>请选择功能</option>
  33. {% for item in action_list %}
  34. <option value="{{ item.name }}">{{ item.text }}</option>
  35. {% endfor %}
  36. </select>
  37. <input class="btn btn-primary" type="submit" value="执行">
  38. </div>
  39. {% endif %}
  40. {#使用table展示数据#}
  41. <table class="table table-bordered">
  42. <thead>
  43. <tr>
  44. {% for item in header_list %}
  45. <th>{{ item }}</th>
  46. {% endfor %}
  47.  
  48. </tr>
  49. </thead>
  50. <tbody>
  51. {% for row_list in body_list %}
  52. <tr>
  53. {% for col in row_list %}
  54. <td>{{ col }}</td>
  55. {% endfor %}
  56.  
  57. </tr>
  58. {% endfor %}
  59. </tbody>
  60. </table>
  61. </form>
  62. </div>
  63.  
  64. {% endblock %}

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

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

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

搜索框就没有了!

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

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

  1. from django.conf.urls import url
  2. from django.shortcuts import HttpResponse,render,redirect
  3. from types import FunctionType
  4. from django.utils.safestring import mark_safe
  5. from django.urls import reverse
  6. from django import forms
  7. from django.db.models import Q
  8.  
  9. class StarkConfig(object):
  10. def __init__(self,model_class,site):
  11. self.model_class = model_class
  12. self.site = site
  13.  
  14. def display_checkbox(self,row=None,header=False): # 显示复选框
  15. if header:
  16. # 输出中文
  17. return "选择"
  18. # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id
  19. return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)
  20.  
  21. def display_edit(self, row=None, header=False):
  22. if header:
  23. return "编辑"
  24.  
  25. return mark_safe(
  26. '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))
  27.  
  28. def display_del(self, row=None, header=False):
  29. if header:
  30. return "删除"
  31.  
  32. return mark_safe(
  33. '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))
  34.  
  35. def display_edit_del(self, row=None, header=False):
  36. if header:
  37. return "操作"
  38. tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
  39. <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
  40. """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
  41. return mark_safe(tpl)
  42.  
  43. def multi_delete(self, request): # 批量删除
  44. """
  45. 批量删除的action
  46. :param request:
  47. :return:
  48. """
  49. pk_list = request.POST.getlist('pk')
  50. self.model_class.objects.filter(pk__in=pk_list).delete()
  51. # return HttpResponse('删除成功')
  52.  
  53. multi_delete.text = "批量删除" # 添加自定义属性text
  54.  
  55. def multi_init(self,request): # 批量初始化
  56. print('批量初始化')
  57.  
  58. multi_init.text = "批量初始化" # 添加自定义属性text
  59.  
  60. order_by = [] # 需要排序的字段,由用户自定义
  61. list_display = [] # 定义显示的列,由用户自定义
  62. model_form_class = None # form组件需要的model_class
  63. action_list = [] # 批量操作方法
  64. # 搜索字段,如果是跨表字段,要按照ORM语法来
  65. search_list = []
  66.  
  67. def get_order_by(self): # 获取排序列表
  68. return self.order_by
  69.  
  70. def get_list_display(self): # 获取显示的列
  71. return self.list_display
  72.  
  73. def get_add_btn(self): # 显示添加按钮
  74. return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())
  75.  
  76. def get_model_form_class(self):
  77. """
  78. 获取ModelForm类
  79. :return:
  80. """
  81. if self.model_form_class:
  82. return self.model_form_class
  83.  
  84. class AddModelForm(forms.ModelForm):
  85. class Meta:
  86. model = self.model_class
  87. fields = "__all__"
  88.  
  89. return AddModelForm
  90.  
  91. def get_action_list(self): # 获取批量操作方法
  92. val = [] # 空列表
  93. # 扩展列表的元素
  94. val.extend(self.action_list)
  95. return val
  96.  
  97. def get_action_dict(self): # 获取匹配操作字典
  98. val = {}
  99. for item in self.action_list:
  100. # 以方法名为key
  101. val[item.__name__] = item
  102. return val
  103.  
  104. def get_search_list(self): # 获取搜索字段
  105. val = []
  106. val.extend(self.search_list)
  107. return val
  108.  
  109. def get_search_condition(self, request): # 根据关键字,组合ORM查询语句
  110. search_list = self.get_search_list() # ['name','tel']
  111. q = request.GET.get('q', "") # 搜索条件
  112. con = Q()
  113. con.connector = "OR" # 以OR作为连接符
  114. if q: # 判断条件不为空
  115. for field in search_list:
  116. # 合并条件进行查询, __contains表示使用like查询
  117. con.children.append(('%s__contains' % field, q))
  118.  
  119. return search_list, q, con
  120.  
  121. def changelist_view(self, request):
  122. """
  123. 所有URL查看列表页面
  124. :param request:
  125. :return:
  126. """
  127. if request.method == 'POST':
  128. action_name = request.POST.get('action')
  129. action_dict = self.get_action_dict()
  130. if action_name not in action_dict:
  131. return HttpResponse('非法请求')
  132.  
  133. response = getattr(self, action_name)(request)
  134. if response:
  135. return response
  136.  
  137. ### 处理搜索 ###
  138. search_list, q, con = self.get_search_condition(request)
  139.  
  140. # 根据排序列表进行排序
  141. queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
  142. ### 批量操作 ###
  143. action_list = self.get_action_list()
  144. # 获取函数名以及text属性
  145. action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
  146. # print(action_list)
  147. ### 添加按钮 ###
  148. add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示
  149.  
  150. list_display = self.list_display # 定义显示的列
  151. header_list = [] # 定义头部,用来显示verbose_name
  152. if list_display:
  153. for name_or_func in list_display:
  154. if isinstance(name_or_func,FunctionType):
  155. # 执行函数,默认显示中文
  156. verbose_name = name_or_func(self,header=True)
  157. else:
  158. # 获取指定字段的verbose_name
  159. verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
  160.  
  161. header_list.append(verbose_name)
  162. else:
  163. # 如果list_display为空,添加表名
  164. header_list.append(self.model_class._meta.model_name)
  165.  
  166. body_list = [] # 显示内容
  167.  
  168. for row in queryset:
  169. # 这里的row是对象,它表示表里面的一条数据
  170. row_list = [] # 展示每一行数据
  171. if not list_display: # 如果不在list_display里面
  172. # 添加对象
  173. row_list.append(row)
  174. body_list.append(row_list)
  175. continue
  176.  
  177. for name_or_func in list_display:
  178. if isinstance(name_or_func,FunctionType):
  179. val = name_or_func(self,row=row) # 执行函数获取,传递row对象
  180. else:
  181. # 使用反射获取对象的值
  182. val = getattr(row, name_or_func)
  183.  
  184. row_list.append(val)
  185.  
  186. body_list.append(row_list)
  187.  
  188. # 注意:要传入参数
  189. return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list})
  190.  
  191. def add_view(self, request):
  192. """
  193. 所有的添加页面,都在此方法处理
  194. 使用ModelForm实现
  195. :param request:
  196. :return:
  197. """
  198. # 添加数据,使用ModelForm
  199. AddModelForm = self.get_model_form_class()
  200.  
  201. if request.method == "GET":
  202. form = AddModelForm()
  203. return render(request,'stark/change.html',{'form':form})
  204.  
  205. form = AddModelForm(request.POST) # 接收POST数据
  206. if form.is_valid(): # 验证数据
  207. form.save() # 自动保存数据
  208. # 反向生成url,跳转到列表页面
  209. return redirect(self.reverse_list_url())
  210. # 渲染页面,此时会保存表单数据
  211. return render(request, 'stark/change.html', {'form': form})
  212.  
  213. def change_view(self, request, pk):
  214. """
  215. 所有编辑页面
  216. :param request:
  217. :param pk:
  218. :return:
  219. """
  220. # 查看单条数据
  221. obj = self.model_class.objects.filter(pk=pk).first()
  222. if not obj:
  223. return HttpResponse('数据不存在')
  224. # 获取model_form类
  225. ModelFormClass = self.get_model_form_class()
  226. if request.method == 'GET':
  227. # instance表示生成默认值
  228. form = ModelFormClass(instance=obj)
  229. # 渲染页面,添加和修改可以共用一个一个模板文件
  230. return render(request, 'stark/change.html', {'form': form})
  231. # instance = obj 表示指定给谁做修改
  232. form = ModelFormClass(data=request.POST, instance=obj)
  233. if form.is_valid():
  234. form.save() # 修改数据
  235. # 跳转到列表页面
  236. return redirect(self.reverse_list_url())
  237. return render(request, 'stark/change.html', {'form': form})
  238.  
  239. def delete_view(self, request, pk):
  240. """
  241. 所有删除页面
  242. :param request:
  243. :param pk:
  244. :return:
  245. """
  246. if request.method == "GET":
  247. # cancel_url表示用户点击取消时,跳转到列表页面
  248. return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
  249. # 定位单条数据,并删除!
  250. self.model_class.objects.filter(pk=pk).delete()
  251. return redirect(self.reverse_list_url())
  252.  
  253. def wrapper(self,func):
  254. pass
  255.  
  256. def get_urls(self):
  257. info = self.model_class._meta.app_label, self.model_class._meta.model_name
  258. urlpatterns = [
  259. url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
  260. url(r'^add/$', self.add_view, name='%s_%s_add' % info),
  261. url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
  262. url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
  263. ]
  264.  
  265. extra = self.extra_url()
  266. if extra: # 判断变量不为空
  267. # 扩展路由
  268. urlpatterns.extend(extra)
  269.  
  270. # print(urlpatterns)
  271. return urlpatterns
  272.  
  273. def extra_url(self): # 额外的路由,由调用者重构
  274. pass
  275.  
  276. def reverse_list_url(self): # 反向生成访问列表的url
  277. app_label = self.model_class._meta.app_label
  278. model_name = self.model_class._meta.model_name
  279. namespace = self.site.namespace
  280. name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
  281. list_url = reverse(name)
  282. return list_url
  283.  
  284. def reverse_add_url(self): # 反向生成添加url
  285. app_label = self.model_class._meta.app_label
  286. model_name = self.model_class._meta.model_name
  287. namespace = self.site.namespace
  288. name = '%s:%s_%s_add' % (namespace, app_label, model_name)
  289. add_url = reverse(name)
  290. return add_url
  291.  
  292. def reverse_edit_url(self, row): # 反向生成编辑行内容的url
  293. app_label = self.model_class._meta.app_label # app名
  294. model_name = self.model_class._meta.model_name # 表名
  295. namespace = self.site.namespace # 命名空间
  296. # 拼接字符串,这里为change
  297. name = '%s:%s_%s_change' % (namespace, app_label, model_name)
  298. # 反向生成url,传入参数pk=row.pk
  299. edit_url = reverse(name, kwargs={'pk': row.pk})
  300. return edit_url
  301.  
  302. def reverse_del_url(self, row): # 反向生成删除行内容的url
  303. app_label = self.model_class._meta.app_label
  304. model_name = self.model_class._meta.model_name
  305. namespace = self.site.namespace
  306. # 注意:这里为del
  307. name = '%s:%s_%s_del' % (namespace, app_label, model_name)
  308. del_url = reverse(name, kwargs={'pk': row.pk})
  309. return del_url
  310.  
  311. @property
  312. def urls(self):
  313. return self.get_urls()
  314.  
  315. class AdminSite(object):
  316. def __init__(self):
  317. self._registry = {}
  318. self.app_name = 'stark'
  319. self.namespace = 'stark'
  320.  
  321. def register(self,model_class,stark_config=None):
  322. # not None的结果为Ture
  323. if not stark_config:
  324. # 也就是说,当其他应用调用register时,如果不指定stark_config参数
  325. # 那么必然执行下面这段代码!
  326. # stark_config和StarkConfig是等值的!都能实例化
  327. stark_config = StarkConfig
  328.  
  329. # 添加键值对,实例化类StarkConfig,传入参数model_class
  330. # self指的是AdminSite类
  331. self._registry[model_class] = stark_config(model_class,self)
  332.  
  333. # print(self._registry) # 打印字典
  334. """
  335. {
  336. app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
  337. app02.models.Role:RoleConfig(app02.models.Role)
  338. }
  339. """
  340.  
  341. # for k, v in self._registry.items():
  342. # print(k,v)
  343.  
  344. def get_urls(self):
  345. urlpatterns = []
  346.  
  347. for k, v in self._registry.items():
  348. # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象
  349. # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象
  350. app_label = k._meta.app_label
  351. model_name = k._meta.model_name
  352. urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))
  353.  
  354. return urlpatterns
  355.  
  356. @property
  357. def urls(self):
  358. # 调用get_urls方法
  359. # self.app_name和self.namespace值是一样的,都是stark
  360. return self.get_urls(), self.app_name, self.namespace
  361.  
  362. site = AdminSite() # 实例化类

修改app01-->stark.py,开启search_list变量

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

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

三、保留原搜索条件

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

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

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

答案是可以的!

装饰器

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

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

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

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

修改get_urls方法,应用装饰器

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

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

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

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

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

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

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

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

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

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

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

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

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

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

修改一条数据,点击提交

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

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

效果如下:

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

四、自定义分页

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

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

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

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

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

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

打开表app01_depart,添加几条数据

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

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

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

效果如下:

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

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

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

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

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

基本测试

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

测试搜索条件

五、拆分代码

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

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

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

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

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

inclusion_tag+yield

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

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

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

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

{% extends 'stark/layout.html' %}
{% load stark %} {% block content %}
<h1>列表页面</h1>
<div>
{#添加按钮#}
{% if cl.add_btn %}
<div style="margin: 5px 0;">
{{ cl.add_btn }}
</div>
{% endif %}
{#搜索框#}
{% if cl.search_list %}
<div style="float: right;">
<form method="GET" class="form-inline">
<div class="form-group">
<input class="form-control" type="text" name="q" value="{{ cl.q }}" placeholder="关键字搜索">
<button class="btn btn-primary" type="submit">
<i class="fa fa-search" aria-hidden="true"></i>
</button>
</div>
</form>
</div>
{% endif %} <form class="form-inline" method="post">
{% csrf_token %}
{#批量操作#}
{% if cl.action_list %}
<div class="form-group">
<select name="action" class="form-control" style="min-width: 200px;">
<option>请选择功能</option>
{% for item in cl.action_list %}
<option value="{{ item.name }}">{{ item.text }}</option>
{% endfor %}
</select>
<input class="btn btn-primary" type="submit" value="执行">
</div>
{% endif %}
{#使用table展示数据#}
{% table cl %}
{#分页展示#}
<nav aria-label="Page navigation">
<ul class="pagination">
{{ cl.page.page_html|safe }}
</ul>
</nav>
</form>
</div> {% endblock %}

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

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

效果如下:

总结:

1. 批量操作[扩展]
- 反射
- __name__
- 一切皆对象
def multi_delete(self,request):
"""
批量删除的action
:param request:
:return:
"""
pk_list = request.POST.getlist('pk')
self.model_class.objects.filter(pk__in=pk_list).delete()
# return HttpResponse('删除成功') multi_delete.text = "批量删除" 2. 搜索[扩展]
- Q
- __contains 3. 保留原搜索条件
- QueryDict,request.GET/request.POST
- urlencode()
- _mutable = True
- 深拷贝
- urllib.parse.urlencode 4. 分页
- 分页组件
- 保留原条件 5. 拆分
- ChangeList类封装
- inclusion_tag
- 生成器

完整代码,请参数github:

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

python 全栈开发,Day115(urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码)的更多相关文章

  1. python全栈开发day115、116-websocket、websocket原理、websocket加解密、简单问答机器人实现

    1.websocket 1.websocket 与轮询 轮询: 不断向服务器发起询问,服务器还不断的回复 浪费带宽,浪费前后端资源 保证数据的实时性 长轮询: 1.客户端向服务器发起消息,服务端轮询, ...

  2. python全栈开发day64-模板-变量和(.)的使用,filters和自定义filter

    一.上周内容回顾 day64 内容回顾: 1. 所有的django命令 1. 安装 pip install django==1.11.14 pip install -i 源 django==1.11. ...

  3. python 全栈开发之路 day1

    python 全栈开发之路 day1   本节内容 计算机发展介绍 计算机硬件组成 计算机基本原理 计算机 计算机(computer)俗称电脑,是一种用于高速计算的电子计算机器,可以进行数值计算,又可 ...

  4. python 全栈开发,Day99(作业讲解,DRF版本,DRF分页,DRF序列化进阶)

    昨日内容回顾 1. 为什么要做前后端分离? - 前后端交给不同的人来编写,职责划分明确. - API (IOS,安卓,PC,微信小程序...) - vue.js等框架编写前端时,会比之前写jQuery ...

  5. 学习笔记之Python全栈开发/人工智能公开课_腾讯课堂

    Python全栈开发/人工智能公开课_腾讯课堂 https://ke.qq.com/course/190378 https://github.com/haoran119/ke.qq.com.pytho ...

  6. Python全栈开发【面向对象进阶】

    Python全栈开发[面向对象进阶] 本节内容: isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__geta ...

  7. Python全栈开发【面向对象】

    Python全栈开发[面向对象] 本节内容: 三大编程范式 面向对象设计与面向对象编程 类和对象 静态属性.类方法.静态方法 类组合 继承 多态 封装 三大编程范式 三大编程范式: 1.面向过程编程 ...

  8. Python全栈开发【模块】

    Python全栈开发[模块] 本节内容: 模块介绍 time random os sys json & picle shelve XML hashlib ConfigParser loggin ...

  9. Python全栈开发【基础四】

    Python全栈开发[基础四] 本节内容: 匿名函数(lambda) 函数式编程(map,filter,reduce) 文件处理 迭代器 三元表达式 列表解析与生成器表达式 生成器 匿名函数 lamb ...

随机推荐

  1. Scala进阶之路-进程控制之执行shell脚本

    Scala进阶之路-进程控制之执行shell脚本 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 废话不多说,我这里直接放一个案例. /* @author :yinzhengjie ...

  2. python---自定义字段验证

    各个字段类,含正则和验证方法 #字段类 class IPField: REGULAR = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4] ...

  3. 添加dubbo.xsd的方法

    整合dubbo-spring的时候,配置文件会报错 因为     阿里关闭在线的域名了.需要本地下载xsd文件  所以,需要下载本地引入. 解决方式: 在dubbo的开源项目上找到xsd文件: htt ...

  4. 淘宝开源编辑器Kissy Editor和简易留言编辑器【转】

    原来也写过一篇关于百度Ueditor编辑器的介绍:百度Ueditor编辑器的使用,ASP.NET也可上传图片 最开始是使用CuteEditor控件,需要好几mb的空间,因为刚开始学习ASP.NET的时 ...

  5. Spark2.1.0安装

    1.解压安装spark tar zxf spark-2.1.O-bin-2.6.0-CDH5.10.0.tgz 2.修改配置文件 vim /etc/profile export SPARK_HOME= ...

  6. 收集服务器网卡和IP信息

    收集服务器网卡和IP信息 Python2环境 #!/usr/bin/python2 # -*- coding:utf-8 -*- import os,sys import socket, fcntl, ...

  7. C# 比较不错的拓扑图控件

    群内有下载 616945527

  8. ASP.NET MVC中的Session设置

    最近在ASP.NET MVC项目中碰到这样的情况:在一个controller中设置了Session,但在另一个controller的构造函数中无法获取该Session,会报"System.N ...

  9. split('\r\n')

    '\r'是回车,'\n'是换行,前者使光标到行首,后者使光标下移一格.通常用的Enter是两个加起来. 实际我的脚本读取FTP的列表,如果用的split("\r\n"),可以获得正 ...

  10. 在使用NSArray打印的时候如果遇到中文字符那么会打印出来编码。

    在使用NSArray打印的时候如果遇到中文字符那么会打印出来编码,如下代码: - (void)viewDidLoad { [super viewDidLoad]; // Do any addition ...