django-admin效果仿写,stark自定义search以及分页显示,url参数保留
search模糊查询功能搞定,
我们的form表单里面action如果为空,那么不论我们的method请求是get还是post,我们的url都是走的同一个,这一点需要记住
还有我们的url里面有参数:在?后面加上的=左右两边都是键值对的方式发送的数据请求,我们的url里面的这样的键值对参数是需要保存的,不论我们以后跳转到哪里去,都要把这些参数携带着,这里就需要我们使用一个知识点
from django.http import QueryDict
在这个QueryDict里面封装的方法,可以帮我们获取url里面的键值对参数,然后我们用它取出来即可,就是在取值的过程中,有一点小障碍,在内部源码级别,我们这里有一个参数,
mutable=False,这个意思是我们不能够改动这个键值对,我们在使用的时候给这个键值对赋值就把它改成True就可以了,然后使用这个QueryDict就可以把我们url里面的键值对保存,然后在跳转的时候就会携带着他们了
https://i.cnblogs.com/EditArticles.aspx?postid=8585099
---- Changelist类-----服务展示页面 search模糊查询 action批量处理 上节回顾: 基于modelForm实现 添加页面 编辑页面 今日内容: ----跳转页面时如何保留搜索条件 分页组件 知识点1:QueryDict from django.http import QueryDict print(request.GET) # <QueryDict: {}> # <QueryDict: {'id': ['1']}> request.GET._mutable=True request.GET["a"]=1 # <QueryDict: {'id': ['1'], 'a': [1]}> # urlencode 编码 "a=1&b=2$c=3" print(request.GET.urlencode()) # "id=1&a=1" print(request.GET) #创建QueryDict qd=QueryDict(mutable=True) qd["name"]="yuan" if self.all_pager<11: begin=1 end=all_pager+1 else: if self.current_page<5: begin=1 end=11+1 elif self.current_page+5>self.all_pager: begin=self.all_pager-10 end=self.all_pager+1 else: begin=self.current_page-5 end=self.current_page+5+1 for i in range(begin,end): s="<li><a href=''>i<a></li>" 编辑按钮,删除按钮 知识点2: form action=http://127.0.0.1:8000/stark/app01/book/?a=1&b=2 method="get" GET请求: request.GET {"a":1,"b":2} request.POST {} form action=http://127.0.0.1:8000/stark/app01/book/?a=1&b=2 method="post" <input type="text" name="user" value="123"> POST请求: request.GET {"a":1,"b":2} request.POST {"user":"123"} Changelist类-----服务展示页面 search模糊查询 ---------- action filter pop(js)
1 from django.conf.urls import url 2 from django.shortcuts import render, redirect, reverse 3 from django.utils.safestring import mark_safe 4 from django.forms import ModelForm # 这个ModelForm里面封装了很强大的功能,要把源码过一遍 5 6 7 class StandLi(object): 8 def __init__(self, config, request, queryset): 9 """ 10 :param config: 它就是我们下面的类ModelSubject所传过来的它的self实例对象, 11 我们在这里要使用那些方法和变量就需要把它的这个实例对象拿过来,否则如下搬过来的代码块都会失效 12 :param request: 我们这个类是在下面的ModelSubject里面调用然后在那里实例化出来的对象,所以这个request是它传过来的 13 :param queryset: 同上,这个queryset也是ModelSubject所传过来的参数,供下面的代码调用 14 """ 15 self.config = config 16 self.request = request 17 self.queryset = queryset 18 19 # 生成分页器 20 path = self.request.path_info 21 params = self.request.GET 22 page_num = request.GET.get('page', 1) # 如果没有找到page,就返回1,也就是第一页 23 from file.utensil.page import MyPage 24 count = queryset.count() 25 page = MyPage(page_num, count, path, params) 26 self.pagination = page 27 data_list = self.queryset[page.start:page.end] 28 self.data_list = data_list 29 # page_html = page.page_html() # 这里我们可以把page_html方法直接在前端HTML模板里面引用 30 31 def get_header(self): 32 # 生成表头数据 33 # ['id','title','price',edit] 34 header_list = [] 35 for field in self.config.get_list_display(): 36 if callable(field): 37 ret = field(self, is_header=True) 38 header_list.append(ret) 39 else: 40 if field == '__str__': # 这里是判断我们的list_display列表里面是否有我们自定义传入的值,如果没有的话, 41 # 就是直接等于我们在静态属性里面设定的那个默认的'__str__',也就是说如果这一步判断成立, 42 # 那么就证明我们的用户没有自定义展示的字段,我们就需要自己给浏览器一个字段去展示,那个字段就是我们这里所设定的那个大写的表名 43 header_list.append(self.config.model._meta.model_name.upper()) # 我们这里的操作是 44 else: 45 obj = self.config.model._meta.get_field(field) # 我们的list_display里面是一个个的字符串, 46 # 把字符串放到get_field里面来可以把我们的字符串转换成类对象, 47 header_list.append(obj.verbose_name) # 我们这里的verbose_name在model里面是内置方法, 48 # 我们的verbose_name本质上是对我们的字段进行描述的,比如我们的book里面的title可以在字段约束里面设 49 # verbose_name='书名',类似于这样,把它变成中文,然后我们在前端HTML模板里面渲染的时候就可以渲染出来中文了, 50 # 而不是使用默认的英文,当然了我们如果不设置verbose_name的值那么就使用默认的title作为字段名传到浏览器 51 52 return header_list 53 54 def get_body(self): 55 # 生成表单数据列表(我们把这个函数挪到上面这个类里面来之后,把循环遍历的数据改动了,之前是把当前表格的数据取出来之后就直接遍历它, 56 # 后来我们有了搜索功能,那么就不能遍历表格里面的所有数据了,需要把我们过滤查询出来的数据给遍历出来 57 # ret = self.config.model.objects.all().count() 58 # print('self.list_display', self.list_display) 59 data_list = [] 60 for obj in self.data_list: # 我们遍历这个queryset集合得到的obj是它的每一个对象 61 temp = [] 62 for field in self.config.get_list_display(): # 我们遍历list_display得到每一个字符串 63 if callable(field): 64 # res = field(obj) # @@@更上面的特殊标识的代码块相呼应 65 res = field(self.config, obj) # &&& 这里跟上面同样标识的代码块相呼应的,上面我们使用的类名去调用函数名, 66 # 得到的是一个函数,这里就是给所调用的函数传参的,self,和obj都是我们传给函数的参数; 67 # 如果我们使用self这个对象去调用函数名的方法的话,就不需要再传一个self作为参数进去了,我们两种方法都可以,需要对应上即可 68 else: 69 res = getattr(obj, field) # 使用getattr方法去判断该对象是否具有,field属性方法, 70 # getattr里面需要两个参数(类对象,字符串属性方法) 71 if field in self.config.list_display_links: # 我们这里是判断表单里面的字段是否在links表格里面被自定义作为可跳转标签, 72 # 如果答案是肯定的,那么我们就需要把a标签给拼出来 73 res = self.config.get_link_tag(obj, res) 74 temp.append(res) 75 data_list.append(temp) 76 77 # print('data_list', data_list) 78 79 """ 80 我们最终得到的数据类型是如下格式:列表套着列表 81 [ 82 使用orm语句得到的每一个类对象,有几个表格就有几个对象 83 ] 84 list_display=['id','title',] 85 [ 86 [1,'python',<a>编辑</a>], 87 [2,'java',<a>编辑</a>], 88 ] 89 """ 90 return data_list 91 92 93 class ModelSubject(object): 94 """ 95 我们在这里模拟admin源码里面的ModelAdmin, 96 """ 97 list_display = ["__str__"] # 我们在这里给空列表里面加上"__str__",它就相当于是一个默认值, 98 model_form_class = None # 为下面我们判断用户是否有自定义ModelForm校验方式而做铺垫 99 search_fields = [] 100 list_display_links = [] # 为我们后面用户是否有自定义可跳转字段做铺垫 101 # 就像我们的函数里面有默认值的参数一样,如果有传参就使用我们的自定义传参,如果没有传参就使用我们默认的参数也就是这个字符串, 102 # 这里是为了给我们后面的代码做铺垫,我们的目的是在我们的数据展示页面里面默认就会把复选框和编辑还有删除按钮都加上, 103 # 在这里把空列表里面添加上一个默认的字符串,是为了我们后面往该列表里面添加默认固定数据也就是复选框和编辑删除按钮做铺垫 104 105 # 静态内置方法 106 def __init__(self, model, site): 107 self.model = model # 当我们生成一个实例化对象的时候需要把model这个参数传进来, 108 # 必须要传,它是位置参数,然后我们所传入的那个model就是我们在models.py里面定义的每一个表名也就是类名 109 self.site = site 110 self.namespace = '{}_{}'.format(self.model._meta.app_label, self.model._meta.model_name) 111 # self.app_model_name = (self.model._meta.app_label, self.model._meta.model_name) # 这里写得跟上面一句是一样的效果, 112 # 这里调用的时候需要有两个%s,因为这里是一个元祖,而我们上面的namespace是一个字符串,不是一个元祖,所以只需要一个%s即可,调用的时候就这点区别 113 # 我们这里的namespace是因为会频繁使用到所以就把它作为一个内置静态属性写入到这里,其他地方如果要调用它就直接使用self.namespace即可 114 # .format的方法:'{}_{}'.format(a,b) 115 116 # 获取展示页面的url 117 def get_stand_url(self): 118 stand_url = reverse('%s_standlist' % self.namespace) 119 return stand_url 120 121 # 获取编辑页面的url 122 def get_edit_url(self, obj): 123 edit_url = reverse('%s_edit' % self.namespace, args=(obj.pk,)) 124 return edit_url 125 126 # 获取删除页面的url 127 def get_dele_url(self, obj): 128 dele_url = reverse('%s_dele' % self.namespace, args=(obj.pk,)) 129 return dele_url 130 131 # 获取增加页面的url 132 def get_add_url(self): 133 add_url = reverse('%s_add' % self.namespace) 134 return add_url 135 136 # 展示页面默认附带的编辑按钮 137 def edit(self, obj=None, is_header=False): 138 if is_header: 139 return '操作' 140 return mark_safe('<a href="%s">编辑</a>' % reverse('%s_edit' % self.namespace, args=(obj.pk,))) 141 142 # 展示页面默认附带的删除按钮 143 def dele(self, obj=None, is_header=False): 144 if is_header: 145 return '删除' 146 return mark_safe("<a href='%s'>删除</a>" % reverse('%s_dele' % self.namespace, args=(obj.pk,))) 147 148 # 展示页面附带的默认复选框 149 def checkbox(self, obj=None, is_header=False): 150 if is_header: 151 return mark_safe("<input id='action-toggle' type='checkbox'>") 152 return mark_safe("<input type='checkbox' values='%s'>" % obj.pk) 153 154 # 展示页面默认显示按钮被存放的列表 155 def get_list_display(self): 156 new_li = [] 157 new_li.extend(self.list_display) # 我们这里的extend是把它后面的列表里面的数据都取出来放到我们自己的这个列表里面来 158 if not self.list_display_links: 159 new_li.append(ModelSubject.edit) # &&& 跟如下同样特征的代码块相呼应我们在这里使用类名去调用函数名,得到的是一个函数的方法, 160 # 函数如果有参数是需要我们传参数的;但是我们如果使用self去调用的话,self就是实例化出来的对象, 161 # 而我们的对象去调用函数方法的时候就不需要去传自己了也就是self, 162 new_li.append(ModelSubject.dele) 163 new_li.insert(0, ModelSubject.checkbox) # 把checkbox放到第一个位置,使用insert插入到索引为0 164 """ 165 # @@@ 跟下面特殊标识的代码块相呼应 166 new_li.append(self.edit) 167 new_li.append(self.dele) 168 new_li.insert(0,self.checkbox) 169 """ 170 return new_li 171 172 def get_search_condition(self): 173 from django.db.models import Q 174 search_condition = Q() 175 search_condition.connector = 'or' 176 if self.search_fields: 177 key_word = self.request.GET.get('q') 178 if key_word: 179 for search_field in self.search_fields: 180 search_condition.children.append((search_field+"__contains", key_word)) 181 return search_condition 182 183 # 处理用户自定义的link超链接字段标签,然后让超链接携带url键值对参数方法 184 def get_link_tag(self, obj, val): 185 params = self.request.GET 186 import copy 187 params = copy.deepcopy(params) 188 params._mutable = True 189 from django.http import QueryDict 190 qqx = QueryDict(mutable=True) 191 qqx['list_filter'] = params.urlencode() 192 whh = mark_safe("<a href='{}?{}'>{}</a>".format(self.get_edit_url(obj), qqx.urlencode(), val)) 193 return whh 194 195 # 展示页面 196 def stand_li(self, request): 197 # print(self.model) 198 # 所以我们在这里可以获取到当前的url里面的表名,然后直接使用orm语句即可得到当前表格的所有信息 199 self.request = request 200 201 # 关于search的模糊查询 202 search_condition = self.get_search_condition() 203 204 # action 205 queryset = self.model.objects.filter(search_condition) 206 # a=self.model.objects.all().count() # 这样就是可以获取我们的queryset集合的总数据长度, 207 # 然后就可以用它去传给我们的分页组件,用它也可以,直接用count就能获取数据长度,或者是用len也行,我之前都是用len获取的 208 sl = StandLi(self, request, queryset) # 这里是把我们的StandLi这个类所需要的参数都传给它,然后通过StandLi实例化出来的一个对象 209 # 然后在这里实例化出来一个对象我在这里调用那个对象就能够使用那个类里面的封装的方法了 210 add_url = self.get_add_url() 211 return render(request, 'file/hello.html', locals()) 212 213 # ModelForm校验添加和编辑页面 214 def get_modelform_class(self): 215 from django.forms import widgets 216 217 class AllModelForm(ModelForm): 218 class Meta: 219 model = self.model 220 fields = '__all__' 221 if not self.model_form_class: # 这里的model_form_class在上面被定义了默认是None, 222 # 我们的分发下去的App里面自定义的file文件里面注册model类的时候实例化出来的对象,在注册的时候传过来的这个变量 223 return AllModelForm 224 else: 225 return self.model_form_class 226 227 # ModelForm校验数据添加页面 228 def add_view(self, request): 229 FormClass = self.get_modelform_class() 230 if request.method == 'GET': 231 form = FormClass() 232 return render(request, 'file/add.html', {'form': form}) 233 else: 234 data_list = FormClass(data=request.POST) 235 if data_list.is_valid(): 236 data_list.save() 237 return redirect(self.get_stand_url()) 238 else: 239 return render(request, 'file/add.html', {'form': data_list}) 240 241 # ModelForm校验数据编辑页面 242 def edit_view(self, request, id): 243 edit_list = self.model.objects.filter(pk=id).first() 244 FormClass = self.get_modelform_class() 245 if request.method == 'GET': 246 data_list = FormClass(instance=edit_list) 247 return render(request, 'file/edit.html', {'form': data_list}) 248 else: 249 data_list = FormClass(data=request.POST, instance=edit_list) 250 if data_list.is_valid(): 251 data_list.save() 252 return redirect(self.get_stand_url()) 253 else: 254 return render(request, 'file/edit.html', {'form': data_list}) 255 256 # 数据删除页面 257 def dele_view(self, request, id): 258 del_obj = self.model.objects.filter(pk=id).first() 259 if request.method == 'GET': 260 stand_url = self.get_stand_url() 261 return render(request, 'file/dele.html', {'del_obj': del_obj, 'list_url': stand_url}) 262 else: 263 del_obj.delete() 264 265 return redirect(self.get_stand_url()) 266 267 # 获取url,此为第二次分发 268 def get_urls(self): 269 temp = [] 270 temp.append(url(r'^$', self.stand_li, name='%s_standlist' % self.namespace)) 271 temp.append(url(r'^(\d+)/dele/', self.dele_view, name='%s_dele' % self.namespace)) 272 temp.append(url(r'^(\d+)/edit/', self.edit_view, name='%s_edit' % self.namespace)) 273 temp.append(url(r'^add/', self.add_view, name='%s_add' % self.namespace)) 274 return temp 275 276 @property 277 def urls(self): 278 return self.get_urls() 279 280 281 class Stark(object): 282 """ 283 我们这里面的功能是可以跟上面的类写到一起去的,但是我们为了功能解耦,所以就分开写了,这里的主要功能就是 284 生成registry的字典,把键值对生成,然后我们最终的结果是要得到一个实例化对象,供我们后面的程序调用,这里的类才是主要的,核心的代码块 285 而我们上面的那个ModelSubject是辅助我们这里的功能,它之所以分发出去是为了便于扩展其他的功能,我们的自定义样式, 286 还有很多的方法和参数,就像我们的admin里面的ModelAdmin一样,长达1400多行代码,单独把它分离出去便于功能的扩展 287 """ 288 289 def __init__(self): 290 self._registry = {} # 这里是定义一个私有属性,就是为了避免被子类修改 291 292 # 注册model表 293 def register(self, model, model_config=None): # 我们是仿照着admin的源码写的组件,这里的model_config默认值是None, 294 # 我们在传参的时候,如果给它传值,那么就使用我们传入的值替换掉这个None 295 # 它的源码里面有这几个参数,我们也要按照顺序把这几个参数加进来 296 if not model_config: 297 model_config = ModelSubject # 我们这里的model_config我们上面的类ModelSubject实例化出来的对象, 298 # 它是上面的类所实例化出来的对象,这一句写得明明白白的,这大白话再看不懂就真是白学了, 299 self._registry[model] = model_config(model, self) 300 301 # 获取url,第一次分发 302 def get_urls(self): 303 li = [] 304 for model, model_config in self._registry.items(): # 我们在这里所循环的model_config就是 305 # 我们往上数第四行所实例化出来的那个model_config,它是上面的ModelSubject这个类所实例化出来对象, 306 model_name = model._meta.model_name # 这里的._meta.model_name是获取字符串格式的类名, 307 app_label = model._meta.app_label # 这里的._meta.app_labe获取的是字符串格式的App名,都是为了跟url做匹配, 308 sli = url(r'%s%s/' % (app_label, model_name), (model_config.urls, None, None)) # 我们这里的model_config, 309 # 它后面的.urls是在调用一个私有方法,我们的私有方法就是使用.urls来调用,不用加上括号, 310 # 因为有@property这个装饰器在里面起到的作用,然后我们需要找到model_config这个实例对象是哪个类生成的, 311 # 然后找到该类所拥有的方法,从里面找到urls,届时,那个urls就是我们在这里调用的那个urls了, 312 # 所以关键的点就是我们的model_config,老师讲课的时候一再地强调过这个model_config从何而来,这个是关键, 313 li.append(sli) 314 return li 315 316 # 我们最终的数据结构就是这样的,嵌套多层 317 # [ 318 # url( 319 # r'',( 320 # [ 321 # (url(r'',views.add)), 322 # (url(r'',views.edit)), 323 # ], 324 # none,none) 325 # ) 326 # ] 327 @property 328 def urls(self): 329 return self.get_urls(), None, None 330 331 332 site = Stark()
前端模板:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> 8 <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> 9 <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> 10 <title>Title</title> 11 </head> 12 <body> 13 <div class="jumbotron"> 14 <div class="container"> 15 <h1>数据展示</h1> 16 <h2>code change the world</h2> 17 </div> 18 </div> 19 20 <div class="container"> 21 <div class="row"> 22 <div class="col-md-8"> 23 <div> 24 <a href="{{ add_url }}"> 25 <button class="btn btn-primary" value="添加">添加</button> 26 </a> 27 {% if sl.config.search_fields %} 28 <div class="pull-right form-group"> 29 30 <form action="" method="get" class="form-inline"> 31 <input type="text" class="form-control" name="q" value=""> 32 <input type="submit" class="btn btn-info" value="search"> 33 </form> 34 35 </div> 36 {% endif %} 37 <table class="table table-stripped table-hover"> 38 39 <thead> 40 <tr> 41 {% for foo in sl.get_header %} 42 <td>{{ foo }}</td> 43 {% endfor %} 44 </tr> 45 </thead> 46 <tbody> 47 {% for data in sl.get_body %} 48 <tr> 49 {% for item in data %} 50 <td>{{ item }}</td> 51 {% endfor %} 52 </tr> 53 {% endfor %} 54 55 </tbody> 56 57 </table> 58 </div> 59 <div class="text-center"> 60 <nav aria-label="Page navigation"> 61 <ul class="pagination"> 62 {{ sl.pagination.page_html|safe }} 63 </ul> 64 </nav> 65 </div> 66 </div> 67 </div> 68 </div> 69 <script src="/static/js/hello.js"> 70 {# 我们的js代码里面需要注意单引号和双引号的区别,不能轻易改写,还有选择器,组合选择器之间需要空格#} 71 </script> 72 </body> 73 </html>
分页器:
1 # 分页封装方法(带参数版) 2 class MyPage(object): 3 4 def __init__(self, page_num, total_count, base_url, params, per_page_num=3, max_show=5): 5 """ 6 :param params: 当前页码所携带的url里面的键值对参数 7 :param page_num: 当前页面 8 :param total_count: 数据总个数 9 :param base_url: 分页页码跳转的url 10 :param per_page_num: 每一页显示多少条数据 11 :param max_show: 页面上最多显示多少页码 12 """ 13 # 实例化时传进来的参数 14 # 我们在这里捕捉一下异常,把传进去的参数改成数字类型,否则就返回第一页 15 try: 16 page_num = int(page_num) 17 except Exception as e: 18 page_num = 1 19 if page_num < 1: # 如果我们的当前页码出现负数的时候,这里就直接返回第一页,避免出现负数的情况 20 page_num = 1 21 self.params = params 22 self.page_num = page_num 23 self.total_count = total_count 24 self.base_url = base_url 25 self.per_page_num = per_page_num 26 self.max_show = max_show 27 self.half_show = int((self.max_show-1)/2) 28 total_page_num, more = divmod(total_count, per_page_num) 29 """ 30 我们使用总数据的个数对每页显示的数据个数取余,得到的商是页码数,如果有余数就在商的页码数上加一 31 """ 32 if more: 33 total_page_num += 1 34 self.total_page_num = total_page_num 35 36 import copy 37 params = copy.deepcopy(params) # 这里的QueryDict里面是有内置方法,它存储的是一个字典, 38 # 我们的url里面的参数都在这里面,我们要用它就需要对其进行赋值操作,然后它有一个参数是_mutable默认值False, 39 # 不能修改也不能被赋值,我们需要把params给copy一份,使用deepcopy,然后deepcopy的基础上进行赋值 40 params._mutable = True 41 self.params = params # self.params: {'page':23, 'title': python, 'nid': 3} 42 # 这里就是我们的url里面携带的键值对,都封装到params里面了 43 44 # 首页 45 @property 46 def start(self): 47 return (self.page_num-1)*self.per_page_num 48 49 # 尾页 50 @property 51 def end(self): 52 return self.page_num*self.per_page_num 53 54 def page_html(self): 55 """ 56 返回页面上可以用的一段HTML 57 一段可用的分页页码的HTML 58 :return: 59 """ 60 # 如果总页码数<=最大显示页码数,那么起始页码数分别是什么 61 if self.total_page_num <= self.max_show: 62 page_start = 1 63 page_end = self.total_page_num 64 else: # 如果当前页<=最多显示的页码数的一半,那么起始页码分别是什么 65 """ 66 假设,我们最多显示7个分页,那么第一页就是1,最后一页就是7,但是我们的当前页是2, 67 它往前倒推7/2商3余1,2-3得到当前页的首页-1,就是负数了,所以这个时候需要做限制,让首页等于1, 68 """ 69 if self.page_num <= self.half_show: 70 page_start = 1 71 page_end = self.max_show 72 else: 73 # 如果当前页>=总页码数-最多显示的页码数的一半,那么起始页码分别是什么 74 """ 75 如果我们在第6页,我们的页面最多显示7个页码,而我们的总数据就只能显示8页的话, 76 倒推的6+7/2商3余1,就是(6+3)>8,我们的第9页就是空白页,这个时候就需要加以限制, 77 此时第一页就是8-6+1,也就是从第二页开始,一直到第八页,就刚好是7页,这样就完美了 78 此时最后一页就是数据最多所展示的页码第8页 79 """ 80 if self.page_num >= self.total_page_num - self.half_show: 81 page_start = self.total_page_num - self.max_show + 1 82 page_end = self.total_page_num 83 else: # 最后到这里我们判断了数据所在的页码出现在最前面把负数页码排除了,也判断了数据所在的页码出现最后面把空白页码排除了, 84 # 也判断了总页码数还不够我们所设置的最大页码显示,就剩下最后一种情况了,那就是当前页不在最后也不在最前, 85 # 直接用当前页加上或减去最大显示页的1/2,就得到了起始页面 86 page_start = self.page_num - self.half_show 87 page_end = self.page_num + self.half_show 88 89 # 生成前页码的HTML 90 page_html_list = [] 91 92 # 生成第一页 93 self.params['page'] = 1 94 page_first_tmp = '<li><a href="{}?{}">首页</a><li>'.format(self.base_url, self.params.urlencode()) 95 page_html_list.append(page_first_tmp) 96 97 # 生成上一页 98 if self.page_num <= 1: 99 page_prev_tmp = '<li class=disabled><a href="#">上一页</a></li>' 100 else: 101 self.params['page'] = self.page_num-1 102 page_prev_tmp = '<li><a href="{}?{}">上一页</a></li>'.format(self.base_url, self.params.urlencode()) 103 104 page_html_list.append(page_prev_tmp) 105 106 # 生成页码中间页数前半截 107 for i in range(page_start, page_end+1): 108 self.params['page'] = i 109 if i == self.page_num: 110 tmp = '<li class="active"><a href="{}?{}">{}</a></li>'.format(self.base_url, self.params.urlencode(), i) 111 else: 112 tmp = '<li><a href="{}?{}">{}</a></li>'.format(self.base_url, self.params.urlencode(), i,) 113 114 page_html_list.append(tmp) 115 116 # 生成页码中间页数后半截 117 if self.page_num + 1 > self.total_page_num: 118 page_next_tmp = '<li class="disabled"><a href="#">下一页</a></li>' 119 else: 120 self.params['page'] = self.page_num+1 121 page_next_tmp = '<li><a href="{}?{}">下一页</a></li>'.format(self.base_url, self.params.urlencode(),) 122 123 page_html_list.append(page_next_tmp) 124 125 # 生成最后一页 126 self.params['page'] = self.total_page_num 127 page_last_tmp = '<li><a href="{0}?{1}">尾页</a></li>'.format(self.base_url, self.params.urlencode(),) 128 page_html_list.append(page_last_tmp) 129 130 return "".join(page_html_list)
组件的使用:
1 class BookConfig(ModelSubject): 2 list_display = ['id', 'title'] 3 list_display_links = ['title'] 4 search_fields = ['title', ] 5 6 site.register(Book, BookConfig)