者:HelloGitHub-追梦人物
博客文章的模型有一个 excerpt 字段,这个字段用于存储文章的摘要。目前为止,还只能在 django admin 后台手动为文章输入摘要。每次手动输入摘要比较麻烦,对有些文章来说,只要摘取正文的前 N 个字符作为摘要,以便提供文章预览就可以了。因此我们来实现如果文章没有输入摘要,则自动摘取正文的前 N 个字符作为摘要,这有两种实现方法。
覆写 save 方法
第一种方法是通过覆写模型的 save 方法,从正文字段摘取前 N 个字符保存到摘要字段。在 创作后台开启,请开始你的表演[1] 中我们提到过 save 方法中执行的是保存模型实例数据到数据库的逻辑,因此通过覆写 save 方法,在保存数据库前做一些事情,比如填充某个缺失字段的值。
回顾一下博客文章模型代码:
blog/models.py ? class Post(models.Model): # 其它字段... body=models.TextField() excerpt=models.CharField(max_length=200, blank=True) def save(self, *args, **kwargs): self.modified_time=timezone.now() super().save(*args, **kwargs)
其中 body 字段存储的是正文,excerpt 字段用于存储摘要。通过覆写模型的 save 方法,在数据被保存到数据库前,先从 body 字段摘取 N 个字符保存到 excerpt 字段中,从而实现自动摘要的目的。具体代码如下:
blog/models.py ? import markdown from django.utils.html import strip_tags ? class Post(models.Model): # 其它字段... body=models.TextField() excerpt=models.CharField(max_length=200, blank=True) # 其它方法... def save(self, *args, **kwargs): self.modified_time=timezone.now() ? # 首先实例化一个 Markdown 类,用于渲染 body 的文本。 # 由于摘要并不需要生成文章目录,所以去掉了目录拓展。 md=markdown.Markdown(extensions=[ 'markdown.extensions.extra', 'markdown.extensions.codehilite', ]) ? # 先将 Markdown 文本渲染成 HTML 文本 # strip_tags 去掉 HTML 文本的全部 HTML 标签 # 从文本摘取前 54 个字符赋给 excerpt self.excerpt=strip_tags(md.convert(self.body))[:54] ? super().save(*args, **kwargs)
这里生成摘要的方案是,先将 body 中的 Markdown 文本转为 HTML 文本,去掉 HTML 文本里的 HTML 标签,然后摘取文本的前 54 个字符作为摘要。去掉 HTML 标签的目的是防止前 54 个字符中存在块级 HTML 标签而使得摘要格式比较难看。可以看到很多网站都采用这样一种生成摘要的方式。
然后在模板中适当的地方使用模板标签引用 {{ post.excerpt }} 显示摘要的值即可:
templates/blog/index.html ? <article class="post post-{{ post.pk }}"> ... <div class="entry-content clearfix"> <p>{{ post.excerpt }}...</p> <div class="read-more cl-effect-14"> <a href="{{ post.get_absolute_url }}" class="more-link">继续阅读 <span class="meta-nav">→</span></a> </div> </div> </article>
新添加一篇文章(这样才能触发 save 方法,此前添加的文章不会自动生成摘要,要手动保存一下触发 save 方法),可以看到摘要效果了。
使用 truncatechars 模板过滤器
第二种方法是使用 truncatechars 模板过滤器(Filter)。在 django 的模板系统中,模板过滤器的使用语法为 {{ var | filter: arg }}。可以将模板过滤看做一个函数,它会作用于被它过滤的模板变量,从而改变模板变量的值。例如这里的 truncatechars 过滤器可以截取模板变量值的前 N 个字符显示。关于模板过滤器,我们之前使用过 safe 过滤器,可以参考 让博客支持 Markdown 语法和代码高亮[2] 这篇文章中对模板过滤器的说明。
例如摘要效果,需要显示 post.body 的前 54 的字符,那么可以在模板中使用 {{ post.body | truncatechars:54 }}。
templates/blog/index.html ? <article class="post post-{{ post.pk }}"> ... <div class="entry-content clearfix"> <p>{{ post.body|truncatechars:54 }}</p> <div class="read-more cl-effect-14"> <a href="{{ post.get_absolute_url }}" class="more-link">继续阅读 <span class="meta-nav">→</span></a> </div> </div> </article>
不过这种方法的一个缺点就是如果前 54 个字符含有块级 HTML 元素标签的话(比如一段代码块),会使摘要比较难看。所以推荐使用第一种方法。
References
[1] 创作后台开启,请开始你的表演: https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/65/
[2]让博客支持 Markdown 语法和代码高亮:
https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/67/
『讲解开源项目系列』——让对开源项目感兴趣的人不再畏惧、让开源项目的发起者不再孤单。跟着我们的文章,你会发现编程的乐趣、使用和发现参与开源项目如此简单。欢迎加入我们,让更多人爱上开源、贡献开源~
文章中的某段文字转化为链接:
混沌现象——扇形内部的光线反射现象_百度经验
https://jingyan.baidu.com/article/0eb457e5ca429103f1a90507.html
先熟悉一下这个网页!
然后在Runoob网页里面运行如下代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<p>
<a href="https://jingyan.baidu.com/article/0eb457e5ca429103f1a90507.html">扇形内部的光线反射现象</a>
是一个指向百度经验网站中的一个页面的链接。</p>
</body>
</html>
点击超链接,就会直接跳转:
在移动商城的设计中,除商品和分类查询是完全开放权限的页面外,其他涉及个人隐私的个人信息、订单查询和购物车等都必须进行权限管理。
有关用户权限管理的功能,在这里根据移动设备的特点,使用了本地存储的方式,提供了用户登录设计和账号切换设计。
注意,为了节省篇幅,这里的用户信息只是一个演示数据,并没有跟实际用户服务进行绑定。
用户登录设计
在用户登录设计中,为了保证用户身份的真实性,可以由用户提供手机号,并通过短信接收到的验证码进行验证。
用户登录设计主要在视图 verify.html 中实现,这是一个H5单页设计,主要使用本地存储来保存用户的登录状态,代码如下所示:
<! DOCTYPE html>
<html xmlns:th- "http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<meta content="yes" name="apple-mobile-web-app-capable"/>
<meta content="black" name="apple-mobile-web-app-status-bar-style"/><meta name="format-detection" content="telephone=no"/>
<script th:src="@{/scripts/viewscale.js}"></script>
<script th:src="@{/scripts/jquery-1.10.2.min.js)"></script><title>用户登录</title>
<link th:href="@(/styles/microApply.css}" rel="stylesheet"
type="text/css"/>
<style type="text/css">
article, aside,dialog, footer, header,section, footer, nav,figure, menu {display:block}
</style></head>
<body>
<div class="content">
<div class="iphone">
<pclass="tips">手机号码</p>
<input type="text" placeholder="" value="13500001111"/></div>
<div class=" verifyBox">
<p class="tips">验证码</p>
<input type="text" placeholder="" value="123456"/></div>
<div class="verifyErro" style="display:none; ">
<span></span>
<pclass="tips">验证码错误</p><p class="countdown"></p>
</div>
<div class="sure"><input class="verifyBtn" type="submit" value="确定
"/></div>
</div>
<div class="copy">关于我们</div></body>
<script>
/*<![CDATA[*/
$(function(){
$('.verifyBtn') ﹒click(function(){//验证失败
//$(".verifyErro") .show();
var storage=window.localStorage;
var customer=new Object(o;
new Object ();
customer-phone="13500001111";customer.userid-"11111235";
var str=JSON .stringify(customer);storage.setItem ("user", str);
window. location.href="./index";});
$( '.verifyBoX').find ( 'input').click(function(){
$( " .verifyErro").hide();
});
H);/*]]>*/</script>
</html>
这里为了简化设计,我们把手机号和验证码都写进了程序中。
当用户通过验证后,将在本地存储中登记用户的手机号和用户ID,让用户处于登录状态中直到用户切换账号时,才退出当前登录状态。所以在测试时,直接单击“确定”按钮后,即可保存用户的登录状态。
用户登录设计完成之后,显示效果如图9-4所示。
用户登录之后,当需要进行身份确认时,就可以通过本地存储取得用户信息,执行相关的操作流程。
账号切换设计
如果用户没有清除移动设备的缓存,则本地存储将长期存在。为了让用户能够退出登录状态,或者切换到另一个账号进行操作,这里提供了一个切换账号设计。
切换账号视图设计“switch.html”是一个H5单页,实现代码如下所示:
<!DOCTYPE html>
<html xmlns: th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<meta content="yes" name="apple-mobile-web-app-capable"/>
<meta content="black" name="apple-mobile-web-app-status-bar-style<meta name="format-detection" content="telephone=no"/>
<script th:src="@{/scripts/viewscale.js}"></script>
<script th:src="@(/scripts/jquery-1.10.2.min.js}"></script><title>切换账号</title>
<link th:href="@{/styles/microApply.css}" rel="stylesheet"
type="text/css"/>
<style type="text/css">
article, aside,dialog,footer,header,section,footer,nav,figure, menu{display:block}
</style></head>
<body>
<div class="content">
<div class="iphone">
<pclass="swit">切换登录账号</p></div>
<div class="sure"><input class="switchBtn" type="submit" value="确
定
"/></div>
</div>
<div class="copy">关于我们</div></body>
<script>
/*<![CDATA[*/
$(function(){
$(' .switchBtn') .click (function(){
var storage=window. 1ocalStorage;
storage. removeItem ("user");
window. location.href="./index";});
});/*]]>*/</script>
</html>
从上面的代码可以看出,只要在本地存储中清除用户登录时保存的用户对象,就可以退出登录状态,然后将用户引导到订单查询的主页上,在这里会自动要求用户进行登录。
切换账号设计完成之后,显示效果如图9-5所示。
在订单查询设计中,主要是使用订单列表的方式显示每一个特定用户的订单。
首先在OrderController 控制器中,设计一个订单的主页链接,通过主页显示订单列表及其详细信息,代码如下所示:
@RestController
@RequestMapping ("/order")CSlf4j
public class OrderController {
@Autowired
private OrderRestService orderRestService;
@RequestMapping (value="/index")
public ModelAndView index(ModelMap model) throws Exception{
return new ModelAndview ( "order/index");
}
@RequestMapping(value="/list")
public Page<Map<String,Object>> findAll (0rderQo orderQo){
String json=orderRestService.findPage(orderQo);
Pageable pageable=PageRequest.of (orderQo.getPage(),orderQo.getSize(),
null);
List<0rderQo> list=new Gson() .fromJson(json,new
TypeToken<List<orderQo>>(){}.getType());
for(0rderQo order :list){
order.setStatusStr (StatusEnum.valueOf (order.getStatus ()).getName ());
}
String count=orderRestService.getCount ();
return new Page Impl(list, pageable,new Long ( count));
}
}
在上面的代码中,通过“/index”链接,返回订单查询的主页视图设计“index.html”
订单查询的主页视图设计与商品查询设计相似,都是通过屏幕的滑动下拉实现自动分页功能的,不同之处在于权限管理和信息显示处理设计。
为了保证每个用户只能查询自己的订单,在订单列表查询视图设计中会检查用户的登录状态。如果用户未登录,则提示用户登录,实现代码如下所示:
<script>
var storage=window. localStorage;
var user=storage.getItem("user");
var userid;var orderno;
if(user){
var a=JSON .parse(user);userid=a.userid;
Jelse{
window.location. href="./verify";
</script>
另外,在订单的信息处理中,使用了如下所示的设计:
<script>
/*<! [CDATA[*/
//刷新页面数据
function listData(pageNum, pageSize, callback){
var $dataUl=$(" .dataUl");$.ajax({
url: "./list",data:{
orderNo:orderno,userid:userid,page: pageNum,size:pageSize},
type: "GET",
dataType: "json",
success: function (data){
$('#totalPage ').val(data.totalPages);var html='';
$.each(data.content, function (i,v){
html +='<li>';
html +='<div class="orderInfList">';
html +='<div class="orderInfTxtclearPb">';
html+='<p>订?单?号:'+v.orderNo +'('+
v.statusStr +')</p>';
html +='<p>订单金额:¥'+v.amount.toFixed(2)+'</p>';html +='<p>下单时间:'+new Date(v.created).format
("yyyy-MM-dd HH:mm:ss")+ '</p>';
html +='</div>';
html +='<div class="orderPicList">';$.each(v.orderDetails, function (j,w){
html +='<img src="' +w.photo +'"/>';});
html +='</div>';html +='</div>';html +='</li>';
});
$dataUl .append (html);callback &&callback();
});
/*]]>*/
</script>
在上面的代码中使用了四个参数进行查询,即订单号 ( orderNo)、用户编号(userid)页码(page)和每页行数(size)。其中,订单号可以由用户输入,如果用户未提供订单号,则显示所有的订单。同时,针对订单金额的小数位数也进行了设定,以避免因为浮点数的原因出现很长的小数位,影响用户体验。订单明细数据则以商品图片的形式进行显示。
订单查询设计完成之后,显示效果如图9-6所示。
当移动商城设计完成之后,可以进行一个集成测试。在这个集成测试中,可以按下列顺序启动相关应用:
启动完成之后,可以通过注册中心查看所有应用的启动情况,如图9-7所示。
下面在浏览器中输入如下链接,打开移动商城进行测试:
http://localhost: 7090
如果打开成功,则可以将浏览器调整成手机或iPad的显示界面,模拟移动设备操作,如图9-8所示。
当在手机或者iPad 上进行测试时,请确认手机或iPad与电脑处于同一个局域网中,然后将上面的“localhost”改成电脑上的P地址进行访问。在 iPad上打开的移动商城首页如图9-9所示。这里用到的P地址是“192.168.0.104”。
在手机上测试时,可以参考前面在开发讲解中提供的各种场景的效果图。
当我们在测试中进行了一些操作之后,会生成一些数据,这时可以通过如下链接打开PC端的订单管理后台,查看订单列表,进行订单管理操作。
http://localhost:8095
打开链接之后,可以看到如图9-10所示页面。这是第8章的工作成果,即订单管理后台主页的操作界面。在这个界面中,可以进行一些订单管理操作。
本章使用前面章节设计的各种接口服务,设计并开发了一个移动端的商城。在这个设计中,演示了微服务接口的调用方法,同时,针对移动设备进行了一些H5的单页设计实践。在整个开发过程中,读者可以更加深刻地体会到微服务之间的接口调用是非常方便的。而使用SpringCloud工具套件进行移动端应用的开发,同样是轻量级且令人感到愉快的。
*请认真填写需求信息,我们会在24小时内与您取得联系。