前言
本人在一学期时间实现了Python+Flask+MysqL的web建设,页面具有简单的登录注册发布文章搜索文章等功能。
这篇文章总结了我最近一段时间的学习成果:使用Flask框架搭建一个web service,并在其中加上一些简单的css,js,html等。在本文中以实际的用户模块为例。之所以写这篇文章是因为想要总结自己一学期的学习成果并且与大家分享。由于是新手如有错漏请见谅。
一、使用工具
python3.5
实现此页面所有的static文件、templates文件与py文件
二、完成基本的页面设计
1.导航条(父模板)
这里的界面都为继承父模板后的界面.后面将会有相关介绍
2.登录界面
3.注册界面
3.发布问答界面
三、Flask & 概览
from flask import Flask app = Flask(__name__) @app.route(‘api/test‘) def hello(): return ‘Hello World!‘ if __name__ == ‘__main__‘: app.run()
四、加载静态文件,父模板与其他界面的继承
1.登陆用url_for加载静态文件
1.<script src="{{ url_for(‘static‘,filename=‘js/login.js‘) }}"></script>
2.flask 从static文件夹开始寻找
3.可用于加载css, js, image文件
2.继承和扩展
1.把一些公共的代码放在父模板中,避免每个模板写同样的内容。base.html
2.子模板继承父模板
1.{% extends ‘base.html’ %}
3.父模板提前定义好子模板可以实现一些自己需求的位置及名称。block
1.<title>{% block title %}{% endblock %}-MIS问答平台</title>
2.{% block head %}{% endblock %}
3.{% block main %}{% endblock %}
4.子模板中写代码实现自己的需求。block
1.{% block title %}登录{% endblock %}
以下有如下13个步骤:
五、数据库连接池
数据库工具:mysql
1.安装与配置python3.6+flask+mysql数据库
1.下载安装MySQL数据库
2.下载安装MySQL-python 中间件(pip install flask-sqlalchemy (Python的ORM框架SQLAlchemy))
2.mysql创建数据库
3.数据库配置信息config.py
SQLALCHEMY_DATABASE_URI = ‘mysql+pymysql://root:@localhost:3306/zhuce?charset=utf8‘ SQLALCHEMY_TRACK_MODIFICATIONS = False
4.建立mysql和app的连接
from flask import Flask from flask_sqlalchemy import SQLAlchemy import config app = Flask(__name__) app.config.from_object(config) db=SQLAlchemy(app) class User(db.Model): __tablename__ = ‘User‘ id = db.Column(db.Integer,primary_key=True,autoincrement=True) username = db.Column(db.String(20),nullable=False) password = db.Column(db.String(20),nullable=False) nickname = db.Column(db.String(20)) db.create_all() @app.route(‘/‘) def hello_world(): return ‘Hello World!‘ if __name__ == ‘__main__‘: app.run()
5.创建用户模型
六、通过用户模型,对数据库进行增删改查
from flask import Flask from flask_sqlalchemy import SQLAlchemy import config app = Flask(__name__) app.config.from_object(config) db=SQLAlchemy(app) class User(db.Model): __tablename__ = ‘User‘ id = db.Column(db.Integer,primary_key=True,autoincrement=True) username = db.Column(db.String(20),nullable=False) password = db.Column(db.String(20),nullable=False) nickname = db.Column(db.String(20)) #db.create_all() #增 user=User(username=‘liu1234‘,password=‘123456789‘) db.session.add(user) db.session.commit() #删 user=User.query.filter(User.username==‘two1234‘).first() user.password=‘12345678910‘ db.session.delete(user) db.session.commit() #改 user=User.query.filter(User.username==‘two1234‘).first() user.username=‘three1234‘ db.session.add(user) db.session.commit() #查 user=User.query.filter(User.username==‘two1234‘).first() print(user.id,user.username,user.password) @app.route(‘/‘) def hello_world(): return ‘Hello World!‘ if __name__ == ‘__main__‘: app.run()
七、完成注册功能
- js文件: onclick函数return True时才提交表单,return False时不提交表单。
- html文件:
- <form>中设置 action和method="post"
- <input> 中设置 name
- 主py文件中:
- from flask import request, redirect, url_for
- @app.route(‘/regist/‘, methods=[‘GET‘, ‘POST’])
@app.route(‘/sign_up/‘,methods=[‘GET‘,‘POST‘]) def sign_up(): if request.method == ‘GET‘: return render_template(‘zhuce.html‘) else: username = request.form.get(‘username‘) password = request.form.get(‘password‘) user = User.query.filter(User.username == username).first() if user: return ‘username existed.‘ else: user1 = User(username=username, password=password) db.session.add(user1) db.session.commit() return redirect(url_for(‘sign_in‘))
八、完成登录功能
1.js:设置return
function myLogin() { var oUname=document.getElementById("uname"); var oPass=document.getElementById("upass"); var oError=document.getElementById("error_box"); oError.innerHTML="<br>"; //username if(oUname.value.length<6||oUname.value.length>20){ oError.innerHTML="username must be 6-20."; return false; }else if((oUname.value.charCodeAt(0)>=48)&&(oUname.value.charCodeAt(0)<=57)){ oError.innerHTML="firt name can‘t numbuter."; return false; }else for(var i=0;i<oUname.value.length;i++){ if((oUname.value.charCodeAt(i)<48||oUname.value.charCodeAt(i)>57)&&(oUname.value.charCodeAt(i)<97||oUname.value.charCodeAt(i)>122)){ oError.innerHTML="only number and letter"; return false; } } if(oPass.value.length<6||oPass.value.length>20){ oError.innerHTML="password must be 6-20."; return false; } return true; // window.alert("Login Successful") }
2.html:设置
1.form
2.input
3.onclick="return fnLogin()"
{% extends‘base.html‘ %} {% block title %} Login {% endblock %} {% block head %} <script src="{{ url_for(‘static‘,filename=‘js/denglu1.js‘) }}"></script> <link rel="stylesheet" href="{{ url_for(‘static‘,filename=‘css/denglu1.css‘)}}"> {% endblock %} {% block main %} <body> <div class="login"> <h1>Login</h1> <form class="form" method="post" action="{{ url_for(‘sign_in‘) }}"> <p class="field"> <input id="uname" type="text" name="username" placeholder="Username" required/> <i class="fa fa-user"><img src="../static/images/daohang1.jpg" width="44px" height="auto"></i> </p> <p class="field"> <input id="upass" type="password" name="password" placeholder="Password" required/> <i class="fa fa-lock"><img src="../static/images/daohang1.jpg" width="44px" height="auto"></i> </p> <button class="button" onclick="return myLogin()">Login</button> <div id="error_box"><br></div> </form> </div> </body> {% endblock %} </html>
3.py:
[email protected]设置methods
2.GET
3.POST
1.读取表单数据
2.查询数据库
1.用户名密码对:
1.记住用户名
2.跳转到首页
1.用户名密码不对:
1.提示相应错误。
from flask import Flask,render_template,request,redirect,url_for,session from flask_sqlalchemy import SQLAlchemy import config app = Flask(__name__) app.config.from_object(config) db=SQLAlchemy(app) class User(db.Model): __tablename__ = ‘User‘ id = db.Column(db.Integer,primary_key=True,autoincrement=True) username = db.Column(db.String(20),nullable=False) password = db.Column(db.String(20),nullable=False) #db.create_all() @app.route(‘/‘) def home(): return render_template(‘shouye.html‘) @app.route(‘/sign_in/‘,methods=[‘GET‘,‘POST‘]) def sign_in(): if request.method == ‘GET‘: return render_template(‘denglu1.html‘) else: username = request.form.get(‘username‘) password = request.form.get(‘password‘) user = User.query.filter(User.username == username).first() if user: if user.password == password: session[‘user‘]=username session.permanent = True return redirect(url_for(‘home‘)) else: return ‘password error‘ else: return ‘username is not existed.‘ @app.route(‘/sign_up/‘,methods=[‘GET‘,‘POST‘]) def sign_up(): if request.method == ‘GET‘: return render_template(‘zhuce.html‘) else: username = request.form.get(‘username‘) password = request.form.get(‘password‘) user = User.query.filter(User.username == username).first() if user: return ‘username existed.‘ else: user1 = User(username=username, password=password) db.session.add(user1) db.session.commit() return redirect(url_for(‘sign_in‘)) @app.route(‘/question/‘) def question(): return render_template(‘question.html‘) if __name__ == ‘__main__‘: app.run(debug=True)
3.session:
1.从`flask`中导入`session`
2.设置`SECRET_KEY`
3.操作字典一样操作`session`:增加用户名`session[‘username‘]=`username
import os SECRET_KEY = os.urandom(24) SQLALCHEMY_DATABASE_URI = ‘mysql+pymysql://root:@localhost:3306/zhuce?charset=utf8‘ SQLALCHEMY_TRACK_MODIFICATIONS = False
九、登录后更新导航
- 用上下文处理器app_context_processor定义函数
- 获取session中保存的值
- 返回字典
app = Flask(__name__) app.config.from_object(config) db=SQLAlchemy(app)
@app.context_processor def mycontext(): username=session.get(‘user‘) if username: return {‘username‘:username} else: return {}
2.在父模板中更新导航,插入登录状态判断代码。
-
- 注意用{% ... %}表示指令。
- {{ }}表示变量
<div> {% if username %} <a href="#" style="margin-right: -10px;color:deepskyblue">{{ username }}</a> <a href="{{ url_for(‘logout‘) }}" >注销</a> {% else %} <a href="{{ url_for(‘sign_in‘) }}" style="margin-right: -10px">登录</a> <a href="{{ url_for(‘sign_up‘) }}" >注册</a> {% endif %} </div>
3.完成注销功能。
-
- 清除session
- 跳转
@app.route(‘/logout/‘) def logout(): session.clear() return redirect(url_for(‘shouye‘))
十、完成发布功能(与注册登录功能相似)
1.编写要求登录的装饰器
from functools import wraps
def loginFirst(func): #参数是函数
@wraps(func)
def wrapper(*args, ** kwargs): #定义个函数将其返回
#要求登录
return func(*args, ** kwargs)
return wrapper #返回一个函数
2.应用装饰器,要求在发布前进行登录,登录后可发布。
@app.route(‘/question/‘,methods=[‘GET‘,‘POST‘])@loginFirstdef question():
3.建立发布内容的对象关系映射。
class Question(db.Model):
4.完成发布函数。
保存到数据库。
重定向到首页
十一、在首页显示问答列表(用户可自行选择显示内容,本文以显示问答列表为例)
1. 在首页添加显示问答的列表,并定义好相应的样式。
无序列表
<ul >
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ul>
2. 用字典向index.html传递参数。
- 首页列表显示全部问答:
- 将数据库查询结果传递到前端页面 Question.query.all()
- 前端页面循环显示整个列表。
- 问答排序
- 完成问答详情页布局:
- 包含问答的全部信息
- 评论区
- 以往评论列表显示区。
- 在首页点击问答标题,链接到相应详情页。
十二、从首页问答列表标题链接到详情页
- 主PY文件写视图函数,带id参数。
@app.route(‘/detail/<question_id>‘)
def detail(question_id):
quest =
return render_template(‘detail.html‘, ques = quest) - 首页标题的标签做带参数的链接。
{{ url_for(‘detail‘,question_id = foo.id) }} - 在详情页将数据的显示在恰当的位置。
{{ ques.title}}
{{ ques.id }}{{ ques.creat_time }}{{ ques.author.username }}
{{ ques.detail }} - 建立评论的对象关系映射:
class Comment(db.Model):
__tablename__=‘comment‘
十三、实现发布评论
1.定义评论的视图函数
@app.route(‘/comment/‘,methods=[‘POST‘])
def comment():
读取前端页面数据,保存到数据库中
@app.route(‘/comment/‘,methods=[‘GET‘,‘POST‘]) @loginFirst def comment(): if request.method == ‘GET‘: return render_template(‘question_detail.html‘) else: detail = request.form.get(‘detail‘) author_id =User.query.filter(User.username == session.get(‘user‘)).first().id question_id=request.form.get(‘question_id‘) comments = Comment(detail=detail,author_id=author_id,question_id=question_id) db.session.add(comments) db.session.commit() return redirect(url_for(‘question_detail‘,question_id=question_id))
2.用<input type="hidden" 方法获取前端的"question_id"
3.显示评论次数
4.要求评论前登录
{% extends‘base.html‘ %} {% block title %} Home {% endblock %} {% block head %} <link rel="stylesheet" href="{{ url_for(‘static‘,filename=‘css/question_detail.css‘)}}" type="text/css"> {% endblock %} {% block main %} <body> <div class="detail"> <div class="detail_left"> <h2>{{ question.title }}</h2> <a class="username">{{ question.author.username }}</a> <span class="badge">{{ question.creatTime }}</span> <hr> <a style="white-space: pre-wrap" >{{ question.detail }}</a> <hr> <form action="{{ url_for(‘comment‘) }}" method="post"> <textarea name=‘detail‘ class="form-control" rows="6" id="questionDetail"></textarea> <br> <button class="btn-default">发布</button> <input name="question_id" value="{{ question.id }}" type="hidden" /> </form> <p class="comment_num"><img class="heart" src="../static/images/heart.png">[{{ question.comments|length }}]</p> <ul class="comment"> {% for foo in question.comments %} <span class="icon" aria-hidden="true"><img src="../static/images/icon.jpg"></span> <a href="#" class="name">{{ foo.author.username }}</a> <span class="badge2">{{ foo.creatTime }}</span> <br> <p class="neirong">{{ foo.detail }}</p> {% endfor %} </ul> </div> </div> </body> {% endblock %}
十四、完成个人中心相关信息
- 个人中心—视图函数带标签页面参数tag
- @app.route(‘/usercenter/<user_id>/<tag>‘)
def usercenter(user_id, tag):
if tag == ‘1‘:
return render_template(‘usercenter1.html‘, **context)
@app.route(‘/self/<user_id>/<tag>‘) @loginFirst def self(user_id,tag): user=User.query.filter(User.id==user_id).first() context={ ‘user‘:user } if tag==‘1‘: return render_template(‘allquestion.html‘,**context) elif tag==‘2‘: return render_template(‘allcomments.html‘,**context) else: return render_template(‘information.html‘,**context)
3.个人中心—导航标签链接增加tag参数
<li role=“presentation”><a href=“{{ url_for(‘usercenter’,user_id = user.id,tag = ‘1’) }}">全部问答</a></li>
<li role="presentation"><a href="{{ url_for(‘self‘,user_id=user.id,tag=‘1‘) }}">全部问答</a> </li> <li role="presentation"><a href="{{ url_for(‘self‘,user_id=user.id,tag=‘2‘) }}">全部评论</a> </li> <li role="presentation"><a href="{{ url_for(‘self‘,user_id=user.id,tag=‘3‘) }}">个人信息</a> </li>
4.个人中心—有链接到个人中心页面的url增加tag参数
<a href="{{ url_for(‘usercenter‘,user_id = session.get(‘userid‘), tag=1) }}">{{ session.get(‘user‘) }}</a>
十五、实现导航条中的搜索功能
1.修改base.html 中搜索输入框所在的
- <form action="{{ url_for(‘search‘) }}" method="get">
- <input name="q" type="text" placeholder="请输入关键字">
2.完成视图函数search()
- 获取搜索关键字
q = request.args.get(‘q’) - 条件查询
qu = Question.query.filter(Question.title.contains(q)).order_by(‘-creat_time’) - 加载查询结果:
return render_template(‘index.html‘, question=qu)
@app.route(‘/search/‘) def search(): qu=request.args.get(‘q‘) ques=Question.query.filter( or_( Question.title.contains(qu), Question.detail.contains(qu) ) ).order_by(‘-creatTime‘) return render_template(‘shouye.html‘,question=ques)
十六、密码保护
对于密码保护,用户在注册页面输入的密码存进数据库时已经变成编译后的密码,外部从数据库看到的密码则是编译后的。
1.更新User对象,设置对内的_password
class User(db.Model):
__tablename__ = ‘user‘
_password = db.Column(db.String(200), nullable=False) #内部使用
class User(db.Model): __tablename__ = ‘User‘ id = db.Column(db.Integer,primary_key=True,autoincrement=True) username = db.Column(db.String(20),nullable=False) _password = db.Column(db.String(200),nullable=False) #内部使用
2.编写对外的password
from werkzeug.security import generate_password_hash, check_password_hash
@property
def password(self): #外部使用,取值
return self._password
@password.setter
def password(self, row_password):#外部使用,赋值
self._password = generate_password_hash(row_password)
@property def password(selfs): return self._password @password.setter def password(self,row_password): self._password=generate_password_hash(row_password)
3.密码验证:
def check_password(self, row_password): #密码验证
result = check_password_hash(self._password,row_password)
return result
def check_password(self,row_password): result=check_password_hash(self._password,row_password) return result
4.登录验证:
password1 = request.form.get(‘password‘)
user = User.query.filter(User.username == username).first()
if user:
if user.check_password(password1):
@app.route(‘/sign_in/‘,methods=[‘GET‘,‘POST‘]) def sign_in(): if request.method == ‘GET‘: return render_template(‘denglu1.html‘) else: username = request.form.get(‘username‘) password = request.form.get(‘password‘) user = User.query.filter(User.username == username).first() if user: if user.check_password(password): session[‘user‘]=username session[‘userid‘] = user.id session.permanent = True return redirect(url_for(‘home‘)) else: return ‘password error‘ else: return ‘username is not existed.‘
本文介绍的功能如上所示,用户可以根据自己的需求添加功能。再接再厉