今日内容前戏

静态字段和字段

先来看下面一段代码

  1. class Foo:
  2. x = 1 # 类变量、静态字段、静态属性
  3. def __init__(self):
  4. y = 6 # 实例变量、字段、对象属性
  5. # 实例方法
  6. def func(self):
  7. pass
  8. # 静态方法
  9. @staticmethod
  10. def func():
  11. pass
  12. # 类方法
  13. @classmethod
  14. def func():
  15. pass
  16. @property
  17. def start(self)
  18. 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. res = '&'.join(list_1)
  7. 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

Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图1

修改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. # Create your views here.
  3. def index(request):
  4. print(request.GET,type(request.GET))
  5. return HttpResponse('ok')

启动django项目,访问index页面

Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图2

查看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': ['20'], 'name': ['xiao']}> <class 'django.http.request.QueryDict'>

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

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

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

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

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

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

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

修改views.py,增加一个值

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

刷新页面,效果如下:

Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图3

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

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

  1. request.GET._mutable = True
一定要重启django,否则不生效!刷新页面,效果如下: Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图4 查看Pycharm控制台输出: python <QueryDict: {'age': ['20'], 'name': ['xiao']}> k1=666&age=20&name=xiao 发现k1已经添加进去了! 为了避免对后续的程序有影响,需要使用深copy python from django.shortcuts import render,HttpResponse # Create your views here. def index(request): from django.http.request import QueryDict import copy print(request.GET) params = copy.deepcopy(request.GET) # 使用深copy params._mutable = True # 允许修改 params['k1'] = 666 # 增加一个值 v = params.urlencode() print(v) return HttpResponse('ok') 重启django项目,刷新页面,效果同上! 查看Pycharm控制台输出,效果同上! QueryDict 源码里面,提供了方法deepcopy** ,它也是做深copy的 python def __deepcopy__(self, memo): result = self.__class__('', mutable=True, encoding=self.encoding) memo[id(self)] = result for key, value in six.iterlists(self): result.setlist(copy.deepcopy(key, memo), copy.deepcopy(value, memo)) return result 查看request.GET.copy()的源码,它实际上,就是调用了deepcopy python def copy(self): """Returns a mutable copy of this object.""" return self.__deepcopy__({}) 修改views.py,改用request.GET.copy() python from django.shortcuts import render,HttpResponse # Create your views here. def index(request): from django.http.request import QueryDict import copy print(request.GET) params = request.GET.copy() # 使用深copy params._mutable = True # 允许修改 params['k1'] = 666 # 增加一个值 v = params.urlencode() print(v) return HttpResponse('ok') 重启django项目,刷新页面,效果同上! 查看Pycharm控制台输出,效果同上! 修改views.py,增加一个列表 python from django.shortcuts import render,HttpResponse # Create your views here. def index(request): from django.http.request import QueryDict import copy print(request.GET) params = request.GET.copy() # 使用深copy params._mutable = True # 允许修改 params['k1'] = 666 # 增加一个值 params['k2'] = [11,12] # 增加一个列表 v = params.urlencode() print(v) return HttpResponse('ok') 刷新页面,查看Pycharm控制台输出: python <QueryDict: {'age': ['20'], 'name': ['xiao']}> name=xiao&k2=%5B11%2C+12%5D&k1=666&age=20 发现k2=%5B11%2C+12%5D,这并不是我们想要的结果! 列表需要使用setlist才行 修改views.py,使用setlist python from django.shortcuts import render,HttpResponse # Create your views here. def index(request): from django.http.request import QueryDict import copy print(request.GET) params = request.GET.copy() # 使用深copy params._mutable = True # 允许修改 params['k1'] = 666 # 增加一个值 params.setlist('k4',[11,12]) # 增加一个列表 v = params.urlencode() print(v) return HttpResponse('ok') 重启django项目,刷新页面。查看Pycharm控制台输出: python <QueryDict: {'name': ['xiao'], 'age': ['20']}> k1=666&k4=11&k4=12&name=xiao&age=20 可以看到k4=11&k4=12,它分别设置了2个值! 那么获取k4使用getlist python from django.shortcuts import render,HttpResponse # Create your views here. def index(request): from django.http.request import QueryDict import copy print(request.GET) params = request.GET.copy() # 使用深copy params._mutable = True # 允许修改 params['k1'] = 666 # 增加一个值 params.setlist('k4',[11,12]) # 增加一个列表 print(params.get('k1')) print(params.getlist('k4')) # 获取列表,使用getlist v = params.urlencode() print(v) return HttpResponse('ok') 刷新页面,查看Pycharm控制台输出: python <QueryDict: {'name': ['xiao'], 'age': ['20']}> 666 [11, 12] k4=11&k4=12&k1=666&name=xiao&age=20 在列表中,增加一个值,使用append 修改views.py python from django.shortcuts import render,HttpResponse # Create your views here. def index(request): from django.http.request import QueryDict import copy print(request.GET) params = request.GET.copy() # 使用深copy params._mutable = True # 允许修改 params['k1'] = 666 # 增加一个值 params.setlist('k4',[11,12]) # 增加一个列表 old = params.getlist('k4') # 获取列表 old.append('v4') # 最加一个值 params.setlist('k4',old) # 重新设置 v = params.urlencode() print(v) return HttpResponse('ok') 刷新页面,查看Pycharm控制台输出: python <QueryDict: {'name': ['xiao'], 'age': ['20']}> name=xiao&k1=666&age=20&k4=11&k4=12&k4=v4 可以发现,k4有3个值 ## 保留原来搜索条件 先来访问一个链接: python http://127.0.0.1:8000/index/?name=xiao&age=20 需要在视图函数中的request.GET中,添加一个值_filter。用来保存原来的搜索条件 python _filter = "name=xiao&age=20" 最后变成QueryDict,如何操作? 修改views.py python from django.shortcuts import render,HttpResponse # Create your views here. def index(request): from django.http.request import QueryDict url_params_str = request.GET.urlencode() # mutable=True 表示可修改 query_dic = QueryDict(mutable=True) # 添加一个key为_filter query_dic['_filter'] = url_params_str print(query_dic) # 重新编码 new_params = query_dic.urlencode() print(new_params) return HttpResponse('ok') 刷新页面,查看Pycharm控制台输出: python <QueryDict: {'_filter': ['name=xiao&age=20']}> _filter=name%3Dxiao%26age%3D20 最后一个值,使用urlencode编码了 ### 举例: 假设一个场景,先搜索到了一些学生。注意:此时url是有搜索条件的 点击添加按钮,跳转到添加页面。注意:此时的url带有原搜索条件 添加完成后,跳转到原来的页面。注意:此时的url带有原搜索条件 修改urls.py,增加路径 python urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index), url(r'^add_stu/', views.add_stu), ] 修改views.py,增肌视图函数 python from django.shortcuts import render,HttpResponse,redirect # Create your views here. def index(request): from django.http.request import QueryDict url_params_str = request.GET.urlencode() # mutable=True 表示可修改 query_dic = QueryDict(mutable=True) # 添加一个key为_filter query_dic['_filter'] = url_params_str print(query_dic) # 重新编码 new_params = query_dic.urlencode() print(new_params) # 跳转地址 target_url = "/add_stu?%s" %new_params print(target_url) # 渲染一个a标签 return HttpResponse('<a href="%s">添加学生</a>'%target_url) def add_stu(request): if request.method == "GET": return render(request,"add_stu.html") # 接收到数据,保存到数据库... # 获取搜索条件 origin_params = request.GET.get('_filter') # 返回地址,保留原搜索添加 back_url = "/index/?%s" %origin_params # 重定向页面 return redirect(back_url) 重启django程序,访问页面 注意:这里直接访问的是带有搜索条件的 python http://127.0.0.1:8000/index/?name=xiao&age=20 效果如下: Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图5 点击添加学生,页面跳转 注意:这里面的_filter,就是原来的搜索条件 Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图6 点击提交按钮,页面跳转 此时页面:还是带有原来的搜索条件 Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图7 ## Q查询高级用法 一般页面使用的搜索功能,都是使用了模糊搜索 比如:查询name包含 “大”或者email包含 “qq”的记录 python q = Q() q.connecter = "OR" # 使用OR作为连接符 # 合并条件进行查询,__contains表示使用like查询 q.children.append(('name__contains', '大')) q.children.append(('email__contains', 'qq')) 它相当于 name like '%大%' OR email like '%qq%' # 一、批量操作 务必下载github代码: https://github.com/987334176/luffy_stark/archive/v1.1.zip 因为下面的内容,都是这份代码来修改的! 修改stark—>templates—>stark—>changelist.html,增加select标签 html {% extends 'stark/layout.html' %} {% block content %} <h1>列表页面</h1> <div> {% if add_btn %} <div style="margin: 5px 0;"> {{ add_btn }} </div> {% endif %} <form class="form-inline" method="post"> {% csrf_token %} <div class="form-group"> <select name="action" class="form-control" style="min-width: 200px;"> <option>请选择功能</option> <option value="1">批量删除</option> <option value="2">初始化</option> </select> <input class="btn btn-primary" type="submit" value="执行"> </div> </form> <table class="table table-bordered"> <thead> <tr> {% for item in header_list %} <th>{{ item }}</th> {% endfor %} </tr> </thead> <tbody> {% for row_list in body_list %} <tr> {% for col in row_list %} <td>{{ col }}</td> {% endfor %} </tr> {% endfor %} </tbody> </table> </div> {% endblock %} 修改 app01—>stark.py,注释部分代码。增加复选框显示 python from stark.server.stark import site, StarkConfig from app01 import models from django import forms from django.shortcuts import render from django.conf.urls import url class UserInfoConfig(StarkConfig): list_display = ['id', 'username'] class DepartModelForm(forms.ModelForm): class Meta: model = models.Depart fields = "__all__" def clean_name(self): # 定义钩子 # print(self.cleaned_data['name']) return self.cleaned_data['name'] class DepartConfig(StarkConfig): list_display = [StarkConfig.display_checkbox,'name', 'tel', 'user'] # model_form_class = DepartModelForm # def get_add_btn(self): # 返回None,表示不显示添加按钮 # pass # def changelist_view(self, request): # 重写changelist_view方法 # # 渲染自定义的列表页面 # return render(request,'stark/custom_list.html') # def get_urls(self): # 自定义路由 # info = self.model_class._meta.app_label, self.model_class._meta.model_name # # urlpatterns = [ # url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info), # ] # return urlpatterns site.register(models.UserInfo, UserInfoConfig) site.register(models.Depart, DepartConfig) 访问页面: http://127.0.0.1:8000/stark/app01/userinfo/list/ 点击下拉菜单,效果如下: Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图8 下拉框里面的元素应该是动态的,它是可配置的,每个选项包含了一个id。 如果使用表单提交,那么表单数据就很大了 修改stark—>server—>stark.py,添加变量actionlist。用来定义要批量操作的选项 为了防止列表来回更改,导致列表元素重复。使用extend 添加multidelete和multiinit方法,将actionlist传给模板 ```python from django.conf.urls import url from django.shortcuts import HttpResponse,render,redirect from types import FunctionType from django.utils.safestring import marksafe from django.urls import reverse from django import forms class StarkConfig(object): def __init(self,model_class,site): self.model_class = model_class self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框 if header: # 输出中文 return “选择” # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id return mark_safe(‘‘ %row.pk) def display_edit(self, row=None, header=False): if header: return “编辑” return mark_safe( ‘‘ % self.reverse_edit_url(row)) def display_del(self, row=None, header=False): if header: return “删除” return mark_safe( ‘‘ % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False): if header: return “操作” tpl = “”” | “”” % (self.reverse_edit_url(row), self.reverse_del_url(row),) return mark_safe(tpl) order_by = [] # 需要排序的字段,由用户自定义 list_display = [] # 定义显示的列,由用户自定义 model_form_class = None # form组件需要的model_class action_list = [] # 批量操作方法 def multi_delete(self): # 批量删除 pass def multi_init(self): # 批量初始化 pass def get_order_by(self): # 获取排序列表 return self.order_by def get_list_display(self): # 获取显示的列 return self.list_display def get_add_btn(self): # 显示添加按钮 return mark_safe(‘添加‘ % self.reverse_add_url()) def get_model_form_class(self): “”” 获取ModelForm类 :return: “”” if self.model_form_class: return self.model_form_class class AddModelForm(forms.ModelForm): class Meta: model = self.model_class fields = “__all“ return AddModelForm def get_action_list(self): # 获取批量操作方法 val = [] # 空列表 # 扩展列表的元素 val.extend(self.action_list) return val def changelist_view(self, request): “”” 所有URL查看列表页面 :param request: :return: “”” # 根据排序列表进行排序 queryset = self.model_class.objects.all().order_by(*self.get_order_by()) ### 批量操作 ### action_list = self.get_action_list() ### 添加按钮 ### add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列 header_list = [] # 定义头部,用来显示verbose_name if list_display: for name_or_func in list_display: if isinstance(name_or_func,FunctionType): # 执行函数,默认显示中文 verbose_name = name_or_func(self,header=True) else: # 获取指定字段的verbose_name verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name) else: # 如果list_display为空,添加表名 header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset: # 这里的row是对象,它表示表里面的一条数据 row_list = [] # 展示每一行数据 if not list_display: # 如果不在list_display里面 # 添加对象 row_list.append(row) body_list.append(row_list) continue for name_or_func in list_display: if isinstance(name_or_func,FunctionType): val = name_or_func(self,row=row) # 执行函数获取,传递row对象 else: # 使用反射获取对象的值 val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn return render(request,’stark/changelist.html’,{‘header_list’:header_list,’body_list’:body_list,’add_btn’:add_btn,’action_list’:action_list}) def add_view(self, request): “”” 所有的添加页面,都在此方法处理 使用ModelForm实现 :param request: :return: “”” # 添加数据,使用ModelForm AddModelForm = self.get_model_form_class() if request.method == “GET”: form = AddModelForm() return render(request,’stark/change.html’,{‘form’:form}) form = AddModelForm(request.POST) # 接收POST数据 if form.is_valid(): # 验证数据 form.save() # 自动保存数据 # 反向生成url,跳转到列表页面 return redirect(self.reverse_list_url()) # 渲染页面,此时会保存表单数据 return render(request, ‘stark/change.html’, {‘form’: form}) def change_view(self, request, pk): “”” 所有编辑页面 :param request: :param pk: :return: “”” # 查看单条数据 obj = self.model_class.objects.filter(pk=pk).first() if not obj: return HttpResponse(‘数据不存在’) # 获取model_form类 ModelFormClass = self.get_model_form_class() if request.method == ‘GET’: # instance表示生成默认值 form = ModelFormClass(instance=obj) # 渲染页面,添加和修改可以共用一个一个模板文件 return render(request, ‘stark/change.html’, {‘form’: form}) # instance = obj 表示指定给谁做修改 form = ModelFormClass(data=request.POST, instance=obj) if form.is_valid(): form.save() # 修改数据 # 跳转到列表页面 return redirect(self.reverse_list_url()) return render(request, ‘stark/change.html’, {‘form’: form}) def delete_view(self, request, pk): “”” 所有删除页面 :param request: :param pk: :return: “”” if request.method == “GET”: # cancel_url表示用户点击取消时,跳转到列表页面 return render(request, ‘stark/delete.html’, {‘cancel_url’: self.reverse_list_url()}) # 定位单条数据,并删除! self.model_class.objects.filter(pk=pk).delete() return redirect(self.reverse_list_url()) def wrapper(self,func): pass def get_urls(self): info = self.model_class._meta.app_label, self.model_class._meta.model_name urlpatterns = [ url(r’^list/$’, self.changelist_view, name=’%s%schangelist’ % info), url(r’^add/$’, self.add_view, name=’%s%sadd’ % info), url(r’^(?P\d+)/change/‘, self.change_view, name=’%s%schange’ % info), url(r’^(?P\d+)/del/‘, self.delete_view, name=’%s%sdel’ % 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%schangelist’ % (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%sadd’ % (namespace, app_label, model_name) add_url = reverse(name) return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url app_label = self.model_class._meta.app_label # app名 model_name = self.model_class._meta.model_name # 表名 namespace = self.site.namespace # 命名空间 # 拼接字符串,这里为change name = ‘%s:%s%schange’ % (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%sdel’ % (namespace, applabel, modelname) delurl = reverse(name, kwargs={‘pk’: row.pk}) return delurl @property def urls(self): return self.get_urls() class AdminSite(object): def __init(self): self._registry = {} self.app_name = ‘stark’ self.namespace = ‘stark’ def register(self,model_class,stark_config=None): # not None的结果为Ture if not stark_config: # 也就是说,当其他应用调用register时,如果不指定stark_config参数 # 那么必然执行下面这段代码! # stark_config和StarkConfig是等值的!都能实例化 stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class # self指的是AdminSite类 self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典 “”” { app01.models.UserInfo:StarkConfig(app01.models.UserInfo) app02.models.Role:RoleConfig(app02.models.Role) } “”” # for k, v in self._registry.items(): # print(k,v) def get_urls(self): urlpatterns = [] for k, v in self._registry.items(): # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象 # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象 app_label = k._meta.app_label model_name = k._meta.model_name urlpatterns.append(url(r’^%s/%s/‘ % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property def urls(self): # 调用get_urls方法 # self.app_name和self.namespace值是一样的,都是stark return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类 修改app01-->stark.py,定义action_listpython from stark.server.stark import site, StarkConfig from app01 import models from django import forms from django.shortcuts import render from django.conf.urls import url class UserInfoConfig(StarkConfig): list_display = [‘id’, ‘username’] class DepartModelForm(forms.ModelForm): class Meta: model = models.Depart fields = “__all“ def clean_name(self): # 定义钩子 # print(self.cleaned_data[‘name’]) return self.cleaned_data[‘name’] class DepartConfig(StarkConfig): list_display = [StarkConfig.display_checkbox,’name’, ‘tel’, ‘user’] # model_form_class = DepartModelForm # 批量操作 action_list = [StarkConfig.multi_delete,StarkConfig.multi_init] # def get_add_btn(self): # 返回None,表示不显示添加按钮 # pass # def changelist_view(self, request): # 重写changelist_view方法 # # 渲染自定义的列表页面 # return render(request,’stark/custom_list.html’) # def get_urls(self): # 自定义路由 # info = self.model_class._meta.app_label, self.model_class._meta.model_name # # urlpatterns = [ # url(r’^list/$’, self.changelist_view, name=’%s%schangelist’ % info), # ] # return urlpatterns site.register(models.UserInfo, UserInfoConfig) site.register(models.Depart, DepartConfig) 修改 stark-->templates-->stark-->changelist.html,使用for循环action_listhtml {% extends ‘stark/layout.html’ %} {% block content %}

列表页面

{% if addbtn %}
{{ addbtn }}
{% endif %}
{% csrftoken %}
{% for item in headerlist %} {% endfor %} {% for rowlist in body_list %} {% for col in row_list %} {% endfor %} {% endfor %}
{{ item }}
{{ col }}
{% endblock %} 重启django,查看页面:[http://127.0.0.1:8000/stark/app01/depart/list/](http://127.0.0.1:8000/stark/app01/depart/list/) 效果如下: ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602234604669-098630f5-afac-4932-b06a-f03e29ca4ba2.png) 提示 变量和属性不能从下划线开始 怎么办呢?让后端把函数名传过来,使用列表生成式 修改stark-->server-->stark.py,使用列表生成式python from django.conf.urls import url from django.shortcuts import HttpResponse,render,redirect from types import FunctionType from django.utils.safestring import mark_safe from django.urls import reverse from django import forms class StarkConfig(object): def __init(self,model_class,site): self.model_class = model_class self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框 if header: # 输出中文 return “选择” # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id return mark_safe(‘‘ %row.pk) def display_edit(self, row=None, header=False): if header: return “编辑” return mark_safe( ‘‘ % self.reverse_edit_url(row)) def display_del(self, row=None, header=False): if header: return “删除” return mark_safe( ‘‘ % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False): if header: return “操作” tpl = “”” | “”” % (self.reverse_edit_url(row), self.reverse_del_url(row),) return mark_safe(tpl) order_by = [] # 需要排序的字段,由用户自定义 list_display = [] # 定义显示的列,由用户自定义 model_form_class = None # form组件需要的model_class action_list = [] # 批量操作方法 def multi_delete(self): # 批量删除 pass def multi_init(self): # 批量初始化 pass def get_order_by(self): # 获取排序列表 return self.order_by def get_list_display(self): # 获取显示的列 return self.list_display def get_add_btn(self): # 显示添加按钮 return mark_safe(‘添加‘ % self.reverse_add_url()) def get_model_form_class(self): “”” 获取ModelForm类 :return: “”” if self.model_form_class: return self.model_form_class class AddModelForm(forms.ModelForm): class Meta: model = self.model_class fields = “__all
“ return AddModelForm def get_action_list(self): # 获取批量操作方法 val = [] # 空列表 # 扩展列表的元素 val.extend(self.action_list) return val def changelist_view(self, request): “”” 所有URL查看列表页面 :param request: :return: “”” # 根据排序列表进行排序 queryset = self.model_class.objects.all().order_by(*self.get_order_by()) ### 批量操作 ### action_list = self.get_action_list() action_list = [ func.__name for func in action_list ] ### 添加按钮 ### add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列 header_list = [] # 定义头部,用来显示verbose_name if list_display: for name_or_func in list_display: if isinstance(name_or_func,FunctionType): # 执行函数,默认显示中文 verbose_name = name_or_func(self,header=True) else: # 获取指定字段的verbose_name verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name) else: # 如果list_display为空,添加表名 header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset: # 这里的row是对象,它表示表里面的一条数据 row_list = [] # 展示每一行数据 if not list_display: # 如果不在list_display里面 # 添加对象 row_list.append(row) body_list.append(row_list) continue for name_or_func in list_display: if isinstance(name_or_func,FunctionType): val = name_or_func(self,row=row) # 执行函数获取,传递row对象 else: # 使用反射获取对象的值 val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn return render(request,’stark/changelist.html’,{‘header_list’:header_list,’body_list’:body_list,’add_btn’:add_btn,’action_list’:action_list}) def add_view(self, request): “”” 所有的添加页面,都在此方法处理 使用ModelForm实现 :param request: :return: “”” # 添加数据,使用ModelForm AddModelForm = self.get_model_form_class() if request.method == “GET”: form = AddModelForm() return render(request,’stark/change.html’,{‘form’:form}) form = AddModelForm(request.POST) # 接收POST数据 if form.is_valid(): # 验证数据 form.save() # 自动保存数据 # 反向生成url,跳转到列表页面 return redirect(self.reverse_list_url()) # 渲染页面,此时会保存表单数据 return render(request, ‘stark/change.html’, {‘form’: form}) def change_view(self, request, pk): “”” 所有编辑页面 :param request: :param pk: :return: “”” # 查看单条数据 obj = self.model_class.objects.filter(pk=pk).first() if not obj: return HttpResponse(‘数据不存在’) # 获取model_form类 ModelFormClass = self.get_model_form_class() if request.method == ‘GET’: # instance表示生成默认值 form = ModelFormClass(instance=obj) # 渲染页面,添加和修改可以共用一个一个模板文件 return render(request, ‘stark/change.html’, {‘form’: form}) # instance = obj 表示指定给谁做修改 form = ModelFormClass(data=request.POST, instance=obj) if form.is_valid(): form.save() # 修改数据 # 跳转到列表页面 return redirect(self.reverse_list_url()) return render(request, ‘stark/change.html’, {‘form’: form}) def delete_view(self, request, pk): “”” 所有删除页面 :param request: :param pk: :return: “”” if request.method == “GET”: # cancel_url表示用户点击取消时,跳转到列表页面 return render(request, ‘stark/delete.html’, {‘cancel_url’: self.reverse_list_url()}) # 定位单条数据,并删除! self.model_class.objects.filter(pk=pk).delete() return redirect(self.reverse_list_url()) def wrapper(self,func): pass def get_urls(self): info = self.model_class._meta.app_label, self.model_class._meta.model_name urlpatterns = [ url(r’^list/$’, self.changelist_view, name=’%s%schangelist’ % info), url(r’^add/$’, self.add_view, name=’%s%sadd’ % info), url(r’^(?P\d+)/change/‘, self.change_view, name=’%s%schange’ % info), url(r’^(?P\d+)/del/‘, self.delete_view, name=’%s%sdel’ % 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%schangelist’ % (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%sadd’ % (namespace, app_label, model_name) add_url = reverse(name) return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url app_label = self.model_class._meta.app_label # app名 model_name = self.model_class._meta.model_name # 表名 namespace = self.site.namespace # 命名空间 # 拼接字符串,这里为change name = ‘%s:%s%schange’ % (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%sdel’ % (namespace, applabel, model_name) del_url = reverse(name, kwargs={‘pk’: row.pk}) return del_url @property def urls(self): return self.get_urls() class AdminSite(object): def __init(self): self._registry = {} self.app_name = ‘stark’ self.namespace = ‘stark’ def register(self,model_class,stark_config=None): # not None的结果为Ture if not stark_config: # 也就是说,当其他应用调用register时,如果不指定stark_config参数 # 那么必然执行下面这段代码! # stark_config和StarkConfig是等值的!都能实例化 stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class # self指的是AdminSite类 self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典 “”” { app01.models.UserInfo:StarkConfig(app01.models.UserInfo) app02.models.Role:RoleConfig(app02.models.Role) } “”” # for k, v in self._registry.items(): # print(k,v) def get_urls(self): urlpatterns = [] for k, v in self._registry.items(): # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象 # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象 app_label = k._meta.app_label model_name = k._meta.model_name urlpatterns.append(url(r’^%s/%s/‘ % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property def urls(self): # 调用get_urls方法 # self.app_name和self.namespace值是一样的,都是stark return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类 修改 stark-->templates-->stark-->changelist.html,修改变量名html {% extends ‘stark/layout.html’ %} {% block content %}

列表页面

{% if add_btn %}
{{ add_btn }}
{% endif %}
{% csrf_token %}
{% for item in header_list %} {% endfor %} {% for row_list in body_list %} {% for col in row_list %} {% endfor %} {% endfor %}
{{ item }}
{{ col }}
{% endblock %} ``` 刷新页面,效果如下: Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图9 它是英文的,要中文展示,怎么搞? python一切皆对象,可以
为函数添加一个属性。属性名无所谓! 新建一个a.py文件,内容如下: python def func(): print(123) func.aa = '批量删除' print(func.aa) 执行输出:批量删除 同样的道理,给函数multidelete和multiinit,添加属性text 在列表生成式中,获取text属性 修改stark—>server—>stark.py, ```python from django.conf.urls import url from django.shortcuts import HttpResponse,render,redirect from types import FunctionType from django.utils.safestring import marksafe from django.urls import reverse from django import forms class StarkConfig(object): def init(self,model_class,site): self.model_class = model_class self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框 if header: # 输出中文 return “选择” # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id return mark_safe(‘‘ %row.pk) def display_edit(self, row=None, header=False): if header: return “编辑” return mark_safe( ‘‘ % self.reverse_edit_url(row)) def display_del(self, row=None, header=False): if header: return “删除” return mark_safe( ‘‘ % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False): if header: return “操作” tpl = “”” | “”” % (self.reverse_edit_url(row), self.reverse_del_url(row),) return mark_safe(tpl) order_by = [] # 需要排序的字段,由用户自定义 list_display = [] # 定义显示的列,由用户自定义 model_form_class = None # form组件需要的model_class action_list = [] # 批量操作方法 def multi_delete(self): # 批量删除 pass multi_delete.text = “批量删除” # 添加自定义属性text def multi_init(self): # 批量初始化 pass multi_init.text = “批量初始化” # 添加自定义属性text def get_order_by(self): # 获取排序列表 return self.order_by def get_list_display(self): # 获取显示的列 return self.list_display def get_add_btn(self): # 显示添加按钮 return mark_safe(‘添加‘ % self.reverse_add_url()) def get_model_form_class(self): “”” 获取ModelForm类 :return: “”” if self.model_form_class: return self.model_form_class class AddModelForm(forms.ModelForm): class Meta: model = self.model_class fields = “__all“ return AddModelForm def get_action_list(self): # 获取批量操作方法 val = [] # 空列表 # 扩展列表的元素 val.extend(self.action_list) return val def changelist_view(self, request): “”” 所有URL查看列表页面 :param request: :return: “”” # 根据排序列表进行排序 queryset = self.model_class.objects.all().order_by(*self.get_order_by()) ### 批量操作 ### action_list = self.get_action_list() # 获取text属性 action_list = [ func.text for func in action_list ] ### 添加按钮 ### add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列 header_list = [] # 定义头部,用来显示verbose_name if list_display: for name_or_func in list_display: if isinstance(name_or_func,FunctionType): # 执行函数,默认显示中文 verbose_name = name_or_func(self,header=True) else: # 获取指定字段的verbose_name verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name) else: # 如果list_display为空,添加表名 header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset: # 这里的row是对象,它表示表里面的一条数据 row_list = [] # 展示每一行数据 if not list_display: # 如果不在list_display里面 # 添加对象 row_list.append(row) body_list.append(row_list) continue for name_or_func in list_display: if isinstance(name_or_func,FunctionType): val = name_or_func(self,row=row) # 执行函数获取,传递row对象 else: # 使用反射获取对象的值 val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn return render(request,’stark/changelist.html’,{‘header_list’:header_list,’body_list’:body_list,’add_btn’:add_btn,’action_list’:action_list}) def add_view(self, request): “”” 所有的添加页面,都在此方法处理 使用ModelForm实现 :param request: :return: “”” # 添加数据,使用ModelForm AddModelForm = self.get_model_form_class() if request.method == “GET”: form = AddModelForm() return render(request,’stark/change.html’,{‘form’:form}) form = AddModelForm(request.POST) # 接收POST数据 if form.is_valid(): # 验证数据 form.save() # 自动保存数据 # 反向生成url,跳转到列表页面 return redirect(self.reverse_list_url()) # 渲染页面,此时会保存表单数据 return render(request, ‘stark/change.html’, {‘form’: form}) def change_view(self, request, pk): “”” 所有编辑页面 :param request: :param pk: :return: “”” # 查看单条数据 obj = self.model_class.objects.filter(pk=pk).first() if not obj: return HttpResponse(‘数据不存在’) # 获取model_form类 ModelFormClass = self.get_model_form_class() if request.method == ‘GET’: # instance表示生成默认值 form = ModelFormClass(instance=obj) # 渲染页面,添加和修改可以共用一个一个模板文件 return render(request, ‘stark/change.html’, {‘form’: form}) # instance = obj 表示指定给谁做修改 form = ModelFormClass(data=request.POST, instance=obj) if form.is_valid(): form.save() # 修改数据 # 跳转到列表页面 return redirect(self.reverse_list_url()) return render(request, ‘stark/change.html’, {‘form’: form}) def delete_view(self, request, pk): “”” 所有删除页面 :param request: :param pk: :return: “”” if request.method == “GET”: # cancel_url表示用户点击取消时,跳转到列表页面 return render(request, ‘stark/delete.html’, {‘cancel_url’: self.reverse_list_url()}) # 定位单条数据,并删除! self.model_class.objects.filter(pk=pk).delete() return redirect(self.reverse_list_url()) def wrapper(self,func): pass def get_urls(self): info = self.model_class._meta.app_label, self.model_class._meta.model_name urlpatterns = [ url(r’^list/$’, self.changelist_view, name=’%s%schangelist’ % info), url(r’^add/$’, self.add_view, name=’%s%sadd’ % info), url(r’^(?P\d+)/change/‘, self.change_view, name=’%s%schange’ % info), url(r’^(?P\d+)/del/‘, self.delete_view, name=’%s%sdel’ % 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%schangelist’ % (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%sadd’ % (namespace, app_label, model_name) add_url = reverse(name) return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url app_label = self.model_class._meta.app_label # app名 model_name = self.model_class._meta.model_name # 表名 namespace = self.site.namespace # 命名空间 # 拼接字符串,这里为change name = ‘%s:%s%schange’ % (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%sdel’ % (namespace, applabel, modelname) delurl = reverse(name, kwargs={‘pk’: row.pk}) return delurl @property def urls(self): return self.geturls() 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,刷新页面,效果如下: ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602234604508-67d13ff3-fae7-4963-b5bc-937c17c7e84b.png) 光有中文,无法知道它对应的是哪个函数?value值都是1 修改stark-->server-->stark.py,更改列表生成式的元素为字典python from django.conf.urls import url from django.shortcuts import HttpResponse,render,redirect from types import FunctionType from django.utils.safestring import mark_safe from django.urls import reverse from django import forms class StarkConfig(object): def __init(self,model_class,site): self.model_class = model_class self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框 if header: # 输出中文 return “选择” # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id return mark_safe(‘‘ %row.pk) def display_edit(self, row=None, header=False): if header: return “编辑” return mark_safe( ‘‘ % self.reverse_edit_url(row)) def display_del(self, row=None, header=False): if header: return “删除” return mark_safe( ‘‘ % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False): if header: return “操作” tpl = “”” | “”” % (self.reverse_edit_url(row), self.reverse_del_url(row),) return mark_safe(tpl) order_by = [] # 需要排序的字段,由用户自定义 list_display = [] # 定义显示的列,由用户自定义 model_form_class = None # form组件需要的model_class action_list = [] # 批量操作方法 def multi_delete(self): # 批量删除 pass multi_delete.text = “批量删除” # 添加自定义属性text def multi_init(self): # 批量初始化 pass multi_init.text = “批量初始化” # 添加自定义属性text def get_order_by(self): # 获取排序列表 return self.order_by def get_list_display(self): # 获取显示的列 return self.list_display def get_add_btn(self): # 显示添加按钮 return mark_safe(‘添加‘ % self.reverse_add_url()) def get_model_form_class(self): “”” 获取ModelForm类 :return: “”” if self.model_form_class: return self.model_form_class class AddModelForm(forms.ModelForm): class Meta: model = self.model_class fields = “__all“ return AddModelForm def get_action_list(self): # 获取批量操作方法 val = [] # 空列表 # 扩展列表的元素 val.extend(self.action_list) return val def changelist_view(self, request): “”” 所有URL查看列表页面 :param request: :return: “”” # 根据排序列表进行排序 queryset = self.model_class.objects.all().order_by(*self.get_order_by()) ### 批量操作 ### action_list = self.get_action_list() # 获取函数名以及text属性 action_list = [{‘name’: func.__name, ‘text’: func.text} for func in action_list] ### 添加按钮 ### add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列 header_list = [] # 定义头部,用来显示verbose_name if list_display: for name_or_func in list_display: if isinstance(name_or_func,FunctionType): # 执行函数,默认显示中文 verbose_name = name_or_func(self,header=True) else: # 获取指定字段的verbose_name verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name) else: # 如果list_display为空,添加表名 header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset: # 这里的row是对象,它表示表里面的一条数据 row_list = [] # 展示每一行数据 if not list_display: # 如果不在list_display里面 # 添加对象 row_list.append(row) body_list.append(row_list) continue for name_or_func in list_display: if isinstance(name_or_func,FunctionType): val = name_or_func(self,row=row) # 执行函数获取,传递row对象 else: # 使用反射获取对象的值 val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn return render(request,’stark/changelist.html’,{‘header_list’:header_list,’body_list’:body_list,’add_btn’:add_btn,’action_list’:action_list}) def add_view(self, request): “”” 所有的添加页面,都在此方法处理 使用ModelForm实现 :param request: :return: “”” # 添加数据,使用ModelForm AddModelForm = self.get_model_form_class() if request.method == “GET”: form = AddModelForm() return render(request,’stark/change.html’,{‘form’:form}) form = AddModelForm(request.POST) # 接收POST数据 if form.is_valid(): # 验证数据 form.save() # 自动保存数据 # 反向生成url,跳转到列表页面 return redirect(self.reverse_list_url()) # 渲染页面,此时会保存表单数据 return render(request, ‘stark/change.html’, {‘form’: form}) def change_view(self, request, pk): “”” 所有编辑页面 :param request: :param pk: :return: “”” # 查看单条数据 obj = self.model_class.objects.filter(pk=pk).first() if not obj: return HttpResponse(‘数据不存在’) # 获取model_form类 ModelFormClass = self.get_model_form_class() if request.method == ‘GET’: # instance表示生成默认值 form = ModelFormClass(instance=obj) # 渲染页面,添加和修改可以共用一个一个模板文件 return render(request, ‘stark/change.html’, {‘form’: form}) # instance = obj 表示指定给谁做修改 form = ModelFormClass(data=request.POST, instance=obj) if form.is_valid(): form.save() # 修改数据 # 跳转到列表页面 return redirect(self.reverse_list_url()) return render(request, ‘stark/change.html’, {‘form’: form}) def delete_view(self, request, pk): “”” 所有删除页面 :param request: :param pk: :return: “”” if request.method == “GET”: # cancel_url表示用户点击取消时,跳转到列表页面 return render(request, ‘stark/delete.html’, {‘cancel_url’: self.reverse_list_url()}) # 定位单条数据,并删除! self.model_class.objects.filter(pk=pk).delete() return redirect(self.reverse_list_url()) def wrapper(self,func): pass def get_urls(self): info = self.model_class._meta.app_label, self.model_class._meta.model_name urlpatterns = [ url(r’^list/$’, self.changelist_view, name=’%s%schangelist’ % info), url(r’^add/$’, self.add_view, name=’%s%sadd’ % info), url(r’^(?P\d+)/change/‘, self.change_view, name=’%s%schange’ % info), url(r’^(?P\d+)/del/‘, self.delete_view, name=’%s%sdel’ % 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%schangelist’ % (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%sadd’ % (namespace, app_label, model_name) add_url = reverse(name) return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url app_label = self.model_class._meta.app_label # app名 model_name = self.model_class._meta.model_name # 表名 namespace = self.site.namespace # 命名空间 # 拼接字符串,这里为change name = ‘%s:%s%schange’ % (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%sdel’ % (namespace, applabel, modelname) delurl = reverse(name, kwargs={‘pk’: row.pk}) return delurl @property def urls(self): return self.geturls() 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,value值为函数名html {% extends ‘stark/layout.html’ %} {% block content %}

列表页面

{% if add_btn %}
{{ add_btn }}
{% endif %}
{% csrf_token %}
{% for item in header_list %} {% endfor %} {% for row_list in body_list %} {% for col in row_list %} {% endfor %} {% endfor %}
{{ item }}
{{ col }}
{% endblock %} 刷新页面,效果如下: ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602234604552-5e1b4c2d-bbb5-4f0e-bb58-9367501e994b.png) 可以看到value值已经渲染出来了! 那么问题来了,table表格的数据,没有包含到form表单里面 修改 stark-->templates-->stark-->changelist.html,更改form表单html {% extends ‘stark/layout.html’ %} {% block content %}

列表页面

{% if add_btn %}
{{ add_btn }}
{% endif %}
{% csrf_token %}
{% for item in header_list %} {% endfor %} {% for row_list in body_list %} {% for col in row_list %} {% endfor %} {% endfor %}
{{ item }}
{{ col }}
{% endblock %} 修改stark-->server-->stark.py,修改changelist_view方法,接收post数据python from django.conf.urls import url from django.shortcuts import HttpResponse,render,redirect from types import FunctionType from django.utils.safestring import mark_safe from django.urls import reverse from django import forms class StarkConfig(object): def __init
(self,model_class,site): self.model_class = model_class self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框 if header: # 输出中文 return “选择” # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id return mark_safe(““ % row.pk) def display_edit(self, row=None, header=False): if header: return “编辑” return mark_safe( ‘‘ % self.reverse_edit_url(row)) def display_del(self, row=None, header=False): if header: return “删除” return mark_safe( ‘‘ % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False): if header: return “操作” tpl = “”” | “”” % (self.reverse_edit_url(row), self.reverse_del_url(row),) return mark_safe(tpl) order_by = [] # 需要排序的字段,由用户自定义 list_display = [] # 定义显示的列,由用户自定义 model_form_class = None # form组件需要的model_class action_list = [] # 批量操作方法 def multi_delete(self): # 批量删除 pass multi_delete.text = “批量删除” # 添加自定义属性text def multi_init(self): # 批量初始化 pass multi_init.text = “批量初始化” # 添加自定义属性text def get_order_by(self): # 获取排序列表 return self.order_by def get_list_display(self): # 获取显示的列 return self.list_display def get_add_btn(self): # 显示添加按钮 return mark_safe(‘添加‘ % self.reverse_add_url()) def get_model_form_class(self): “”” 获取ModelForm类 :return: “”” if self.model_form_class: return self.model_form_class class AddModelForm(forms.ModelForm): class Meta: model = self.model_class fields = “__all
“ return AddModelForm def get_action_list(self): # 获取批量操作方法 val = [] # 空列表 # 扩展列表的元素 val.extend(self.action_list) return val def changelist_view(self, request): “”” 所有URL查看列表页面 :param request: :return: “”” if request.method == “POST”: print(request.POST) # 获取post数据 # 根据排序列表进行排序 queryset = self.model_class.objects.all().order_by(*self.get_order_by()) ### 批量操作 ### action_list = self.get_action_list() # 获取函数名以及text属性 action_list = [{‘name’: func.__name
, ‘text’: func.text} for func in action_list] # print(action_list) ### 添加按钮 ### add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列 header_list = [] # 定义头部,用来显示verbose_name if list_display: for name_or_func in list_display: if isinstance(name_or_func,FunctionType): # 执行函数,默认显示中文 verbose_name = name_or_func(self,header=True) else: # 获取指定字段的verbose_name verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name) else: # 如果list_display为空,添加表名 header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset: # 这里的row是对象,它表示表里面的一条数据 row_list = [] # 展示每一行数据 if not list_display: # 如果不在list_display里面 # 添加对象 row_list.append(row) body_list.append(row_list) continue for name_or_func in list_display: if isinstance(name_or_func,FunctionType): val = name_or_func(self,row=row) # 执行函数获取,传递row对象 else: # 使用反射获取对象的值 val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn return render(request,’stark/changelist.html’,{‘header_list’:header_list,’body_list’:body_list,’add_btn’:add_btn,’action_list’:action_list}) def add_view(self, request): “”” 所有的添加页面,都在此方法处理 使用ModelForm实现 :param request: :return: “”” # 添加数据,使用ModelForm AddModelForm = self.get_model_form_class() if request.method == “GET”: form = AddModelForm() return render(request,’stark/change.html’,{‘form’:form}) form = AddModelForm(request.POST) # 接收POST数据 if form.is_valid(): # 验证数据 form.save() # 自动保存数据 # 反向生成url,跳转到列表页面 return redirect(self.reverse_list_url()) # 渲染页面,此时会保存表单数据 return render(request, ‘stark/change.html’, {‘form’: form}) def change_view(self, request, pk): “”” 所有编辑页面 :param request: :param pk: :return: “”” # 查看单条数据 obj = self.model_class.objects.filter(pk=pk).first() if not obj: return HttpResponse(‘数据不存在’) # 获取model_form类 ModelFormClass = self.get_model_form_class() if request.method == ‘GET’: # instance表示生成默认值 form = ModelFormClass(instance=obj) # 渲染页面,添加和修改可以共用一个一个模板文件 return render(request, ‘stark/change.html’, {‘form’: form}) # instance = obj 表示指定给谁做修改 form = ModelFormClass(data=request.POST, instance=obj) if form.is_valid(): form.save() # 修改数据 # 跳转到列表页面 return redirect(self.reverse_list_url()) return render(request, ‘stark/change.html’, {‘form’: form}) def delete_view(self, request, pk): “”” 所有删除页面 :param request: :param pk: :return: “”” if request.method == “GET”: # cancel_url表示用户点击取消时,跳转到列表页面 return render(request, ‘stark/delete.html’, {‘cancel_url’: self.reverse_list_url()}) # 定位单条数据,并删除! self.model_class.objects.filter(pk=pk).delete() return redirect(self.reverse_list_url()) def wrapper(self,func): pass def get_urls(self): info = self.model_class._meta.app_label, self.model_class._meta.model_name urlpatterns = [ url(r’^list/$’, self.changelist_view, name=’%s
%schangelist’ % info), url(r’^add/$’, self.add_view, name=’%s%sadd’ % info), url(r’^(?P\d+)/change/‘, self.change_view, name=’%s%schange’ % info), url(r’^(?P\d+)/del/‘, self.delete_view, name=’%s%sdel’ % 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%schangelist’ % (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%sadd’ % (namespace, app_label, model_name) add_url = reverse(name) return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url app_label = self.model_class._meta.app_label # app名 model_name = self.model_class._meta.model_name # 表名 namespace = self.site.namespace # 命名空间 # 拼接字符串,这里为change name = ‘%s:%s%schange’ % (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%sdel’ % (namespace, applabel, modelname) delurl = reverse(name, kwargs={‘pk’: row.pk}) return delurl @property def urls(self): return self.geturls() 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() # 实例化类 刷新页面,选择批量删除,勾选几条数据。点击提交 ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602234604666-2a5d6444-071d-4b86-92ec-77871d624129.png) 查看Pycharm控制台输出:python 获取得到数据了! 接下来需要根据获取的方法名来执行方法。怎么判断呢?使用if?如果有多个方法呢? 这个时候,应该使用反射 修改stark-->server-->stark.py,使用反射执行对应的方法。修改那2个方法python from django.conf.urls import url from django.shortcuts import HttpResponse,render,redirect from types import FunctionType from django.utils.safestring import mark_safe from django.urls import reverse from django import forms class StarkConfig(object): def __init(self,model_class,site): self.model_class = model_class self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框 if header: # 输出中文 return “选择” # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id return mark_safe(““ % row.pk) def display_edit(self, row=None, header=False): if header: return “编辑” return mark_safe( ‘‘ % self.reverse_edit_url(row)) def display_del(self, row=None, header=False): if header: return “删除” return mark_safe( ‘‘ % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False): if header: return “操作” tpl = “”” | “”” % (self.reverse_edit_url(row), self.reverse_del_url(row),) return mark_safe(tpl) order_by = [] # 需要排序的字段,由用户自定义 list_display = [] # 定义显示的列,由用户自定义 model_form_class = None # form组件需要的model_class action_list = [] # 批量操作方法 def multi_delete(self,request): # 批量删除 print(‘批量删除’) multi_delete.text = “批量删除” # 添加自定义属性text def multi_init(self,request): # 批量初始化 print(‘批量初始化’) multi_init.text = “批量初始化” # 添加自定义属性text def get_order_by(self): # 获取排序列表 return self.order_by def get_list_display(self): # 获取显示的列 return self.list_display def get_add_btn(self): # 显示添加按钮 return mark_safe(‘添加‘ % self.reverse_add_url()) def get_model_form_class(self): “”” 获取ModelForm类 :return: “”” if self.model_form_class: return self.model_form_class class AddModelForm(forms.ModelForm): class Meta: model = self.model_class fields = “__all“ return AddModelForm def get_action_list(self): # 获取批量操作方法 val = [] # 空列表 # 扩展列表的元素 val.extend(self.action_list) return val def changelist_view(self, request): “”” 所有URL查看列表页面 :param request: :return: “”” if request.method == “POST”: action_name = request.POST.get(‘action’) getattr(self,action_name)(request) # 根据排序列表进行排序 queryset = self.model_class.objects.all().order_by(*self.get_order_by()) ### 批量操作 ### action_list = self.get_action_list() # 获取函数名以及text属性 action_list = [{‘name’: func.__name, ‘text’: func.text} for func in action_list] # print(action_list) ### 添加按钮 ### add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列 header_list = [] # 定义头部,用来显示verbose_name if list_display: for name_or_func in list_display: if isinstance(name_or_func,FunctionType): # 执行函数,默认显示中文 verbose_name = name_or_func(self,header=True) else: # 获取指定字段的verbose_name verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name) else: # 如果list_display为空,添加表名 header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset: # 这里的row是对象,它表示表里面的一条数据 row_list = [] # 展示每一行数据 if not list_display: # 如果不在list_display里面 # 添加对象 row_list.append(row) body_list.append(row_list) continue for name_or_func in list_display: if isinstance(name_or_func,FunctionType): val = name_or_func(self,row=row) # 执行函数获取,传递row对象 else: # 使用反射获取对象的值 val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn return render(request,’stark/changelist.html’,{‘header_list’:header_list,’body_list’:body_list,’add_btn’:add_btn,’action_list’:action_list}) def add_view(self, request): “”” 所有的添加页面,都在此方法处理 使用ModelForm实现 :param request: :return: “”” # 添加数据,使用ModelForm AddModelForm = self.get_model_form_class() if request.method == “GET”: form = AddModelForm() return render(request,’stark/change.html’,{‘form’:form}) form = AddModelForm(request.POST) # 接收POST数据 if form.is_valid(): # 验证数据 form.save() # 自动保存数据 # 反向生成url,跳转到列表页面 return redirect(self.reverse_list_url()) # 渲染页面,此时会保存表单数据 return render(request, ‘stark/change.html’, {‘form’: form}) def change_view(self, request, pk): “”” 所有编辑页面 :param request: :param pk: :return: “”” # 查看单条数据 obj = self.model_class.objects.filter(pk=pk).first() if not obj: return HttpResponse(‘数据不存在’) # 获取model_form类 ModelFormClass = self.get_model_form_class() if request.method == ‘GET’: # instance表示生成默认值 form = ModelFormClass(instance=obj) # 渲染页面,添加和修改可以共用一个一个模板文件 return render(request, ‘stark/change.html’, {‘form’: form}) # instance = obj 表示指定给谁做修改 form = ModelFormClass(data=request.POST, instance=obj) if form.is_valid(): form.save() # 修改数据 # 跳转到列表页面 return redirect(self.reverse_list_url()) return render(request, ‘stark/change.html’, {‘form’: form}) def delete_view(self, request, pk): “”” 所有删除页面 :param request: :param pk: :return: “”” if request.method == “GET”: # cancel_url表示用户点击取消时,跳转到列表页面 return render(request, ‘stark/delete.html’, {‘cancel_url’: self.reverse_list_url()}) # 定位单条数据,并删除! self.model_class.objects.filter(pk=pk).delete() return redirect(self.reverse_list_url()) def wrapper(self,func): pass def get_urls(self): info = self.model_class._meta.app_label, self.model_class._meta.model_name urlpatterns = [ url(r’^list/$’, self.changelist_view, name=’%s%schangelist’ % info), url(r’^add/$’, self.add_view, name=’%s%sadd’ % info), url(r’^(?P\d+)/change/‘, self.change_view, name=’%s%schange’ % info), url(r’^(?P\d+)/del/‘, self.delete_view, name=’%s%sdel’ % 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%schangelist’ % (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%sadd’ % (namespace, app_label, model_name) add_url = reverse(name) return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url app_label = self.model_class._meta.app_label # app名 model_name = self.model_class._meta.model_name # 表名 namespace = self.site.namespace # 命名空间 # 拼接字符串,这里为change name = ‘%s:%s%schange’ % (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%sdel’ % (namespace, applabel, modelname) delurl = reverse(name, kwargs={‘pk’: row.pk}) return delurl @property def urls(self): return self.geturls() class AdminSite(object): def init(self): self.registry = {} self.appname = ‘stark’ self.namespace = ‘stark’ def register(self,modelclass,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() # 实例化类 刷新页面,再次选择批量删除,勾选几条数据,点击提交! 查看Pycharm控制台输出: 批量删除 假如有不法分子,修改html呢? ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602234604506-a88457ec-22bc-4e3b-9c83-f9520d28040d.png) 为了避免攻击,使用字典判断,因为字典查询快 修改stark-->server-->stark.py,增加方法get_action_dictpython from django.conf.urls import url from django.shortcuts import HttpResponse,render,redirect from types import FunctionType from django.utils.safestring import mark_safe from django.urls import reverse from django import forms class StarkConfig(object): def __init(self,model_class,site): self.model_class = model_class self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框 if header: # 输出中文 return “选择” # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id return mark_safe(““ % row.pk) def display_edit(self, row=None, header=False): if header: return “编辑” return mark_safe( ‘‘ % self.reverse_edit_url(row)) def display_del(self, row=None, header=False): if header: return “删除” return mark_safe( ‘‘ % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False): if header: return “操作” tpl = “”” | “”” % (self.reverse_edit_url(row), self.reverse_del_url(row),) return mark_safe(tpl) order_by = [] # 需要排序的字段,由用户自定义 list_display = [] # 定义显示的列,由用户自定义 model_form_class = None # form组件需要的model_class action_list = [] # 批量操作方法 def multi_delete(self,request): # 批量删除 print(‘批量删除’) return HttpResponse(‘批量删除’) multi_delete.text = “批量删除” # 添加自定义属性text def multi_init(self,request): # 批量初始化 print(‘批量初始化’) multi_init.text = “批量初始化” # 添加自定义属性text def get_order_by(self): # 获取排序列表 return self.order_by def get_list_display(self): # 获取显示的列 return self.list_display def get_add_btn(self): # 显示添加按钮 return mark_safe(‘添加‘ % self.reverse_add_url()) def get_model_form_class(self): “”” 获取ModelForm类 :return: “”” if self.model_form_class: return self.model_form_class class AddModelForm(forms.ModelForm): class Meta: model = self.model_class fields = “__all“ return AddModelForm def get_action_list(self): # 获取批量操作方法 val = [] # 空列表 # 扩展列表的元素 val.extend(self.action_list) return val def get_action_dict(self): # 获取匹配操作字典 val = {} for item in self.action_list: # 以方法名为key val[item.__name] = item return val def changelist_view(self, request): “”” 所有URL查看列表页面 :param request: :return: “”” if request.method == ‘POST’: action_name = request.POST.get(‘action’) action_dict = self.get_action_dict() if action_name not in action_dict: return HttpResponse(‘非法请求’) response = getattr(self, action_name)(request) if response: return response # 根据排序列表进行排序 queryset = self.model_class.objects.all().order_by(*self.get_order_by()) ### 批量操作 ### action_list = self.get_action_list() # 获取函数名以及text属性 action_list = [{‘name’: func.__name, ‘text’: func.text} for func in action_list] # print(action_list) ### 添加按钮 ### add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列 header_list = [] # 定义头部,用来显示verbose_name if list_display: for name_or_func in list_display: if isinstance(name_or_func,FunctionType): # 执行函数,默认显示中文 verbose_name = name_or_func(self,header=True) else: # 获取指定字段的verbose_name verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name) else: # 如果list_display为空,添加表名 header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset: # 这里的row是对象,它表示表里面的一条数据 row_list = [] # 展示每一行数据 if not list_display: # 如果不在list_display里面 # 添加对象 row_list.append(row) body_list.append(row_list) continue for name_or_func in list_display: if isinstance(name_or_func,FunctionType): val = name_or_func(self,row=row) # 执行函数获取,传递row对象 else: # 使用反射获取对象的值 val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn return render(request,’stark/changelist.html’,{‘header_list’:header_list,’body_list’:body_list,’add_btn’:add_btn,’action_list’:action_list}) def add_view(self, request): “”” 所有的添加页面,都在此方法处理 使用ModelForm实现 :param request: :return: “”” # 添加数据,使用ModelForm AddModelForm = self.get_model_form_class() if request.method == “GET”: form = AddModelForm() return render(request,’stark/change.html’,{‘form’:form}) form = AddModelForm(request.POST) # 接收POST数据 if form.is_valid(): # 验证数据 form.save() # 自动保存数据 # 反向生成url,跳转到列表页面 return redirect(self.reverse_list_url()) # 渲染页面,此时会保存表单数据 return render(request, ‘stark/change.html’, {‘form’: form}) def change_view(self, request, pk): “”” 所有编辑页面 :param request: :param pk: :return: “”” # 查看单条数据 obj = self.model_class.objects.filter(pk=pk).first() if not obj: return HttpResponse(‘数据不存在’) # 获取model_form类 ModelFormClass = self.get_model_form_class() if request.method == ‘GET’: # instance表示生成默认值 form = ModelFormClass(instance=obj) # 渲染页面,添加和修改可以共用一个一个模板文件 return render(request, ‘stark/change.html’, {‘form’: form}) # instance = obj 表示指定给谁做修改 form = ModelFormClass(data=request.POST, instance=obj) if form.is_valid(): form.save() # 修改数据 # 跳转到列表页面 return redirect(self.reverse_list_url()) return render(request, ‘stark/change.html’, {‘form’: form}) def delete_view(self, request, pk): “”” 所有删除页面 :param request: :param pk: :return: “”” if request.method == “GET”: # cancel_url表示用户点击取消时,跳转到列表页面 return render(request, ‘stark/delete.html’, {‘cancel_url’: self.reverse_list_url()}) # 定位单条数据,并删除! self.model_class.objects.filter(pk=pk).delete() return redirect(self.reverse_list_url()) def wrapper(self,func): pass def get_urls(self): info = self.model_class._meta.app_label, self.model_class._meta.model_name urlpatterns = [ url(r’^list/$’, self.changelist_view, name=’%s%schangelist’ % info), url(r’^add/$’, self.add_view, name=’%s%sadd’ % info), url(r’^(?P\d+)/change/‘, self.change_view, name=’%s%schange’ % info), url(r’^(?P\d+)/del/‘, self.delete_view, name=’%s%sdel’ % 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%schangelist’ % (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%sadd’ % (namespace, app_label, model_name) add_url = reverse(name) return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url app_label = self.model_class._meta.app_label # app名 model_name = self.model_class._meta.model_name # 表名 namespace = self.site.namespace # 命名空间 # 拼接字符串,这里为change name = ‘%s:%s%schange’ % (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%sdel’ % (namespace, applabel, modelname) delurl = reverse(name, kwargs={‘pk’: row.pk}) return delurl @property def urls(self): return self.geturls() class AdminSite(object): def init(self): self.registry = {} self.appname = ‘stark’ self.namespace = ‘stark’ def register(self,modelclass,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() # 实例化类 刷新页面,点击匹配初始化,还是会跳转到当前页面。因为此方法,没有返回值。 点击批量删除,页面效果如下: ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602234604668-510b34fb-482c-45ce-b95e-189581893891.png) 修改stark-->server-->stark.py,修改multi_delete方法python from django.conf.urls import url from django.shortcuts import HttpResponse,render,redirect from types import FunctionType from django.utils.safestring import mark_safe from django.urls import reverse from django import forms class StarkConfig(object): def __init(self,model_class,site): self.model_class = model_class self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框 if header: # 输出中文 return “选择” # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id return mark_safe(““ % row.pk) def display_edit(self, row=None, header=False): if header: return “编辑” return mark_safe( ‘‘ % self.reverse_edit_url(row)) def display_del(self, row=None, header=False): if header: return “删除” return mark_safe( ‘‘ % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False): if header: return “操作” tpl = “”” | “”” % (self.reverse_edit_url(row), self.reverse_del_url(row),) return mark_safe(tpl) order_by = [] # 需要排序的字段,由用户自定义 list_display = [] # 定义显示的列,由用户自定义 model_form_class = None # form组件需要的model_class action_list = [] # 批量操作方法 def multi_delete(self, request): # 批量删除 “”” 批量删除的action :param request: :return: “”” pk_list = request.POST.getlist(‘pk’) self.model_class.objects.filter(pkin=pk_list).delete() # return HttpResponse(‘删除成功’) multi_delete.text = “批量删除” # 添加自定义属性text def multi_init(self,request): # 批量初始化 print(‘批量初始化’) multi_init.text = “批量初始化” # 添加自定义属性text def get_order_by(self): # 获取排序列表 return self.order_by def get_list_display(self): # 获取显示的列 return self.list_display def get_add_btn(self): # 显示添加按钮 return mark_safe(‘添加‘ % self.reverse_add_url()) def get_model_form_class(self): “”” 获取ModelForm类 :return: “”” if self.model_form_class: return self.model_form_class class AddModelForm(forms.ModelForm): class Meta: model = self.model_class fields = “all“ return AddModelForm def get_action_list(self): # 获取批量操作方法 val = [] # 空列表 # 扩展列表的元素 val.extend(self.action_list) return val def get_action_dict(self): # 获取匹配操作字典 val = {} for item in self.action_list: # 以方法名为key val[item.__name] = item return val def changelist_view(self, request): “”” 所有URL查看列表页面 :param request: :return: “”” if request.method == ‘POST’: action_name = request.POST.get(‘action’) action_dict = self.get_action_dict() if action_name not in action_dict: return HttpResponse(‘非法请求’) response = getattr(self, action_name)(request) if response: return response # 根据排序列表进行排序 queryset = self.model_class.objects.all().order_by(*self.get_order_by()) ### 批量操作 ### action_list = self.get_action_list() # 获取函数名以及text属性 action_list = [{‘name’: func.__name, ‘text’: func.text} for func in action_list] # print(action_list) ### 添加按钮 ### add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列 header_list = [] # 定义头部,用来显示verbose_name if list_display: for name_or_func in list_display: if isinstance(name_or_func,FunctionType): # 执行函数,默认显示中文 verbose_name = name_or_func(self,header=True) else: # 获取指定字段的verbose_name verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name) else: # 如果list_display为空,添加表名 header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset: # 这里的row是对象,它表示表里面的一条数据 row_list = [] # 展示每一行数据 if not list_display: # 如果不在list_display里面 # 添加对象 row_list.append(row) body_list.append(row_list) continue for name_or_func in list_display: if isinstance(name_or_func,FunctionType): val = name_or_func(self,row=row) # 执行函数获取,传递row对象 else: # 使用反射获取对象的值 val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn return render(request,’stark/changelist.html’,{‘header_list’:header_list,’body_list’:body_list,’add_btn’:add_btn,’action_list’:action_list}) def add_view(self, request): “”” 所有的添加页面,都在此方法处理 使用ModelForm实现 :param request: :return: “”” # 添加数据,使用ModelForm AddModelForm = self.get_model_form_class() if request.method == “GET”: form = AddModelForm() return render(request,’stark/change.html’,{‘form’:form}) form = AddModelForm(request.POST) # 接收POST数据 if form.is_valid(): # 验证数据 form.save() # 自动保存数据 # 反向生成url,跳转到列表页面 return redirect(self.reverse_list_url()) # 渲染页面,此时会保存表单数据 return render(request, ‘stark/change.html’, {‘form’: form}) def change_view(self, request, pk): “”” 所有编辑页面 :param request: :param pk: :return: “”” # 查看单条数据 obj = self.model_class.objects.filter(pk=pk).first() if not obj: return HttpResponse(‘数据不存在’) # 获取model_form类 ModelFormClass = self.get_model_form_class() if request.method == ‘GET’: # instance表示生成默认值 form = ModelFormClass(instance=obj) # 渲染页面,添加和修改可以共用一个一个模板文件 return render(request, ‘stark/change.html’, {‘form’: form}) # instance = obj 表示指定给谁做修改 form = ModelFormClass(data=request.POST, instance=obj) if form.is_valid(): form.save() # 修改数据 # 跳转到列表页面 return redirect(self.reverse_list_url()) return render(request, ‘stark/change.html’, {‘form’: form}) def delete_view(self, request, pk): “”” 所有删除页面 :param request: :param pk: :return: “”” if request.method == “GET”: # cancel_url表示用户点击取消时,跳转到列表页面 return render(request, ‘stark/delete.html’, {‘cancel_url’: self.reverse_list_url()}) # 定位单条数据,并删除! self.model_class.objects.filter(pk=pk).delete() return redirect(self.reverse_list_url()) def wrapper(self,func): pass def get_urls(self): info = self.model_class._meta.app_label, self.model_class._meta.model_name urlpatterns = [ url(r’^list/$’, self.changelist_view, name=’%s%schangelist’ % info), url(r’^add/$’, self.add_view, name=’%s%sadd’ % info), url(r’^(?P\d+)/change/‘, self.change_view, name=’%s%schange’ % info), url(r’^(?P\d+)/del/‘, self.delete_view, name=’%s%sdel’ % 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%schangelist’ % (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%sadd’ % (namespace, app_label, model_name) add_url = reverse(name) return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url app_label = self.model_class._meta.app_label # app名 model_name = self.model_class._meta.model_name # 表名 namespace = self.site.namespace # 命名空间 # 拼接字符串,这里为change name = ‘%s:%s%schange’ % (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%sdel’ % (namespace, applabel, modelname) delurl = reverse(name, kwargs={‘pk’: row.pk}) return delurl @property def urls(self): return self.geturls() class AdminSite(object): def init(self): self.registry = {} self.appname = ‘stark’ self.namespace = ‘stark’ def register(self,modelclass,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() # 实例化类 刷新页面,选中一条数据,点击提交 ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602234604586-67fec146-8e3a-487b-8069-6e31f95533ab.png) 发现少了一条数据 ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602234604597-00d5f703-9a07-40c2-b213-0b35fbf7080b.png) 访问其它页面,比如: [http://127.0.0.1:8000/stark/app01/userinfo/list/](http://127.0.0.1:8000/stark/app01/userinfo/list/) 发现下拉框是空的,那么它就不应该显示! ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602234604523-c683c62a-8221-4a69-a217-da3274290dfc.png) 修改stark-->templates-->stark-->changelist.html,添加if判断html {% extends ‘stark/layout.html’ %} {% block content %}

列表页面

{% if add_btn %}
{{ add_btn }}
{% endif %}
{% csrf_token %} {% if action_list %}
{% endif %} {% for item in header_list %} {% endfor %} {% for row_list in body_list %} {% for col in row_list %} {% endfor %} {% endfor %}
{{ item }}
{{ col }}
{% endblock %} 刷新页面,效果如下: ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602234604558-d268f3a4-482f-46e7-8c7c-72f9cd3c6369.png) 多选框,没有了! 其他页面,想使用批量操作,定义action_list变量,就可以了! # 二、快速搜索 搜索什么内容,取哪个字段搜,都是可以定制的! 修改stark-->server-->stark.py,增加search_list变量。定义name和tel可以搜索! 增加方法get_search_list方法python from django.conf.urls import url from django.shortcuts import HttpResponse,render,redirect from types import FunctionType from django.utils.safestring import mark_safe from django.urls import reverse from django import forms class StarkConfig(object): def __init
(self,model_class,site): self.model_class = model_class self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框 if header: # 输出中文 return “选择” # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id return mark_safe(““ % row.pk) def display_edit(self, row=None, header=False): if header: return “编辑” return mark_safe( ‘‘ % self.reverse_edit_url(row)) def display_del(self, row=None, header=False): if header: return “删除” return mark_safe( ‘‘ % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False): if header: return “操作” tpl = “”” | “”” % (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(pkin=pk_list).delete() # return HttpResponse(‘删除成功’) multi_delete.text = “批量删除” # 添加自定义属性text def multi_init(self,request): # 批量初始化 print(‘批量初始化’) multi_init.text = “批量初始化” # 添加自定义属性text order_by = [] # 需要排序的字段,由用户自定义 list_display = [] # 定义显示的列,由用户自定义 model_form_class = None # form组件需要的model_class action_list = [] # 批量操作方法 search_list = [‘name’,’tel’] # 固定搜索字段 def get_order_by(self): # 获取排序列表 return self.order_by def get_list_display(self): # 获取显示的列 return self.list_display def get_add_btn(self): # 显示添加按钮 return mark_safe(‘添加‘ % self.reverse_add_url()) def get_model_form_class(self): “”” 获取ModelForm类 :return: “”” if self.model_form_class: return self.model_form_class class AddModelForm(forms.ModelForm): class Meta: model = self.model_class fields = “all
“ return AddModelForm def get_action_list(self): # 获取批量操作方法 val = [] # 空列表 # 扩展列表的元素 val.extend(self.action_list) return val def get_action_dict(self): # 获取匹配操作字典 val = {} for item in self.action_list: # 以方法名为key val[item.__name
] = item return val def get_search_list(self): # 获取搜索字段 val = [] val.extend(self.search_list) return val def changelist_view(self, request): “”” 所有URL查看列表页面 :param request: :return: “”” if request.method == ‘POST’: action_name = request.POST.get(‘action’) action_dict = self.get_action_dict() if action_name not in action_dict: return HttpResponse(‘非法请求’) response = getattr(self, action_name)(request) if response: return response ### 处理搜索 ### from django.db.models import Q search_list = self.get_search_list() # [‘name’,’tel’] q = request.GET.get(‘q’, “”) # 搜索条件 con = Q() con.connector = “OR” # 以OR作为连接符 if q: # 判断条件不为空 for field in search_list: # 合并条件进行查询, contains表示使用like查询 con.children.append((‘%scontains’ % field, q)) # 根据排序列表进行排序 queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by()) ### 批量操作 ### action_list = self.get_action_list() # 获取函数名以及text属性 action_list = [{‘name’: func.__name
, ‘text’: func.text} for func in action_list] # print(action_list) ### 添加按钮 ### add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列 header_list = [] # 定义头部,用来显示verbose_name if list_display: for name_or_func in list_display: if isinstance(name_or_func,FunctionType): # 执行函数,默认显示中文 verbose_name = name_or_func(self,header=True) else: # 获取指定字段的verbose_name verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name) else: # 如果list_display为空,添加表名 header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset: # 这里的row是对象,它表示表里面的一条数据 row_list = [] # 展示每一行数据 if not list_display: # 如果不在list_display里面 # 添加对象 row_list.append(row) body_list.append(row_list) continue for name_or_func in list_display: if isinstance(name_or_func,FunctionType): val = name_or_func(self,row=row) # 执行函数获取,传递row对象 else: # 使用反射获取对象的值 val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入add_btn return render(request,’stark/changelist.html’,{‘header_list’:header_list,’body_list’:body_list,’add_btn’:add_btn,’action_list’:action_list}) def add_view(self, request): “”” 所有的添加页面,都在此方法处理 使用ModelForm实现 :param request: :return: “”” # 添加数据,使用ModelForm AddModelForm = self.get_model_form_class() if request.method == “GET”: form = AddModelForm() return render(request,’stark/change.html’,{‘form’:form}) form = AddModelForm(request.POST) # 接收POST数据 if form.is_valid(): # 验证数据 form.save() # 自动保存数据 # 反向生成url,跳转到列表页面 return redirect(self.reverse_list_url()) # 渲染页面,此时会保存表单数据 return render(request, ‘stark/change.html’, {‘form’: form}) def change_view(self, request, pk): “”” 所有编辑页面 :param request: :param pk: :return: “”” # 查看单条数据 obj = self.model_class.objects.filter(pk=pk).first() if not obj: return HttpResponse(‘数据不存在’) # 获取model_form类 ModelFormClass = self.get_model_form_class() if request.method == ‘GET’: # instance表示生成默认值 form = ModelFormClass(instance=obj) # 渲染页面,添加和修改可以共用一个一个模板文件 return render(request, ‘stark/change.html’, {‘form’: form}) # instance = obj 表示指定给谁做修改 form = ModelFormClass(data=request.POST, instance=obj) if form.is_valid(): form.save() # 修改数据 # 跳转到列表页面 return redirect(self.reverse_list_url()) return render(request, ‘stark/change.html’, {‘form’: form}) def delete_view(self, request, pk): “”” 所有删除页面 :param request: :param pk: :return: “”” if request.method == “GET”: # cancel_url表示用户点击取消时,跳转到列表页面 return render(request, ‘stark/delete.html’, {‘cancel_url’: self.reverse_list_url()}) # 定位单条数据,并删除! self.model_class.objects.filter(pk=pk).delete() return redirect(self.reverse_list_url()) def wrapper(self,func): pass def get_urls(self): info = self.model_class._meta.app_label, self.model_class._meta.model_name urlpatterns = [ url(r’^list/$’, self.changelist_view, name=’%s
%schangelist’ % info), url(r’^add/$’, self.add_view, name=’%s%sadd’ % info), url(r’^(?P\d+)/change/‘, self.change_view, name=’%s%schange’ % info), url(r’^(?P\d+)/del/‘, self.delete_view, name=’%s%sdel’ % 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%schangelist’ % (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%sadd’ % (namespace, app_label, model_name) add_url = reverse(name) return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url app_label = self.model_class._meta.app_label # app名 model_name = self.model_class._meta.model_name # 表名 namespace = self.site.namespace # 命名空间 # 拼接字符串,这里为change name = ‘%s:%s%schange’ % (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%sdel’ % (namespace, applabel, modelname) delurl = reverse(name, kwargs={‘pk’: row.pk}) return delurl @property def urls(self): return self.geturls() class AdminSite(object): def init(self): self.registry = {} self.appname = ‘stark’ self.namespace = ‘stark’ def register(self,modelclass,stark_config=None): # not None的结果为Ture if not stark_config: # 也就是说,当其他应用调用register时,如果不指定stark_config参数 # 那么必然执行下面这段代码! # stark_config和StarkConfig是等值的!都能实例化 stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class # self指的是AdminSite类 self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典 “”” { app01.models.UserInfo:StarkConfig(app01.models.UserInfo) app02.models.Role:RoleConfig(app02.models.Role) } “”” # for k, v in self._registry.items(): # print(k,v) def get_urls(self): urlpatterns = [] for k, v in self._registry.items(): # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象 # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象 app_label = k._meta.app_label model_name = k._meta.model_name urlpatterns.append(url(r’^%s/%s/‘ % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property def urls(self): # 调用get_urls方法 # self.app_name和self.namespace值是一样的,都是stark return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类 访问url,要带上q参数python http://127.0.0.1:8000/stark/app01/depart/list/?q=总 效果如下: ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602234604641-2b5d9780-1ca6-44d3-b0a7-10796845c5fa.png) 去掉参数,有2条数据 ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602234604644-11e94843-9c2d-4d5e-a29a-5f21ea68800f.png) 这样体验不好,要用户输入才行! 修改stark-->server-->stark.py,给changelist.html传入参数qpython from django.conf.urls import url from django.shortcuts import HttpResponse,render,redirect from types import FunctionType from django.utils.safestring import mark_safe from django.urls import reverse from django import forms class StarkConfig(object): def __init(self,model_class,site): self.model_class = model_class self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框 if header: # 输出中文 return “选择” # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id return mark_safe(““ % row.pk) def display_edit(self, row=None, header=False): if header: return “编辑” return mark_safe( ‘‘ % self.reverse_edit_url(row)) def display_del(self, row=None, header=False): if header: return “删除” return mark_safe( ‘‘ % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False): if header: return “操作” tpl = “”” | “”” % (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(pkin=pk_list).delete() # return HttpResponse(‘删除成功’) multi_delete.text = “批量删除” # 添加自定义属性text def multi_init(self,request): # 批量初始化 print(‘批量初始化’) multi_init.text = “批量初始化” # 添加自定义属性text order_by = [] # 需要排序的字段,由用户自定义 list_display = [] # 定义显示的列,由用户自定义 model_form_class = None # form组件需要的model_class action_list = [] # 批量操作方法 search_list = [‘name’,’tel’] # 固定搜索字段 def get_order_by(self): # 获取排序列表 return self.order_by def get_list_display(self): # 获取显示的列 return self.list_display def get_add_btn(self): # 显示添加按钮 return mark_safe(‘添加‘ % self.reverse_add_url()) def get_model_form_class(self): “”” 获取ModelForm类 :return: “”” if self.model_form_class: return self.model_form_class class AddModelForm(forms.ModelForm): class Meta: model = self.model_class fields = “all“ return AddModelForm def get_action_list(self): # 获取批量操作方法 val = [] # 空列表 # 扩展列表的元素 val.extend(self.action_list) return val def get_action_dict(self): # 获取匹配操作字典 val = {} for item in self.action_list: # 以方法名为key val[item.__name] = item return val def get_search_list(self): # 获取搜索字段 val = [] val.extend(self.search_list) return val def changelist_view(self, request): “”” 所有URL查看列表页面 :param request: :return: “”” if request.method == ‘POST’: action_name = request.POST.get(‘action’) action_dict = self.get_action_dict() if action_name not in action_dict: return HttpResponse(‘非法请求’) response = getattr(self, action_name)(request) if response: return response ### 处理搜索 ### from django.db.models import Q search_list = self.get_search_list() # [‘name’,’tel’] q = request.GET.get(‘q’, “”) # 搜索条件 con = Q() con.connector = “OR” # 以OR作为连接符 if q: # 判断条件不为空 for field in search_list: # 合并条件进行查询, contains表示使用like查询 con.children.append((‘%scontains’ % field, q)) # 根据排序列表进行排序 queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by()) ### 批量操作 ### action_list = self.get_action_list() # 获取函数名以及text属性 action_list = [{‘name’: func.__name, ‘text’: func.text} for func in action_list] # print(action_list) ### 添加按钮 ### add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列 header_list = [] # 定义头部,用来显示verbose_name if list_display: for name_or_func in list_display: if isinstance(name_or_func,FunctionType): # 执行函数,默认显示中文 verbose_name = name_or_func(self,header=True) else: # 获取指定字段的verbose_name verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name) else: # 如果list_display为空,添加表名 header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset: # 这里的row是对象,它表示表里面的一条数据 row_list = [] # 展示每一行数据 if not list_display: # 如果不在list_display里面 # 添加对象 row_list.append(row) body_list.append(row_list) continue for name_or_func in list_display: if isinstance(name_or_func,FunctionType): val = name_or_func(self,row=row) # 执行函数获取,传递row对象 else: # 使用反射获取对象的值 val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入参数 return render(request,’stark/changelist.html’,{‘header_list’:header_list,’body_list’:body_list,’add_btn’:add_btn,’action_list’:action_list,’q’:q}) def add_view(self, request): “”” 所有的添加页面,都在此方法处理 使用ModelForm实现 :param request: :return: “”” # 添加数据,使用ModelForm AddModelForm = self.get_model_form_class() if request.method == “GET”: form = AddModelForm() return render(request,’stark/change.html’,{‘form’:form}) form = AddModelForm(request.POST) # 接收POST数据 if form.is_valid(): # 验证数据 form.save() # 自动保存数据 # 反向生成url,跳转到列表页面 return redirect(self.reverse_list_url()) # 渲染页面,此时会保存表单数据 return render(request, ‘stark/change.html’, {‘form’: form}) def change_view(self, request, pk): “”” 所有编辑页面 :param request: :param pk: :return: “”” # 查看单条数据 obj = self.model_class.objects.filter(pk=pk).first() if not obj: return HttpResponse(‘数据不存在’) # 获取model_form类 ModelFormClass = self.get_model_form_class() if request.method == ‘GET’: # instance表示生成默认值 form = ModelFormClass(instance=obj) # 渲染页面,添加和修改可以共用一个一个模板文件 return render(request, ‘stark/change.html’, {‘form’: form}) # instance = obj 表示指定给谁做修改 form = ModelFormClass(data=request.POST, instance=obj) if form.is_valid(): form.save() # 修改数据 # 跳转到列表页面 return redirect(self.reverse_list_url()) return render(request, ‘stark/change.html’, {‘form’: form}) def delete_view(self, request, pk): “”” 所有删除页面 :param request: :param pk: :return: “”” if request.method == “GET”: # cancel_url表示用户点击取消时,跳转到列表页面 return render(request, ‘stark/delete.html’, {‘cancel_url’: self.reverse_list_url()}) # 定位单条数据,并删除! self.model_class.objects.filter(pk=pk).delete() return redirect(self.reverse_list_url()) def wrapper(self,func): pass def get_urls(self): info = self.model_class._meta.app_label, self.model_class._meta.model_name urlpatterns = [ url(r’^list/$’, self.changelist_view, name=’%s%schangelist’ % info), url(r’^add/$’, self.add_view, name=’%s%sadd’ % info), url(r’^(?P\d+)/change/‘, self.change_view, name=’%s%schange’ % info), url(r’^(?P\d+)/del/‘, self.delete_view, name=’%s%sdel’ % 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%schangelist’ % (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%sadd’ % (namespace, app_label, model_name) add_url = reverse(name) return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url app_label = self.model_class._meta.app_label # app名 model_name = self.model_class._meta.model_name # 表名 namespace = self.site.namespace # 命名空间 # 拼接字符串,这里为change name = ‘%s:%s%schange’ % (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%sdel’ % (namespace, applabel, model_name) del_url = reverse(name, kwargs={‘pk’: row.pk}) return del_url @property def urls(self): return self.get_urls() class AdminSite(object): def __init(self): self._registry = {} self.app_name = ‘stark’ self.namespace = ‘stark’ def register(self,model_class,stark_config=None): # not None的结果为Ture if not stark_config: # 也就是说,当其他应用调用register时,如果不指定stark_config参数 # 那么必然执行下面这段代码! # stark_config和StarkConfig是等值的!都能实例化 stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class # self指的是AdminSite类 self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典 “”” { app01.models.UserInfo:StarkConfig(app01.models.UserInfo) app02.models.Role:RoleConfig(app02.models.Role) } “”” # for k, v in self._registry.items(): # print(k,v) def get_urls(self): urlpatterns = [] for k, v in self._registry.items(): # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象 # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象 app_label = k._meta.app_label model_name = k._meta.model_name urlpatterns.append(url(r’^%s/%s/‘ % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property def urls(self): # 调用get_urls方法 # self.app_name和self.namespace值是一样的,都是stark return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类 ``` 修改 stark—>templates—>stark—>changelist.html,增加输入框
即使页面刷新,input输入框还会保留搜索的关键字! html {% extends 'stark/layout.html' %} {% block content %} <h1>列表页面</h1> <div> {#添加按钮#} {% if add_btn %} <div style="margin: 5px 0;"> {{ add_btn }} </div> {% endif %} {#搜索框#} <div style="float: right;"> <form method="GET" class="form-inline"> <div class="form-group"> <input class="form-control" type="text" name="q" value="{{ q }}" placeholder="关键字搜索"> <button class="btn btn-primary" type="submit"> <i class="fa fa-search" aria-hidden="true"></i> </button> </div> </form> </div> <form class="form-inline" method="post"> {% csrf_token %} {#批量操作#} {% if action_list %} <div class="form-group"> <select name="action" class="form-control" style="min-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> </form> </div> {% endblock %} 访问url: http://127.0.0.1:8000/stark/app01/depart/list/ 效果如下: Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图10 输入关键字534,点击搜索按钮。那么url会自动带上参数,这个是GET本来就有的功能。因为GET参数在url里面! 同时结果只有一条 Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图11 如果要搜索负责人呢?注意:这个字段涉及到跨表了。那么填写searchlist参数时,要带上下划线 修改stark—>server—>stark.py,修改searchlist变量 python # 固定搜索字段,如果是跨表字段,要按照ORM语法来 search_list = ['name','tel','user__username'] 刷新页面,重新制定关键字 Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图12 上面是为了方便调试,把搜索字段给固定死了。它应该是可以定制的! 修改stark—>server—>stark.py,将searchlist变量设置为空 python # 搜索字段,如果是跨表字段,要按照ORM语法来 search_list = [] 修改 app01—>stark.py,指定变量search_list ```python from stark.server.stark import site, StarkConfig from app01 import models from django import forms from django.shortcuts import render from django.conf.urls import url class UserInfoConfig(StarkConfig): list_display = [‘id’, ‘username’] class DepartModelForm(forms.ModelForm): class Meta: model = models.Depart fields = “__all“ def clean_name(self): # 定义钩子 # print(self.cleaned_data[‘name’]) return self.cleaned_data[‘name’] class DepartConfig(StarkConfig): list_display = [StarkConfig.display_checkbox,’name’, ‘tel’, ‘user’] # model_form_class = DepartModelForm # 批量操作 action_list = [StarkConfig.multi_delete,StarkConfig.multi_init] # 搜索关键字 # 固定搜索字段,如果是跨表字段,要按照ORM语法来 search_list = [‘name’, ‘tel’, ‘user__username’] # def get_add_btn(self): # 返回None,表示不显示添加按钮 # pass # def changelist_view(self, request): # 重写changelist_view方法 # # 渲染自定义的列表页面 # return render(request,’stark/custom_list.html’) # def get_urls(self): # 自定义路由 # info = self.model_class._meta.app_label, self.model_class._meta.model_name # # urlpatterns = [ # url(r’^list/$’, self.changelist_view, name=’%s%schangelist’ % info), # ] # return urlpatterns site.register(models.UserInfo, UserInfoConfig) site.register(models.Depart, DepartConfig) 重启django,刷新页面,效果同上! 如果没有定义search_list变量,那么search_list默认为空,它不应该展示! 修改stark-->server-->stark.py,给changelist.html传入参数search_listpython from django.conf.urls import url from django.shortcuts import HttpResponse,render,redirect from types import FunctionType from django.utils.safestring import marksafe from django.urls import reverse from django import forms class StarkConfig(object): def init(self,modelclass,site): self.modelclass = modelclass self.site = site def displaycheckbox(self,row=None,header=False): # 显示复选框 if header: # 输出中文 return “选择” # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id return marksafe(““ % row.pk) def display_edit(self, row=None, header=False): if header: return “编辑” return mark_safe( ‘‘ % self.reverse_edit_url(row)) def display_del(self, row=None, header=False): if header: return “删除” return mark_safe( ‘‘ % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False): if header: return “操作” tpl = “”” | “”” % (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(pkin=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(‘添加‘ % self.reverse_add_url()) def get_model_form_class(self): “”” 获取ModelForm类 :return: “”” if self.model_form_class: return self.model_form_class class AddModelForm(forms.ModelForm): class Meta: model = self.model_class fields = “all“ return AddModelForm def get_action_list(self): # 获取批量操作方法 val = [] # 空列表 # 扩展列表的元素 val.extend(self.action_list) return val def get_action_dict(self): # 获取匹配操作字典 val = {} for item in self.action_list: # 以方法名为key val[item.__name] = item return val def get_search_list(self): # 获取搜索字段 val = [] val.extend(self.search_list) return val def changelist_view(self, request): “”” 所有URL查看列表页面 :param request: :return: “”” if request.method == ‘POST’: action_name = request.POST.get(‘action’) action_dict = self.get_action_dict() if action_name not in action_dict: return HttpResponse(‘非法请求’) response = getattr(self, action_name)(request) if response: return response ### 处理搜索 ### from django.db.models import Q search_list = self.get_search_list() # [‘name’,’tel’] q = request.GET.get(‘q’, “”) # 搜索条件 con = Q() con.connector = “OR” # 以OR作为连接符 if q: # 判断条件不为空 for field in search_list: # 合并条件进行查询, contains表示使用like查询 con.children.append((‘%scontains’ % field, q)) # 根据排序列表进行排序 queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by()) ### 批量操作 ### action_list = self.get_action_list() # 获取函数名以及text属性 action_list = [{‘name’: func.__name, ‘text’: func.text} for func in action_list] # print(action_list) ### 添加按钮 ### add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列 header_list = [] # 定义头部,用来显示verbose_name if list_display: for name_or_func in list_display: if isinstance(name_or_func,FunctionType): # 执行函数,默认显示中文 verbose_name = name_or_func(self,header=True) else: # 获取指定字段的verbose_name verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name) else: # 如果list_display为空,添加表名 header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset: # 这里的row是对象,它表示表里面的一条数据 row_list = [] # 展示每一行数据 if not list_display: # 如果不在list_display里面 # 添加对象 row_list.append(row) body_list.append(row_list) continue for name_or_func in list_display: if isinstance(name_or_func,FunctionType): val = name_or_func(self,row=row) # 执行函数获取,传递row对象 else: # 使用反射获取对象的值 val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入参数 return render(request,’stark/changelist.html’,{‘header_list’:header_list,’body_list’:body_list,’add_btn’:add_btn,’action_list’:action_list,’q’:q,’search_list’:search_list}) def add_view(self, request): “”” 所有的添加页面,都在此方法处理 使用ModelForm实现 :param request: :return: “”” # 添加数据,使用ModelForm AddModelForm = self.get_model_form_class() if request.method == “GET”: form = AddModelForm() return render(request,’stark/change.html’,{‘form’:form}) form = AddModelForm(request.POST) # 接收POST数据 if form.is_valid(): # 验证数据 form.save() # 自动保存数据 # 反向生成url,跳转到列表页面 return redirect(self.reverse_list_url()) # 渲染页面,此时会保存表单数据 return render(request, ‘stark/change.html’, {‘form’: form}) def change_view(self, request, pk): “”” 所有编辑页面 :param request: :param pk: :return: “”” # 查看单条数据 obj = self.model_class.objects.filter(pk=pk).first() if not obj: return HttpResponse(‘数据不存在’) # 获取model_form类 ModelFormClass = self.get_model_form_class() if request.method == ‘GET’: # instance表示生成默认值 form = ModelFormClass(instance=obj) # 渲染页面,添加和修改可以共用一个一个模板文件 return render(request, ‘stark/change.html’, {‘form’: form}) # instance = obj 表示指定给谁做修改 form = ModelFormClass(data=request.POST, instance=obj) if form.is_valid(): form.save() # 修改数据 # 跳转到列表页面 return redirect(self.reverse_list_url()) return render(request, ‘stark/change.html’, {‘form’: form}) def delete_view(self, request, pk): “”” 所有删除页面 :param request: :param pk: :return: “”” if request.method == “GET”: # cancel_url表示用户点击取消时,跳转到列表页面 return render(request, ‘stark/delete.html’, {‘cancel_url’: self.reverse_list_url()}) # 定位单条数据,并删除! self.model_class.objects.filter(pk=pk).delete() return redirect(self.reverse_list_url()) def wrapper(self,func): pass def get_urls(self): info = self.model_class._meta.app_label, self.model_class._meta.model_name urlpatterns = [ url(r’^list/$’, self.changelist_view, name=’%s%schangelist’ % info), url(r’^add/$’, self.add_view, name=’%s%sadd’ % info), url(r’^(?P\d+)/change/‘, self.change_view, name=’%s%schange’ % info), url(r’^(?P\d+)/del/‘, self.delete_view, name=’%s%sdel’ % 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%schangelist’ % (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%sadd’ % (namespace, app_label, model_name) add_url = reverse(name) return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url app_label = self.model_class._meta.app_label # app名 model_name = self.model_class._meta.model_name # 表名 namespace = self.site.namespace # 命名空间 # 拼接字符串,这里为change name = ‘%s:%s%schange’ % (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%sdel’ % (namespace, applabel, modelname) delurl = reverse(name, kwargs={‘pk’: row.pk}) return delurl @property def urls(self): return self.geturls() class AdminSite(object): def init(self): self.registry = {} self.appname = ‘stark’ self.namespace = ‘stark’ def register(self,modelclass,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,增加if 判断html {% extends ‘stark/layout.html’ %} {% block content %}

列表页面

{#添加按钮#} {% if add_btn %}
{{ add_btn }}
{% endif %} {#搜索框#} {% if search_list %}
{% endif %}
{% csrf_token %} {#批量操作#} {% if action_list %}
{% endif %} {#使用table展示数据#} {% for item in header_list %} {% endfor %} {% for row_list in body_list %} {% for col in row_list %} {% endfor %} {% endfor %}
{{ item }}
{{ col }}
{% endblock %} 修改app01-->stark.py,注释掉search_list变量html # 固定搜索字段,如果是跨表字段,要按照ORM语法来 # search_list = [‘name’, ‘tel’, ‘userusername’] 访问url: [http://127.0.0.1:8000/stark/app01/depart/list/](http://127.0.0.1:8000/stark/app01/depart/list/) 搜索框就没有了! ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602234604609-158e6d50-720f-4eaf-ac90-a6081e17223e.png) 在changelist_view中,处理搜索的代码,可以封装成方法。 修改stark-->server-->stark.py,增加get_search_condition方法python from django.conf.urls import url from django.shortcuts import HttpResponse,render,redirect from types import FunctionType from django.utils.safestring import mark_safe from django.urls import reverse from django import forms from django.db.models import Q class StarkConfig(object): def init
(self,model_class,site): self.model_class = model_class self.site = site def display_checkbox(self,row=None,header=False): # 显示复选框 if header: # 输出中文 return “选择” # 注意:这里要写row.pk,不能写row.id。你不能保证每一个表的主键都是id return mark_safe(““ % row.pk) def display_edit(self, row=None, header=False): if header: return “编辑” return mark_safe( ‘‘ % self.reverse_edit_url(row)) def display_del(self, row=None, header=False): if header: return “删除” return mark_safe( ‘‘ % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False): if header: return “操作” tpl = “”” | “”” % (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(pkin=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(‘添加‘ % 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((‘%scontains’ % field, q)) return search_list, q, con def changelist_view(self, request): “”” 所有URL查看列表页面 :param request: :return: “”” if request.method == ‘POST’: action_name = request.POST.get(‘action’) action_dict = self.get_action_dict() if action_name not in action_dict: return HttpResponse(‘非法请求’) response = getattr(self, action_name)(request) if response: return response ### 处理搜索 ### search_list, q, con = self.get_search_condition(request) # 根据排序列表进行排序 queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by()) ### 批量操作 ### action_list = self.get_action_list() # 获取函数名以及text属性 action_list = [{‘name’: func.__name
, ‘text’: func.text} for func in action_list] # print(action_list) ### 添加按钮 ### add_btn = self.get_add_btn() # 添加按钮返回值,不为空展示,否则不展示 list_display = self.list_display # 定义显示的列 header_list = [] # 定义头部,用来显示verbose_name if list_display: for name_or_func in list_display: if isinstance(name_or_func,FunctionType): # 执行函数,默认显示中文 verbose_name = name_or_func(self,header=True) else: # 获取指定字段的verbose_name verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name header_list.append(verbose_name) else: # 如果list_display为空,添加表名 header_list.append(self.model_class._meta.model_name) body_list = [] # 显示内容 for row in queryset: # 这里的row是对象,它表示表里面的一条数据 row_list = [] # 展示每一行数据 if not list_display: # 如果不在list_display里面 # 添加对象 row_list.append(row) body_list.append(row_list) continue for name_or_func in list_display: if isinstance(name_or_func,FunctionType): val = name_or_func(self,row=row) # 执行函数获取,传递row对象 else: # 使用反射获取对象的值 val = getattr(row, name_or_func) row_list.append(val) body_list.append(row_list) # 注意:要传入参数 return render(request,’stark/changelist.html’,{‘header_list’:header_list,’body_list’:body_list,’add_btn’:add_btn,’action_list’:action_list,’q’:q,’search_list’:search_list}) def add_view(self, request): “”” 所有的添加页面,都在此方法处理 使用ModelForm实现 :param request: :return: “”” # 添加数据,使用ModelForm AddModelForm = self.get_model_form_class() if request.method == “GET”: form = AddModelForm() return render(request,’stark/change.html’,{‘form’:form}) form = AddModelForm(request.POST) # 接收POST数据 if form.is_valid(): # 验证数据 form.save() # 自动保存数据 # 反向生成url,跳转到列表页面 return redirect(self.reverse_list_url()) # 渲染页面,此时会保存表单数据 return render(request, ‘stark/change.html’, {‘form’: form}) def change_view(self, request, pk): “”” 所有编辑页面 :param request: :param pk: :return: “”” # 查看单条数据 obj = self.model_class.objects.filter(pk=pk).first() if not obj: return HttpResponse(‘数据不存在’) # 获取model_form类 ModelFormClass = self.get_model_form_class() if request.method == ‘GET’: # instance表示生成默认值 form = ModelFormClass(instance=obj) # 渲染页面,添加和修改可以共用一个一个模板文件 return render(request, ‘stark/change.html’, {‘form’: form}) # instance = obj 表示指定给谁做修改 form = ModelFormClass(data=request.POST, instance=obj) if form.is_valid(): form.save() # 修改数据 # 跳转到列表页面 return redirect(self.reverse_list_url()) return render(request, ‘stark/change.html’, {‘form’: form}) def delete_view(self, request, pk): “”” 所有删除页面 :param request: :param pk: :return: “”” if request.method == “GET”: # cancel_url表示用户点击取消时,跳转到列表页面 return render(request, ‘stark/delete.html’, {‘cancel_url’: self.reverse_list_url()}) # 定位单条数据,并删除! self.model_class.objects.filter(pk=pk).delete() return redirect(self.reverse_list_url()) def wrapper(self,func): pass def get_urls(self): info = self.model_class._meta.app_label, self.model_class._meta.model_name urlpatterns = [ url(r’^list/$’, self.changelist_view, name=’%s
%schangelist’ % info), url(r’^add/$’, self.add_view, name=’%s%sadd’ % info), url(r’^(?P\d+)/change/‘, self.change_view, name=’%s%schange’ % info), url(r’^(?P\d+)/del/‘, self.delete_view, name=’%s%sdel’ % 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%schangelist’ % (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%sadd’ % (namespace, app_label, model_name) add_url = reverse(name) return add_url def reverse_edit_url(self, row): # 反向生成编辑行内容的url app_label = self.model_class._meta.app_label # app名 model_name = self.model_class._meta.model_name # 表名 namespace = self.site.namespace # 命名空间 # 拼接字符串,这里为change name = ‘%s:%s%schange’ % (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%sdel’ % (namespace, applabel, model_name) del_url = reverse(name, kwargs={‘pk’: row.pk}) return del_url @property def urls(self): return self.get_urls() class AdminSite(object): def __init(self): self._registry = {} self.app_name = ‘stark’ self.namespace = ‘stark’ def register(self,model_class,stark_config=None): # not None的结果为Ture if not stark_config: # 也就是说,当其他应用调用register时,如果不指定stark_config参数 # 那么必然执行下面这段代码! # stark_config和StarkConfig是等值的!都能实例化 stark_config = StarkConfig # 添加键值对,实例化类StarkConfig,传入参数model_class # self指的是AdminSite类 self._registry[model_class] = stark_config(model_class,self) # print(self._registry) # 打印字典 “”” { app01.models.UserInfo:StarkConfig(app01.models.UserInfo) app02.models.Role:RoleConfig(app02.models.Role) } “”” # for k, v in self._registry.items(): # print(k,v) def get_urls(self): urlpatterns = [] for k, v in self._registry.items(): # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封装:model_class=UserInfo,site=site对象 # k=modes.Role,v=RoleConfig(models.Role) # 封装:model_class=Role,site=site对象 app_label = k._meta.app_label model_name = k._meta.model_name urlpatterns.append(url(r’^%s/%s/‘ % (app_label, model_name,), (v.urls, None, None))) return urlpatterns @property def urls(self): # 调用get_urls方法 # self.app_name和self.namespace值是一样的,都是stark return self.get_urls(), self.app_name, self.namespace site = AdminSite() # 实例化类 修改app01-->stark.py,开启search_list变量python # 搜索字段,如果是跨表字段,要按照ORM语法来 search_list = [‘name’, ‘tel’, ‘user__username’] ``` 重启django,刷新页面。测试搜索功能,效果同上! # 三、保留原搜索条件 在内容前戏部分,讲到了 保留原搜索条件。关键点在于:
搜索到结果后,跳转其他页面时,url要带上原搜索条件。 比如页面默认展示的是20条数据。搜索了2个用户,并编辑保存。跳转的页面应该也还是之前2个客户的数据,而不是20数据(返回列表首页) 页面上的a标签,button按钮。这些url是反向生成的,能不能加条件?加上原来的搜索条件? 答案是可以的! ## 装饰器 查看 stark—>server—>stark.py,StarkConfig类里面的geturls方法。里面定义了4个视图函数,它们是有request变量的。除此之外,其他函数也需要使用request变量,怎么办?一个一个传? 太麻烦了,使用装饰器就可以解决! 修改 stark—>server—>stark.py,修改StarkConfig类的init方法,添加变量request。 增加装饰器wrapper。并定义self.request = request,对request重新赋值! 修改geturls方法,应用装饰器 ```python import functools from django.conf.urls import url from django.shortcuts import HttpResponse,render,redirect from types import FunctionType from django.utils.safestring import marksafe 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,modelclass,site): self.modelclass = modelclass 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(““ % row.pk) def display_edit(self, row=None, header=False): if header: return “编辑” return mark_safe( ‘‘ % self.reverse_edit_url(row)) def display_del(self, row=None, header=False): if header: return “删除” return mark_safe( ‘‘ % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False): if header: return “操作” tpl = “”” | “”” % (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(pkin=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(‘添加‘ % 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((‘%scontains’ % 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 geturls(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%schangelist’ % info), url(r’^add/$’, self.wrapper(self.add_view), name=’%s%sadd’ % info), url(r’^(?P\d+)/change/‘, self.wrapper(self.change_view), name=’%s%schange’ % info), url(r’^(?P\d+)/del/‘, self.wrapper(self.delete_view), name=’%s%sdel’ % 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%schangelist’ % (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%sadd’ % (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%schange’ % (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%sdel’ % (namespace, applabel, modelname) delurl = reverse(name, kwargs={‘pk’: row.pk}) return delurl @property def urls(self): return self.geturls() 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也同样需要添加python 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(““ % row.pk) def display_edit(self, row=None, header=False): if header: return “编辑” return mark_safe( ‘‘ % self.reverse_edit_url(row)) def display_del(self, row=None, header=False): if header: return “删除” return mark_safe( ‘‘ % self.reverse_del_url(row)) def display_edit_del(self, row=None, header=False): if header: return “操作” tpl = “”” | “”” % (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(pkin=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(‘添加‘ % 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((‘%scontains’ % 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.getorderby()) ### 批量操作 ### actionlist = 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%schangelist’ % info), url(r’^add/$’, self.wrapper(self.add_view), name=’%s%sadd’ % info), url(r’^(?P\d+)/change/‘, self.wrapper(self.change_view), name=’%s%schange’ % info), url(r’^(?P\d+)/del/‘, self.wrapper(self.delete_view), name=’%s%sdel’ % 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%schangelist’ % (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%sadd’ % (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%schange’ % (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%sdel’ % (namespace, applabel, modelname) delurl = reverse(name, kwargs={‘pk’: row.pk}) if not self.request.GET: return delurl 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/](http://127.0.0.1:8000/stark/app01/depart/list/) 输入搜索条件 xiao,点击搜索按钮 查看添加按钮的跳转地址,发下已经添加了搜索条件 ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602234604626-88060531-1aa0-4f45-b67f-16b42120d1b8.png) 修改app01-->stark.py,增加编辑和删除选项python 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属性 ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602234604671-28fe3bd6-3495-42a7-8df6-f1d68f8510e0.png) 发送链接地址,也加上了搜索条件。 点击编辑按钮,此时的url地址为:python http://127.0.0.1:8000/stark/app01/depart/1/change/?_filter=q%3Dxiao 修改一条数据,点击提交 ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602234604666-eb0b2f95-76d2-4639-8436-a2fb14d09dcc.png) 页面跳转地址,此时之前的搜索条件还在!python http://127.0.0.1:8000/stark/app01/depart/list/?q=xiao ``` 效果如下: Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图13 如果有分页功能,这个搜索条件,还要不要?当然要啊! # 四、自定义分页 展示数据页面数据过多,一般会采用分页处理。 这里使用的分页,不是django自带的分页器(paginator) ,使用的是自定义分页类。为什么?虽然使用paginator也可以完成。但是我们要做的是,跨框架的组件。即使在flask框架中,也依然能使用! 进入stark应用目录,创建文件夹utils,它表示工具类。在此文件夹下创建文件pagination.py 注意:分页跳转时,它是带有搜索条件的 这里没有使用request.GET.urlencode(),为了做到通用性。其他框架也可以使用!
  1. """
  2. 分页组件
  3. """
  4. from urllib.parse import urlencode
  5. class Pagination(object):
  6. def __init__(self, current_page, all_count, base_url,query_params, per_page=10, pager_page_count=11):
  7. """
  8. 分页初始化
  9. :param current_page: 当前页码
  10. :param per_page: 每页显示数据条数
  11. :param all_count: 数据库中总条数
  12. :param base_url: 基础URL
  13. :param query_params: QueryDict对象,内部含所有当前URL的原条件
  14. :param pager_page_count: 页面上最多显示的页码数量
  15. """
  16. self.base_url = base_url
  17. try:
  18. self.current_page = int(current_page)
  19. if self.current_page <= 0: # 当前页码数不能小于等于0
  20. raise Exception()
  21. except Exception as e:
  22. self.current_page = 1
  23. self.query_params = query_params
  24. self.per_page = per_page
  25. self.all_count = all_count
  26. self.pager_page_count = pager_page_count
  27. pager_count, b = divmod(all_count, per_page)
  28. if b != 0:
  29. pager_count += 1
  30. self.pager_count = pager_count
  31. half_pager_page_count = int(pager_page_count / 2)
  32. self.half_pager_page_count = half_pager_page_count
  33. @property
  34. def start(self):
  35. """
  36. 数据获取值起始索引
  37. :return:
  38. """
  39. return (self.current_page - 1) * self.per_page
  40. @property
  41. def end(self):
  42. """
  43. 数据获取值结束索引
  44. :return:
  45. """
  46. return self.current_page * self.per_page
  47. def page_html(self):
  48. """
  49. 生成HTML页码
  50. :return:
  51. """
  52. # 如果数据总页码pager_count<11 pager_page_count
  53. if self.pager_count < self.pager_page_count:
  54. pager_start = 1
  55. pager_end = self.pager_count
  56. else:
  57. # 数据页码已经超过11
  58. # 判断: 如果当前页 <= 5 half_pager_page_count
  59. if self.current_page <= self.half_pager_page_count:
  60. pager_start = 1
  61. pager_end = self.pager_page_count
  62. else:
  63. # 如果: 当前页+5 > 总页码
  64. if (self.current_page + self.half_pager_page_count) > self.pager_count:
  65. pager_end = self.pager_count
  66. pager_start = self.pager_count - self.pager_page_count + 1
  67. else:
  68. pager_start = self.current_page - self.half_pager_page_count
  69. pager_end = self.current_page + self.half_pager_page_count
  70. page_list = []
  71. if self.current_page <= 1:
  72. prev = '<li><a href="#">上一页</a></li>'
  73. else:
  74. self.query_params['page'] = self.current_page - 1
  75. prev = '<li><a href="%s?%s">上一页</a></li>' % (self.base_url,self.query_params.urlencode())
  76. page_list.append(prev)
  77. for i in range(pager_start, pager_end + 1):
  78. self.query_params['page'] = i
  79. if self.current_page == i:
  80. tpl = '<li class="active"><a href="%s?%s">%s</a></li>' % (
  81. self.base_url, self.query_params.urlencode(), i,)
  82. else:
  83. tpl = '<li><a href="%s?%s">%s</a></li>' % (self.base_url, self.query_params.urlencode(), i,)
  84. page_list.append(tpl)
  85. if self.current_page >= self.pager_count:
  86. nex = '<li><a href="#">下一页</a></li>'
  87. else:
  88. self.query_params['page'] = self.current_page + 1
  89. nex = '<li><a href="%s?%s">下一页</a></li>' % (self.base_url, self.query_params.urlencode(),)
  90. page_list.append(nex)
  91. page_str = "".join(page_list)
  92. return page_str

打开表app01_depart,添加几条数据

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

  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. class UserInfoConfig(StarkConfig):
  7. list_display = ['id', 'username']
  8. class DepartModelForm(forms.ModelForm):
  9. class Meta:
  10. model = models.Depart
  11. fields = "__all__"
  12. def clean_name(self): # 定义钩子
  13. # print(self.cleaned_data['name'])
  14. return self.cleaned_data['name']
  15. class DepartConfig(StarkConfig):
  16. list_display = [StarkConfig.display_checkbox,'id','name', 'tel', 'user',StarkConfig.display_edit_del]
  17. # model_form_class = DepartModelForm
  18. # 批量操作
  19. action_list = [StarkConfig.multi_delete,StarkConfig.multi_init]
  20. # 搜索字段,如果是跨表字段,要按照ORM语法来
  21. search_list = ['name', 'tel', 'user__username']
  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. site.register(models.UserInfo, UserInfoConfig)
  35. site.register(models.Depart, DepartConfig)

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

效果如下:

Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图14

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

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

Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图15

测试搜索条件

Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图16

五、拆分代码

上面的 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

效果如下:

Day115 urlencode,批量操作,快速搜索,保留原搜索条件,自定义分页,拆分代码 - 图17

总结:

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