django练手(二十):图标上传页面增加分页的功能

一、思路

  1. 前端使用bootstrap的分页组件;
  2. 后端使用django自带的分页功能。

二、前端功能实现

  1. bootstrap文档上复制分页代码,粘贴到app-templates-app-avatar.html的合适位置。复制的地址是https://v3.bootcss.com/components/#pagination 具体的代码是:

    <nav aria-label="Page navigation">
      <ul class="pagination">
    	<li>
    	  <a href="#" aria-label="Previous">
    		<span aria-hidden="true">&laquo;</span>
    	  </a>
    	</li>
    	<li><a href="#">1</a></li>
    	<li><a href="#">2</a></li>
    	<li><a href="#">3</a></li>
    	<li><a href="#">4</a></li>
    	<li><a href="#">5</a></li>
    	<li>
    	  <a href="#" aria-label="Next">
    		<span aria-hidden="true">&raquo;</span>
    	  </a>
    	</li>
      </ul>
    </nav>
    
  2. 修改后台的图标上传视图,增加分页功能
    要修改的文件是:app-views-app-account.py。视图名称是:avatar_upload。具体的代码如下:

    # 图标上传
    @login_required
    def avatar_upload(request):
    	if request.method == 'GET':
    		# 获取所有的用户图标数据
    		avatars = Avatar.objects.filter().all().order_by('-id')
    		# 注意:Paginator查询集能处理的queryset必须是有序的
    		paginator = Paginator(avatars, 12)
    		# 获浏览器传递的取页码数
    		page_num = request.GET.get('page_num', '')
    
        # 如果page_num非空且page_num字符串只有数字构成。方法isnumeric() 检测字符串是否只由数字组成。这种方法是只针对unicode对象。
        if page_num is not None and page_num.isnumeric():
            page_num = int(page_num)
        else:
            page_num = 1
        if page_num not in paginator.page_range:
            page_num = 1
        avatars = paginator.page(page_num)
        #
        page_range = paginator.page_range
    	# 下一行的new_page_range方法是我自己写的一个小工具。其功能是在分页数据量很大的情况下,始终保持页面上显示的页码数是10。这个小工具的代码见下一部分。
        page_range=new_page_range(page_range,page_num)
    
        return render(request, 'app/avatar.html', {"avatars": avatars, "page_range": page_range})
    if request.method == "POST":
        # 1、判断文件路径是否存在,如果不存在,生成文件路径。
        image_folder_path = os.path.join(settings.MEDIA_ROOT, 'image', 'avatar')  # 文件路径
        image_folder_path_exists = os.path.exists(image_folder_path)  # 判断文件路径是否存在
        # 如果不存在,则生成文件路径
        if not image_folder_path_exists:
            os.makedirs(image_folder_path)
        # 获取所有上传的图标
        file_list = request.FILES.getlist('avatar_upload')
        if file_list:
            for file in file_list:
                # 1、把文件名替换成整个系统唯一的文件名。
                name = uuid_name(file.name)
                # 2、获取文件中文名
                title = os.path.splitext(file.name)[0]
                # 3、文件存储的位置。
                file_path = image_folder_path + '\\' + name
                # 4、把文件写入硬盘
                with open(file_path, 'wb', ) as f:
                    for part in file.chunks():
                        f.write(part)  # 内容块写入文件
                        f.flush()  # 清除内存
                # 5、生成存储在数据库里的路径。
                database_path = 'uploads/image/avatar/' + name
                # 6、把文件信息写入数据库
                avatar = Avatar()
                avatar.title = title
                avatar.img_url = database_path
                avatar.save()
        return JsonResponse({"status": True, })
    
  3. 当分页数据很多的情况下,始终维持前台显示的页码数为10的小工具。
    代码如下:

    from django.core.paginator import Paginator
    
    
    def new_page_range(page_range, page_num):
    	"""
    	这是一个重写的分页功能。改写了page_range。在分页数据量很大的情况下,始终保持页面上显示的页码数是10。
    	page_range:分页的页码列表
    	page_num:页码数
    	返回的page_range是一个始终有10个分页的range对象
    	"""
    	page_rage_len = len(page_range)
    	if page_rage_len > 10:
    		if page_num > 5:
    			if page_num + 5 <= page_rage_len:
    				page_range = range(page_num - 5, page_num + 5)
    				return page_range
    			if page_num + 5 > page_rage_len:
    				page_range = range(page_rage_len - 10, page_rage_len)
    				return page_range
    		if page_num <= 5:
    			page_range = range(1, 11)
    			return page_range
    	else:
    		return page_range
    
    
  4. 修订bootstrap上复制的代码,把后台传到前台的数据展示出来
    修订后的代码如下:

    {% extends 'app/layout/basic.html' %}
    {% block css %}
    	<style>
    		.thumbnail {
    			width: fit-content;
    			height: fit-content;
    		}
    	</style>
    {% endblock %}
    {% block content %}
    	<div class="container-fluid">
    
    		<!--图片显示的代码开始-->
    		<div class="row" id="avatar">
    			<div id="avatar_child">
    				{% for avatar in avatars %}
    					<div class="col-sm-6 col-md-2">
    						<div class="thumbnail">
    
    							<img src="../../../{{ avatar.img_url }}" alt="{{ avatar.title }}" class="img-responsive">
    							<div class="caption">
    								<h5>{{ avatar.title }}</h5>
    
    								<p><a href="#" class="btn btn-primary btn-sm" role="button">Button</a>
    									<a href="#" class="btn btn-default btn-sm" role="button">Button</a>
    								</p>
    							</div>
    						</div>
    					</div>
    
    				{% endfor %}
    
    			</div>
    		</div>
    
    		<!--图片显示的代码结束-->
    		<!--分页的代码开始-->
    		<div class="row" style="text-align: center">
    			<nav aria-label="Page navigation">
    				<ul class="pagination">
    					{% if avatars.has_previous %}
    						<li>
    							<a href="{% url 'app:avatar_upload' %}?page_num={{ avatars.previous_page_number }}"
    							   aria-label="Previous">
    								<span aria-hidden="true">上一页</span>
    							</a>
    						</li>
    					{% endif %}
    
    					{% for num in page_range %}
    						<li><a href="{% url 'app:avatar_upload' %}?page_num={{ num }}" class="active">{{ num }}</a></li>
    					{% endfor %}
    					{% if avatars.has_next %}
    						<li>
    							<a href="{% url 'app:avatar_upload' %}?page_num={{ avatars.next_page_number }}"
    							   aria-label="Next">
    								<span aria-hidden="true">下一页</span>
    							</a>
    						</li>
    					{% endif %}
    
    				</ul>
    			</nav>
    		</div>
    		<!--分页的代码结束-->
    		<div class="row">
    			<!--
    			上传文件的form。
    			action:后台的地址;
    			method:发送的方式。上传文件一般用post方式发送;
    			enctype:即encode type的缩写。意思是编码方式。 multipart/form-data 代表form数据有多种编码方式。
    			-->
    			<form name="file" method="post" action="{% url 'app:avatar_upload' %}" enctype="multipart/form-data"
    				  id="form_upload">
    				{% csrf_token %}
    				<!--
    				上传文件的input标签。
    				type的参数必须是file,代表上传文件。
    				accept表示允许上传的文件类型。"image/*"代表所用图片类型。
    				multiple 表示一次可以传多个文件。没有这个参数一次只能上传一个文件。
    				required 表示在提交前必须选择一个文件
    				-->
    				<input type="file" accept="image/*" name="avatar_upload" id="input_upload" multiple required>
    				<input type="button" value="上传" class="btn btn-default btn-custom" id="btn_upload">
    			</form>
    		</div>
    	</div>
    {% endblock %}
    {% block js %}
    	<script>
    		$("#btn_upload").click(
    			function () {
    				// 实例化一个FormData对象。这个对象包含form的内容。这个数据作为后面ajax上传的数据使用。
    				let formData = new FormData($("#form_upload")[0]);
    				// 获取input中上传的内容,类型为FileList
    				let files = $("#input_upload")[0].files;
    				// 定义一个数组,用来存储图片大小大于10000b的图片名称
    				let exceedArr = [];
    
    				// files.length==0,表示FileList的长度为0,即没有选择图片。则提示没有选择图片,否则通过ajax进行上传动作。
    				if (files.length === 0) {
    					alert("你没有选择图片");
    				} else {
    
    					//依次获取上传图片的大小,如果10000b,则把这个图片的名称存到数组里
    					for (let i = 0; i < files.length; i++) {
    						if (files[i].size > 40000) {
    							exceedArr.push(files[i].name);
    						}
    					}
    
    					/*
    					1、如果数组的长度大于0,则把数组中所用的图片名称拼接成一个字符串,并提示用户那些图片的大小超过了,同时清空文件选择控件。
    					2、如果数组的长度不大于0,则执行上传动作。上传完成后清空文件控件。
    					 */
    
    					if (exceedArr.length > 0) {
    						let alertStr = '';
    						for (let i = 0; i < exceedArr.length; i++) {
    							alertStr = alertStr + exceedArr[i] + ",";
    						}
    						alert(alertStr + " 大于10000b,请重新选择");
    						$("#input_upload").val("");
    					} else {
    						$.ajax({
    							url: "{% url 'app:avatar_upload' %}", //请求路径
    							type: 'POST', // 请求类型
    							data: formData, // 请求数据
    							dataType: "JSON", // 返回数据格式
    							contentType: false, //表示不处理数据
    							processData: false,//默认为true,表示会序列化数据。这里设置为false,表示不会序列化数据。
    							cache: false,
    							success: function (res) {
    								if (res.status) {
    									$("#input_upload").val(""); //清空选择控件的内容
    									$("#avatar_child").load(location.href + " #avatar_child>*", ""); //起局部刷新<div>的作用
    								}
    							},
    							error: function (res) {
    								console.log(res);
    							}
    						});
    					}
    				}
    			}
    		)
    	</script>
    {% endblock %}
    
    

四、 功能截图

image

本节代码已上传至gitee.com。仓库地址是:https://gitee.com/yanfenglucky/bokeyuan

posted @ 2024-01-12 11:04  喜气洋洋白云山  阅读(58)  评论(0)    收藏  举报