django-admin 仿写stark组件action,filter筛选过滤,search查询

时间:2022-01-01 20:08:27

写在StandLi里面的方法都是通过ModelSubject这个类里面的stand_li方法,在它里面用StandLi这个类实例化出来一个对象,这个实例化出来的对象把数据传给前端HTML模板进行渲染,所以StandLi这个类里面的方法如果你找不到就是在给前端传数据用的

tag.py

 from django.conf.urls import url
from django.shortcuts import render, redirect, reverse
from django.utils.safestring import mark_safe
from django.forms import ModelForm # 这个ModelForm里面封装了很强大的功能,要把源码过一遍
from django.db.models import ForeignKey, ManyToManyField
from django.utils.http import urlencode
import copy # 这个类主要帮我们处理多级过滤的a标签,我们之前是把多级过滤的a标签给写到get_filter_link_tags这个函数里面,
# 后来为了实现功能解耦,避免单个函数代码量过大,就把这个功能封装成了一个类,以便于阅读,以及功能扩展
class LinkTagsGen(object):
def __init__(self, data, filter_field, request):
self.data = data
self.filter_field = filter_field
self.request = request def __iter__(self):
"""
所有的可迭代对象内部都是实现了__iter__方法,我们把数据写到这里就是实现的数据的可迭代
:return: yield 多级过滤的A标签
"""
current_id = int(self.request.GET.get(self.filter_field.filter_name, 0))
params = copy.deepcopy(self.request.GET)
params._mutable = True
if params.get(self.filter_field.filter_name):
del params[self.filter_field.filter_name]
_url = "%s?%s" % (self.request.path_info, params.urlencode())
yield mark_safe("<a href='%s'>All</a>" % _url)
else:
yield mark_safe("<a href='#' class='active'>All</a>") for item in self.data: # self.data是一个个的queryset集合以及元祖,((1,'已出版'),(2,'未出版')),
# <QuerySet[<Publish:人民出版社>,<Publish:北京出版社>]>
pk, text = None, None
if self.filter_field.filter_obj.choices:
pk, text = item[0], item[1]
elif isinstance(self.filter_field.filter_obj, ForeignKey) or isinstance(self.filter_field.filter_obj, ManyToManyField):
pk, text = item.pk, item params[self.filter_field.filter_name] = pk
_url = "%s?%s" % (self.request.path_info, params.urlencode())
if current_id == pk:
link_tag = "<a class='active' href='%s'>%s</a>" %(_url, text)
else:
link_tag = "<a href='%s'>%s</a>" % (_url, text)
yield mark_safe(link_tag) # 为每一个过滤的字段封装成整体类
class FilterField(object):
def __init__(self, filter_name, filter_obj):
self.filter_name = filter_name
self.filter_obj = filter_obj def get_data(self):
if isinstance(self.filter_obj, ForeignKey) or isinstance(self.filter_obj, ManyToManyField):
return self.filter_obj.rel.to.objects.all()
elif self.filter_obj.choices:
return self.filter_obj.choices # 服务于ModelSubject下面的stand_li,我们把stand_li里面的很多方法给封装到这个类里面实现功能解耦,
# 主要是为了减轻我们的StandLi里面的代码量
class StandLi(object):
def __init__(self, config, request, queryset):
"""
:param config: 它就是我们下面的类ModelSubject所传过来的它的self实例对象,
我们在这里要使用那些方法和变量就需要把它的这个实例对象拿过来,否则如下搬过来的代码块都会失效
:param request: 我们这个类是在下面的ModelSubject里面调用然后在那里实例化出来的对象,所以这个request是它传过来的
:param queryset: 同上,这个queryset也是ModelSubject所传过来的参数,供下面的代码调用
"""
self.config = config
self.request = request
self.queryset = queryset # 生成分页器
path = self.request.path_info
params = self.request.GET
page_num = request.GET.get('page', 1) # 如果没有找到page,就返回1,也就是第一页
from file.utensil.page import MyPage
count = queryset.count()
page = MyPage(page_num, count, path, params)
self.pagination = page
data_list = self.queryset[page.start:page.end]
self.data_list = data_list
# page_html = page.page_html() # 这里我们可以把page_html方法直接在前端HTML模板里面引用 # actions 实现批量操作功能
self.actions = self.config.get_actions() # [patch_init, patch_delete]
# print('actions', self.actions) # filter 实现筛选功能
self.list_filter = self.config.list_filter # 实现多级过滤的类里面封装的一个函数
def get_filter_link_tags(self):
for filter_name in self.list_filter:
filter_obj = self.config.model._meta.get_field(filter_name)
filter_field = FilterField(filter_name, filter_obj)
# print("filter_field", filter_field.get_data())
"""
filter_field <QuerySet [<Publish: 长春出版社>, <Publish: 香港中文大学出版社>, <Publish: 中信出版社>]>
filter_field ((0, '已出版'), (1, '未出版'))
filter_field <QuerySet [<Writer: White>, <Writer: Black>, <Writer: Miss Lin>]>
"""
val = LinkTagsGen(filter_field.get_data(), filter_field, self.request)
yield val # 这里是使用两个yield去实现的多级过滤
# 展示筛选条件
# def get_filter_link_tags(self): # list_filter=['state','publish','authors']
#
# for filter_name in self.list_filter:
# current_id = int(self.request.GET.get(filter_name, 0)) # 这里加上int之后我们点击超链接标签的时候就会有字体颜色的变化
# # current_id = self.request.GET.get(filter_name, 0) # 这里没有加上int点击超链接的时候不会有变化
# # print('current_id', current_id)
# filter_obj = self.config.model._meta.get_field(filter_name)
# # print('filter_obj', filter_obj)
# filter_field = FilterField(filter_name, filter_obj)
# # print("filter_field",filter_field)
# def inner(filter_field, current_id):
# # print(filter_field.get_data())
#
# # 这里得出的结果是我们的多对多字段和一对多字段的所有关联数据
# # <QuerySet [<Publish: 长春出版社>, <Publish: 香港中文大学出版社>, <Publish: 中信出版社>]>
# # <QuerySet [<Writer: White>, <Writer: Black>, <Writer: Miss Lin>]>
#
# for obj in filter_field.get_data():
# if isinstance(filter_field.filter_obj, ForeignKey) or \
# isinstance(filter_field.filter_obj, ManyToManyField):
# # link_tags=[]
# params = copy.deepcopy(self.request.GET)
# params._mutable = True
# params[filter_field.filter_name] = obj.pk
#
# if current_id == obj.pk:
# yield mark_safe("<a class='active' href='?%s'>%s</a>" % (params.urlencode(), obj))
# else:
# yield mark_safe("<a href='?%s'>%s</a>" % (params.urlencode(), obj))
# elif filter_field.filter_obj.choices:
# params = copy.deepcopy(self.request.GET)
# params._mutable = True
# params[filter_field.filter_name] = obj[0]
# if current_id == obj[0]:
# yield mark_safe("<a class='active' href='?%s'>%s</a>" % (params.urlencode(), obj[1]))
# else:
# yield mark_safe("<a href='?%s'>%s</a>" % (params.urlencode(), obj[1]))
# else:
# # params = copy.deepcopy(self.request.GET)
# # params._mutable = True
# # params[filter_field.filter_name] = obj
# # yield mark_safe("<a href='?%s'>%s</a>" % (params.urlencode(), obj))
# ...
# yield inner(filter_field, current_id) # 把自定义的action内容放到一个列表里面,以键值对的数据类型
def handle_actions(self):
temp = []
for action_func in self.actions:
temp.append({'name': action_func.__name__, "desc": action_func.desc})
return temp # 获取表头数据
def get_header(self):
# 生成表头数据
# ['id','title','price',edit]
header_list = []
for field in self.config.get_list_display():
if callable(field):
ret = field(self, is_header=True)
header_list.append(ret)
else:
if field == '__str__': # 这里是判断我们的list_display列表里面是否有我们自定义传入的值,如果没有的话,
# 就是直接等于我们在静态属性里面设定的那个默认的'__str__',也就是说如果这一步判断成立,
# 那么就证明我们的用户没有自定义展示的字段,我们就需要自己给浏览器一个字段去展示,那个字段就是我们这里所设定的那个大写的表名
header_list.append(self.config.model._meta.model_name.upper()) # 我们这里的操作是
else:
obj = self.config.model._meta.get_field(field) # 我们的list_display里面是一个个的字符串,
# 把字符串放到get_field里面来可以把我们的字符串转换成类对象,
header_list.append(obj.verbose_name) # 我们这里的verbose_name在model里面是内置方法,
# 我们的verbose_name本质上是对我们的字段进行描述的,比如我们的book里面的title可以在字段约束里面设
# verbose_name='书名',类似于这样,把它变成中文,然后我们在前端HTML模板里面渲染的时候就可以渲染出来中文了,
# 而不是使用默认的英文,当然了我们如果不设置verbose_name的值那么就使用默认的title作为字段名传到浏览器 return header_list # 获取表单数据
def get_body(self):
# 生成表单数据列表(我们把这个函数挪到上面这个类里面来之后,把循环遍历的数据改动了,之前是把当前表格的数据取出来之后就直接遍历它,
# 后来我们有了搜索功能,那么就不能遍历表格里面的所有数据了,需要把我们过滤查询出来的数据给遍历出来
# ret = self.config.model.objects.all().count()
# print('self.list_display', self.list_display)
data_list = []
for obj in self.data_list: # 我们遍历这个queryset集合得到的obj是它的每一个对象
temp = []
for field in self.config.get_list_display(): # 我们遍历list_display得到每一个字符串
if callable(field):
# res = field(obj) # @@@更上面的特殊标识的代码块相呼应
res = field(self.config, obj) # &&& 这里跟上面同样标识的代码块相呼应的,上面我们使用的类名去调用函数名,
# 得到的是一个函数,这里就是给所调用的函数传参的,self,和obj都是我们传给函数的参数;
# 如果我们使用self这个对象去调用函数名的方法的话,就不需要再传一个self作为参数进去了,我们两种方法都可以,需要对应上即可
else:
res = getattr(obj, field) # 使用getattr方法去判断该对象是否具有,field属性方法,
# getattr里面需要两个参数(类对象,字符串属性方法)
if field in self.config.list_display_links: # 我们这里是判断表单里面的字段是否在links表格里面被自定义作为可跳转标签,
# 如果答案是肯定的,那么我们就需要把a标签给拼出来
res = self.config.get_link_tag(obj, res)
temp.append(res)
data_list.append(temp) # print('data_list', data_list) """
我们最终得到的数据类型是如下格式:列表套着列表
[
使用orm语句得到的每一个类对象,有几个表格就有几个对象
]
list_display=['id','title',]
[
[1,'python',<a>编辑</a>],
[2,'java',<a>编辑</a>],
]
"""
return data_list class ModelSubject(object):
"""
我们在这里模拟admin源码里面的ModelAdmin,
"""
list_display = ["__str__"] # 我们在这里给空列表里面加上"__str__",它就相当于是一个默认值,
model_form_class = None # 为下面我们判断用户是否有自定义ModelForm校验方式而做铺垫
search_fields = []
list_display_links = [] # 为我们后面用户是否有自定义可跳转字段做铺垫
# 就像我们的函数里面有默认值的参数一样,如果有传参就使用我们的自定义传参,如果没有传参就使用我们默认的参数也就是这个字符串,
# 这里是为了给我们后面的代码做铺垫,我们的目的是在我们的数据展示页面里面默认就会把复选框和编辑还有删除按钮都加上,
# 在这里把空列表里面添加上一个默认的字符串,是为了我们后面往该列表里面添加默认固定数据也就是复选框和编辑删除按钮做铺垫
actions = []
list_filter = [] # 多级过滤 # 静态内置方法
def __init__(self, model, site):
self.model = model # 当我们生成一个实例化对象的时候需要把model这个参数传进来,
# 必须要传,它是位置参数,然后我们所传入的那个model就是我们在models.py里面定义的每一个表名也就是类名
self.site = site
self.namespace = '{}_{}'.format(self.model._meta.app_label, self.model._meta.model_name)
# self.app_model_name = (self.model._meta.app_label, self.model._meta.model_name) # 这里写得跟上面一句是一样的效果,
# 这里调用的时候需要有两个%s,因为这里是一个元祖,而我们上面的namespace是一个字符串,不是一个元祖,所以只需要一个%s即可,调用的时候就这点区别
# 我们这里的namespace是因为会频繁使用到所以就把它作为一个内置静态属性写入到这里,其他地方如果要调用它就直接使用self.namespace即可
# .format的方法:'{}_{}'.format(a,b) # 默认actions批量删除
def patch_delete(self, queryset):
queryset.delete()
return None
patch_delete.desc = '批量删除' # 获取真正展示的actions
def get_actions(self):
temp = []
temp.extend(self.actions) # [patch_init,patch_delete]
temp.append(ModelSubject.patch_delete)
return temp # 获取展示页面的url
def get_stand_url(self):
stand_url = reverse('%s_standlist' % self.namespace)
return stand_url # 获取编辑页面的url
def get_edit_url(self, obj):
edit_url = reverse('%s_edit' % self.namespace, args=(obj.pk,))
return edit_url # 获取删除页面的url
def get_dele_url(self, obj):
dele_url = reverse('%s_dele' % self.namespace, args=(obj.pk,))
return dele_url # 获取增加页面的url
def get_add_url(self):
add_url = reverse('%s_add' % self.namespace)
return add_url # 展示页面默认附带的编辑按钮
def edit(self, obj=None, is_header=False):
if is_header:
return '操作'
return mark_safe('<a href="%s">编辑</a>' % reverse('%s_edit' % self.namespace, args=(obj.pk,))) # 展示页面默认附带的删除按钮
def dele(self, obj=None, is_header=False):
if is_header:
return '删除'
return mark_safe("<a href='%s'>删除</a>" % reverse('%s_dele' % self.namespace, args=(obj.pk,))) # 展示页面附带的默认复选框
def checkbox(self, obj=None, is_header=False):
if is_header:
return mark_safe("<input id='action-toggle' type='checkbox'>")
return mark_safe("<input type='checkbox' value='%s' name='_selected_action'>" % obj.pk) # 展示页面默认显示按钮被存放的列表
def get_list_display(self):
new_li = []
new_li.extend(self.list_display) # 我们这里的extend是把它后面的列表里面的数据都取出来放到我们自己的这个列表里面来
if not self.list_display_links:
new_li.append(ModelSubject.edit) # &&& 跟如下同样特征的代码块相呼应我们在这里使用类名去调用函数名,得到的是一个函数的方法,
# 函数如果有参数是需要我们传参数的;但是我们如果使用self去调用的话,self就是实例化出来的对象,
# 而我们的对象去调用函数方法的时候就不需要去传自己了也就是self,
new_li.append(ModelSubject.dele)
new_li.insert(0, ModelSubject.checkbox) # 把checkbox放到第一个位置,使用insert插入到索引为0
"""
# @@@ 跟下面特殊标识的代码块相呼应
new_li.append(self.edit)
new_li.append(self.dele)
new_li.insert(0,self.checkbox)
"""
return new_li # 模糊查询
def get_search_condition(self):
from django.db.models import Q
search_condition = Q()
search_condition.connector = 'or'
if self.search_fields: # 如果用户有自定义的查询字段,我们就走这个if下面的代码,
key_word = self.request.GET.get('q') # 取出用户输入的input里面的值,
if key_word:
for search_field in self.search_fields: # 遍历用户自定义的查询字段列表,
search_condition.children.append((search_field + "__contains", key_word))
return search_condition # 处理用户自定义的link超链接字段标签,然后让超链接携带url键值对参数方法,供get_body使用
def get_link_tag(self, obj, val):
params = self.request.GET
params = copy.deepcopy(params)
params._mutable = True
# from django.http import QueryDict
# qqx = QueryDict(mutable=True)
qqx = {} # 这里只写一行,等效于上面的两行,当然了前提是我们要引入urlencoded
qqx['list_filter'] = params.urlencode()
whh = mark_safe("<a href='{}?{}'>{}</a>".format(self.get_edit_url(obj), urlencode(qqx), val))
return whh # 获取多级过滤的数据
def get_filter_condition(self):
from django.db.models import Q
fiter_condition = Q()
for field, val in self.request.GET.items():
if field in self.list_filter:
fiter_condition.children.append((field, val))
return fiter_condition # 展示页面
def stand_li(self, request):
# print(self.model)
# 所以我们在这里可以获取到当前的url里面的表名,然后直接使用orm语句即可得到当前表格的所有信息
if request.method == 'POST':
# print('request.POST.get', request.POST.get('action'))
# print('request.POST.getlist', request.POST.getlist("_selected_action"))
# 打印出来的结果是request.POST.getlist ['on', 'on'] 这个on是从何而来的,我的input里面的所有value值都设定的是obj.pk,
# 这个obj.pk是有值的,
# 为什么这里打印出来的getlist是两个on?我们的input标签上面有name值,
# 然后我们使用request.POST.get后面的括号里面放的是name属性的值,然后它的返回值是我们的input标签里面的value值,
# 都说了是value值,怎么还能加上s呢?简直愚蠢, pk_list = request.POST.getlist("_selected_action")
queryset = self.model.objects.filter(pk__in=pk_list)
func_name = request.POST.get("action")
func = getattr(self, func_name)
ret = func(queryset) self.request = request # 关于search的模糊查询
search_condition = self.get_search_condition() # action
# a=self.model.objects.all().count() # 这样就是可以获取我们的queryset集合的总数据长度,
# 然后就可以用它去传给我们的分页组件,用它也可以,直接用count就能获取数据长度,或者是用len也行,我之前都是用len获取的 # filter多级过滤,
get_filter_condition = self.get_filter_condition()
queryset = self.model.objects.filter(search_condition).filter(get_filter_condition)
add_url = self.get_add_url()
sl = StandLi(self, request, queryset) # 这里是把我们的StandLi这个类所需要的参数都传给它,然后通过StandLi实例化出来的一个对象
# 然后在这里实例化出来一个对象我在这里调用那个对象就能够使用那个类里面的封装的方法了 return render(request, 'file/hello.html', locals()) # ModelForm校验添加和编辑页面
def get_modelform_class(self):
from django.forms import widgets class AllModelForm(ModelForm):
class Meta:
model = self.model
fields = '__all__'
if not self.model_form_class: # 这里的model_form_class在上面被定义了默认是None,
# 我们的分发下去的App里面自定义的file文件里面注册model类的时候实例化出来的对象,在注册的时候传过来的这个变量
return AllModelForm
else:
return self.model_form_class # ModelForm校验数据添加页面
def add_view(self, request):
FormClass = self.get_modelform_class()
if request.method == 'GET':
form = FormClass()
return render(request, 'file/add.html', {'form': form})
else:
data_list = FormClass(data=request.POST)
if data_list.is_valid():
data_list.save()
return redirect(self.get_stand_url())
else:
return render(request, 'file/add.html', {'form': data_list}) # ModelForm校验数据编辑页面
def edit_view(self, request, id):
edit_list = self.model.objects.filter(pk=id).first()
FormClass = self.get_modelform_class()
if request.method == 'GET':
data_list = FormClass(instance=edit_list)
return render(request, 'file/edit.html', {'form': data_list})
else:
data_list = FormClass(data=request.POST, instance=edit_list)
if data_list.is_valid():
data_list.save()
return redirect(self.get_stand_url())
else:
return render(request, 'file/edit.html', {'form': data_list}) # 数据删除页面
def dele_view(self, request, id):
del_obj = self.model.objects.filter(pk=id).first()
if request.method == 'GET':
stand_url = self.get_stand_url()
return render(request, 'file/dele.html', {'del_obj': del_obj, 'list_url': stand_url})
else:
del_obj.delete() return redirect(self.get_stand_url()) # 获取url,此为第二次分发
def get_urls(self):
temp = []
temp.append(url(r'^$', self.stand_li, name='%s_standlist' % self.namespace))
temp.append(url(r'^(\d+)/dele/', self.dele_view, name='%s_dele' % self.namespace))
temp.append(url(r'^(\d+)/edit/', self.edit_view, name='%s_edit' % self.namespace))
temp.append(url(r'^add/', self.add_view, name='%s_add' % self.namespace))
return temp @property
def urls(self):
return self.get_urls() class Stark(object):
"""
我们这里面的功能是可以跟上面的类写到一起去的,但是我们为了功能解耦,所以就分开写了,这里的主要功能就是
生成registry的字典,把键值对生成,然后我们最终的结果是要得到一个实例化对象,供我们后面的程序调用,这里的类才是主要的,核心的代码块
而我们上面的那个ModelSubject是辅助我们这里的功能,它之所以分发出去是为了便于扩展其他的功能,我们的自定义样式,
还有很多的方法和参数,就像我们的admin里面的ModelAdmin一样,长达1400多行代码,单独把它分离出去便于功能的扩展
""" def __init__(self):
self._registry = {} # 这里是定义一个私有属性,就是为了避免被子类修改 # 注册model表
def register(self, model, model_config=None): # 我们是仿照着admin的源码写的组件,这里的model_config默认值是None,
# 我们在传参的时候,如果给它传值,那么就使用我们传入的值替换掉这个None
# 它的源码里面有这几个参数,我们也要按照顺序把这几个参数加进来
if not model_config:
model_config = ModelSubject # 我们这里的model_config我们上面的类ModelSubject实例化出来的对象,
# 它是上面的类所实例化出来的对象,这一句写得明明白白的,这大白话再看不懂就真是白学了,
self._registry[model] = model_config(model, self) # 获取url,第一次分发
def get_urls(self):
li = []
for model, model_config in self._registry.items(): # 我们在这里所循环的model_config就是
# 我们往上数第四行所实例化出来的那个model_config,它是上面的ModelSubject这个类所实例化出来对象,
model_name = model._meta.model_name # 这里的._meta.model_name是获取字符串格式的类名,
app_label = model._meta.app_label # 这里的._meta.app_labe获取的是字符串格式的App名,都是为了跟url做匹配,
sli = url(r'%s%s/' % (app_label, model_name), (model_config.urls, None, None)) # 我们这里的model_config,
# 它后面的.urls是在调用一个私有方法,我们的私有方法就是使用.urls来调用,不用加上括号,
# 因为有@property这个装饰器在里面起到的作用,然后我们需要找到model_config这个实例对象是哪个类生成的,
# 然后找到该类所拥有的方法,从里面找到urls,届时,那个urls就是我们在这里调用的那个urls了,
# 所以关键的点就是我们的model_config,老师讲课的时候一再地强调过这个model_config从何而来,这个是关键,
li.append(sli)
return li # 我们最终的数据结构就是这样的,嵌套多层
# [
# url(
# r'',(
# [
# (url(r'',views.add)),
# (url(r'',views.edit)),
# ],
# none,none)
# )
# ]
@property
def urls(self):
return self.get_urls(), None, None site = Stark()

tag.py

HTML模板渲染:

 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/static/plugins/bs/css/bootstrap.css">
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<title>Title</title> <style>
.filter a {
padding: 3px 6px;
border: 1px solid #336699;
color: white;
background-color: pink;
} .active {
background-color: #336699 !important;
color: white !important;
}
</style> </head>
<body>
<div class="jumbotron">
<div class="container">
<h1>数据展示</h1>
<h2>code change the world</h2>
</div>
</div> <div class="container">
<div class="row">
<div class="col-md-8"> <a href="{{ add_url }}">
<button class="btn btn-primary" value="添加">添加数据</button>
</a>
{% if sl.config.search_fields %}
<div class="pull-right form-group"> <form action="" method="get" class="form-inline">
<input type="text" class="form-control" name="q" value="">
<input type="submit" class="btn btn-info" value="search">
</form> </div>
{% endif %} <form action="" method="post">
{% csrf_token %}
<div>
<select class="form-control" name="action" id="" style="width:200px;margin:8px 2px;
display:inline-block;vertical-align:-1px">
<option value="">---------</option>
{% for item in sl.handle_actions %}
<option value="{{ item.name }}">{{ item.desc }}</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-primary">Go</button>
</div> <table class="table table-stripped table-hover"> <thead>
<tr>
{% for foo in sl.get_header %}
<td>{{ foo }}</td>
{% endfor %}
</tr>
</thead>
<tbody>
{% for data in sl.get_body %}
<tr>
{% for item in data %}
<td>{{ item }}</td>
{% endfor %}
</tr>
{% endfor %} </tbody> </table>
</form> <div class="text-center">
<nav aria-label="Page navigation">
<ul class="pagination">
{{ sl.pagination.page_html|safe }}
</ul>
</nav>
</div> </div> <div class="col-md-3">
<div class="filter">
{% for filter_link_tag in sl.get_filter_link_tags %} <div class="well">{% for data in filter_link_tag %}
<div>{{ data }}</div>
{% endfor %}
</div> {% endfor %}
</div>
</div>
</div>
</div>
<script src="/static/js/hello.js">
{# 我们的js代码里面需要注意单引号和双引号的区别,不能轻易改写,还有选择器,组合选择器之间需要空格#}
</script>
</body>
</html>

前端HTML模板

静态文件:

 $("#action-toggle").click(function(){
if($(this).prop("checked")){
$("tbody :checkbox").prop("checked",true);
}else{
$("tbody :checkbox").prop("checked",false);
}
});

js代码