Django 复习
一、如何建立配置Django
1.创建django项目
djangoadmin startproject mysite
mysite/
├── manage.py # 管理文件
└── mysite # 项目目录
├── init.py
├── settings.py # 配置
├── urls.py # 路由 --> URL和函数的对应关系
└── wsgi.py # runserver命令就使用wsgiref模块做简单的web server
2.运行Django项目
python3 manage.py runserver
3.模板文件配置
TEMPLATES = [
{
‘BACKEND‘: ‘django.template.backends.django.DjangoTemplates‘,
‘DIRS‘: [os.path.join(BASE_DIR, "template")], # template文件夹位置
‘APP_DIRS‘: True,
‘OPTIONS‘: {
‘context_processors‘: [
‘django.template.context_processors.debug‘,
‘django.template.context_processors.request‘,
‘django.contrib.auth.context_processors.auth‘,
‘django.contrib.messages.context_processors.messages‘,
],
},
},]
4.静态文件配置
STATIC_URL = ‘/static/‘ # HTML中使用的静态文件夹前缀
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"), # 静态文件存放位置]
二、Django基础必备的三板斧
from django.shortcuts import HttpResponse, render, redirect
2.三板斧
2.1 HttpResponse : 内部传入字符串参数,返回给浏览器
def index(request):
# 业务逻辑代码
return HttpResponse("OK")
2.2 render : 除request参数外还接受一个待渲染的模板文件和一个保存具体数据的字典参数。将数据填充进模板文件,最后把结果返回给浏览器。
def index(request):
# 业务逻辑代码
return render(request, "index.html", {"name": "alex", "hobby": ["烫头", "泡吧"]})
2.3 redirect : 接受一个URL参数,表示跳转到指定的URL。
def index(request):
# 业务逻辑代码
return redirect("/home/")
三、路由层
from django.conf.urls import url
?
urlpatterns = [
url(正则表达式, views视图函数,参数,别名),]
?
from django.urls import path
?
urlpatterns = [
path(‘articles/2003/‘, views.special_case_2003),
path(‘articles/
path(‘articles/
path(‘articles/
1.1 参数说明
正则表达式:一个正则表达式字符串
views视图函数:一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
参数:可选的要传递给视图函数的默认参数(字典形式)
别名:一个可选的name参数
from django.conf.urls import include, url
?
urlpatterns = [
url(r‘^admin/‘, admin.site.urls),
url(r‘^blog/‘, include(‘blog.urls‘)), # 可以包含其他的URLconfs文件]
3.0 反向解析
例子:
路由配置
url(r‘^home‘, views.home, name=‘home‘), # 给我的url匹配模式起名为 home
url(r‘^index/(\d*)‘, views.index, name=‘index‘), # 给我的url匹配模式起名为index
模板里面引用
{% url ‘home‘ %}
views 函数中引用
from django.urls import reverse
?
reverse("index", args=("2018", ))
例子:
project中的urls.py
from django.conf.urls import url, include
urlpatterns = [
url(r‘^app01/‘, include(‘app01.urls‘, namespace=‘app01‘)),
url(r‘^app02/‘, include(‘app02.urls‘, namespace=‘app02‘)),]
app01中的urls.py
from django.conf.urls import url
from app01 import views
app_name = ‘app01‘
urlpatterns = [
url(r‘^(?P
app02中的urls.py
from django.conf.urls import url
from app02 import views
app_name = ‘app02‘
urlpatterns = [
url(r‘^(?P
现在,我的两个app中 url名称重复了,我反转URL的时候就可以通过命名空间的名称得到我当前的URL。
模板中使用
{% url ‘app01:detail‘ pk=12 pp=99 %}
views中的函数中使用
v = reverse(‘app01:detail‘, kwargs={‘pk‘:11})
这样即使app中URL的命名相同,我也可以反转得到正确的URL了。
四. 视图层
一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应。
响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片。
无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你当前项目目录下面。除此之外没有更多的要求了——可以说“没有什么神奇的地方”。为了将代码放在某处,大家约定成俗将视图放置在项目(project)或应用程序(app)目录中的名为views.py的文件中。
参考地址:https://www.cnblogs.com/Dominic-Ji/articles/10982298.html
五、模板层
常用语法:
只需要记两种特殊符号:
{{ }}和 {% %}
变量相关的用{{}},逻辑相关的用{%%}。
变量
在Django的模板语言中按此语法使用:{{ 变量名 }}。
当模版引擎遇到一个变量,它将计算这个变量,然后用结果替换掉它本身。 变量的命名包括任何字母数字以及下划线 ("_")的组合。 变量名称中不能有空格或标点符号。
例子
def template_test(request):
l = [11, 22, 33]
d = {"name": "alex"}
class Person(object):
def init(self, name, age):
self.name = name
self.age = age
?
def dream(self):
return "{} is dream...".format(self.name)
?
Alex = Person(name="Alex", age=34)
Egon = Person(name="Egon", age=9000)
Eva_J = Person(name="Eva_J", age=18)
?
person_list = [Alex, Egon, Eva_J]
return render(request, "template_test.html", {"l": l, "d": d, "person_list": person_list})
模板中支持的写法
{# 取l中的第一个参数 #}
{{ l.0 }}
{# 取字典中key的值 #}
{{ d.name }}
{# 取对象的name属性 #}
{{ person_list.0.name }}
{# .操作只能调用不带参数的方法 #}
{{ person_list.0.dream }}
{{ value|default:"nothing"}}
{{ value|length }}
?
返回value的长度,如 value=[‘a‘, ‘b‘, ‘c‘, ‘d‘]的话,就显示4.
{{ value|filesizeformat }}
如果 value 是 123456789,输出将会是 117.7 MB。
{{value|slice:"2:-1"}}
{{ value|date:"Y-m-d H:i:s"}}
value = "点我"
?
{{ value|safe}}
参数:截断的字符数
?
{{ value|truncatechars:9}}
{{ value|truncatewords:9}}
{{ value|cut:‘ ‘ }}
filter
forloop.counter 当前循环的索引值(从1开始)
forloop.counter0 当前循环的索引值(从0开始)
forloop.revcounter 当前循环的倒序索引值(从1开始)
forloop.revcounter0 当前循环的倒序索引值(从0开始)
forloop.first 当前循环是不是第一次循环(布尔值)
forloop.last 当前循环是不是最后一次循环(布尔值)
forloop.parentloop 本层循环的外层循环
{% if user_list %}
用户人数:{{ user_list|length }}
{% elif black_list %}
黑名单数:{{ black_list|length }}
{% else %}
没有用户
{% endif %}
{% with total=business.employees.count %}
{{ total }} employee{{ total|pluralize }}
{% endwith %}
{% with business.employees.count as total %}
{{ total }} employee{{ total|pluralize }}
{% endwith %}
<!DOCTYPE html>
{% endblock %}
{% extends ‘layouts.html‘ %}
块:通过在母板中使用{% block xxx %}来定义"块"。
在子页面中通过定义母板中的block名来对应替换母板中相应的内容。
{% block page-main %}
世情薄
人情恶
雨送黄昏花易落
{% include ‘navbar.html‘ %}
静态文件
{% load static %}
<img src="{% static "images/hi.jpg" %}" alt="Hi!" />
六、Django ORM
常用字段
int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。
一个整数类型,范围在 -2147483648 to 2147483647。(一般不用它来存手机号(位数也不够),直接用字符串存,)
字符类型,必须提供max_length参数, max_length表示字符长度。
from django.db import models
?
class FixCharField(models.Field):
‘‘‘
自定义的char类型的字段类
‘‘‘
def init(self,max_length,*args,**kwargs):
self.max_length=max_length
super().__init__(max_length=max_length,*args,**kwargs)
?
def db_type(self, connection):
‘‘‘
限定生成的数据库表字段类型char,长度为max_length指定的值
:param connection:
:return:
‘‘‘
return ‘char(%s)‘%self.max_length
class Class(models.Model):
id=models.AutoField(primary_key=True)
title=models.CharField(max_length=32)
class_name=FixCharField(max_length=16)
gender_choice=((1,‘男‘),(2,‘女‘),(3,‘保密‘))
gender=models.SmallIntegerField(choices=gender_choice,default=3)
?
自定义及使用
2.字段合集
o
AutoField(Field)
- int自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
?
注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models
?
class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)
?
class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
?
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
?
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647
?
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
?
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
?
BooleanField(Field)
- 布尔值类型
?
NullBooleanField(Field):
- 可以为空的布尔值
?
CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度
?
TextField(Field)
- 文本类型
?
EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制
?
IPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
?
GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
- 参数:
protocol,用于指定Ipv4或Ipv6, ‘both‘,"ipv4","ipv6"
unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"
?
URLField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证 URL
?
SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
?
CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字
?
UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
?
FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
?
FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
?
ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
?
DateTimeField(DateField)
- 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
?
DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD
?
TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
?
DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
?
FloatField(Field)
- 浮点型
?
DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
?
BinaryField(Field)
- 二进制类型
对应关系:
‘AutoField‘: ‘integer AUTO_INCREMENT‘,
‘BigAutoField‘: ‘bigint AUTO_INCREMENT‘,
‘BinaryField‘: ‘longblob‘,
‘BooleanField‘: ‘bool‘,
‘CharField‘: ‘varchar(%(max_length)s)‘,
‘CommaSeparatedIntegerField‘: ‘varchar(%(max_length)s)‘,
‘DateField‘: ‘date‘,
‘DateTimeField‘: ‘datetime‘,
‘DecimalField‘: ‘numeric(%(max_digits)s, %(decimal_places)s)‘,
‘DurationField‘: ‘bigint‘,
‘FileField‘: ‘varchar(%(max_length)s)‘,
‘FilePathField‘: ‘varchar(%(max_length)s)‘,
‘FloatField‘: ‘double precision‘,
‘IntegerField‘: ‘integer‘,
‘BigIntegerField‘: ‘bigint‘,
‘IPAddressField‘: ‘char(15)‘,
‘GenericIPAddressField‘: ‘char(39)‘,
‘NullBooleanField‘: ‘bool‘,
‘OneToOneField‘: ‘integer‘,
‘PositiveIntegerField‘: ‘integer UNSIGNED‘,
‘PositiveSmallIntegerField‘: ‘smallint UNSIGNED‘,
‘SlugField‘: ‘varchar(%(max_length)s)‘,
‘SmallIntegerField‘: ‘smallint‘,
‘TextField‘: ‘longtext‘,
‘TimeField‘: ‘time‘,
‘UUIDField‘: ‘char(32)‘,
?
ORM字段与MySQL字段对应关系
unique
如果设置为unique=True 则该字段在此表中必须是唯一的 。
db_index
如果db_index=True 则代表着为此字段设置索引。
default
为该字段设置默认值。
auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
?
auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段。
ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系
5.1 字段参数
设置:models.SET(可执行对象)
models.DO_NOTHING
删除关联数据,引发错误IntegrityError
?
?
models.PROTECT
删除关联数据,引发错误ProtectedError
?
?
models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
?
?
models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
?
?
models.SET
?
删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
?
其余字段参数
例子
def func():
return 10
?
class MyModel(models.Model):
user = models.ForeignKey(
to="User",
to_field="id",
on_delete=models.SET(func)
)
通常一对一字段用来扩展已有字段。(通俗的说就是一个人的所有信息不是放在一张表里面的,简单的信息一张表,隐私的信息另一张表,之间通过一对一外键关联
6.1 字段参数
to
设置要关联的表。
to_field
设置要关联的字段。
on_delete
当删除关联表中的数据时,当前表与其关联的行的行为。(参考上面的例子)
6.2 必会13条
返回QuerySet对象的方法有
all()
?
filter()
?
exclude()
?
order_by()
?
reverse()
?
distinct()
?
特殊的QuerySet
values() 返回一个可迭代的字典序列
?
values_list() 返回一个可迭代的元祖序列
?
返回具体对象的
get()
?
first()
?
last()
?
返回布尔值的方法有:
exists()
?
返回数字的方法有
count()
6.3神奇的下划线操作
models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值
models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in
models.Tb1.objects.filter(name__contains="ven") # 获取name字段包含"ven"的
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
models.Tb1.objects.filter(id__range=[1, 3]) # id范围是1到3的,等价于SQL的bettwen and
类似的还有:startswith,istartswith, endswith, iendswith
?
date字段还可以:
models.Class.objects.filter(first_day__year=2017)
date字段可以通过在其后加__year,__month,__day等来获取date的特点部分数据
#
# Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
# Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
?
# year
#
# Entry.objects.filter(pub_date__year=2005)
# Entry.objects.filter(pub_date__year__gte=2005)
?
# month
#
# Entry.objects.filter(pub_date__month=12)
# Entry.objects.filter(pub_date__month__gte=6)
?
# day
#
# Entry.objects.filter(pub_date__day=3)
# Entry.objects.filter(pub_date__day__gte=3)
?
# week_day
#
# Entry.objects.filter(pub_date__week_day=2)
# Entry.objects.filter(pub_date__week_day__gte=2)
需要注意的是在表示一年的时间的时候,我们通常用52周来表示,因为天数是不确定的,老外就是按周来计算薪资的哦~
?
集锦
book_obj = models.Book.objects.first() # 第一本书对象(第一步)
print(book_obj.publisher) # 得到这本书关联的出版社对象
print(book_obj.publisher.name) # 得到出版社对象的名称
?
models.Book.objects.all().values("publisher__name")
?
7.2 反向查找(跨表)
publisher_obj = models.Publisher.objects.first() # 找到第一个出版社对象
books = publisher_obj.book_set.all() # 找到第一个出版社出版的所有书
titles = books.values_list("title") # 找到第一个出版社出版的所有书的书名
结论:如果想通过一的那一方去查找多的一方,由于外键字段不在一这一方,所以用__set来查找即可
titles = models.Publisher.objects.values("book__title")
① 外键关系反向查询
② 多对多关联关系
简单来说就是在多对多表关系并且这一张多对多的关系表是有Django自动帮你建的情况下,下面的方法才可使用。
create():
创建一个关联对象,并自动写入数据库,且在第三张双方的关联表中自动新建上双方对应关系。
models.Author.objects.first().book_set.create(title="偷塔秘籍")
上面这一句干了哪些事儿:
1.由作者表中的一个对象跨到书籍比表
2.新增名为偷塔秘籍的书籍并保存
3.到作者与书籍的第三张表中新增两者之间的多对多关系并保存
add():
把指定的model对象添加到第三张关联表中。
添加对象:
author_objs = models.Author.objects.filter(id__lt=3)
models.Book.objects.first().authors.add(*author_objs)
添加id:
models.Book.objects.first().authors.add(*[1, 2])
set()
更新某个对象在第三张表中的关联对象。不同于上面的add是添加,set相当于重置
book_obj = models.Book.objects.first()
book_obj.authors.set([2, 3])
remove()
从关联对象集中移除执行的model对象(移除对象在第三张表中与某个关联对象的关系)
book_obj = models.Book.objects.first()
book_obj.authors.remove(3)
clear()
从关联对象集中移除一切对象。(移除所有与对象相关的关系信息)
book_obj = models.Book.objects.first()
book_obj.authors.clear()
注意:
2.对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。
9.聚合查询和分组查询
9.1 聚合查询(利用聚合函数)
aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。
键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。
用到的函数
from django.db.models import Avg, Sum, Max, Min, Count
示例
from django.db.models import Avg, Sum, Max, Min, Count
models.Book.objects.all().aggregate(Avg("price"))
{‘price__avg‘: 13.233333}
重命名
models.Book.objects.aggregate(average_price=Avg(‘price‘))
{‘average_price‘: 13.233333}
多个函数:
models.Book.objects.all().aggregate(Avg("price"), Max("price"), Min("price"))
{‘price__avg‘: 13.233333, ‘price__max‘: Decimal(‘19.90‘), ‘price__min‘: Decimal(‘9.90‘)}
9.2 分组查询
from django.db.models import Avg
models.Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")
总结
value里面的参数对应的是sql语句中的select要查找显示的字段,
filter里面的参数相当于where或者having里面的筛选条件
annotate本身表示group by的作用,前面找寻分组依据,内部放置显示可能用到的聚合运算式,后面跟filter来增加限制条件,最后的value来表示分组后想要查找的字段值
使用方法
from django.db.modles import F
res = models.Product.objects.filter(maichu__gt = F(‘kucun‘))
models.Product.objects.update(price=F(‘price‘)+100)
from django.db.models.functions import Concat
from django.db.models import Value
?
modles.Product.objects.update(Concat(F(‘name‘),Value(‘爆款‘)))
Q 查询
filter()等方法中逗号隔开的条件是与的关系。 如果你需要执行更复杂的查询(例如OR语句),你可以使用Q对象`。
当查询条件想以或的关系查询时候
models.Product.objects.filter(Q(name=‘火影忍者‘)|Q(price=89.99))
models.product.objects.filter(Q(name=‘火影忍者‘),price=89.99)
我们可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。
同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。
完整性
一致性
from django.db import transaction
with transaction.atomic():
# 内部写多个数据库操作
print(‘其他逻辑‘)
建立表的三种方式
class Book(models.Model):
name = models.Charfied(max_length=32)
authors = models.ManyToManyField(to=‘Author‘)
?
class Author(models.Model):
name = models.CharField(max_length=32)
?
class Book(models.Model):
name= models.CharField(max_length=32)
?
class Author(models.Model):
name = models.CharField(max_length=32)
?
class Book2Author(models.Model)
book = models.ForeignKey(to=‘Book‘)
author = models.ForeignKey(to=‘Author‘)
info = models.CharField(max_length=32)
?
class Book(models.Model):
name = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
?
class Book2Author(models.Model):
book = models.ForeignKey(to=‘Book‘)
author = models.Foreignkey(to=‘author‘)
info = models.CharField(max_length=32)
七、Django 与Ajax
formdata:
form表单传输文件的编码格式
? 后端获取文件的格式数据:request.FILES
? 后端获取普通键值对数据:request.POST
application/json
ajax 发送json格式数据
? 需要注意的是:编码与数据格式要一致
浏览器窗口手动输入网址 get请求
a标签的href属性 get请求
form表单 get/post请求(默认get请求)
ajax get/post请求
ajax特点
异步提交
局部刷新
$(‘#d1‘).click(function)(){
$.ajax({
url:‘/index/‘ //提交地址
type:‘post‘, //提交方式
data:{‘name‘:‘jason‘,‘password‘:‘123‘}, //提交的数据
success:function(data){
alert(data) //data接收的就是异步提交返回结果
}
})
}
ipt>
$(‘#d1‘).click(function () {
let formdata = new FormData();
// Formdata 对象不仅仅可以传文件还可以传普通键值对
formdata.append(‘name‘,‘jason‘);
// 获取input框存放的文件 $(‘#i1).files[0]
formdata.append(‘myfile‘,$(‘#i1‘)[0].files[0]);
$.ajax({
url:‘‘,
tyoe:‘post‘,
data:formdata,
//发送文件需要修改两个固定参数
processData:false,
contentType:false,
//回调函数
success:function (data) {
alert(data)
}
})
?
})
6 form表单与ajax的异同点
form表单不支持异步提交局部刷新
form表单不支持传输json格式
form表单与ajax默认传输数据的编码格式都是urlencoded
from app01.utils import my_page
自定义分页器的使用
book_list = models.Book.objects.all()
all_count = book_list.count()
current_page = request.GET.get(‘page‘,1)
page_obj = mypage.Pagination(current_page=current_page, all_count= all_count )
page_queryset = bool_list[page_obj.start:page_obj.end]
前端
{{ page_obj.page_html|safe }} # 帮你渲染的是带有bootstrap样式的分页器
原文:https://www.cnblogs.com/king-home/p/11031788.html