个人学期总结
1.个人学期总结
2.总结Python+Flask+MysqL的web建设技术过程,标准如下:
- 即是对自己所学知识的梳理
- 也可作为初学入门者的简单教程
- 也可作为自己以后复习的向导
- 也是一种向外展示能力的途径
经过一个学期的对Python的学习,在老师的带领下完成了一个网站的基本网页的制作,包括:登录、注册、首页、问答页、问答详情页、用户中心等基本页面。现对这一学期学习的内容进行简单的梳理:
1、首先是使用标签制作基本网页(包括头部导航条、内容、页脚)
①头部导航条
<nav class="nav1"> </nav>
②内容
<div>
内容
</div>
③页脚
2、对页面进行CSS样式设计
CSS盒子模型的应用,
css样式设计的方法有三种:(1)新建相应CSS文件,并link到html文件中;(2)图片文字用div等元素布局形成HTML文件;(3)在HTML中的head标签使用style标签块
3、JS前段验证
<script></script>的三种用法:(1)放在<body>中;(2)放在<head>中;(3)放在外部JS文件中
登录界面实现:HTML+CSS+JS
4、基础页面制作完成之后,进行功能的实现:开始flask项目
使用装饰器,设置路径与函数之间的关系,使用Flask中render_template,用不同的路径,返回首页、登录员、注册页,用视图函数反转得到URL,{{url_for(‘login’)}},完成导航条里的链接。
@app.route('路径') def 函数名(): return render_template('返回页面')
<nav>#导航条里的链接 <a href="{{ url_for('index') }}">首页</a> <a href="{{ url_for('login') }}">login</a> <a href="{{ url_for('register')}}">register</a> </nav>
5、夜间模式的开启与关闭,放置点击的按钮或图片,定义开关切换函数,onclick函数调用。制作网站网页共有元素的父模板html,包括顶部导航,中间区块划分,底部导航,底部说明等,汇总相关的样式形成独立的css文件,汇总相关的js代码形成独立的js文件,形成完整的base.html+css+js
function mySwitch() { var myele = document.getElementById("on_off"); if (myele.src.match("bulbon")){ myele.src="http://www.runoob.com/images/pic_bulboff.gif"; document.getElementById("myBody").style.background="black"; document.getElementById("myBody").style.color="white"; } else { myele.src="http://www.runoob.com/images/pic_bulbon.gif"; document.getElementById("myBody").style.background="white"; document.getElementById("myBody").style.color="black"; } }
6、用url_for加载静态文件
<script src="{{ url_for('static',filename='js/login.js') }}"></script>
以及子模板继承父模板
{% extends 'base.html’ %}
父模板提前定义好子模板可以实现一些自己需求的位置及名称。block
<title>{% block title %}{% endblock %}标题</title>
{% block head %}{% endblock %}
{% block main %}{% endblock %}
子模板中写代码实现自己的需求。block
{% block title %}登录{% endblock %}
7、连接MySQL数据库,创建用户模型
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:@127.0.0.1:3306/mis_db?charset=utf8' SQLALCHEMY_TRACK_MODIFICATIONS = False
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), nullable=False) db.create_all() @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run()
8、完成注册功能
js文件: onclick函数return True时才提交表单,return False时不提交表单。
function fnRegister() { var oUname = document.getElementById("uname"); var oNumber = document.getElementById("number"); var oUpass = document.getElementById("upass"); var oError = document.getElementById("error_box"); var isError = true; oError.innerHTML = "<br>"; //uname if (oUname.value.length == 0) { oError.innerHTML = "用户名不能为空!"; isError = false; return isError; } if (oUname.value.length > 10 || oUname.value.length < 6) { oError.innerHTML = "用户名必须是6-10位!"; 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) < 97 || oUname.value.charCodeAt(i) > 122)) { oError.innerHTML = "用户名只能为数字和字母"; isError = false; return isError; } } //tel if (oNumber.value.length == 0) { oError.innerHTML = "手机号不能为空!"; isError = false; return isError; } if (oNumber.value.length != 11) { oError.innerHTML = "手机号必须是11位!"; isError = false; return isError; } else for (var j = 0; j < oNumber.value.length; j++) { if ((oNumber.value.charCodeAt(j) < 48) && (oNumber.value.charCodeAt(j)) > 57) { oError.innerHTML = "手机号只能为数字!"; isError = false; return isError; } } //upass if (oUpass.value.length == 0) { oError.innerHTML = "密码不能为空!"; isError = false; return isError; } if (oUpass.value.length > 10 || oUpass.value.length < 6) { oError.innerHTML = "用户密码6-10位!"; isError = false; return isError; } if (document.getElementById("upass").value != document.getElementById("chpass").value) { oError.innerHTML = "密码不一致"; isError = false; return isError; } //window.alert("注册成功!") return isError; }
主py文件中:判断用户名是否存在:存在则报错,不存在,存到数据库中,redirect重定向到登录页
@app.route('/register/',methods=['GET','POST']) def register(): if request.method =='GET': return render_template('register.html') else: usern = request.form.get('uname') teln = request.form.get('number') passw = request.form.get('upass') user = User.query.filter(User.username==usern).first() if user: return 'username existed' else: user1= User(username=usern,tel=teln,password=passw) db.session.add(user1) db.session.commit() return redirect(url_for('login'))
9、完成登录功能,用session记住用户名
@app.route('/login/',methods=['GET','POST']) def login(): if request.method == 'GET': return render_template('login.html') else: usern = request.form.get('uname') passw = request.form.get('upass') user = User.query.filter(User.username==usern).first() if user: if user.password == passw: session['user'] = usern; return redirect(url_for('index')) else: return 'password error!' else: return 'username is not exister!'
10、登录功能实现后,用上下文处理器app_context_processor定义函数,获取session中保存的值,返回字典,在父模板中更新导航,插入登录状态判断代码。(注意用{% ... %}表示指令,{{ }}表示变量),完成注销功能,(清除session,跳转页面)。
@app.context_processor def mycontext(): usern = session.get('user') if usern: return{'username':usern} else: return {} @app.route('/logout') def logout(): session.clear() return redirect(url_for('index'))
11、完成发布功能
def loginFirst(func): @wraps(func) def wrapper(*args,** kwargs): if session.get('user'): return func(*args,** kwargs) else: return redirect(url_for('login')) return wrapper
应用装饰器,要求在发布前进行登录,登录后可发布。建立发布内容的对象关系映射。完成发布函数,保存到数据库,重定向到首页。
@app.route('/question/',methods=['GET','POST']) @loginFirst def question(): if request.method =='GET': return render_template('question.html') else: biaoti = request.form.get('biaoti') detail = request.form.get('detail') author_id = User.query.filter(User.username==session.get('user')).first().id question = Question(title=biaoti,detail=detail,author_id=author_id) db.session.add(question) db.session.commit() return redirect(url_for('index'))
12、制作首页的显示列表
<ul class="news-list"> <li style="padding-left: 0px; padding-right: 10px; box-shadow: rgba(0, 0, 0, 0.5) 0px 0px 0px 0px;"> <span class="glyphicon glyphicon-leaf" aria-hidden="true"></span> <a href="#">username</a> <br> <a href="#">title</a> <span class="badgs">creat_time</span> <p>detail{{ questions }}</p> </li> </ul>
13、首页列表显示全部问答:将数据库查询结果传递到前端页面 Question.query.all(),前端页面循环显示整个列表,问答排序,完成问答详情页布局:包含问答的全部信息,评论区,以往评论列表显示区,在首页点击问答标题,链接到相应详情页。
@app.route('/question/',methods=['GET','POST']) @loginFirst def question(): if request.method =='GET': return render_template('question.html') else: biaoti = request.form.get('biaoti') detail = request.form.get('detail') author_id = User.query.filter(User.username==session.get('user')).first().id question = Question(title=biaoti,detail=detail,author_id=author_id) db.session.add(question) db.session.commit() return redirect(url_for('index')) @app.route('/questionDetail/') def questionDetail(): return render_template('questionDetail.html')
<ul class="news-list"> {% for foo in questions %} <li style="padding-left: 0px; padding-right: 10px; box-shadow: rgba(0, 0, 0, 0.5) 0px 0px 0px 0px;"> <span class="glyphicon glyphicon-leaf" aria-hidden="true"></span> <a href="#">{{ foo.author.username }}</a> <br> <a href="#" onclick="window.location.replace({{ url_for("questionDetail") }})">{{ foo.title }}</a> <span class="badgs">{{ foo.creat_time }}</span> <p>{{ foo.detail}}</p> </li> {% endfor %} </ul>
14、从首页问答标题跳转到问答详情页
@app.route('/questionDetail/<question_id>') def questionDetail(question_id): quest=Question.query.filter(Question.id==question_id).first() return render_template('questionDetail.html',ques=quest)
<div class="list-container"> <ul class="news-list"> {% for foo in questions %} <li style="padding-left: 0px; padding-right: 10px; box-shadow: rgba(0, 0, 0, 0.5) 0px 0px 0px 0px;"> <span class="glyphicon glyphicon-leaf" aria-hidden="true"></span> <a href="#">{{ foo.author.username }}</a> <br> <a href=" {{ url_for('questionDetail',question_id=foo.id)}}">{{ foo.title }}</a> <span class="badgs">{{ foo.creat_time }}</span> <p>{{ foo.detail}}</p> </li> {% endfor %} </ul> </div>
15、实现评论功能:
定义评论的视图函数,读取前端页面数据,保存到数据库中
@app.route('/comment/',methods=['POST']) @loginFirst def comment(): comment=request.form.get('new_comment') ques_id=request.form.get('question_id') auth_id=User.query.filter(User.username==session.get('user')).first().id comm=Comment(author_id=auth_id,question_id=ques_id,detail=comment) db.session.add(comm) db.session.commit() return redirect(url_for('questionDetail',question_id=ques_id))
<form action="{{ url_for('comment') }}" method="post"> <div class="pinglun"> <textarea name="new_comment" class="form-control" rows="3" id="new-comment" placeholder="write your comment"> </textarea> <input name="question_id" type="hidden" value="{{ ques.id }}"/> </div> <button type="submit" class="btn btn-default">发送</button> </form> <hr> <h4>评论:({{ ques.comments|length }})</h4> <hr> <ul class="news-list"> {% for foo in ques.comments %} <li style="padding-left: 0px; padding-right: 10px; box-shadow: rgba(0, 0, 0, 0.5) 0px 0px 0px 0px;"> <span class="glyphicon glyphicon-leaf" aria-hidden="true" ></span> <a href="{{ foo.author.username }}"></a> <br> <a href=" {{ url_for('questionDetail',question_id=foo.id)}}">{{ foo.title }}</a> <span class="badgs">{{ foo.creat_time }}</span> <p>{{ foo.detail}}</p> </li> {% endfor %}
16、评论列表显示,个人中心显示:
显示所有评论,所有评论排序,显示评论条数
#用户中心 @app.route('/usercenter/<user_id>/') @loginFirst def usercenter(user_id): user = User.query.filter(User.id==user_id).first() context={ 'username1':user.username, 'questions':user.question, 'comments':user.comments } return render_template('usercenter.html',**context)
{{ ques.comments|length }}
17、实现评论列表显示及排序。
完成个人中心:个人中心的页面布局(html文件及相应的样式文件),定义视图函数向前端页面传递参数,页面显示相应数据:发布的全部问答、发布的全部评论、个人信息;各个页面链接到个人中心。
实现标签页导航:利用嵌套继承,制作个人中心的三个子页面,重写userbase.html中定义的user块,分别用于显示问答、评论、个人信息。个人中心—视图函数、导航标签与HTML页面链接增加tag参数。
#用户中心 @app.route('/usercenter/<user_id>/<tag>') @loginFirst def usercenter(user_id,tag): user = User.query.filter(User.id==user_id).first() context={ 'username_id': user.id, 'username1':user.username, 'questions':user.question, 'comments':user.comments } if tag == '1': return render_template('usercenter.html',**context) elif tag == '2': return render_template('all_question.html', **context) else: return render_template('all_comment.html', **context)
18、实现搜索功能
#搜索 @app.route('/search/') def search(): qu=request.args.get('q') # ques=Question.query.filter(Question.title.contains(qu))#单一条件查询 ques = Question.query.filter( or_( Question.title.contains(qu), Question.detail.contains(qu) ) ).order_by('-creat_time') return render_template('index.html',questions=ques)
{#搜索#} <form action="{{ url_for('search') }}" method="get"> <div class="b"> <input id="search" type="text" name="q" placeholder="请输入关键字"> <button id="button1" type="submit" >查找</button></div> {#<input id="button1" type="button" name="sousuo" value="搜索"></div>#} </form>
19、密码保护
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)#内部使用 nickname = db.Column(db.String(20)) tel = db.Column(db.String(20))
@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
20、模型分离
模型分离--让代码更方便管理:新建models.py,将模型定义全部放到这个独立的文件中。
from datetime import datetime from werkzeug.security import generate_password_hash,check_password_hash from exts import db class User(db.Model): __tablename__ = 'user' # __tablename__ = 'user_test' 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)#内部使用 nickname = db.Column(db.String(20)) tel = db.Column(db.String(20)) @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) creat_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')) detail = db.Column(db.Text,nullable=False) creat_time = db.Column(db.DateTime,default=datetime.now) question = db.relationship('Question',backref=db.backref('comments')) author = db.relationship('User',backref=db.backref('comments'))
新建exts.py,将db = SQLAlchemy()的定义放到这个独立的文件中。
from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy()
models.py和主py文件,都从exts.py中导入db。
在主py文件中,对db进行始化,db.init_app(app)。
db.init_app(app)
21、数据迁移
新建manage.py
from flask_script import Manager from flask_migrate import Migrate,MigrateCommand from first import app from exts import db from models import User,Comment,Question manager=Manager(app) migrate = Migrate(app,db) manager.add_command('aa',MigrateCommand) if __name__ == '__main__': manager.run()#启动migrate
新建decorate.py
from functools import wraps from flask import render_template, request, redirect, url_for,session def loginFirst(func):#定义需要登录装饰器 @wraps(func) def wrapper(*args,** kwargs): if session.get('user'): return func(*args,** kwargs) else: return redirect(url_for('login')) return wrapper
以上即使本学期所学内容。