一、Django的分页:
分页是为了减少一次性从数据库读过多的数据。
先说一下offset和limit。
语句1:select * from student limit 9,4
语句2:slect * from student limit 4 offset 9
这两条语句的查询结果是一样的。同样可以完成分页的功能。
1、Django内置分页:
from django.core.paginator import EmptyPage,PageNotAnInteger,Paginator
def index(request):
current_page = request.GET.get("current_page")
if current_page == None:
current_page = 1
paginator = Paginator(slist, 10) # slist是数据,10代表每一页的显示多少条
# 这个page对象包含了,是否具有上一页,是否有下一页
page = paginator.page(current_page) # 在这里做异常判断,如果用户输入不合法就报异常
return render(request, "index.html", {"slist": page, "current_page": current_page})
前端页面:
# 循环打印数据
<ul>
{% for row in slist.object_list %} # 已经分好页的数据
<li>{{ row.name }}</li>
{% endfor %}
</ul>
# 判断是否有上一页
{% if slist.has_previous %}
# 上一页的页码
<a href="/index?current_page={{ slist.previous_page_number }}">上一页</a>
{% endif %}
总页数{{ slist.paginator.num_pages }}, 当前页:{{ slist.number }}
# 判断是否有下一页
{% if slist.has_next %}
# 下一页的页码
<a href="/index?current_page={{ slist.next_page_number }}">下一页</a>
{% endif %}
两个重要的对象: Paginator 和 page对象。
Paginator:
per_page: 每页显示记录数量
count: 数据总个数
num_pages: 总页数
page_range: 总页数的索引范围,如: (1,10),(1,200)
page: page对象 page(2) 代表第二页数据的对象
page:
has_next 是否有下一页
next_page_number 下一页页码
has_previous 是否有上一页
previous_page_number 上一页页码
object_list 分页之后的当前页数据列表
number 当前页
paginator paginator对象,父类对象
※:可以建一个模板,每次分页都include,提高代码的可重用性。至此Django的内置分页已做好,并没有什么样式,下面开始自定制。
2、扩展Dajngo的内置分页:
通过继承Paginator的方式进行自定义类的方式进行分页。
def index(request):
current_page = request.GET.get("current_page")
if current_page == None:
current_page = 1
# 参数:当前页、页码最大显示、数据列表、每页多少数据
paginator = CustomPaginator(current_page, 5, slist, 10)
page = paginator.page(current_page) # 在这里应该做异常判断,如果用户输入不合法就报异常
return render(request, "index.html", {"slist": page, "current_page": current_page})
class CustomPaginator(Paginator):
# 初始化调用父类的方法
def __init__(self, current_page, per_num, *args, **kwargs):
self.current_page = int(current_page)
self.per_page = per_num
super().__init__(*args, **kwargs)
# 自己定义一个方法进行页码的显示
def getNum(self):
self.part = self.per_page//2
# 当前页码数小于总页数
if self.num_pages < self.per_page:
return range(1,self.num_pages+1)
# 当前页小于显示页码的一半时
if self.current_page <= self.part:
return range(1, self.per_page+1)
# 当当前页加上显示的页码一半大于总页数时
if (self.current_page+self.part) > self.num_pages:
return range(self.num_pages-self.per_page+1,self.num_pages+1)
# 当前页大于显示页码的一半时
if self.current_page > self.part:
return range(self.current_page-self.part, self.current_page+self.part+1)
前端页面:
# 进行数据的遍历
<ul>
{% for row in slist.object_list %}
<li>{{ row.name }}</li>
{% endfor %}
</ul>
# 进行页码遍历
{% if slist.has_previous %}
<a href="/index?current_page={{ slist.previous_page_number }}">上一页</a>
{% endif %}
{% for item in slist.paginator.getNum %}
{% if item == slist.number %}
<a href="/index?current_page={{ item }}" style="font-size: 40px">{{ item }}</a>
{% else %}
<a href="/index?current_page={{ item }}">{{ item}}</a>
{% endif %}
{% endfor %}
{% if slist.has_next %}
<a href="/index?current_page={{ slist.next_page_number }}">下一页</a>
{% endif %}
</body>
3、自定义分页:
通过自己写分页的类。
class Paginator(object):
# 参数: 每页的记录数、当前页码、显示最大页码数、总数据条数
def __init__(self, per_count_page, current_page, per_index_page, total_count):
self.per_count_page = per_count_page
self.current_page = int(current_page)
self.per_index_page = per_index_page
self.total_count = total_count
self.mid = self.per_index_page//2
# 开始页数
def start(self):
return (self.current_page - 1) * self.per_count_page
# 结束页数
def end(self):
return self.current_page * self.per_count_page
# 计算总页数
@property
def page_num(self):
self.a, b = divmod(self.total_count, self.per_count_page)
if b != 0:
return self.a+1 # 在这里最好直接返回一个值,而不是当前的形式
else:
return self.a
# 计算页码显示样式
def getNum(self): # self.part = self.per_page//2
# 当前页码数小于总页数
if self.page_num < self.per_index_page:
return range(1, self.page_num+1)
if self.current_page <= self.mid:
return range(1, self.per_index_page+1)
if (self.current_page+self.mid) > self.page_num:
return range(self.page_num-self.per_index_page+1,self.page_num+1)
if self.current_page > self.mid:
return range(self.current_page-self.mid, self.current_page+self.mid+1)
# 将每个a标签放入,列表中,前端页面不用再循环显示
def page_indexs(self):
index_list = []
first = '<a href="/index1?current_page=1">首页</a>'
index_list.append(first)
if self.current_page-1:
top = '<a href="/index1?current_page=' + str(self.current_page - 1) + '">上一页</a>'
index_list.append(top)
for i in self.getNum():
# 寻找当前页
if self.current_page == i:
temp = '<a style="font-size: 30px" href="/index1?current_page='+str(i)+'">'+str(i)+'</a>'
else:
temp = '<a href="/index1?current_page='+str(i)+'">'+str(i)+'</a>'
index_list.append(temp)
if self.current_page != (self.a+1):
bottom = '<a href="/index1?current_page=' + str(self.current_page + 1) + '">下一页</a>'
index_list.append(bottom)
last = '<a href="/index1?current_page=' + str(self.a+1) + '">尾页</a>'
index_list.append(last)
return "".join(index_list) # 转换成字符串
前端页面:
<body>
# 进行数据遍历
<ul>
{% for row in data %}
<li>{{ row.name}}</li>
{% endfor %}
</ul>
# 后台遍历好了,直接显示
{{ pages|safe }}
</body>
更多内容详见:https://docs.djangoproject.com/en/2.2/topics/pagination/
二、Ajax部分
1、概述:Ajax 的全称是Asynchronous JavaScript and XML(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术。是一种用于创建快速动态网页的技术,是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
2、基本原理
3、$.ajax的一般格式
$.ajax({
url:“路径”,
data:{key1:value1}, // value1可以是数组类型,但不能是字典类型,如果非要传字典类型就把字典传成字符串
type:'POST/GET', //数据的提交方式:get和post
dataType:’json’, // 数据的返回值类型,当返回的数据类型和dataType定义不一样时,不会触发success函数。
traditional:True,
success:function(str){ //成功回调函数,str就是后台传送回来的信息。在回调函数中无法用$(this) 会无法定位到准确位置
},
error:function (err){ //失败回调函数
}
});
dataType 的一些参数值:
当dataType:"json"时,会自动解析json字符串。
当dataType:"text"时,不会进行任何处理。默认就是text。
当dataType:"xml"时,会自动转化为xml。
dataType也可以是html
一些其他的属性:
var data = $("表单的id").serialize() // serialize()方法会自动提交表单的属性到data下。
后台接收到的数据:
<QueryDict: {'slist': ['name=%E6%94%BE%E5%88%B0&pass=123&chose=third']}>
Ajax传数组数据时:data:{slist:[1,2,3,4,5]} 后台收到的是 <QueryDict: {'slist[]':['first', 'second']}> ,这样是不符合常理的。
当设置traditional:true。后台接收到的数据<QueryDict: {'slist': ['first', 'second']}>
比较Ajax和跳转新页面:
新url的方式进行修改: 好处是 独立的页面,数据量大、或者条目多的时候适合用。当用户输入错误时应该保留用户输入的正确字段。
Ajax方式进行修改:数据量小、或者条目少的时候适合用。是否刷新?一般刷新,除非性能要求。要考虑当前页码和自定义属性。
原生的Ajax:
为了解决手机app调用Ajax的问题,所以用原生的Ajax。
GET请求:
var xhr = new XMLhttprequest() // 创建一个对象
xhr.open("GET","") // open方法的两个参数 请求的方式,路径
xhr.onreadystatechange = function(){ // 回调函数
if(xhr.readyState==4){ // 当readyState返回值为4时表示成功
console.log("接收成功")
}
}
xhr.send(null) // 发送数据
POST请求:
var xhr = new XMLhttprequest() // 创建一个对象
xhr.open("POST","") // open方法的两个参数 请求的方式,路径
xhr.onreadystatechange = function(){ // 回调函数
if(xhr.readyState==4){ // 当readyState返回值为4时表示成功
console.log("接收成功")
}
}
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset-UTF-8") // 需要设置请求头否则django不认识
xhr.send("name=default") // 发送数据
伪Ajax请求:
通过iframe的无刷新返回数据的特性,并且能很好的解决兼容性的问题,所以有时候通过伪Ajax请求的方式。
首先在form标签加上target属性,target的值是iframe的name属性的值。
<form method="post" action="" enctype="multipart/form-data" target="iframe">
还需要为iframe标签绑定onload属性,不过不直接写在标签上,而是当点击提交以后通过 js 进行绑定。onload属性相当于回调函数。
document.getElementById("iframe").onload = reloading;
处理iframe的返回值:拿到后台返回的值
原生的js方式:
document.getElementById("iframe").contentWindow.document.body.innerHTML
jQuery的方式:
$("#iframe").contents().find("body").html()
Ajax文件上传:
我们可以为上传按钮进行onchange函数的定义;
<input type="file" onchange="change()"> // 当选中文件以后自动上传
原生Ajax:
var data = new Formdata(); // 通过Formdata的方式进行上传
data.append("img", document.getElementById("load").files[0]) // 获取文件标签的内容
var xhr = new XMLHttpRequest();
xhr.open("post","/upload");
xhr.onreadystatechange() = function(){
if(xhr.readystate == 4){
console.log(xhr.responseText)
}
}
xhr.send(data); // 在这里直接进行发送
jQuery:
var data = new Formdata();
data.append("img", $("#load")[0].files[0])
$.ajax({
url:"/upload",
type:"POST",
data:data,
success:function(arg){
console.log(arg)
}
processData: false, // 告诉jQuery不要去处理发送的数据, 发送对象。
contentType: false, // 告诉jQuery不要去设置Content-Type请求头
})
iframe的形式上传文件:
HTML:
<iframe style="display: none" id="if" name="frame"></iframe>
<form id="form_data" method="post" action="/files/" enctype="multipart/form-data" target="frame">
<div id="up"><span>预览</span></div>
<input type="file" name="img" onchange="upload()">
<input type="text">
</form>
-------------------------------------------------------------------------
js:
function upload() {
document.getElementById("if").onload = show;
document.getElementById("form_data").submit()
}
function show() {
var x=this.contentWindow.document.body.innerHTM;
document.getElementById("up").innerHTML = "<img src="+new_x.path+">"
}
跨域Ajax请求:
由于浏览器有同源策略的限制,所以无法正常接收其他服务器的响应信息。所以通过src属性进行跨域访问。我们通过jsonp的方式进行苦于的访问。以jsonp的形式进行跨域访问,只能以GET的方式进行。
原生js:
js部分
function show1(){
var img = document.createElement("script"); // 创建一个script标签
img.src = " http://127.0.0.1:8080/outer"; // 设置请求路径
document.head.appendChild(img); // 在头内添加信息
document.head.removeChild(img); // 拿到数据就删除
}
function fuck(msg) {
console.log(msg)
}
-------------------------------------------------------------------
views
def outer(request):
return HttpResponse("fuck('外界的响应')") // 返回一个函数名包裹信息,实际上返回的是函数
jQuery:
js部分
function show1(){
$.ajax({
url:" http://127.0.0.1:8080/outer",
type:"get",
success:function (msg) {
console.log(msg)
},
dataType:"jsonp", // 当类型是jsonp的形式时,会自动生成标签等
jsonp:"callback", // 设置参数名
jsonpcallback:"fuck", // 设置回调函数
})
}
function fuck(msg) {
console.log(msg)
}
-------------------------------------------------------------------
views
def outer(request):
name = request.GET.get("callback") // 获取参数值
return HttpResponse(name+"('外界的响应')") // 返回一个函数名包裹信息
跨站资源共享(core):
通过设置头信息,让浏览器不再限制信息的返回,core的方式既可以GET请求也可以用POST请求
function show1(){
$.ajax({
url:"http://127.0.0.1:8080/outer/",
type:"get",
success:function (msg) {
$("#content").text(msg);
}
})
}
-----------------------------------------------
views视图函数: