python设计微型小说网站(基于Django+Bootstrap框架)


Posted in Python onJuly 08, 2019

一、项目背景:

为了回顾关于django的文件上传和分页功能,打算写一个微型的小说网站练练手。花了一个下午的时间,写了个小项目,发现其中其实遇到了许多问题,不过大部分通过debug之后就解决了,其他部分通过阅读了Pagination插件以及Bootstrap-FileInput插件的官方文档。

二、详细设计:

省去小说网站的用户模块的功能,小说网站主要的功能就是上传文件,在线阅读小说。针对这两个功能,

主要用到dajngo内置的Pagination模块,以及选择一个上传文件插件即可。因为用的是Bootsrap前端框架,所以就选择了Bootsrap比较多人用的FileInput插件。

大致的流程:

  • 在首页可以选择上传本地的txt文件到服务器上,然后首页上同时会异步更新已上传的txt文本文件列表。
  • 并且可以在上面选择阅读或者删除的操作。阅读则跳转到另外一个页面,后台会读取该文本文件,并且进行分页操作,返回到前端。主要的流程就是这样。接下来讲讲Pagination和FileInput插件和核心代码。

三、合适的工具:

Django内置的Pagination实现分页功能,这个不用多说,用Django做web开发分页功能都会用到。

Bootstrap本身自带upload file文件上传插件太丑了,加上功能也不够完善。所以选择了Bootstrap FileInput插件。

版本选择:

  • Python 3.6.6
  • Django==2.1.7
  • Bootstrap v4.3.1
  • bootstrap-fileinput v4.5.2

四、代码详解:

首先代码主要分为两块,一块为文件上传后,接收文件对象,保存到指定的目录下;第二块为读取txt文本文件内容,分页展示到前端页面。

首先讲讲文件上传的代码,主要涉及到前端的bootstrap-fileinputt插件。该插件将简单的HTML文件输入转换为高级文件选择器控件。对于不支持JQuery或Javascript的浏览器,将有助于回退到正常的HTML文件输入。

python设计微型小说网站(基于Django+Bootstrap框架)

以上这段是官方的自我介绍,说说我个人感受吧。首先这个插件支持批量上传,异步上传等功能,简化大部分JS逻辑方面的代码,具体只要跟着官方的API文档看一看,修改一些参数即可。其次,对于上传时会显示一个进度条,用于显示上传的完成度,这样直观反映了完成度。

bootstrap-fileinput的github地址:

https://github.com/kartik-v/bootstrap-fileinput

bootstrap-fileinput的官方文档地址:

http://plugins.krajee.com/file-input

bootstrap-fileinput的官方DEMO:

http://plugins.krajee.com/file-basic-usage-demo

4.1、文件上传

HTML代码:

<div dir=rtl class="file-loading">
 <input id="input-b8" name="input-b8" multiple type="file">
</div>

JS代码:

$(document).ready( function() {
$("#input-b8").fileinput({
 rtl: true,
 uploadUrl: '/file_receive/',
 dropZoneEnabled: false,
 showPreview: false,
 allowedFileExtensions: ['txt'],
 initialPreviewConfig: []
});
});

代码说明:

fileinput()方法里面传入的是一个json数据,里面有很多个属性,每个数值代表初始化上传控件时的特性,如果没有设置的属性则按照控件的默认属性设置。简单说下里面几个属性的设置:uploadUrl:上传文件地址;dropZoneEnabled:是否显示拖曳区域;showPreview:是否显示预览区域;allowedFileExtensions:允许上传的文件格式。

后台代码

def file_receive(request):
 # 接收File-Input空间传送的文件
 if request.method == 'POST':
  file = request.FILES['input-b8']
  file_path = "static/books/"+file.name
  with open(file_path,"wb") as f:
   for chunk in file.chunks():
    f.write(chunk)
 return JsonResponse({'status':'success'})

代码说明:

以上是后台接收文件对象并且保存的代码。我这边省略判断上传文件大小的方法,感兴趣的可以在with open()中添加判断。最后接收文件后,会返回给前端一个json数据,前端插件接收到返回的JSON数据才会确定是否上传文件成功,bootstrap Fileinput才会先Done状态。

拓展:

这里有点需要注意的就是,后台接收上传的文件,虽然是通过POST的方式上传,但是不能通过request.POST["filename"]或者request.POST.get("filename","None")两种方式来访问。

而是需要用另外一种方式:

request.FILES["filename"]或者request.FILES.get("filename","None")

接下来已经得到文件对象,需要把在内存中的文件写入到硬盘中。读取文件的几个方法和属性:

  • filename.read():从文件读取整个上传的数据,这个方法只适合小文件
  • filename.chunks():按块返回文件,通过for循环进行迭代,可以将大文件按块写入到服务器中
  • filename.multiple_chunks():当filename文件大于2.5M时,该方法返回True,否则返回False。可以根据该方法来判断选择用1方法还是2方法。

4.2、异步更新已上传的文件列表

HTML代码:

<div style="padding-top: 20px">
 <table id="book_list" class="table table-striped table-bordered table-hover">
  <tr>
   <th>上传书籍</th>
   <th>上传时间</th>
   <th>文件大小</th>
   <th>操作</th>
  </tr>
  {% for book in objects %}
  <tr>
   <td>{{ book.name}}</td>
   <td>{{ book.book_time }}</td>
   <td>{{ book.book_size }}</td>
   <td><a href="/book_read/?book_name={{ book.name }}" rel="external nofollow" >阅读</a>
   <a href="/book_del/?book_name={{ book.name }}" rel="external nofollow" >删除</a></td>
  </tr>
  {% endfor %}
 </table>
</div>

JS代码:

$("#input-b8").on('fileuploaded',function(){
 console.log('success');
 $.get('/book_update/',function(data){
  var book_html ="<tr>\n" +
   "<th>上传书" +
   "籍</th>" +
   "<th>上传时间</th>" +
   "<th>文件大小</th>" +
   "<th>操作</th>"+
   "</tr>";

  console.log(data);
  for (var i in data){
   book_html += "<tr><td>"+ data[i]['name']+"</td>" +
    "<td>"+data[i]['book_time']+"</td>" +
    "<td>"+data[i]['book_size']+"</td>" +
    "<td><a href=\"/book_read/?book_name="+data[i]['name']+"\">阅读</a>"+
    "<a href=\"/book_del/?book_name="+data[i]['name']+"\">删除</a></td>"+
    "</tr>"
  }
  $("#book_list").html(book_html)
  console.log(book_html)
 });
});

代码说明:

$("#input-b8").on('fileuploaded',function(){})这个方法时在上传完文件后进行回调事件的函数;就是指上传一个文件成功后就会调用该方法;所以我将异步更新上传文件列表的代码放在这个回调事件中。当每个文件上传后,就会请求后台,查询指定目录下的文件列表,生成json格式的数据返回前台,前台再通过遍历的形式拿到其中的数据,进行展示,具体效果如下:

python设计微型小说网站(基于Django+Bootstrap框架)

后台代码

def book_list():
 # 获取books目录下的书籍
 file_list = []
 filedir_path = "static/books/"
 list_file = os.listdir(filedir_path)
 for book in list_file:
  book_info = {}
  book_path = filedir_path + book

  book_info['name'] = book
  book_info['timestamp'] = os.path.getctime(book_path)
  book_info['book_time'] = time_format(book_info['timestamp'])
  book_info['book_size'] = os.path.getsize(book_path)
  file_list.append(book_info)
 books = sorted(file_list,key= lambda x:x['timestamp'],reverse=True)
 return books 

def time_format(timestamp):
 # 格式化时间戳成指定的时间
 time_struct = time.localtime(timestamp)
 time_string = time.strftime('%Y-%m-%d %H:%M',time_struct)
 return time_string

代码说明:

代码其实很简单,主要是对通过os模块获取静态目录static下的books目录下的文件列表,然后在获取每个文件的时间戳,通过列表推导式,按时间戳为key值进行逆向排序。

4.3、文章分页模块

HTML代码:

<div class="header text-center ">
 <a href="/index/" rel="external nofollow" style="float: left;">
  <i class="fa fa-home fa-2x" aria-hidden="true">Home</i>
 </a>
 <h3>{{ book_name }}</h3>
</div>

<div class="col-md-12 col-sm-offset-1 main">
 {% for content in book_content %}
 <span>{{ content }}</span>
 {% endfor %}
</div>

<div class="pagination">
 <div class="col-md-4 ">
  {% if book_content.has_previous %}
  <i class="fa fa-arrow-left" aria-hidden="true">
   <a href="?book_name={{ book_name }}&page={{ book_content.previous_page_number }}" rel="external nofollow" >
    上一页
   </a>
  </i>

  {% endif %}
 </div>

 <div class="col-md-4 ">
  <h5>
   第{{ book_content.number }}页/共{{ book_content.paginator.num_pages }}页
  </h5>

 </div>
 {% if book_content.has_next %}
 <div class="col-md-4 ">
  <a href="?book_name={{book_name}}&page={{ book_content.next_page_number }}" rel="external nofollow" >
   下一页
  </a>
  <i class="fa fa-arrow-right" aria-hidden="true">
  </i>
 </div>
 {% endif %}
</div>

JS代码:

def book_read(request):
 # 获取上传书籍的内容
 if request.method == 'GET':
  book_name = request.GET['book_name']   # 书籍名称
  file_path = "static/books/" + book_name   # 书籍路径

  with open(file_path,encoding='gbk', errors='ignore') as f:
   book_contents = f.readlines()

  paginator = Paginator(book_contents, 50)
  try:
   page = int(request.GET['page']) # 页码
   book_content = paginator.page(page)
  except Exception as e:
   book_content = paginator.page(1)
  return render_to_response('book.html',{'book_content': book_content, 'book_name': book_name})

代码说明:

读取文件的所有行,保存在一个列表中(list),每行作为一个元素。然后实例化一个Paginator对象,并且在实例化中传入一个需要分页的对象列表,以及一页包含多少个数据。再从接收前端传送过来的页码,取特定页码的数据,再传回前端。

python设计微型小说网站(基于Django+Bootstrap框架)

python设计微型小说网站(基于Django+Bootstrap框架)

拓展:

1、分页功能有Django内置的Paginator类提供的,该类位于django/core/paginator,需要用的地方导入即可:
from django.core.paginator improt Paginator

2、read()、readline()、readlines()方法的区别:

三者都是读取文件内容:

  • read([size]):从当前位置其读取size字节,如果方法里面没有参数size,读取至文件结束为止。返回的是一个字符串对象。
  • readline():方法调用一次就读文件一行,该方法返回一个字符串。
  • readlines():读取整个文件所有行,保存在一个列表中,每行作为一个元素

3、Paginator对象操作:

实例化对象:

book_list = [1,2,3,4,5,6,7,8]
book_content = Paginator(book_list,3)

取特定页的数据

content = book_content.page(2)

查特定页当前页码数:

content.number

查分页后的总页数

content.num_pages

查询某一页是否有上一页或者查询上一页页码:

content.has_previous()
content.previous_page_number()

查询某一页是否有下一页或者查询下一页页码:

content.has_next()
content.next_page_number()

感兴趣的同学可以上GitHub上,项目代码的地址:

https://github.com/libuliduobuqiuqiu/noval_test

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python同时给两个收件人发送邮件的方法
Apr 30 Python
Python实现曲线点抽稀算法的示例
Oct 12 Python
python 简单搭建阻塞式单进程,多进程,多线程服务的实例
Nov 01 Python
ubuntu中配置pyqt4环境教程
Dec 27 Python
编写多线程Python服务器 最适合基础
Sep 14 Python
对Python 3.2 迭代器的next函数实例讲解
Oct 18 Python
Python代码实现删除一个list里面重复元素的方法
Apr 02 Python
PyQt5实现暗黑风格的计时器
Jul 29 Python
python3 反射的四种基本方法解析
Aug 26 Python
Python3 ID3决策树判断申请贷款是否成功的实现代码
May 21 Python
详解Python yaml模块
Sep 23 Python
在 Python 中使用 7zip 备份文件的操作
Dec 11 Python
python字符串查找函数的用法详解
Jul 08 #Python
python提取log文件内容并画出图表
Jul 08 #Python
Python OpenCV 使用滑动条来调整函数参数的方法
Jul 08 #Python
使用Python opencv实现视频与图片的相互转换
Jul 08 #Python
python基于paramiko将文件上传到服务器代码实现
Jul 08 #Python
Python脚本利用adb进行手机控制的方法
Jul 08 #Python
Python Pandas中根据列的值选取多行数据
Jul 08 #Python
You might like
PHP多个版本的分析解释
2011/07/21 PHP
关于PHP模板Smarty的初级使用方法以及心得分享
2013/06/21 PHP
基于PHP实现的多元线性回归模拟曲线算法
2018/01/30 PHP
asp(javascript)全角半角转换代码 dbc2sbc
2009/08/06 Javascript
chrome原生方法之数组
2011/11/30 Javascript
30个让人兴奋的视差滚动(Parallax Scrolling)效果网站
2012/03/04 Javascript
js去字符串前后空格5种实现方法及比较
2013/04/03 Javascript
javascript 实现 秒杀,团购 倒计时展示的记录 分享
2013/07/12 Javascript
jquery实现手风琴效果实例代码
2013/11/15 Javascript
jquery重复提交请求的原因浅析
2014/05/23 Javascript
javascript的变量、传值、传址、参数之间关系
2015/07/26 Javascript
JavaScript实现斗地主游戏的思路
2016/02/29 Javascript
jQuery ajax中使用confirm,确认是否删除的简单实例
2016/06/17 Javascript
移动端点击图片放大特效PhotoSwipe.js插件实现
2016/08/25 Javascript
微信小程序 支付后台java实现实例
2017/05/09 Javascript
VueJS事件处理器v-on的使用方法
2017/09/27 Javascript
vue .js绑定checkbox并获取、改变选中状态的实例
2018/08/24 Javascript
简单了解vue中父子组件如何相互传递值(基础向)
2019/07/12 Javascript
[17:00]DOTA2 HEROS教学视频教你分分钟做大人-帕克
2014/06/10 DOTA
[01:09]模型精美,特效酷炫!TI9不朽宝藏Ⅰ鉴赏
2019/05/10 DOTA
python实现DNS正向查询、反向查询的例子
2014/04/25 Python
Python处理json字符串转化为字典的简单实现
2016/07/07 Python
python使用PIL和matplotlib获取图片像素点并合并解析
2019/09/10 Python
Python list运算操作代码实例解析
2020/01/20 Python
python中数据库like模糊查询方式
2020/03/02 Python
浅谈Python描述数据结构之KMP篇
2020/09/06 Python
Python项目实战之使用Django框架实现支付宝付款功能
2021/02/23 Python
浅谈html5 video 移动端填坑记
2018/01/15 HTML / CSS
在职研究生自我鉴定
2013/10/16 职场文书
幼儿园老师辞职信
2014/01/20 职场文书
《彭德怀和他的大黑骡子》教学反思
2014/04/12 职场文书
品牌服务方案
2014/06/03 职场文书
勇敢的心观后感
2015/06/09 职场文书
详解vue中v-for的key唯一性
2021/05/15 Vue.js
MySQL系列之三 基础篇
2021/07/02 MySQL
win11怎么用快捷键锁屏? windows11锁屏的几种方法
2021/11/21 数码科技