四则运算题目生成器
这篇文章详细介绍了第三阶段的网页部分(前端)框架和实现原理以及实现的功能,如果想知道需求分析部分,请移步软件工程–四则运算表达式(3)
如果对模型的详细结构设计感兴趣,也请移步上述链接
如果对后端的具体编写过程感兴趣,请移步软件工程–四则运算(4)
网页部分
概览
网页在网站开发中承担着给客户端提供UI界面,接受客户端输入,给客户端提供功能的内容,一个设计合理的网页可以提升网站的可用性和后端的处理效率,那么我们这次所使用的网页的框架有什么,又分别实现了什么功能呢
首先我们使用了bootstrap这个现在在前端开发中常用的CSS/HTML框架,这个框架是由Twitter推出的,具体下载可以看Bootstrap中文网下载对应的版本,我们所使用的bootstrap版本是3.37,也使用了一个JavaScript库-JQuery3.31,通过引入了一个包和一个框架极大的提升了网页的美观程度,也提升了前端开发的效率
base.html 基石网页
前端是由一个base基页面和每一个应用里面自带的页面构成的,所有的渲染文件和框架都在static中,Django通过引入staticfiles允许我们使用各种框架结构和CSS/HTML,同样的使用{% extend base.html %}可以对基页面进行拓展,基页面是可以分块拓展,向对应的block填充
{% load staticfiles %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<title>{% block title %}{% endblock %}</title>
<link href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}" rel="stylesheet">
{% block css %}{% endblock %}
<body>
{% block navbar %}{% endblock %}
<div id="wrapper">
<div class="content-body">
{% block content %}
{% endblock content %}
{% block sidemenu %}
{% endblock %}
</div>
<div id="push"></div>
</div>
{% include 'footer.html' %}
<script src="{% static 'js/jquery-3.3.1.js' %}"></script>
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
<script src="{% static 'layer/layer.js' %}"></script>
{% block script %}{% endblock script %}
{% include 'top.html' %}
<style>
html, body {
height: 100%;
margin: 0;
}
#wrapper {
min-height: 100%;
margin-bottom: -60px;
}
#push {
height: 60px;
}
</style>
</body>
</html>
以上是base.html的内容,其中引入了navbar块,content块和sidemenu块,下方引入了两个html网页,网页是可以动态组合的,通过动态组合一个网页可以快速实现前端功能开发
link href引入了static文件中的三个框架,包括js中的jquery bootstrap-3.3.7-dist/js中的bootstrap.min.js,还引入了layer中的layer的框架
然后还使用了一个script block使引用文件更加方便,可以把javascript代码放在script块中和content中的内容分离,加快页面功能的开发进度
下方的style目的是保证页脚内容不会侵占页面,可以正常显示
页脚
页脚直接使用了footer-class,由一个container组成,代码如下
<footer class="modal-footer" id="footer">
<div class="container">
<h5 class="m-0 text-center text-white">Copyright © 猿题库,一个专注于猪脑心算的网站</h5>
</div>
</footer>
还有一个返回页面顶端的按钮,那个按钮的具体原理我到现在还不清楚
回上按钮
里面主要由一个Javascript函数构成,剩下的是绘制按钮的style库
回到顶部的函数如下:
$(function () {
$('#BackTop').click(function () {
$('html,body').animate({scrollTop: 0}, 500);
});
$(window).scroll(function () {
if ($(this).scrollTop() > 300) {
$('#BackTop').fadeIn(300);
} else {
$('#BackTop').stop().fadeOut(300);
}
}).scroll();
});
base网页的主要功能是提供一个通用的模版,并且加载必须的模版库,其他的网页都是基于base进行动态扩充的,渲染的时候会首先加载base.html然后再对当中的每一个block进行填充(如果有的话),但是这也会带来一个问题就是对于一部分网页(inclusion-tags)这样的,网页加载的时候可能需要传参数进去,但是无论如何这样设计网页都是较方便的方案
login 网页部分
login部分的网页比较简单,因为只需要承担两个功能,一个是接受用户的登录请求并提交,另一个是接受用户的注册请求并提交,登录请求方面一般而言只需要输入用户名和用户密码,注册需要输入用户名用户密码和用户邮箱,并且也需要输入验证码防止恶意注册,注册或者登录失败我们也希望返回失败信息提醒用户,所以要实现的功能还是比较清晰的
下面首先看一下两个网页的效果
登录界面:
注册界面:
能看到还有重置密码的接口
重置密码网页部分
重置密码的功能是一个单独的app,重置密码的大致内容如下:
首先用户输入用户名,然后系统首先根据用户输入的用户名查找是否存在用户,如果不存在用户返回对应的错误信息,如果用户存在继续操作,查看是否已经有用户找回密码的请求存在,如果请求存在,提供一个选择界面,用户可以选择是需要重新发送消息,还是按照已经发送的消息继续操作,如果没有用户找回密码的请求,则调用对应的SMTP服务给用户发送一封带有唯一验证码的邮件,用户得到唯一验证码之后,在相应的界面输入验证码,即可重置密码,每一个用户只有一个单独的ConfirmString类记录,所以每次用户重置密码或者选择重新发送信息需要从数据库中filter并删除已有的记录
以下是ResetPassword功能界面
输入用户名的界面
输入邮件标识符的界面
显示QQ邮箱接收到的消息界面
重置密码的界面
密码更新后的登录界面,确认密码确实已经被更新
查看是否已经有用户的密码重置记录并且让用户选择操作的界面
重置密码部分代码实现
接下来的代码就是关于如何实现的这些网页信息
用户名输入界面
{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}登录{% endblock %}
{% block css %}<link href="{% static 'css/login.css' %}" rel="stylesheet"/>{% endblock %}
{% block navbar %}
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#my-nav"
aria-expanded="false">
<span class="sr-only">切换导航条</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{% url 'SouSouSou:main' %}">猿题库</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="my-nav">
<ul class="nav navbar-nav">
<li class="active"><a href="{% url 'SouSouSou:main' %}">主页</a></li>
<li class="align-baseline"><a href="#">小猿搜题</a></li>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">快搜搜</button>
</form>
<ul class="nav navbar-nav navbar-right">
<li><a href="/login/">登录</a></li>
<li><a href="/register/">注册</a></li>
</ul>
</div>
</div>
</nav>
{% endblock %}
{% block content %}
<div class="container">
<div class="col-md-4 col-md-offset-4">
<form class='form-login' action="." method="post">
{% if message %}
<div class="alert alert-warning">{{ message }}</div>
{% endif %}
{% csrf_token %}
<h2 class="text-center">请输入您的用户名</h2>
<div class="form-group">
{{ username_form.username.label_tag }}
{{ username_form.username}}
</div>
<button type="reset" class="btn btn-default pull-left">重置</button>
<button type="submit" class="btn btn-primary pull-right">提交</button>
</form>
</div>
</div> <!-- /container -->
{% endblock %}
这里是输入用户名的界面,可以看到其中主要部分是content部分,content当中有一个表单,用于提交用户输入的用户信息并且返回给客户端,使用了csrf_token标签防止构造性攻击
操作选择界面
{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}信息{% endblock %}
{% block navbar %}
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#my-nav"
aria-expanded="false">
<span class="sr-only">切换导航条</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/generate/quest_generator">猿题库</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="my-nav">
<ul class="nav navbar-nav">
<li class="active"><a href="/generate/quest_generator">主页</a></li>
<li class="align-baseline"><a href="#">小猿搜题</a></li>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">快搜搜</button>
</form>
<ul class="nav navbar-nav navbar-right">
<li><a href="/login/">登录</a></li>
<li><a href="/register/">注册</a></li>
</ul>
</div>
</div>
</nav>
{% endblock %}
{% block css %}
<link href="{% static 'css/login.css' %}" rel="stylesheet"/>{% endblock %}
{% block content %}
<div class="container">
<div class="col-md-4 col-md-offset-4">
<form class='form-login' action="." method="post">
{% if message %}
<div class="alert alert-warning">{{ message }}</div>
{% endif %}
<h5>要重新发邮件?点击<a href='{% url 'reset_password:again' %}'>这里</a>发送邮件</h5>
<h5>还是进下一页?点击<a href='{% url 'reset_password:reset' %}'>这里</a>进下一页</h5>
</form>
</div>
</div>
{% endblock %}
这里是实现用户选择的界面,当中有两个url链接分别对应了两种不同类型的url匹配规则,这里使用了一种方便快捷的链接设置方法,通过{% url ‘app_name:url_name’ %}的方式给url命名快捷调用
通过reset_password的控制器当中的app_name设定了应用名,urlpatterns的最后一项name设定了url规则名,从此不需要记复杂的url规则,而只需要记住对应的app_name和url_name就能实现轻松的调用
controller部分
app_name = 'reset_password'
urlpatterns = [
path('confirm/', views.user_reset, name='reset'),
path('reset/', views.user_confirm, name='confirm'),
path('again/', views.post, name='again'),
path('password/', views.user_changepass, name='password')
]
然后是输入验证码的网页部分,由于这一部分和前面的功能类似,在此不再展示代码,而只展示最重要的表单提交部分
url名称调用
<form class='form-login' action='{% url 'reset_password:reset' %}' method="post">
表单的提交通过以上代码进行,可以看到表单的提交也是可以使用url命名的空间
最后就是reset_password的修改密码网页部分,同样没有什么好说的
导航栏部分
第一开始我并没有考虑到修改导航栏是多么复杂的一个操作,我把导航栏从base中分离出来,希望对于不同的界面导航栏可以承担不同的功能,但是我发现这简直是天方夜谭,换句话说每个界面我都要填充对应的navbar部分,所以我只好把所有的navbar统一起来,虽然分散在每个网页里但是实现的功能大体都是相同的
显示器
我们可以看到导航栏再不同的界面稍有差别
上面的是登录界面的导航栏
上面的是没有登录的主界面的导航栏
上面是登录了的主界面的导航栏
Navbar在bootstrap的官方文档里已经有很成熟的模版了,所以这里直接套用了navbar的模版,可以看到适配的效果还是很好的
对于不同的网页navbar实现不同的功能是很容易控制的,但是对于登录的用户,navbar可以显示用户名未登录用户显示一个指向login的链接的功能是通过一个判断条件控制的
这里就涉及到了Django如何进行前端渲染的问题,Django进行前端渲染的条件、控制、循环语句都是封装在一个形似{% %}的包里
导航栏部分代码
{% block navbar %}
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#my-nav"
aria-expanded="false">
<span class="sr-only">切换导航条</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{% url 'SouSouSou:main' %}">猿题库</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="my-nav">
<ul class="nav navbar-nav">
<li class="active"><a href="{% url 'SouSouSou:main' %}">主页</a></li>
<li class="align-baseline"><a href="#">小猿搜题</a></li>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">快搜搜</button>
</form>
<ul class="nav navbar-nav navbar-right">
{% if request.session.is_login %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
aria-haspopup="true"
aria-expanded="false">客官来看看呗<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{% url 'Quest_Generator:History' %}">查看历史记录</a></li>
<li><a href="{% url 'SouSouSou:Generator' %}">为小学生出题</a></li>
<li role="separator" class="divider"></li>
<li><a href="">联系我们</a></li>
</ul>
</li>
<li><a href="#">当前在线:{{ request.session.user_name }}</a></li>
<li><a href="/logout/">登出</a></li>
{% else %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
aria-haspopup="true"
aria-expanded="false">快来登录试试<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">给小学生出题</a></li>
<li><a href="#">给我打钱感谢我</a></li>
<li role="separator" class="divider"></li>
<li><a href="">联系我们</a></li>
</ul>
</li>
<li><a href="/login/">登录</a></li>
<li><a href="/register/">注册</a></li>
{% endif %}
</ul>
</div>
</div>
</nav>
{% endblock %}
如上就实现了一个navbar的设计操作,可以看到其中有一条判断语句为reqeust.session.is_login,这是在服务器端进行渲染并且传回给用户端的时候服务器端用于判断的,我们在用户登录的时候在session表中添加了一个字段is_login并设置为true,所以如果这是一个登录用户request.session.is_login条件为真,我们进行对应的渲染,否则进行另一种渲染,所以会出现登录用户的功能和界面与非登录用户不同的情况