django-admin 仿写stark组件action,filter筛选过滤,search查询
写在StandLi里面的方法都是通过ModelSubject这个类里面的stand_li方法,在它里面用StandLi这个类实例化出来一个对象,这个实例化出来的对象把数据传给前端HTML模板进行渲染,所以StandLi这个类里面的方法如果你找不到就是在给前端传数据用的
tag.py
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 from django.db.models import ForeignKey, ManyToManyField 6 from django.utils.http import urlencode 7 import copy 8 9 10 # 这个类主要帮我们处理多级过滤的a标签,我们之前是把多级过滤的a标签给写到get_filter_link_tags这个函数里面, 11 # 后来为了实现功能解耦,避免单个函数代码量过大,就把这个功能封装成了一个类,以便于阅读,以及功能扩展 12 class LinkTagsGen(object): 13 def __init__(self, data, filter_field, request): 14 self.data = data 15 self.filter_field = filter_field 16 self.request = request 17 18 def __iter__(self): 19 """ 20 所有的可迭代对象内部都是实现了__iter__方法,我们把数据写到这里就是实现的数据的可迭代 21 :return: yield 多级过滤的A标签 22 """ 23 current_id = int(self.request.GET.get(self.filter_field.filter_name, 0)) 24 params = copy.deepcopy(self.request.GET) 25 params._mutable = True 26 if params.get(self.filter_field.filter_name): 27 del params[self.filter_field.filter_name] 28 _url = "%s?%s" % (self.request.path_info, params.urlencode()) 29 yield mark_safe("<a href='%s'>All</a>" % _url) 30 else: 31 yield mark_safe("<a href='#' class='active'>All</a>") 32 33 for item in self.data: # self.data是一个个的queryset集合以及元祖,((1,'已出版'),(2,'未出版')), 34 # <QuerySet[<Publish:人民出版社>,<Publish:北京出版社>]> 35 pk, text = None, None 36 if self.filter_field.filter_obj.choices: 37 pk, text = item[0], item[1] 38 elif isinstance(self.filter_field.filter_obj, ForeignKey) or isinstance(self.filter_field.filter_obj, ManyToManyField): 39 pk, text = item.pk, item 40 41 params[self.filter_field.filter_name] = pk 42 _url = "%s?%s" % (self.request.path_info, params.urlencode()) 43 if current_id == pk: 44 link_tag = "<a class='active' href='%s'>%s</a>" %(_url, text) 45 else: 46 link_tag = "<a href='%s'>%s</a>" % (_url, text) 47 yield mark_safe(link_tag) 48 49 50 # 为每一个过滤的字段封装成整体类 51 class FilterField(object): 52 def __init__(self, filter_name, filter_obj): 53 self.filter_name = filter_name 54 self.filter_obj = filter_obj 55 56 def get_data(self): 57 if isinstance(self.filter_obj, ForeignKey) or isinstance(self.filter_obj, ManyToManyField): 58 return self.filter_obj.rel.to.objects.all() 59 elif self.filter_obj.choices: 60 return self.filter_obj.choices 61 62 63 # 服务于ModelSubject下面的stand_li,我们把stand_li里面的很多方法给封装到这个类里面实现功能解耦, 64 # 主要是为了减轻我们的StandLi里面的代码量 65 class StandLi(object): 66 def __init__(self, config, request, queryset): 67 """ 68 :param config: 它就是我们下面的类ModelSubject所传过来的它的self实例对象, 69 我们在这里要使用那些方法和变量就需要把它的这个实例对象拿过来,否则如下搬过来的代码块都会失效 70 :param request: 我们这个类是在下面的ModelSubject里面调用然后在那里实例化出来的对象,所以这个request是它传过来的 71 :param queryset: 同上,这个queryset也是ModelSubject所传过来的参数,供下面的代码调用 72 """ 73 self.config = config 74 self.request = request 75 self.queryset = queryset 76 77 # 生成分页器 78 path = self.request.path_info 79 params = self.request.GET 80 page_num = request.GET.get('page', 1) # 如果没有找到page,就返回1,也就是第一页 81 from file.utensil.page import MyPage 82 count = queryset.count() 83 page = MyPage(page_num, count, path, params) 84 self.pagination = page 85 data_list = self.queryset[page.start:page.end] 86 self.data_list = data_list 87 # page_html = page.page_html() # 这里我们可以把page_html方法直接在前端HTML模板里面引用 88 89 # actions 实现批量操作功能 90 self.actions = self.config.get_actions() # [patch_init, patch_delete] 91 # print('actions', self.actions) 92 93 # filter 实现筛选功能 94 self.list_filter = self.config.list_filter 95 96 # 实现多级过滤的类里面封装的一个函数 97 def get_filter_link_tags(self): 98 for filter_name in self.list_filter: 99 filter_obj = self.config.model._meta.get_field(filter_name) 100 filter_field = FilterField(filter_name, filter_obj) 101 # print("filter_field", filter_field.get_data()) 102 """ 103 filter_field <QuerySet [<Publish: 长春出版社>, <Publish: 香港中文大学出版社>, <Publish: 中信出版社>]> 104 filter_field ((0, '已出版'), (1, '未出版')) 105 filter_field <QuerySet [<Writer: White>, <Writer: Black>, <Writer: Miss Lin>]> 106 """ 107 val = LinkTagsGen(filter_field.get_data(), filter_field, self.request) 108 yield val 109 110 # 这里是使用两个yield去实现的多级过滤 111 # 展示筛选条件 112 # def get_filter_link_tags(self): # list_filter=['state','publish','authors'] 113 # 114 # for filter_name in self.list_filter: 115 # current_id = int(self.request.GET.get(filter_name, 0)) # 这里加上int之后我们点击超链接标签的时候就会有字体颜色的变化 116 # # current_id = self.request.GET.get(filter_name, 0) # 这里没有加上int点击超链接的时候不会有变化 117 # # print('current_id', current_id) 118 # filter_obj = self.config.model._meta.get_field(filter_name) 119 # # print('filter_obj', filter_obj) 120 # filter_field = FilterField(filter_name, filter_obj) 121 # # print("filter_field",filter_field) 122 # def inner(filter_field, current_id): 123 # # print(filter_field.get_data()) 124 # 125 # # 这里得出的结果是我们的多对多字段和一对多字段的所有关联数据 126 # # <QuerySet [<Publish: 长春出版社>, <Publish: 香港中文大学出版社>, <Publish: 中信出版社>]> 127 # # <QuerySet [<Writer: White>, <Writer: Black>, <Writer: Miss Lin>]> 128 # 129 # for obj in filter_field.get_data(): 130 # if isinstance(filter_field.filter_obj, ForeignKey) or \ 131 # isinstance(filter_field.filter_obj, ManyToManyField): 132 # # link_tags=[] 133 # params = copy.deepcopy(self.request.GET) 134 # params._mutable = True 135 # params[filter_field.filter_name] = obj.pk 136 # 137 # if current_id == obj.pk: 138 # yield mark_safe("<a class='active' href='?%s'>%s</a>" % (params.urlencode(), obj)) 139 # else: 140 # yield mark_safe("<a href='?%s'>%s</a>" % (params.urlencode(), obj)) 141 # elif filter_field.filter_obj.choices: 142 # params = copy.deepcopy(self.request.GET) 143 # params._mutable = True 144 # params[filter_field.filter_name] = obj[0] 145 # if current_id == obj[0]: 146 # yield mark_safe("<a class='active' href='?%s'>%s</a>" % (params.urlencode(), obj[1])) 147 # else: 148 # yield mark_safe("<a href='?%s'>%s</a>" % (params.urlencode(), obj[1])) 149 # else: 150 # # params = copy.deepcopy(self.request.GET) 151 # # params._mutable = True 152 # # params[filter_field.filter_name] = obj 153 # # yield mark_safe("<a href='?%s'>%s</a>" % (params.urlencode(), obj)) 154 # ... 155 # yield inner(filter_field, current_id) 156 157 # 把自定义的action内容放到一个列表里面,以键值对的数据类型 158 def handle_actions(self): 159 temp = [] 160 for action_func in self.actions: 161 temp.append({'name': action_func.__name__, "desc": action_func.desc}) 162 return temp 163 164 # 获取表头数据 165 def get_header(self): 166 # 生成表头数据 167 # ['id','title','price',edit] 168 header_list = [] 169 for field in self.config.get_list_display(): 170 if callable(field): 171 ret = field(self, is_header=True) 172 header_list.append(ret) 173 else: 174 if field == '__str__': # 这里是判断我们的list_display列表里面是否有我们自定义传入的值,如果没有的话, 175 # 就是直接等于我们在静态属性里面设定的那个默认的'__str__',也就是说如果这一步判断成立, 176 # 那么就证明我们的用户没有自定义展示的字段,我们就需要自己给浏览器一个字段去展示,那个字段就是我们这里所设定的那个大写的表名 177 header_list.append(self.config.model._meta.model_name.upper()) # 我们这里的操作是 178 else: 179 obj = self.config.model._meta.get_field(field) # 我们的list_display里面是一个个的字符串, 180 # 把字符串放到get_field里面来可以把我们的字符串转换成类对象, 181 header_list.append(obj.verbose_name) # 我们这里的verbose_name在model里面是内置方法, 182 # 我们的verbose_name本质上是对我们的字段进行描述的,比如我们的book里面的title可以在字段约束里面设 183 # verbose_name='书名',类似于这样,把它变成中文,然后我们在前端HTML模板里面渲染的时候就可以渲染出来中文了, 184 # 而不是使用默认的英文,当然了我们如果不设置verbose_name的值那么就使用默认的title作为字段名传到浏览器 185 186 return header_list 187 188 # 获取表单数据 189 def get_body(self): 190 # 生成表单数据列表(我们把这个函数挪到上面这个类里面来之后,把循环遍历的数据改动了,之前是把当前表格的数据取出来之后就直接遍历它, 191 # 后来我们有了搜索功能,那么就不能遍历表格里面的所有数据了,需要把我们过滤查询出来的数据给遍历出来 192 # ret = self.config.model.objects.all().count() 193 # print('self.list_display', self.list_display) 194 data_list = [] 195 for obj in self.data_list: # 我们遍历这个queryset集合得到的obj是它的每一个对象 196 temp = [] 197 for field in self.config.get_list_display(): # 我们遍历list_display得到每一个字符串 198 if callable(field): 199 # res = field(obj) # @@@更上面的特殊标识的代码块相呼应 200 res = field(self.config, obj) # &&& 这里跟上面同样标识的代码块相呼应的,上面我们使用的类名去调用函数名, 201 # 得到的是一个函数,这里就是给所调用的函数传参的,self,和obj都是我们传给函数的参数; 202 # 如果我们使用self这个对象去调用函数名的方法的话,就不需要再传一个self作为参数进去了,我们两种方法都可以,需要对应上即可 203 else: 204 res = getattr(obj, field) # 使用getattr方法去判断该对象是否具有,field属性方法, 205 # getattr里面需要两个参数(类对象,字符串属性方法) 206 if field in self.config.list_display_links: # 我们这里是判断表单里面的字段是否在links表格里面被自定义作为可跳转标签, 207 # 如果答案是肯定的,那么我们就需要把a标签给拼出来 208 res = self.config.get_link_tag(obj, res) 209 temp.append(res) 210 data_list.append(temp) 211 212 # print('data_list', data_list) 213 214 """ 215 我们最终得到的数据类型是如下格式:列表套着列表 216 [ 217 使用orm语句得到的每一个类对象,有几个表格就有几个对象 218 ] 219 list_display=['id','title',] 220 [ 221 [1,'python',<a>编辑</a>], 222 [2,'java',<a>编辑</a>], 223 ] 224 """ 225 return data_list 226 227 228 class ModelSubject(object): 229 """ 230 我们在这里模拟admin源码里面的ModelAdmin, 231 """ 232 list_display = ["__str__"] # 我们在这里给空列表里面加上"__str__",它就相当于是一个默认值, 233 model_form_class = None # 为下面我们判断用户是否有自定义ModelForm校验方式而做铺垫 234 search_fields = [] 235 list_display_links = [] # 为我们后面用户是否有自定义可跳转字段做铺垫 236 # 就像我们的函数里面有默认值的参数一样,如果有传参就使用我们的自定义传参,如果没有传参就使用我们默认的参数也就是这个字符串, 237 # 这里是为了给我们后面的代码做铺垫,我们的目的是在我们的数据展示页面里面默认就会把复选框和编辑还有删除按钮都加上, 238 # 在这里把空列表里面添加上一个默认的字符串,是为了我们后面往该列表里面添加默认固定数据也就是复选框和编辑删除按钮做铺垫 239 actions = [] 240 list_filter = [] # 多级过滤 241 242 # 静态内置方法 243 def __init__(self, model, site): 244 self.model = model # 当我们生成一个实例化对象的时候需要把model这个参数传进来, 245 # 必须要传,它是位置参数,然后我们所传入的那个model就是我们在models.py里面定义的每一个表名也就是类名 246 self.site = site 247 self.namespace = '{}_{}'.format(self.model._meta.app_label, self.model._meta.model_name) 248 # self.app_model_name = (self.model._meta.app_label, self.model._meta.model_name) # 这里写得跟上面一句是一样的效果, 249 # 这里调用的时候需要有两个%s,因为这里是一个元祖,而我们上面的namespace是一个字符串,不是一个元祖,所以只需要一个%s即可,调用的时候就这点区别 250 # 我们这里的namespace是因为会频繁使用到所以就把它作为一个内置静态属性写入到这里,其他地方如果要调用它就直接使用self.namespace即可 251 # .format的方法:'{}_{}'.format(a,b) 252 253 # 默认actions批量删除 254 def patch_delete(self, queryset): 255 queryset.delete() 256 return None 257 patch_delete.desc = '批量删除' 258 259 # 获取真正展示的actions 260 def get_actions(self): 261 temp = [] 262 temp.extend(self.actions) # [patch_init,patch_delete] 263 temp.append(ModelSubject.patch_delete) 264 return temp 265 266 # 获取展示页面的url 267 def get_stand_url(self): 268 stand_url = reverse('%s_standlist' % self.namespace) 269 return stand_url 270 271 # 获取编辑页面的url 272 def get_edit_url(self, obj): 273 edit_url = reverse('%s_edit' % self.namespace, args=(obj.pk,)) 274 return edit_url 275 276 # 获取删除页面的url 277 def get_dele_url(self, obj): 278 dele_url = reverse('%s_dele' % self.namespace, args=(obj.pk,)) 279 return dele_url 280 281 # 获取增加页面的url 282 def get_add_url(self): 283 add_url = reverse('%s_add' % self.namespace) 284 return add_url 285 286 # 展示页面默认附带的编辑按钮 287 def edit(self, obj=None, is_header=False): 288 if is_header: 289 return '操作' 290 return mark_safe('<a href="%s">编辑</a>' % reverse('%s_edit' % self.namespace, args=(obj.pk,))) 291 292 # 展示页面默认附带的删除按钮 293 def dele(self, obj=None, is_header=False): 294 if is_header: 295 return '删除' 296 return mark_safe("<a href='%s'>删除</a>" % reverse('%s_dele' % self.namespace, args=(obj.pk,))) 297 298 # 展示页面附带的默认复选框 299 def checkbox(self, obj=None, is_header=False): 300 if is_header: 301 return mark_safe("<input id='action-toggle' type='checkbox'>") 302 return mark_safe("<input type='checkbox' value='%s' name='_selected_action'>" % obj.pk) 303 304 # 展示页面默认显示按钮被存放的列表 305 def get_list_display(self): 306 new_li = [] 307 new_li.extend(self.list_display) # 我们这里的extend是把它后面的列表里面的数据都取出来放到我们自己的这个列表里面来 308 if not self.list_display_links: 309 new_li.append(ModelSubject.edit) # &&& 跟如下同样特征的代码块相呼应我们在这里使用类名去调用函数名,得到的是一个函数的方法, 310 # 函数如果有参数是需要我们传参数的;但是我们如果使用self去调用的话,self就是实例化出来的对象, 311 # 而我们的对象去调用函数方法的时候就不需要去传自己了也就是self, 312 new_li.append(ModelSubject.dele) 313 new_li.insert(0, ModelSubject.checkbox) # 把checkbox放到第一个位置,使用insert插入到索引为0 314 """ 315 # @@@ 跟下面特殊标识的代码块相呼应 316 new_li.append(self.edit) 317 new_li.append(self.dele) 318 new_li.insert(0,self.checkbox) 319 """ 320 return new_li 321 322 # 模糊查询 323 def get_search_condition(self): 324 from django.db.models import Q 325 search_condition = Q() 326 search_condition.connector = 'or' 327 if self.search_fields: # 如果用户有自定义的查询字段,我们就走这个if下面的代码, 328 key_word = self.request.GET.get('q') # 取出用户输入的input里面的值, 329 if key_word: 330 for search_field in self.search_fields: # 遍历用户自定义的查询字段列表, 331 search_condition.children.append((search_field + "__contains", key_word)) 332 return search_condition 333 334 # 处理用户自定义的link超链接字段标签,然后让超链接携带url键值对参数方法,供get_body使用 335 def get_link_tag(self, obj, val): 336 params = self.request.GET 337 params = copy.deepcopy(params) 338 params._mutable = True 339 # from django.http import QueryDict 340 # qqx = QueryDict(mutable=True) 341 qqx = {} # 这里只写一行,等效于上面的两行,当然了前提是我们要引入urlencoded 342 qqx['list_filter'] = params.urlencode() 343 whh = mark_safe("<a href='{}?{}'>{}</a>".format(self.get_edit_url(obj), urlencode(qqx), val)) 344 return whh 345 346 # 获取多级过滤的数据 347 def get_filter_condition(self): 348 from django.db.models import Q 349 fiter_condition = Q() 350 for field, val in self.request.GET.items(): 351 if field in self.list_filter: 352 fiter_condition.children.append((field, val)) 353 return fiter_condition 354 355 # 展示页面 356 def stand_li(self, request): 357 # print(self.model) 358 # 所以我们在这里可以获取到当前的url里面的表名,然后直接使用orm语句即可得到当前表格的所有信息 359 if request.method == 'POST': 360 # print('request.POST.get', request.POST.get('action')) 361 # print('request.POST.getlist', request.POST.getlist("_selected_action")) 362 # 打印出来的结果是request.POST.getlist ['on', 'on'] 这个on是从何而来的,我的input里面的所有value值都设定的是obj.pk, 363 # 这个obj.pk是有值的, 364 # 为什么这里打印出来的getlist是两个on?我们的input标签上面有name值, 365 # 然后我们使用request.POST.get后面的括号里面放的是name属性的值,然后它的返回值是我们的input标签里面的value值, 366 # 都说了是value值,怎么还能加上s呢?简直愚蠢, 367 368 pk_list = request.POST.getlist("_selected_action") 369 queryset = self.model.objects.filter(pk__in=pk_list) 370 func_name = request.POST.get("action") 371 func = getattr(self, func_name) 372 ret = func(queryset) 373 374 self.request = request 375 376 # 关于search的模糊查询 377 search_condition = self.get_search_condition() 378 379 # action 380 # a=self.model.objects.all().count() # 这样就是可以获取我们的queryset集合的总数据长度, 381 # 然后就可以用它去传给我们的分页组件,用它也可以,直接用count就能获取数据长度,或者是用len也行,我之前都是用len获取的 382 383 # filter多级过滤, 384 get_filter_condition = self.get_filter_condition() 385 queryset = self.model.objects.filter(search_condition).filter(get_filter_condition) 386 add_url = self.get_add_url() 387 sl = StandLi(self, request, queryset) # 这里是把我们的StandLi这个类所需要的参数都传给它,然后通过StandLi实例化出来的一个对象 388 # 然后在这里实例化出来一个对象我在这里调用那个对象就能够使用那个类里面的封装的方法了 389 390 return render(request, 'file/hello.html', locals()) 391 392 # ModelForm校验添加和编辑页面 393 def get_modelform_class(self): 394 from django.forms import widgets 395 396 class AllModelForm(ModelForm): 397 class Meta: 398 model = self.model 399 fields = '__all__' 400 if not self.model_form_class: # 这里的model_form_class在上面被定义了默认是None, 401 # 我们的分发下去的App里面自定义的file文件里面注册model类的时候实例化出来的对象,在注册的时候传过来的这个变量 402 return AllModelForm 403 else: 404 return self.model_form_class 405 406 # ModelForm校验数据添加页面 407 def add_view(self, request): 408 FormClass = self.get_modelform_class() 409 if request.method == 'GET': 410 form = FormClass() 411 return render(request, 'file/add.html', {'form': form}) 412 else: 413 data_list = FormClass(data=request.POST) 414 if data_list.is_valid(): 415 data_list.save() 416 return redirect(self.get_stand_url()) 417 else: 418 return render(request, 'file/add.html', {'form': data_list}) 419 420 # ModelForm校验数据编辑页面 421 def edit_view(self, request, id): 422 edit_list = self.model.objects.filter(pk=id).first() 423 FormClass = self.get_modelform_class() 424 if request.method == 'GET': 425 data_list = FormClass(instance=edit_list) 426 return render(request, 'file/edit.html', {'form': data_list}) 427 else: 428 data_list = FormClass(data=request.POST, instance=edit_list) 429 if data_list.is_valid(): 430 data_list.save() 431 return redirect(self.get_stand_url()) 432 else: 433 return render(request, 'file/edit.html', {'form': data_list}) 434 435 # 数据删除页面 436 def dele_view(self, request, id): 437 del_obj = self.model.objects.filter(pk=id).first() 438 if request.method == 'GET': 439 stand_url = self.get_stand_url() 440 return render(request, 'file/dele.html', {'del_obj': del_obj, 'list_url': stand_url}) 441 else: 442 del_obj.delete() 443 444 return redirect(self.get_stand_url()) 445 446 # 获取url,此为第二次分发 447 def get_urls(self): 448 temp = [] 449 temp.append(url(r'^$', self.stand_li, name='%s_standlist' % self.namespace)) 450 temp.append(url(r'^(\d+)/dele/', self.dele_view, name='%s_dele' % self.namespace)) 451 temp.append(url(r'^(\d+)/edit/', self.edit_view, name='%s_edit' % self.namespace)) 452 temp.append(url(r'^add/', self.add_view, name='%s_add' % self.namespace)) 453 return temp 454 455 @property 456 def urls(self): 457 return self.get_urls() 458 459 460 class Stark(object): 461 """ 462 我们这里面的功能是可以跟上面的类写到一起去的,但是我们为了功能解耦,所以就分开写了,这里的主要功能就是 463 生成registry的字典,把键值对生成,然后我们最终的结果是要得到一个实例化对象,供我们后面的程序调用,这里的类才是主要的,核心的代码块 464 而我们上面的那个ModelSubject是辅助我们这里的功能,它之所以分发出去是为了便于扩展其他的功能,我们的自定义样式, 465 还有很多的方法和参数,就像我们的admin里面的ModelAdmin一样,长达1400多行代码,单独把它分离出去便于功能的扩展 466 """ 467 468 def __init__(self): 469 self._registry = {} # 这里是定义一个私有属性,就是为了避免被子类修改 470 471 # 注册model表 472 def register(self, model, model_config=None): # 我们是仿照着admin的源码写的组件,这里的model_config默认值是None, 473 # 我们在传参的时候,如果给它传值,那么就使用我们传入的值替换掉这个None 474 # 它的源码里面有这几个参数,我们也要按照顺序把这几个参数加进来 475 if not model_config: 476 model_config = ModelSubject # 我们这里的model_config我们上面的类ModelSubject实例化出来的对象, 477 # 它是上面的类所实例化出来的对象,这一句写得明明白白的,这大白话再看不懂就真是白学了, 478 self._registry[model] = model_config(model, self) 479 480 # 获取url,第一次分发 481 def get_urls(self): 482 li = [] 483 for model, model_config in self._registry.items(): # 我们在这里所循环的model_config就是 484 # 我们往上数第四行所实例化出来的那个model_config,它是上面的ModelSubject这个类所实例化出来对象, 485 model_name = model._meta.model_name # 这里的._meta.model_name是获取字符串格式的类名, 486 app_label = model._meta.app_label # 这里的._meta.app_labe获取的是字符串格式的App名,都是为了跟url做匹配, 487 sli = url(r'%s%s/' % (app_label, model_name), (model_config.urls, None, None)) # 我们这里的model_config, 488 # 它后面的.urls是在调用一个私有方法,我们的私有方法就是使用.urls来调用,不用加上括号, 489 # 因为有@property这个装饰器在里面起到的作用,然后我们需要找到model_config这个实例对象是哪个类生成的, 490 # 然后找到该类所拥有的方法,从里面找到urls,届时,那个urls就是我们在这里调用的那个urls了, 491 # 所以关键的点就是我们的model_config,老师讲课的时候一再地强调过这个model_config从何而来,这个是关键, 492 li.append(sli) 493 return li 494 495 # 我们最终的数据结构就是这样的,嵌套多层 496 # [ 497 # url( 498 # r'',( 499 # [ 500 # (url(r'',views.add)), 501 # (url(r'',views.edit)), 502 # ], 503 # none,none) 504 # ) 505 # ] 506 @property 507 def urls(self): 508 return self.get_urls(), None, None 509 510 511 site = Stark()
HTML模板渲染:
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="/static/plugins/bs/css/bootstrap.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 12 <style> 13 .filter a { 14 padding: 3px 6px; 15 border: 1px solid #336699; 16 color: white; 17 background-color: pink; 18 } 19 20 .active { 21 background-color: #336699 !important; 22 color: white !important; 23 } 24 </style> 25 26 </head> 27 <body> 28 <div class="jumbotron"> 29 <div class="container"> 30 <h1>数据展示</h1> 31 <h2>code change the world</h2> 32 </div> 33 </div> 34 35 <div class="container"> 36 <div class="row"> 37 <div class="col-md-8"> 38 39 <a href="{{ add_url }}"> 40 <button class="btn btn-primary" value="添加">添加数据</button> 41 </a> 42 {% if sl.config.search_fields %} 43 <div class="pull-right form-group"> 44 45 <form action="" method="get" class="form-inline"> 46 <input type="text" class="form-control" name="q" value=""> 47 <input type="submit" class="btn btn-info" value="search"> 48 </form> 49 50 </div> 51 {% endif %} 52 53 <form action="" method="post"> 54 {% csrf_token %} 55 <div> 56 <select class="form-control" name="action" id="" style="width:200px;margin:8px 2px; 57 display:inline-block;vertical-align:-1px"> 58 <option value="">---------</option> 59 {% for item in sl.handle_actions %} 60 <option value="{{ item.name }}">{{ item.desc }}</option> 61 {% endfor %} 62 </select> 63 <button type="submit" class="btn btn-primary">Go</button> 64 </div> 65 66 <table class="table table-stripped table-hover"> 67 68 <thead> 69 <tr> 70 {% for foo in sl.get_header %} 71 <td>{{ foo }}</td> 72 {% endfor %} 73 </tr> 74 </thead> 75 <tbody> 76 {% for data in sl.get_body %} 77 <tr> 78 {% for item in data %} 79 <td>{{ item }}</td> 80 {% endfor %} 81 </tr> 82 {% endfor %} 83 84 </tbody> 85 86 </table> 87 </form> 88 89 90 <div class="text-center"> 91 <nav aria-label="Page navigation"> 92 <ul class="pagination"> 93 {{ sl.pagination.page_html|safe }} 94 </ul> 95 </nav> 96 </div> 97 98 99 </div> 100 101 <div class="col-md-3"> 102 <div class="filter"> 103 {% for filter_link_tag in sl.get_filter_link_tags %} 104 105 106 <div class="well">{% for data in filter_link_tag %} 107 <div>{{ data }}</div> 108 {% endfor %} 109 </div> 110 111 {% endfor %} 112 </div> 113 </div> 114 </div> 115 </div> 116 <script src="/static/js/hello.js"> 117 {# 我们的js代码里面需要注意单引号和双引号的区别,不能轻易改写,还有选择器,组合选择器之间需要空格#} 118 </script> 119 </body> 120 </html>
静态文件:
1 $("#action-toggle").click(function(){ 2 if($(this).prop("checked")){ 3 $("tbody :checkbox").prop("checked",true); 4 }else{ 5 $("tbody :checkbox").prop("checked",false); 6 } 7 });