Python+Flask+MysqL的web建设技术过程
1. 个人学期总结
第一次接触Python,在不了解Python的情况下,基础又差,望而生畏,一开始就给自己打了个叉。然而,通过前期turtle库的学习,我发现这门语言似乎很有趣,用其他语言要写好几十行的代码,用Python语言只要十几二十行的代码就能实现,它的turtle库很简单地用一些条件、循环、函数定义,就能画出漂亮的图案,像五角星、同心圆、太阳花、中国国旗这些图案,我才发觉这是一门多么简洁实用又功能强大的语言。有了开始的兴趣,我也就沉下那颗从前就浮躁不安的心,在杜云梅老师的带领下,慢慢地一步一步地去熟悉了解python。
在前面的学习中,我了解turtle库(海龟库),利用条件、循环、函数定义画出了五角星、同心圆、太阳花、中国国旗等;我学习了字符串的基本操作,输出代码计算后的结果,还有凯撒密码、GDP格式化输出、九九乘法表等简单操作;利用python进行英文词汇统计,组合数据类型练习,用文件形式实现完成的英文词频统计、中文词频统计;利用datetime处理日期和时间,将字符串转化成imestamp与timedelta等等。在后面,我们使用了PyCharm学会了网页的开发设计,第一次知道网页设计也有前端、后台,还要连接数据库。在这个学习的过程中,我遇到很多问题,有时候一些小小的错误都要找上老半天,但是看到自己的成果,还是会甘之如饴,有时甚至还会犯一些低级错误,例如mysql没有开启,代码拼写错误,相对应的功能代码没有放在对应的位置上,粗心导致我犯了不少错误,这告诫我们需要细心,更需要细心领会每一行代码所代表的意思,要运用在那里。同时,在这门课程的学习中,我也得到了很多同学的帮助,相互帮忙有时候能够更快找出错误在哪里,当然,自己也要更加努力。编程就像做数学题,一道数学题目可能有多种解法,但我们不必要像解数学题那样讲得很详细,只要一目了然并能清晰简洁的表达和实现功能即可,只有多去练习多去运用它,少用复制粘贴、投机取巧,我们才能在编程道路上越走越远。第一次自己设计网站,诸多不足,但是在最后看到自己的作品,虽然还不是很完善,但是心里还是忍不住有点自豪。之后我会继续努力学习Python语言,尽量吸取更多的知识,做出更加美观实用的网站。
2. 使用工具:pycharm64.exe + Python 3.6 64-bit + Navicat for MySQL
Python下载地址:https://www.python.org/downloads/
MySQL下载地址:https://www.mysql.com/downloads/
Pycharm下载地址:https://www.jetbrains.com/pycharm/download/#section=windows
(安装指南可以自己去网上查看教程,很容易就安装好了,这里就不加赘述)
3. 网页基本布局及基本功能的实现
1)加载静态文件,父模板及其他页面的继承
a. 加载静态文件(用 url_for 反转链接相关文件)
<link rel="stylesheet" type="text/css" href="{{url_for('static',filename='css/base.css') }}"> <script src="{{ url_for('static',filename='js/base.js') }}"></script>
b. 父模板的设置
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title> {% block title %} {% endblock %} </title> {% block head %} {% endblock %} </head> <body> {% block aa %}{% endblock %} </body> </html>
c. 子模板继承
{% extends 'base.html' %}
{% block title %}
首页
{% endblock %}
{% block main %}
{% endblock %}
2)注册页面
主要代码:HTML+JS
{% extends 'base.html' %} {% block title %} 欢迎开启美食之旅 {% endblock %} {% block main %} <link href="../static/css/hh.css" rel="stylesheet" type="text/css"> <script src="../static/js/zc.js"></script> <div><h1>美食祭|注册</h1></div><br><br><br> <form action="{{ url_for('zhuce') }}" method="post"> <div class="box"> <p class="input_box"> 账户: <input id="uname" type="text" placeholder="请输入您的昵称" name="username"> </p> <p class="input_box"> 密码: <input id="upass" type="password" placeholder="请设置您的密码" name="password"> </p> <p class="input_box"> 验证: <input id="upass1" type="password" placeholder="请再次输入密码" name="password"> </p> <p class="input_box"> 邮箱: <input id="youxiang" type="password" placeholder="请输入您的邮箱" name="email"> </p> <div id="error_box"><br></div> <div class="input_button"> <button type="submit" onclick="return fozhuce()">立即注册</button> </div> </div> </form> {% endblock %}
function fozhuce() { var oUname = document.getElementById("uname"); var oError = document.getElementById("error_box"); var oUpass = document.getElementById("upass"); var oUpass1 = document.getElementById("upass1"); var isError = true; oError.innerHTML = "<br>"; if (oUname.value.length < 6 || oUname.value.length > 20) { oError.innerHTML = "用户名要6-20位"; isError = false; return isError; }else if(oUname.value.charCodeAt(0)>=48 &&(oUname.value.charCodeAt(0)<=57)){ oError.innerHTML="首位不能为数字"; isError = false; return isError; }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)<58)&&(oUname.value.charCodeAt(i)>97)){ oError.innerHTML="只能为数字和字母"; isError = false; return isError; } }if (oUpass.value.length < 6 || oUpass.value.length > 20) { oError.innerHTML = "密码要6-20位"; isError = false; return isError; }else if(oUpass.value!=oUpass1.value) { oError.innerHTML = "设置密码和验证密码不一致"; isError = false; return isError; } return ture; }
3)登录页面
主要代码:HTML+JS
{% extends 'base.html' %} {% block title %} 登录 {% endblock %} {% block main %} <link rel="stylesheet" type="text/css" href="../static/css/hh.css"> <script src="../static/js/dl.js"></script> <div><h1>美食祭|登录</h1></div> <br><br><br> <body><div class="box"> <form action="{{ url_for('denglu') }}" method="post"> <p class="input_box"> 账户: <input id="uname" type="text" placeholder="敢问阁下大名"name="username"> </p> <p class="input_box"> 密码: <input id="upass" type="password" placeholder="请输入通关密码" name="password"> </p> <div id="error_box"><br></div> <div class="input_box"> <button onclick="return fnLogin()">登录</button> </div> </form> {% endblock %} </body>
function foLogin() { var oUname = document.getElementById("uname"); var oError = document.getElementById("error_box"); var oUpass = document.getElementById("upass"); var isError = true; oError.innerHTML = "<br>"; if (oUname.value.length < 6 || oUname.value.length > 12) { oError.innerHTML = "用户名要6-12位"; isError = false; return; }else if(oUname.value.charCodeAt(0)>=48 &&(oUname.value.charCodeAt(0)<=57)){ oError.innerHTML="首位不能为数字"; isError = false; return isError; }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)<58)&&(oUname.value.charCodeAt(i)>97)){ oError.innerHTML="只能为数字和字母"; isError = false; return isError; } } if (oUpass.value.length < 6 || oUpass.value.length > 12) { oError.innerHTML = "密码要6-20位"; isError = false; isError = false; return isError; } return ture; }
4)问答平台及问题详情页
{% extends 'base.html' %} {% block title %} 问答平台 {% endblock %} {% block main %} <link type="text/css" rel="stylesheet" href="{{ url_for('static',filename='css/hh.css') }}"> <form action="{{ url_for('wenda') }}" method="post"> <div class="box"> <h2 align="center">发布问答</h2> <div class="q"> <label for="questionDetail">问题</label><br> <textarea class="form-control" id="questionDetail" rows="2" style="width: 500px"name="title"></textarea></div><br> <div class="form-group"> <label for="result">详情</label><br> <textarea class="form-control" id="result" rows="4" style="width: 500px" name="detail"></textarea></div><br> </div> <div id="error_box"><br></div> <div class="input-area"> <button type="submit" onclick="foLogin()">发布问答</button> </div> </div> </form> {% endblock %}
{% extends 'base.html' %} {% block title %} 问答详情 {% endblock %} {% block main %} <link rel="stylesheet" type="text/css" href="../static/css/hh.css"> <div class="box"> <h3 href="#" >问题标题:{{ ques.title }}</h3> <small>发布者: {{ ques.author.username }}<span class="badge" style="margin-left: 75%">发布时间{{ ques.create_time }}</span></small> <hr> <p style="font-size: 18px;color: black;">问题详情:{{ ques.detail }}</p> <hr> <form action="{{ url_for('comment') }}" method="post"> <div><textarea class="form-control" id="comment" rows="3" style="margin-left: 1%" name="new_comment" placeholder="write your comment"></textarea><br></div> <input type="hidden" name="question_id" value="{{ ques.id }}"> <button type="submit" >发送</button> </form> <h4>评论:({{ ques.comments|length }})</h4> <ul class="list-unstyled"> {% for foo in ques.comments %} <li class="list-group-item"> <a href="{{ url_for('usercenter',user_id = foo.author.id,tag='1') }}">{{ foo.author.username }}</a> <span class="badge pull-right">{{ foo.create_time }}</span> <p>{{ foo.detail }}</p> <br> </li> {% endfor %} </ul> </div> {% endblock %}
5)个人中心:个人中心父模板 + 全部评论 + 全部问答 + 个人信息
个人中心父模板
{% extends 'base.html' %} {% block title %} {% endblock %} {% block main %} <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> <div class="container"> <div class="row clearfix"> <div class="col-md-2 column"></div> <ul class="nav nav-pills"> <li><a href="{{ url_for('usercenter',user_id=user.id,tag = 1) }}">全部评论</a></li> <li><a href="{{ url_for('usercenter',user_id=user.id,tag = 2) }}">全部问答</a></li> <li><a href="{{ url_for('usercenter',user_id=user.id,tag = 3) }}">个人信息</a></li> </ul> {% block hh %}{% endblock %} {% endblock %}
{% extends 'userbase.html' %} {% block title %} 全部评论 {% endblock %} {% block hh %} <div class="container"> <div class="row clearfix"> <div class="col-md-2 column"></div> <div class="col-md-8 column"> <ul class="list-unstyled" > {% for foo in comments %} <li class="list-group-item"> <img style="width: 30px" src="{{ url_for('static',filename='css/tx.jpg') }}" alt="64"> <a href="#">{{ username }}</a><br> <span class="badge pull-right">{{ foo.create_time }}</span> <a href="{{ url_for('detail',question_id=foo.id) }}">文章标题:{{ foo.question.title }}</a><br> <p style="align-content: center">评论内容:{{ foo.detail }}</p> <br> </li> {% endfor %} </ul> </div> <div class="col-md-2 column"> </div> </div> </div> {% endblock %}
{% extends 'userbase.html' %} {% block title %} 全部问答 {% endblock %} {% block hh %} <div class="container"> <div class="row clearfix"> <div class="col-md-2 column"></div> <div class="col-md-8 column"> <ul class="list-unstyled"> {% for foo in questions %} <li class="list-group-item"> <img style="width: 30px" src="{{ url_for('static',filename='css/tx.jpg') }}" alt="64"> <a href="#">{{ username }}</a><br> <a href="{{ url_for('detail',question_id=foo.id) }}">问题:{{ foo.title }}</a><br> <p style="align-content: center">{{ foo.detail }}</p> <span>评论数: ({{ foo.comments|length }})</span> <span class="badge" style="margin-left: 60%">{{ foo.create_time }}</span> <p style="margin-left: 25%"></p><br> </li> {% endfor %} </ul> </div> <div class="col-md-2 column"> </div> </div> </div>{% endblock %}
{% extends 'userbase.html' %} {% block title %} 个人信息 {% endblock %} {% block hh %} <div class="container"> <div class="row clearfix"> <div class="col-md-2 column"></div> <div class="col-md-8 column"> <ul class="list-unstyled"> <li class="list-group-item" > <img style="width: 30px" src="{{ url_for('static',filename='css/tx.jpg') }}" alt="64"> <small><a href="#">{{ username }}</a></small> <a>评论数: {{ comments|length }}</a><br> <a>文章篇数:{{ questions|length }}</a> <p style="margin-left: 25%"></p><br> </li> </ul> </div> <div class="col-md-2 column"> </div> </div> </div>{% endblock %}
6)密码保护:
a. 更新User对象,设置对内的_password
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) #内部使用
b. 编写对外的password
@property def password(selfs): #外部使用,取值 return self._password @password.setter def password(self,row_password): #外部使用,赋值 self._password=generate_password_hash(row_password)
c. 密码验证:
def check_password(self,row_password): result=check_password_hash(self._password,row_password) return result
d. 登录验证:
@app.route('/denglu/', methods=['GET', 'POST']) def denglu(): if request.method == 'GET': return render_template('dl.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['id'] = user.id session.permanent = True return redirect(url_for('shouye')) else: return '密码错误' else: return '此用户不存在'
4.Flask应用程序及数据库连接
1)Flask应用程序
所有的Flask应用程序都必须创建一个 应用程序实例 。使用web服务器网关接口协议将所有从客户端接收的请求传递给这个对象处理。这个应用程序实例就是Flask类的一个对象,通常使用下面的方式创建:
from flask import Flask app = Flask(__name__) #构造函数的name参数,__name__变量是需要传递的值
客户端(如web浏览器)发送请求给web服务,进而将它们发送给Flask应用程序实例。应用程序实例需要知道对于各个URL请求需要运行哪些代码,所以需要给Python函数建立了一个URLs映射。这些在URL和函数之间建立联系的操作被称之为路由 。
在Flask应程序中定义路由的最便捷的方式是通过显示定义在应用程序实例之上的app.route装饰器,注册被装饰的函数来作为一个路由。装饰器是Python语言的标准特性;它们可以以不同方式改变函数的行为。一个常见的模式是使用装饰器来注册函数作为一个事件处理程序。
# 使用装饰器来申明一个路由 hello_world()为视图函数 @app.route('/') def hello_world(): return render_template('base.html') @app.route('/zhuce/',methods=['GET','POST'])
项目所需导入的库
2)数据库连接:
建立mysql和app的连接,创建用户模型,在py文件中创建表格
from flask import Flask from flask_sqlalchemy import SQLAlchemy import databases app = Flask(__name__) app.config.from_object(databases)#app连接数据库 db = SQLAlchemy(app) class User(db.Model): #创建user表格 __tablename__ = 'user' id = db.Column(db.Integer, primary_key=True, autoincrement=True) username = db.Column(db.String(100), nullable=False) _password = db.Column(db.String(500), nullable=False) @property def password(self): # 外部使用 return self._password @password.setter def password(self, row_password): self._password = generate_password_hash(row_password) def check_password(self, row_password): result = check_password_hash(self._password, row_password) return result class Question(db.Model): __tablename__ = 'question' id = db.Column(db.Integer, primary_key=True, autoincrement=True) title= db.Column(db.String(100), nullable=False) detail = db.Column(db.Text, nullable=False) create_time = db.Column(db.DateTime, default=datetime.now ) author_id = db.Column(db.Integer, db.ForeignKey('user.id')) author=db.relationship('User',backref=db.backref('question')) class Comment(db.Model): __tablename__ = 'comment' id = db.Column(db.Integer, primary_key=True, autoincrement=True) author_id = db.Column(db.Integer, db.ForeignKey('user.id')) question_id = db.Column(db.Integer, db.ForeignKey('question.id')) create_time = db.Column(db.DateTime, default=datetime.now) detail = db.Column(db.Text, nullable=False) question = db.relationship('Question',backref=db.backref('comments',order_by=create_time.desc)) author = db.relationship('User',backref=db.backref('comments')) #链接到user表,通过user.comments可以获取到comment表中的内容 db.create_all() #数据库创建表格语句
数据库配置信息
import os DEBUG = True SECRET_KEY = os.urandom(24) SQLALCHEMY_DATABASE_URI='mysql+pymysql://root:@localhost:3306/wlin?charset=utf8' SQLALCHEMY_TRACK_MODIFICATTONS = False
5.运行过程中可能会遇到的错误:
1)程序没有指定Python运行
解决方案:进入设置——项目——project interpreter 选择运行环境
2)连接不上数据库,有以下三种情况:
a. 本机的数据库没有启动
解决方案:在桌面找到这台电脑——右键选择管理——在服务与运行程序中,选择服务——找到数据库启动
b. 连接数据库时没有输入数据库密码或者输入密码错误
解决方案:在root:后面添加或者修改密码
c. 没有在数据库新建对应的数据库
解决方案:在数据库新建对应的数据库