效果图:

批量删除只是一个例子,可以根据需求定制自己想要的批量操作。

新增函数

  1. def get_action_list(self) 钩子方法,获取要处理的批量操作的函数
  2.  
  3. def action_multi_delete(self, request, *args, **kwargs) 批量删除

一、strak组件

  1. import functools
  2. from types import FunctionType
  3.  
  4. from django import forms
  5. from django.db.models import Q
  6. from django.http import QueryDict
  7. from django.urls import re_path
  8. from django.utils.safestring import mark_safe
  9. from django.shortcuts import HttpResponse, render, reverse, redirect
  10.  
  11. from stark.utils.pagination import Pagination
  12.  
  13. class StarkModelForm(forms.ModelForm):
  14. def __init__(self, *args, **kwargs):
  15. super(StarkModelForm, self).__init__(*args, **kwargs)
  16. # 统一给ModelForm生成字段添加样式
  17. for name, field in self.fields.items():
  18. field.widget.attrs['class'] = 'form-control'
  19.  
  20. def get_choice_text(title, field):
  21. """
  22. 对于Stark组件中定义列时,choice如果想要显示中文信息,调用此方法即可。
  23. :param title: 希望页面显示的表头
  24. :param field: 字段名称
  25. :return:
  26. """
  27.  
  28. def inner(self, obj=None, is_header=None):
  29. if is_header:
  30. return title
  31. method = f"get_{field}_display"
  32. return getattr(obj, method)()
  33. # GENDER_CHOICES = ((MALE, '男'),(FEMALE, '女'),)
  34. # 对于choice字段,如果想获取获取第二个值,可以通过:对象.get_字段名_display()
  35.  
  36. return inner
  37.  
  38. class StarkHandler(object):
  39. list_display = []
  40. order_list = []
  41. search_list = []
  42. per_page_data = 10
  43. has_add_btn = True
  44. model_form_class = None
  45. list_template = None
  46. add_template = None
  47. edit_template = None
  48. delete_template = None
  49. action_list = []
  50.  
  51. def __init__(self, site, model_class, prev):
  52. self.site = site
  53. self.model_class = model_class
  54. self.prev = prev
  55. self.request = None
  56.  
  57. def display_checkbox(self, obj=None, is_header=None, *args, **kwargs):
  58. """
  59. 复选框
  60. :param obj:
  61. :param is_header:
  62. :param args:
  63. :param kwargs:
  64. :return:
  65. """
  66. if is_header:
  67. return '选择'
  68. return mark_safe(f'<input type="checkbox" name="pk" value="{obj.pk}" />')
  69.  
  70. def display_edit(self, obj=None, is_header=None, *args, **kwargs):
  71. """
  72. 自定义页面显示的列(表头和内容)
  73. :param obj:
  74. :param is_header:
  75. :return:
  76. """
  77. if is_header:
  78. return '编辑'
  79. name = f'{self.site.namespace}:{self.get_edit_url_name}'
  80. return mark_safe(f'<a href="{reverse(name, args=(obj.pk,))}">编辑</a>')
  81.  
  82. def display_delete(self, obj=None, is_header=None, *args, **kwargs):
  83. if is_header:
  84. return '删除'
  85. name = f'{self.site.namespace}:{self.get_delete_url_name}'
  86. return mark_safe(f'<a href="{reverse(name, args=(obj.pk,))}">删除</a>')
  87.  
  88. def get_list_display(self, request, *args, **kwargs):
  89. """
  90. 获取页面上应该显示的列,预留的自定义扩展,例如:以后根据用户的不同显示不同的列
  91. :return:
  92. """
  93. value = []
  94. value.extend(self.list_display)
  95. return value
  96.  
  97. def get_search_list(self):
  98. return self.search_list
  99.  
  100. def get_add_btn(self, *args, **kwargs):
  101. if self.has_add_btn:
  102. return f'<a class="btn btn-primary" href="{self.reverse_add_url(*args, **kwargs)}">添加</a>'
  103.  
  104. def get_model_form_class(self, request, *args, **kwargs):
  105. if self.model_form_class:
  106. return self.model_form_class
  107.  
  108. class DynamicModelForm(StarkModelForm):
  109. class Meta:
  110. model = self.model_class
  111. fields = '__all__'
  112.  
  113. return DynamicModelForm
  114.  
  115. def get_order_list(self):
  116. return self.order_list or ['-id', ]
  117.  
  118. def get_action_list(self):
  119. return self.action_list
  120.  
  121. def action_multi_delete(self, request, *args, **kwargs):
  122. """
  123. 批量删除(如果想要定制执行成功后的返回值,那么就为action函数设置返回值即可)
  124. :param request:
  125. :param args:
  126. :param kwargs:
  127. :return:
  128. """
  129. pk_list = request.POST.getlist('pk')
  130. self.model_class.objects.filter(id__in=pk_list).delete()
  131.  
  132. action_multi_delete.text = '批量删除'
  133.  
  134. def list_view(self, request, *args, **kwargs):
  135. """
  136. 列表页面
  137. :param request:
  138. :return:
  139. """
  140.  
  141. # 1. 处理Action
  142. action_list = self.get_action_list()
  143. action_dict = {func.__name__: func.text for func in action_list}
  144. if request.method == 'POST':
  145. action_func_name = request.POST.get('action')
  146. if action_func_name and action_func_name in action_dict:
  147. action_response = getattr(self, action_func_name)(request, *args, **kwargs)
  148. if action_response:
  149. return action_response
  150. # 2. 处理搜索
  151. # 搜索列表写ORM语句,如:['name__contains','email__contains','id__gt','gender']
  152. search_list = self.get_search_list()
  153. search_value = request.GET.get('q', '')
  154. conn = Q()
  155. conn.connector = 'OR' # 通过or链接
  156. if search_value:
  157. for item in search_list:
  158. conn.children.append((item, search_value)) # conn.children.append('name__contains','张三')
  159.  
  160. # 3. 获取排序
  161. order_list = self.get_order_list()
  162. queryset = self.model_class.objects.filter(conn).order_by(*order_list)
  163.  
  164. # 4. 分页处理
  165. all_count = queryset.count()
  166. query_params = request.GET.copy() # 深copy
  167. query_params._mutable = True # query_params默认不可修改
  168.  
  169. pager = Pagination(
  170. current_page=request.GET.get('page'),
  171. all_count=all_count,
  172. base_url=request.path_info,
  173. query_params=query_params,
  174. per_page_data=self.per_page_data,
  175. )
  176. data_list = queryset[pager.start:pager.end]
  177.  
  178. # 5. 处理表格
  179. list_display = self.get_list_display(request, *args, **kwargs) # 会优先调用UserInfoHandler里的get_list_display()方法。
  180. # 5.1 处理表格的表头
  181. header_list = []
  182. if list_display:
  183. for field_or_func in list_display:
  184. if isinstance(field_or_func, FunctionType):
  185. verbose_name = field_or_func(self, obj=None, is_header=True)
  186. else:
  187. verbose_name = self.model_class._meta.get_field(field_or_func).verbose_name
  188. header_list.append(verbose_name)
  189. else:
  190. header_list.append(self.model_class._meta.model_name) # 如果用户没有填写list_display,就显示表名
  191.  
  192. # 5.2 处理表的内容
  193. body_list = []
  194. for obj in data_list:
  195. tr_list = []
  196. if list_display:
  197. for field_or_func in list_display:
  198. if isinstance(field_or_func, FunctionType):
  199. tr_list.append(field_or_func(self, obj, is_header=False, *args, **kwargs))
  200. else:
  201. tr_list.append(getattr(obj, field_or_func))
  202. else:
  203. tr_list.append(obj) # 如果用户没有填写list_display,就显示表对象,所以表类要定义__str__方法
  204. body_list.append(tr_list)
  205.  
  206. # 6 添加按钮
  207. add_btn = self.get_add_btn(*args, **kwargs)
  208.  
  209. context = {
  210. 'data_list': data_list,
  211. 'header_list': header_list,
  212. 'body_list': body_list,
  213. 'pager': pager,
  214. 'add_btn': add_btn,
  215. 'search_list': search_list,
  216. 'search_value': search_value,
  217. 'action_dict': action_dict,
  218. }
  219.  
  220. return render(request, self.list_template or 'stark/data_list.html', context)
  221.  
  222. def save(self, form, is_update=False, *args, **kwargs):
  223. """
  224. 在使用ModelForm保存数据之前预留的钩子方法
  225. :param form:
  226. :param is_update:
  227. :return:
  228. """
  229. form.save()
  230.  
  231. def add_view(self, request, *args, **kwargs):
  232. """
  233. 添加页面
  234. :param request:
  235. :return:
  236. """
  237. model_form_class = self.get_model_form_class(request, *args, **kwargs)
  238. if request.method == 'GET':
  239. form = model_form_class()
  240. return render(request, 'stark/change.html', {'form': form})
  241. form = model_form_class(data=request.POST)
  242. if form.is_valid():
  243. self.save(form, False, *args, **kwargs)
  244. # 在数据库保存成功后,跳转回列表页面(携带原来的参数)。
  245. return redirect(self.reverse_list_url(*args, **kwargs))
  246. return render(request, self.add_template or 'stark/change.html', {'form': form})
  247.  
  248. def edit_view(self, request, pk, *args, **kwargs):
  249. """
  250. 编辑页面
  251. :param request:
  252. :return:
  253. """
  254. current_edit_object = self.model_class.objects.filter(pk=pk).first()
  255. if not current_edit_object:
  256. return HttpResponse('要修改的数据不存在,请重新选择')
  257. model_form_class = self.get_model_form_class(request, *args, **kwargs)
  258. if request.method == 'GET':
  259. form = model_form_class(instance=current_edit_object)
  260. return render(request, 'stark/change.html', {'form': form})
  261. form = self.model_form_class(data=request.POST, instance=current_edit_object)
  262. if form.is_valid:
  263. self.save(form, True, *args, **kwargs)
  264. # 在数据库保存成功后,跳转回列表页面(携带原来的参数)
  265. return redirect(self.reverse_list_url(*args, **kwargs))
  266. return render(request, 'stark/change.html', {'form': form})
  267.  
  268. def delete_view(self, request, pk, *args, **kwargs):
  269. """
  270. 删除页面
  271. :param request:
  272. :param pk:
  273. :return:
  274. """
  275. original_list_url = self.reverse_list_url(*args, **kwargs)
  276. if request.method == 'GET':
  277. return render(request, 'stark/delete.html', {'cancel': original_list_url})
  278. self.model_class.objects.filter(pk=pk).delete()
  279. return redirect(original_list_url)
  280.  
  281. def get_url_name(self, params):
  282. app_label, model_name = self.model_class._meta.app_label, self.model_class._meta.model_name
  283. if self.prev:
  284. return f'{app_label}_{model_name}_{self.prev}_{params}'
  285. return f'{app_label}_{model_name}__{params}'
  286.  
  287. @property
  288. def get_list_url_name(self):
  289. """
  290. 获取列表页面URL的name
  291. :return:
  292. """
  293. return self.get_url_name('list')
  294.  
  295. @property
  296. def get_add_url_name(self):
  297. """
  298. 获取添加页面URL的name
  299. :return:
  300. """
  301. return self.get_url_name('add')
  302.  
  303. @property
  304. def get_edit_url_name(self):
  305. """
  306. 获取编辑页面URL的name
  307. :return:
  308. """
  309. return self.get_url_name('edit')
  310.  
  311. @property
  312. def get_delete_url_name(self):
  313. """
  314. 获取删除页面URL的name
  315. :return:
  316. """
  317. return self.get_url_name('delete')
  318.  
  319. def reverse_common_url(self, name, *args, **kwargs):
  320. """
  321. 生成带有原搜索条件的URL
  322. :param name: url
  323. :param args:
  324. :param kwargs:
  325. :return:
  326. """
  327. name = f'{self.site.namespace}:{name}'
  328. base_url = reverse(name, args=args, kwargs=kwargs)
  329. if not self.request.GET:
  330. reverse_url = base_url
  331. else:
  332. params = self.request.GET.urlencode()
  333. new_query_dict = QueryDict(mutable=True)
  334. new_query_dict['_filter'] = params
  335. reverse_url = f'{base_url}?{new_query_dict.urlencode()}'
  336. return reverse_url
  337.  
  338. def reverse_add_url(self, *args, **kwargs):
  339. """
  340. 带有原搜索条件的增加URL
  341. :param args:
  342. :param kwargs:
  343. :return:
  344. """
  345. return self.reverse_common_url(self.get_add_url_name, *args, **kwargs)
  346.  
  347. def reverse_edit_url(self, *args, **kwargs):
  348. """
  349. 带有原搜索条件的编辑URL
  350. :param args:
  351. :param kwargs:
  352. :return:
  353. """
  354. return self.reverse_common_url(self.get_edit_url_name, *args, **kwargs)
  355.  
  356. def reverse_delete_url(self, *args, **kwargs):
  357. """
  358. 带有原搜索条件的删除URL
  359. :param args:
  360. :param kwargs:
  361. :return:
  362. """
  363. return self.reverse_common_url(self.get_delete_url_name, *args, **kwargs)
  364.  
  365. def reverse_list_url(self, *args, **kwargs):
  366. name = f'{self.site.namespace}:{self.get_list_url_name}'
  367. base_url = reverse(name, args=args, kwargs=kwargs)
  368. params = self.request.GET.get('_filter')
  369. if not params:
  370. return base_url
  371. return f'{base_url}?{params}'
  372.  
  373. def wrapper(self, func):
  374. """
  375. 当每一个request请求进来的时候,把request赋值给类的数据属性self.request
  376. :param func: request请求对应的视图函数
  377. :return:
  378. """
  379.  
  380. @functools.wraps(func) # 保留原函数的原信息,写装饰器建议写上这个。
  381. def inner(request, *args, **kwargs):
  382. self.request = request
  383. return func(request, *args, **kwargs)
  384.  
  385. return inner
  386.  
  387. def get_urls(self):
  388. patterns = [
  389. re_path(r'^list/$', self.wrapper(self.list_view), name=self.get_list_url_name),
  390. re_path(r'^add/$', self.wrapper(self.add_view), name=self.get_add_url_name),
  391. re_path(r'^edit/(\d+)/$', self.wrapper(self.edit_view), name=self.get_edit_url_name),
  392. re_path(r'^delete/(\d+)/$', self.wrapper(self.delete_view), name=self.get_delete_url_name),
  393. ]
  394.  
  395. patterns.extend(self.extra_urls())
  396. return patterns
  397.  
  398. def extra_urls(self):
  399. return []
  400.  
  401. class StarkSite(object):
  402. def __init__(self):
  403. self._registry = []
  404. self.app_name = 'stark'
  405. self.namespace = 'stark'
  406.  
  407. def register(self, model_class, handler_class=None, prev=None):
  408. """
  409. :param model_class: 是models中的数据库表对应的类。
  410. :param handler_class: 处理请求的视图函数所在的类
  411. :param prev: 生成URL的前缀
  412. :return:
  413. """
  414.  
  415. if not handler_class:
  416. handler_class = StarkHandler
  417. self._registry.append(
  418. {'model_class': model_class, 'handler': handler_class(self, model_class, prev), 'prev': prev})
  419.  
  420. def get_urls(self):
  421. patterns = []
  422. for item in self._registry:
  423. model_class = item['model_class']
  424. handler = item['handler']
  425. prev = item['prev']
  426. app_name, model_name = model_class._meta.app_label, model_class._meta.model_name
  427. if prev:
  428. patterns.append(
  429. re_path(rf'^{app_name}/{model_name}/{prev}/', (handler.get_urls(), None, None)))
  430. else:
  431. patterns.append(
  432. re_path(rf'^{app_name}/{model_name}/', (handler.get_urls(), None, None)))
  433.  
  434. return patterns
  435.  
  436. @property
  437. def urls(self):
  438. return self.get_urls(), self.app_name, self.namespace
  439.  
  440. site = StarkSite()

二、业务代码

  1. from stark.service.core_func import site, StarkHandler, StarkModelForm, get_choice_text
  2.  
  3. from web import models
  4.  
  5. class UserInfoModelForm(StarkModelForm):
  6. class Meta:
  7. model = models.UserInfo
  8. fields = ['name', 'gender', 'classes', 'age', 'email']
  9.  
  10. class DepartmentHandler(StarkHandler):
  11. list_display = ['title']
  12.  
  13. class UserInfoHandler(StarkHandler):
  14. per_page_data = 5
  15. order_list = ['gender']
  16. model_form_class = UserInfoModelForm
  17. search_list = ['name__contains', 'email__contains', ]
  18. action_list = [StarkHandler.action_multi_delete, ]
  19. list_display = [
  20. StarkHandler.display_checkbox,
  21. 'name',
  22. get_choice_text('性别', 'gender'),
  23. get_choice_text('班级', 'classes'),
  24. 'age', 'email', 'department',
  25. StarkHandler.display_edit,
  26. StarkHandler.display_delete,
  27. ]
  28.  
  29. def save(self, form, is_update=False, *args, **kwargs):
  30. form.instance.department_id = 1
  31. form.save()
  32.  
  33. site.register(models.Department, DepartmentHandler) # 给部门的url增加了前缀:/stark/web/department/private/
  34. site.register(models.UserInfo, UserInfoHandler)

三、模板渲染

  1. {% extends 'layout.html' %}
  2.  
  3. {% block css %}
  4. <link rel="stylesheet" href="">
  5. {% endblock css %}
  6.  
  7. {% block content %}
  8. <div class="custom-container">
  9.  
  10. <!-- 搜索 -->
  11. {% if search_list %}
  12. <div class="up-down-space right">
  13. <form method="get" class="form-inline">
  14. <div class="form-group">
  15. <input class="form-control" type="text" name="q" value="{{ search_value }}"
  16. 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. {% endif %}
  24. <!-- 搜索结束 -->
  25.  
  26. <!-- 批量操作 -->
  27. <form method="post" class="form-inline">
  28. {% csrf_token %}
  29. {% if action_dict %}
  30. <div class="left up-down-space">
  31. <div class="form-group">
  32. <select class="form-control" name="action">
  33. <option value="">请选择操作</option>
  34. {% for func_name,func_text in action_dict.items %}
  35. <option value="{{ func_name }}">{{ func_text }}</option>
  36. {% endfor %}
  37. </select>
  38. <input class="btn btn-primary" type="submit" value="执行"/>
  39. </div>
  40. </div>
  41. {% endif %}
  42. <!-- 批量操作结束 -->
  43.  
  44. <!-- 添加按钮开始 -->
  45. {% if add_btn %}
  46. <div class="up-down-space left add_btn">
  47. {{ add_btn|safe }}
  48. </div>
  49. {% endif %}
  50. <!-- 添加按钮结束 -->
  51.  
  52. <table class="table table-bordered">
  53. <thead>
  54. <tr>
  55. {% for item in header_list %}
  56. <th>{{ item }}</th>
  57. {% endfor %}
  58. </tr>
  59. </thead>
  60. <tbody>
  61. {% for row in body_list %}
  62. <tr>
  63. {% for ele in row %}
  64. <td>{{ ele }}</td>
  65. {% endfor %}
  66. </tr>
  67. {% endfor %}
  68. </tbody>
  69. </table>
  70. </form>
  71. <nav>
  72. <ul class="pagination">
  73. {{ pager.page_html|safe }}
  74. </ul>
  75. </nav>
  76. </div>
  77. {% endblock content %}

strak组件(10):批量操作的更多相关文章

  1. strak组件(9):关键字搜索

    效果图: 在列表视图函数增加搜索功能. 新增函数 def get_search_list(self) 钩子方法,获取搜索条件 一.strak组件 strak/service/core_func.py ...

  2. Bootstrap入门(十六)组件10:well和具有响应式特性的嵌入内容

    Bootstrap入门(十六)组件10:well和具有响应式特性的嵌入内容 well组件可以为内容增添一种切入效果. 具有响应式特性的嵌入内容可以根据被嵌入内容的外部容器的宽度,自动创建一个固定的比例 ...

  3. strak组件(6):列表定制列应用和引入静态文件

    效果图: 新增函数 def get_choice_text(title, field) 闭包函数,显示choice字段 def inner(self, obj=None, is_header=None ...

  4. amazeui学习笔记--css(常用组件10)--导航条Topbar

    amazeui学习笔记--css(常用组件10)--导航条Topbar 一.总结 1. 导航条:就是页面最顶端的导航条:在容器上添加 .am-topbar class,然后按照示例组织所需内容.< ...

  5. strak组件(8):基本增删改查实现及应用和排序

    效果图: 新增函数: def reverse_common_url(self, name, *args, **kwargs) 反向生成url,需要传增删改的url作为参数,携带原参数 def reve ...

  6. stark组件之批量操作【模仿Django的admin】

    一.先看下django的admin是如何实现批量操作 首先在配置类中定义一个函数 然后我们为这个函数对象设置一个属性,这个属性主要用来显示在select标签中显示的文本内容 最后把函数对象放到一个ac ...

  7. React入门---可复用组件-10

    主要对props更多重要的特性进行学习; 还是用之前代码, index.js代码为: var React = require('react'); var ReactDOM = require('rea ...

  8. strak组件(5):为列表定制预留钩子方法

    效果图:  新增函数 def get_list_display(self): 获取页面上应该显示的列,预留的自定义扩展,例如:以后根据用户的不同显示不同的 一.stark组件 stark/servic ...

  9. strak组件(3):URL别名的优化

    将生成URL别名的功能进行解耦.效果和上一节的一样. 效果图: 新增函数 get_url_name(self, param) # 生成url别名,需要一个参数(list/add/edit/delete ...

随机推荐

  1. 粗看ES6之解构赋值

    标签: javascript es6 什么是解构赋值? 示例如下: <!DOCTYPE html> <html> <head> <meta charset=& ...

  2. ajax提交 返回值为undefined

    easyui  form 表单提交成功,但是返回值为undefined,原因是返回值不是json格式,是字符串的格式,那么解决办法就是把其转化成json格式 示例: $(function () { / ...

  3. (二)JavaScript之[函数]与[作用域]

    3].函数 /** * 事件驱动函数. * 函数执行可重复使用的代码 * * 1.带参的函数 * 2.带返回值的函数 * 3.局部变量 * * 4.全局变量 * 在函数外的:不用var声明,未声明直接 ...

  4. StackTrack for debug

    System.Diagnostics.Debug.WriteLine("Serial port. {0},{1}", this.GetType().FullName, new Sy ...

  5. CloudWAN

    类型: 定制服务 软件包: collaboration Enterprise integration integrated industry internet IT service/informati ...

  6. [topcoder]SRM 633 DIV 2

    第一题,http://community.topcoder.com/stat?c=problem_statement&pm=13462&rd=16076 模拟就可以了. #includ ...

  7. javaWeb上移下移(SpringMVC+Mabits+MySql)

    文章已移至:https://blog.csdn.net/baidu_35468322/article/details/79643356 移动之前: 移动之后: 1.控制层 /** * 修改排序 * * ...

  8. Java I/O 工作机制(二) —— Java 的 I/O 的交互方式分析

    简介: BIO:同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善.  ...

  9. C语言头文件怎么写?(转载)

    ---恢复内容开始--- c语言头文件怎么写?我一直有这样的疑问,但是也一直没去问问到底咋回事:所以今天一定要把它弄明白! 其实学会写头文件之后可以为我们省去不少事情,可以避免书写大量的重复代码,还在 ...

  10. openwrt定制管理

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/qianguozheng/article/details/24673097 近期这个比較火,可是改了东 ...