分页介绍与逻辑
由于当前开发的需求当中各种管理平台的增多,各种各样的列表需求逐渐增多,而往往数据量有很多,很难实现也没有必要将所有的数据都罗列到一个前端页面上,用户用不到,也很浪费资源。所以我们有了分页的需求,将数据分页显示。
现在有1000条文章的数据需要展示在我们的计算机上。
1. 可以将1000条分为每10条一页,这样就形成了分页。我们来看一下分页的逻辑:
页码 | 数据 |
1 | 0 - 9 |
2 | 10 - 19 |
3 | 20 - 29 |
...... | ...... |
n | (n-1)*10 - n*10-1 |
那么这种分页,需要我们定义当前页码还有单页数据长度。
2. 这样的分页,每次请求,都需要对1000条数据进行遍历,也许浏览者只需要第20条数据,所以,我们是不是可以在上面的基础上,对使用者进行”欺诈”呢?
分页码 | 页码 | 数据 |
0 - 29 | 1 | 0 - 9 |
2 | 10 - 19 | |
3 | 20 - 29 | |
30 - 59 | 4 | 30 - 39 |
5 | 40 - 49 | |
6 | 50 - 59 |
这种逻辑,当用户请求数据,先不去查询所有数据,而是查询前30条数据,然后,对这30条数据进行分页,如果有人查询到第4页,我们就接着加载。
到这里,分页的逻辑并没有结束,还需要想明白前端除了分页的数据,还需要什么:
- 分页的数据
- 当前的页面 用来搭建上一页和下一页
- 页码的序列
Vue介绍
上面我们了解了基本的分页,我们今天主要用ajax+vue实现分页的动态加载。当然在这里先需要给大家介绍一下我们的vue.js, 是一套构建用户界面的渐进式框架。只关注视图层的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。
MVC (Model – View - Control)模式
MTV (Model – Template - Views)模式
Vue.js采用的是MVVM(ModelView-viewModel)
强调前端的双向绑定逻辑
通俗的讲,vue.js可以将我们从后端传递过来的json数据传递到HTML当中,进行动态的更新。
首先可以从vue的官网(https://vuejs.org/)上下载vue的脚本,在这里,我们使用已经下载好的文件,将他们放入static下面。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejsExample</title>
<script src = "/static/js/jquery.js"></script>
<script src = "/static/js/vue.min.js"></script>
</head>
<body>
<h1>
这个是我们vuejs的测试页面
</h1>
<!--vue 绑定和Django的jinja2模板系统使用的标签是一样的,为了区分我们需要用verbatim将vue的标签包围起来-->
{% verbatim myblock %}
<div id = "vueExample">
<p> 基本的文本绑定 </p>
<p> hello world I am {{ name }}</p>
</div>
{% endverbatim myblock %}
<script>
new Vue( //实例化vue对象
{
el: "#vueExample", //绑定的元素是一个id为vueExample的div
data:{
name: "while, I am so cool"
} //可供绑定的数据设置
}
)
</script>
</body>
</html>
效果如下:
这个是vue最基础的例子,在这里我们要注意一下几个点:
{% verbatim myblock %}
<div id = "vueExample">
<p> 基本的文本绑定:</p>
<p> hello world I am {{ name }}</p>
</div>
{% endverbatim myblock %}
vue对象在HTML当中调用,在js当中进行数据的声明,其中有一些固定的点和结构需要记忆。首先需要声明vue对象,指定绑定的HTML标签,选择器是jq选择器,这里用#veuExample来绑定。具体的数据都放在一个data的地方
{% verbatim myblock %}
<div id = "vueExample">
<p> 基本的文本绑定:</p>
<p> hello world I am {{ name }}</p>
</div>
{% endverbatim myblock %}
<script>
new Vue(
{
el: "#vueExample", //绑定的对象
data: { //绑定的具体数据
name : "while"
}
}
)
</script>
上面是一个最简单的文本绑定的例子,我们接下来再看几种绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejsExample</title>
<script src = "/static/js/jquery.js"></script>
<script src = "/static/js/vue.min.js"></script>
</head>
<body>
<h1>
这个是我们vuejs的测试页面
</h1>
<!--vue 绑定和Django的jinja2模板系统使用的标签是一样的,为了区分我们需要用verbatim将vue的标签包围起来-->
{% verbatim myblock %}
<div id = "vueExample">
<p> 基本的循环绑定 </p>
<p v-for = "p in project"> hello world I like {{ p }}</p> <!--关注循环体-->
</div>
{% endverbatim myblock %}
<script>
new Vue( //实例化vue对象
{
el: "#vueExample", //绑定的元素是一个id为vueExample的div
data:{
name: "while, I am so cool",
project: ["python", "linux", "php"]
} //可供绑定的数据设置
}
)
</script>
</body>
</html>
效果如下:
带有属性和映射的循环绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejsExample</title>
<script src = "/static/js/jquery.js"></script>
<script src = "/static/js/vue.min.js"></script>
</head>
<body>
<h1>
这个是我们vuejs的测试页面
</h1>
<!--vue 绑定和Django的jinja2模板系统使用的标签是一样的,为了区分我们需要用verbatim将vue的标签包围起来-->
{% verbatim myblock %}
<div id = "vueExample">
<p> 带有属性和映射的循环绑定 </p>
<ul>
<li v-for = "p in projects">
<span v-bind:id = "p.color">
{{ p.name }}
</span>
</li>
</ul>
</div>
{% endverbatim myblock %}
<script>
new Vue( //实例化vue对象
{
el: "#vueExample", //绑定的元素是一个id为vueExample的div
data:{
name: "while, I am so cool",
project: ["python", "linux", "php"],
projects:[
{name: "python", color: "red"},
{name: "linux", color: "green"},
{name: "php", color: "blue"}
]
} //可供绑定的数据设置
}
)
</script>
</body>
</html>
效果如下:
基本的事件绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejsExample</title>
<script src = "/static/js/jquery.js"></script>
<script src = "/static/js/vue.min.js"></script>
</head>
<body>
<h1>
这个是我们vuejs的测试页面
</h1>
<!--vue 绑定和Django的jinja2模板系统使用的标签是一样的,为了区分我们需要用verbatim将vue的标签包围起来-->
{% verbatim myblock %}
<div id = "vueExample">
<p> 基本的事件绑定 </p>
<span v-on:click = "alertthing">按我呀!</span>
</div>
{% endverbatim myblock %}
<script>
new Vue( //实例化vue对象
{
el: "#vueExample", //绑定的元素是一个id为vueExample的div
data:{
name: "while, I am so cool",
project: ["python", "linux", "php"],
projects:[
{name: "python", color: "red"},
{name: "linux", color: "green"},
{name: "php", color: "blue"}
]
}, //可供绑定的数据设置
methods: {
alertthing: function () {
alert("按我干啥?")
}
}
}
)
</script>
</body>
</html>
效果如下:
基于Vue尝试分页
Vue后台数据返回
/OurBlog/Aritcles/views.py
from Article.models import Article
from django.core.paginator import Paginator
from django.http import JsonResponse
def vuePageData(request):
"""
提供分页的ajax数据
"""
if request.method == "GET": #如果是get请求
page = request.GET.get("page") #尝试获取page
if not page: #默认page 为1
page = 1
else:
page = int(page) #get过来的page参数是字符串
aritcles = Article.objects.all() #查询所有的数据
paginator = Paginator(aritcles,3) #对数据进行分页,每页三条
pageData = paginator.page(page) #获取具体页的数据
page_data = [] #对数据进行json结构化,json只接受字典对象
for data in pageData:
classify = data.classify.all() #多对多字段需要首先查询出所有对应的字段,查询出来还是数据库对象
if classify:
classify = [i.label for i in classify] #对字段取指定的lable
else:
classify = "" #空类表不可以建json序列,所以,我们改完字符串
page_data.append(
{
"title": data.title,
"author": data.author.name, #w外键,必须调用具体的字段
"time": data.time,
"description": data.description,
"picture": data.picture.name, #这里的name是由于文件对象有name属性
"classify": classify,
"id": data.id
}
)
result = {
"pageData": page_data
}
return JsonResponse(result)
效果如下:
Html前端进行调用
/OurBlog/template/myArticle.html
{% extends "base.html" %}
{% block title %}
我的博客
{% endblock %}
{% block style %}
<link href="/static/css/main.css" rel="stylesheet">
<script src="/static/js/jquery.js"></script>
<script src="/static/js/vue.min.js"></script>
{% endblock %}
{% block content %}
<div class="container">
<div class="con_content">
<div class="about_box">
<h2 class="nh1"><span>您现在的位置是:<a href="/" target="_blank">网站首页</a>>><a href="#" target="_blank">个人日记</a></span><b>个人日记</b></h2>
{% verbatim myblock %}
<div class="dtxw box" id = "dataList">
<li v-for = "page in page_data">
<div class="dttext f_l">
<ul>
<h2><a href="/">{{ page.title }}</a></h2>
<p v-html="page.description">{{ page.description }}</p> <!-- 注意和Django的safe标签的区分 v-html == safe -->
<span>{{ page.time }}</span>
</ul>
</div>
<div class="xwpic f_r"><a href="/"><img v-bind:src="['/static/'+page.picture]"></a></div> <!-- 注意绑定数据的拼接 -->
</li>
</div>
{% endverbatim myblock %}
<div class="pagelist">页次:1/1 每页25 总数10<a href="/">首页</a><a href="/">上一页</a><a href="/">下一页</a><a href="/">尾页</a></div>
</div>
</div>
<div class="blank"></div>
<!-- container代码 结束 -->
</div>
<script>
$(
function () {
$.ajax(
{
url: "/vuePageData/",
type: "GET",
data: "",
dataType: "json", //我们需要抢到我们的ajax请求需求数据是json
success: function (data) {
new Vue( //注意new地下和var
{
el:"#dataList", //绑定指定元素,注意选择器,注意#
data: {
page_data: data["pageData"] }
}
);
},
error: function (error) {
console.log(error)
}
}
)
}
);
</script>
{% endblock %}
这里我已经提前在admin后台管理中对article进行了添加,所以看到的效果如下:
基于vue-resource尝试分页
上面的逻辑虽然可以实现分页,但是有数据损耗。所以我们主要来实现第二种分页,我们着重看的是分页的后端逻辑
我们先来查看分页的逻辑
分页码 | 页码 | 数据 |
1 | 1 | 0 - 3 0,1,2 |
2 | 3 - 6 3,4 ,5 | |
3 | 6 - 9 6,7,8 | |
2 | 4 | 9 - 12 9,10,11 |
5 | 12 - 15 12,13,14 | |
6 | 15 - 18 15,16,17 |
每次查询,查询3页。每页查询 3条。当收到单页码请求:我们只查询9条
首先完成第一步逻辑
1,2,3 --> 1
4,5,6 --> 2
select_page = 3 #每次3页
page_num = 3 #每页3条
select_num = select_page*page_num #单次查询9条
resutl_dict = {}
query_num = page/page_num #获取查询序列号
if query_num == int(query_num): #如果整除
query_num = int(query_num) #返回整形数
else: #否则
query_num = int(query_num)+1 #返回整形数加1
首先完成第二步逻辑
1 查询 0-9
2 查询 9-18
所以
n 查询 (n-1)*9-n*9
start = (query_num - 1)*select_num
end = query_num*select_num
然后,我们需要了解一个原理
Django模型的查询返回的对象是quertset对象,之后我们需要重写该对象,但是现在我们需要知道这种查询有惰性。
articels = Article.objects.all() #现在当前查询不会执行,直到调用
Good
articels = Article.objects.all()[start:end] #现在当前查询不会执行,直到调用,当前写法的查询效率和数据库limit语句相似
Bad
articels = Article.objects.all() #现在当前查询不会执行,直到调用,当前写法的查询效率和数据库limit语句相似
articles = articels[start:end]
然后我们查看完整的分页逻辑代码
def vuePageData1(request):
"""
第二种分页的视图函数
"""
if request.method == "GET": #如果是get请求
page = request.GET.get("page") #尝试获取page
if not page: #默认page 为1
page = 1
else:
page = int(page) #get过来的page参数是字符串
select_page = 3 #每次3页
page_num = 3 #每页3条
select_num = select_page*page_num #单次查询9条
resutl_dict = {}
query_num = page/page_num #获取查询序列号
if query_num == int(query_num): #如果整除
query_num = int(query_num) #返回整形数
else: #否则
query_num = int(query_num)+1 #返回整形数加1
start = (query_num - 1)*select_num
end = query_num*select_num
articels = Article.objects.all()[start:end] #现在当前查询不会执行,只到调用,当前写法的查询效率和数据库limit语句相似
#进行分页
paginator = Paginator(articels,page_num) #分页值分了9条数据
page = paginator(page)
- 如果查询1,2,3,我们请求的时候page是1,2,3
那如果page = 4 查询的 1
query_num = 2
start = (query_num-1)*select_num = (2-1)*3 = 3
4 – 3 = 1
pageData = paginator(page-start)
分页视图完整代码
\OurBlog\Article\views.py
def vuePageData_1(request):
"""
这个函数是用来返回分页数据的,一定要认真
query_page 分页量
首先划定逻辑
每页3条
每次3页
到第3页我们返回下5页
"""
select_page = 3 # 每次3页
page_num = 3 # 每页3条
select_num = select_page * page_num # 单次查询的总条数
page_result = {} # 返回的结果
if request.method == "GET": # 如果是get请求
page = request.GET.get("page") # 获取page值,在这里注意一定要用get,如果用[]容易导致如果page没有参数会报错
if page and int(page) > 1: # page存在,且大于1
page = int(page)
else: # 设置默认为1
page = 1
query_num = page / page_num
if query_num == int(query_num):
query_num = int(query_num)
else:
query_num = int(query_num) + 1
start = (query_num - 1) * select_num
end = query_num * select_num + 3
articles = Article.objects.all()[start:end]
paginator = Paginator(articles, page_num) # 进行每页5条的分页
print(page)
print(start)
pageData = paginator.page(page-start/page_num) #注意加入页面为4 我们的start是9
page_data = [] # 对数据进行从新整合
for data in pageData:
page_data.append(
{
"title": data.title,
"author": data.author.name,
"time": data.time,
"picture": data.picture.name, # 这里要注意,data.picture 返回的是图片对象,我们获取他的name
'description': data.description,
'id': data.id,
}
)
page_result["pageData"] = page_data
if page % select_page == 0 and len(paginator.page_range) > 3:
page_result["page_range"] = [page + page_num * (query_num - 1) for page in paginator.page_range]
else:
page_result["page_range"] = [page + page_num * (query_num - 1) for page in paginator.page_range][:-1]
return JsonResponse(page_result)
HTML前端
\OurBlog\template\myArticle_v1.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>个人博客模板古典系列之――江南墨卷</title>
<meta name="keywords" content="个人博客模板古典系列之――江南墨卷" />
<meta name="description" content="个人博客模板古典系列之――江南墨卷" />
<link href="/static/css/base.css" rel="stylesheet">
<link href="/static/css/main.css" rel="stylesheet">
<!--[if lt IE 9]>
<script src="js/modernizr.js"></script>
<![endif]-->
<script type="text/javascript" src="/static/js/jquery.js"></script>
<script type="text/javascript" src="/static/js/vue.min.js"></script>
<script type="text/javascript" src="/static/js/vue-resource.js"></script>
</head>
<body>
<div id="wrapper">
<header>
<div class="headtop"></div>
<div class="contenttop">
<div class="logo f_l">个人博客模板古典系列之――江南墨卷</div>
<div class="search f_r">
<form action="/e/search/index.php" method="post" name="searchform" id="searchform">
<input name="keyboard" id="keyboard" class="input_text" value="请输入关键字" style="color: rgb(153, 153, 153);" onfocus="if(value=='请输入关键字'){this.style.color='#000';value=''}" onblur="if(value==''){this.style.color='#999';value='请输入关键字'}" type="text">
<input name="show" value="title" type="hidden">
<input name="tempid" value="1" type="hidden">
<input name="tbname" value="news" type="hidden">
<input name="Submit" class="input_submit" value="搜索" type="submit">
</form>
</div>
<div class="blank"></div>
<nav>
<div class="navigation">
<ul class="menu">
<li><a href="index.html">网站首页</a></li>
<li><a href="#">关于我</a>
<ul>
<li><a href="article.html">个人简介</a></li>
<li><a href="listpic.html">个人相册</a></li>
</ul>
</li>
<li><a href="#">我的日记</a>
<ul>
<li><a href="articleList.html">个人日记</a></li>
<li><a href="articleList.html">学习笔记</a></li>
</ul>
</li>
<li><a href="articleList.html">技术文章</a> </li>
<li><a href="#">给我留言</a> </li>
</ul>
</div>
</nav>
<SCRIPT type=text/javascript>
// Navigation Menu
$(function() {
$(".menu ul").css({display: "none"}); // Opera Fix
$(".menu li").hover(function(){
$(this).find('ul:first').css({visibility: "visible",display: "none"}).slideDown("normal");
},function(){
$(this).find('ul:first').css({visibility: "hidden"});
});
});
</SCRIPT>
</div>
</header>
<body>
<div class="container">
<div class="con_content">
<div class="about_box" id = "vueData">
<h2 class="nh1"><span>您现在的位置是:<a href="/" target="_blank">网站首页</a>>><a href="#" target="_blank">个人日记</a></span><b>个人日记</b></h2>
{% verbatim myblock %} <!--vue的格式化标签-->
<div class="dtxw box">
<li v-for = "page in page_data">
<div class="dttext f_l">
<ul>
<!--注意vue的属性绑定 两个问题
问题一:要用数组类型
问题二:要注意v-bind:后面不可以有空格 强迫症尤其小心
-->
<h2><a v-bind:href="['/article/?id='+page.id]">{{ page.title }}</a></h2>
<p v-html="page.description">{{ page.description }}</p>
<span>{{ page.time }}</span>
</ul>
</div>
<div class="xwpic f_r"><a href="/"><img v-bind:src="['/static/'+page.picture]"></a></div>
</li>
</div>
<div class="pagelist">
<span v-for="p in page_range" >
<a v-on:click="getPage(p)">{{ p }}</a> <!--注意vue的函数绑定和传参-->
</span>
</div>
{% endverbatim myblock %}
</div>
</div>
<div class="blank"></div>
<!-- container代码 结束 -->
<footer>
<div class="footer">
<div class="f_l">
<p>All Rights Reserved 版权所有:<a href="http://www.yangqq.com">杨青个人博客</a> 备案号:蜀ICP备00000000号</p>
</div>
<div class="f_r textr">
<p>Design by DanceSmile</p>
</div>
</div>
</footer>
</div>
</body>
<script>
var list = [];
Vue.use(VueResource); //首先需要声明一下vue对象变成了vueResource对象
new Vue(
{
el: "#vueData",
data: {
page_data: "",
page_range: []
},
// created方法在初始化的时候调用的,相当于我们默认的第一页
created: function () {
var url = "/VP1/";
this.$http.get(url).then(
function (data) {
this.page_data = data["data"]["pageData"]; //用ajax返回的数据更新到我们绑定好的数据当中
this.page_range = data["data"]["page_range"]; //用ajax返回的数据更新到我们绑定好的数据当中
for(var ds in data["data"]["page_range"]){
list.push(data["data"]["page_range"][ds])
}
},
function (error) {
console.log(error)
}
)
},
methods: {
getPage: function (page) {
var url = "/VP1/?page="+page;
this.$http.get(url).then(
function (data) {
page_range = data["data"]["page_range"];
this.page_data = data["data"]["pageData"];
for(var ds in data["data"]["page_range"]){
console.log("++++++++++++++++++++++++++++++++++++++++++++");
pageNumber = page_range[ds];
if($.inArray(pageNumber,list) == -1){
list.push(pageNumber)
}
console.log(pageNumber);
console.log("++++++++++++++++++++++++++++++++++++++++++++");
}
this.page_range = list;
console.log(data)
},
function (error) {
console.log(error)
}
)
}
}
}
)
</script>
</html>