Python+Flask+MysqL的web建设技术过程

一、前言(个人学期总结)

    个人总结一下这学期对于Python+Flask+MysqL的web建设技术过程的学习体会,Flask小辣椒框架相对于其他框架而言,更加稳定,不会有莫名其妙的错误,容错性强,运行效果好,报错信息明了, Python是近几年流行起来的语言,简单易懂,很好上手,强大的库让我们可以站在巨人的肩膀上写代码,应证了人们常说的那句“人生苦短,我用python”,MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,基本功能都能准确无误的给予数据支持。在本次建站过程中,前端界面采用的是html、css、JavaScript,后端采用python作为支持。

    从一开始接触的是turtle库绘制图形,在没有接触基本语法的情况下,以为会因为基础不扎实而学起来吃力,然而,turtle库的学习,打开了我们对python的学习兴趣的闸门,这是一门多么简洁实用的语言,python的第三方库是多么的强大,逐渐的,在对库的使用中熟悉了python的语法,学起来一点也不吃力,有了html基础,加上Bootstrap框架的引用,就可以制作出一个静态页面,再有了python基础以及连接数据库的知识,就可以完成web建站了。

    本次的mysql运用的是远程数据库,便于搭建环境和数据存储,可视化软件是SQLyog。

二、使用工具

 主要工具有:pycharm64.exe + Python 3.6 64-bit + MySQL +SQLyog(辅助工具)

 

 Python下载地址https://www.python.org/downloads/

MySQL下载地址:https://www.mysql.com/downloads/

Pycharm下载地址:https://www.jetbrains.com/pycharm/download/#section=windows

 

 三、完成基本的页面设计

①导航条

②登陆界面

③注册界面

④发布问答界面

 四、Flask & 概览

1、初始化
在这章,你将学到Flask应用程序的不同部分。同时,你将编写和运行你的第一个Flask web应用程序。

所有的Flask应用程序都必须创建一个 应用程序实例 。使用web服务器网关接口协议将所有从客户端接收的请求传递给这个对象处理。这个应用程序实例就是Flask类的一个对象,通常使用下面的方式创建:

1
2
from flask import Flask
app = Flask(__name__)

Flask类构造函数唯一需要的参数就是应用程序的主模块或包。对于大多数应用程序,Python的__name__变量就是那个正确的、你需要传递的值。

注:对于Flask开发者来说,传给Flask应用程序构造函数的name参数是比较容易弄混淆的。Flask使用这个参数来确定应用程序的根目录,这样以后可以相对这个路径来找到资源文件。
稍后你可以看到更复杂的应用程序实例初始化,但是对于简单应用程序这些已经足够了。

2、路由和视图函数
客户端例如web浏览器发送 请求 给web服务,进而将它们发送给Flask应用程序实例。应用程序实例需要知道对于各个URL请求需要运行哪些代码,所以它给Python函数建立了一个URLs映射。这些在URL和函数之间建立联系的操作被称之为 路由 。

在Flask应程序中定义路由的最便捷的方式是通过显示定义在应用程序实例之上的app.route装饰器,注册被装饰的函数来作为一个路由。下面的例子会演示怎样使用装饰器来申明一个路由:

1
2
3
@app.route('/')
def index():
 return '<h1>Hello World!</h1>'

注:装饰器是Python语言的标准特性;它们可以以不同方式改变函数的行为。一个常见的模式是使用装饰器来注册函数作为一个事件处理程序。
在上一个示例给应用程序的根URL注册index()函数作为事件的处理程序。如果这个应用程序被部署在服务器上并绑定了 www.example.com 域名,然后在你的浏览器地址栏中输入 http://www.example.com 将触发index()来运行服务。客户端接收到的这个函数的返回值被称为 响应 。如果客户端是web浏览器,响应则是显示给用户的文档。

类似于index()的函数被称作 视图函数 。通过视图返回的响应可以是简单的HTML内容的字符串,但它也可以市更复杂的形式,正如您将看到的。

注:响应字符串嵌入在Python代码中导致代码难以掌控,在此只是介绍响应的概念。你将在第三章学习正确的方法来生成响应。
如果你注意到你每天使用的一些网站URLs如何形成的,你将会发现很多都有变量。例如,你的Facebook个人信息页的URL是 http://www.facebook.com/<username> ,所以你的用户名是它的一部分。Flask在路由装饰器中使用特殊的语法支持这些类型的URLs。下面的示例定义了一个拥有动态名称组件的路由:

1
2
3
@app.route('/user/<name>')
def user(name):
 return '<h1>Hello, %s!</h1>' % name

用尖括号括起来的部分是动态的部分,所以任何URLs匹配到静态部分都将映射到这个路由。当视图函数被调用,Flask发送动态组件作为一个参数。在前面的示例的视图函数中,这个参数是用于生成一个个性的问候作为响应。

在路由中动态组件默认为字符串,但是可以定义为其他类型。例如,路由/user/<int:id>只匹配有一个整数在id动态段的URLs。Flask路由支持int、float和path。path同样是字符串类型,但并不认为斜杠是分隔符,而认为它们是动态组件的一部分。

3、服务启动
应用程序实例有一个run方法用于启动Flask集成的web服务:

1
2
if __name__ == '__main__':
 app.run(debug=True)

__name__ == '__main__'在此处使用是用于确保web服务已经启动当脚本被立即执行。当脚本被另一个脚本导入,它被看做父脚本将启动不同的服务,所以app.run()调用会被跳过。

一旦服务启动,它将进入循环等待请求并为之服务。这个循环持续到应用程序停止,例如通过按下Ctrl-C。

有几个选项参数可以给app.run()配置web服务的操作模式。在开发期间,可以很方便的开启debug模式,将激活 debugger 和 reloader 。这样做是通过传递debug为True来实现的。

注:Flask提供的web服务并不用于生产环境。你将在十七章学习生产环境的web服务。

4、一个完整的应用程序
在上一节,你学习了Flask web应用程序的不同部分,现在是时候写一个了。整个 hello.py 应用程序脚本只不过将前面描述的三个部分结合在一个文件中。应用程序示例2-1所示。

示例 hello.py:一个完整的Flask应用程序

1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask
app = Flask(__name__)
 
@app.route('/')
def index():
 return '
 
<h1>Hello World!</h1>
 
'
 
if __name__ == '__main__':
 app.run(debug=True)

 经过这个过程,一个完整的Flask应用程序就搭建起来了。

五、加载静态文件,父模板与其他界面的继承

 1.加载静态文件

   <script language="javascript" type="text/javascript" src="{{ url_for('static',filename="js/basic.js") }}"></script>
    <link rel="stylesheet" type="text/css" href="{{ url_for('static',filename="css/basic.css") }}">
 

2.父模板的设置

<!DOCTYPE>
<html>
<head>
    <meta charset="utf-8">
  <title>{% block title %}{% endblock %}</title>
 
</head>
<body id="myBody" style="min- 1500px;" background="{{ url_for('static',filename="img/timg1.jpg") }}">

{% block aa %}{% endblock %}

</body>
</html>

3.子模板继承

主页:

{% extends 'base.html' %}
{% block title %} 主页{% endblock %}
{% block aa %}
{% endblock %}

注册:

  {% extends 'base.html'%}
{% block title %} 注册{% endblock %}
  {% block aa %}
 {% endblock %}

登录:

{% extends 'base.html' %}
{% block title %} 登录{% endblock %}
{% block aa %}
    <link rel="stylesheet" type="text/css" href="{{ url_for('static',filename="css/login.css") }}">
    <script language="javascript" type="text/javascript" src="{{ url_for('static',filename="js/login.js") }}"></script>

{% endblock %}

个人父模板:

{% extends 'base.html' %}
{% block title %} 个人中心{% endblock %}

{% block aa %}

{% block user %}{% endblock %}
 </div>
{% endblock %}

个人子模板:

{% extends 'user.html' %}

{% block user %}

{% endblock %}

问答页:

{% extends 'base.html' %}
{% block title %} 互动{% endblock %}
{% block aa %}
{% endblock %}

六、数据库连接池

数据库工具:mysql

import os
DEBUG=True
SECRET_KEY=os .urandom(24)
#
# DIALECT='mysql'
# DRIVER='mysqldb'
# USERNAME='yu'
# PASSWORD='bozhi'
# HOST='www.decade.wang'
# PORT='3306'
# DATABASE='python'

SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://yu:bozhi@www.decade.wang:3306/decade?charset=utf8'
SQLALCHEMY_TRACK_MODIFICATIONS = False

建立连接:

主py

from werkzeug.security import generate_password_hash,check_password_hash
from datetime import datetime
from exts import db



class User(db.Model):
    __tablename__ = 'user'
    # 建立一个表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)
    image = db.Column(db.String(100))
    say = db.Column(db.String(50))

    @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
#db.create_all()
# 建立一表question
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'))
    classify=db.Column(db.Enum('散文拾贝', '心灵鸡汤','笑谈风声', '随心所欲'),nullable=False)
    click = db.Column(db.INT, default=0)




class Comment(db.Model):
    __tablename__ = 'comment'
    # 建立一个表log
    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'))
    creat_time = db.Column(db.DateTime,default=datetime.now)
    detail = db.Column(db.Text,nullable=False)
    question = db.relationship('Question', backref=db.backref('comment',order_by=creat_time.desc))
    author=db.relationship('User',backref=db.backref('comment'))
#db.create_all()

exts

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

七、通过用户模型,对数据库进行增删改查

添加数据
user=User(username='yujiujiu2',password='11111')
db.session.add(user)
db.session.commit()

添加数据
comment=Comment(detail='6666666')
db.session.add(comment)
db.session.commit()


查找数据
user=User.query.filter(User.username=='yujiujiu').first()
print(user.username,user.password)


修改数据
user=User.query.filter(User.username=='yujiujiu').first()
user.password='666667'
print(user.username,user.password)
db.session.commit()


删除数据
user=User.query.filter(User.username=='yujiujiu2').first()
db.session.delete(user)
db.session.commit()

八、完成注册功能

1.js文件: onclick函数return True时才提交表单,return False时不提交表单。

 

复制代码
 function miao(){
        var oUname=document.getElementById("uname");
        var oUpass=document.getElementById("upass");
        var oUpass2=document.getElementById("upass2");
        var oError=document.getElementById("error_box");
        var isError=true;
        oError.innerHTML="<br>"
    if (oUname.value.length<6 || oUname.value.length>12){
     oError.innerHTML="用户名6-12位";
     isError=false;
     return isError;
    } else if((oUname.value.charCodeAt(0)>=48)&&(oUname.value.charCodeAt(0)<=57)){
       oError.innerHTML="first letter";
       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 = "only letter or number";
                isError=false;
                return isError;
            }

        }

if (oUpass.value.length<6 || oUpass.value.length>12){
   oError.innerHTML="密码6-12位";
   return false;
}
    else if (oUpass.value!==oUpass2.value){
        oError.innerHTML="两次密码不一致";
        return false;
    }
    return true;
    // window.alert("注册成功!")

    }
 function mySwitch() {
    var oBody = document.getElementById("myBody");

    var oOnOff = document.getElementById("myOnOff");
    var ogui = document.getElementById("gui");
    if (oOnOff.src.match('bulbon')) {

        oOnOff.src = "http://www.runoob.com/images/pic_bulboff.gif";
        oBody.style.backgroundColor = "black";
        oBody.background="../static/img/h.jpg"
        oBody.style.color = "white";
        ogui.src = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1509563443074&di=b1bbb3f5714a580e2211e281124636c2&imgtype=0&src=http%3A%2F%2Fpic15.nipic.com%2F20110709%2F7854247_221301558161_2.jpg";
    } else {
        oOnOff.src = "http://www.runoob.com/images/pic_bulbon.gif";
        oBody.style.backgroundColor = "white";
        oBody.style.color = "black";
        ogui.src = "";
        oBody.background="../static/img/timg1.jpg"
    }
}
复制代码

运行结果:

2.html文件:

    1. <form>中设置 action和method="post"
    2. <input> 中设置 name
复制代码
  {% extends 'basic.html'%}
  {% block aa %}
    <link rel="stylesheet" type="text/css" href="{{url_for('static',filename="css/zhuce.css")  }}">
        <script language="javascript" type="text/javascript" src="{{url_for('static',filename="js/zhuce.js")  }}"></script>
<div id=mao ><p></p>
    <div id="header"><h2 align="center"></h2></div>
    <div id="content" >

        <form action="{{ url_for('zhuce')}}" method="post"><br/>
            <div class="form-group">
                <label for="uname" class="col-sm-2 control-label">Username:</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" id="uname" name="username"
                           placeholder="make your username"><br>
                </div>
            </div>
            <div class="form-group">
                <label for="upass" class="col-sm-2 control-label">Password:</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" id="upass" name="password"
                           placeholder="make your password"><br>
                </div>
            </div>
            <div class="form-group">
                <label for="upass2" class="col-sm-2 control-label">AgainPass:</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" id="upass2"
                           placeholder="again your password"><br>
                </div>
            </div>
<div class="form-group1"><label for="question" >SomethingSay</label>
<textarea class="col-sm-10" rows="3" id="question" name="say"></textarea><br><br><br><br>
</div>
            <div >
            <br>  &nbsp &nbsp <input type="radio" name="role" value="stu">student
            <input id="tea" type="radio" name="role" value="tea">teacher<br/>
            </div>
            <div id="error_box"  ></div>
            <div align="center">
               <input id="input_box" type="button" class="btn btn-default btn-lg active" value="注册" onclick="miao()">
             </div>
          </form>
</div>
</div>

  {% endblock %}
复制代码

3.主py文件中:

    1. from flask import  request, redirect, url_for
    2. @app.route('/regist/', methods=['GET', 'POST’])
@app.route('/zhuce/',methods=['GET','POST'])
def zhuce():
    if request.method=='GET':
        return render_template('zhuce.html')
    else:
        username=request.form.get('username')
        password=request.form.get('password')
        say=request.form.get('say')
        user=User.query.filter(User.username==username).first()
        #判断用户名是否存在
        if user:
            return u' username existed'
        else:
            user=User(username=username,password=password,say=say)
            db.session.add(user)
            db.session.commit()
            return redirect(url_for('login'))
#重新定向到登录页

运行结果:

九、完成登陆功能

登录功能完成:

1.js:设置return

同注册,onclick函数return True时才提交表单,return False时不提交表单。

2.html:设置

  1. form
  2. input
   <form action="{{ url_for('login')}}" method="post"><br/>



            <div class="form-group">
                <label for="uname" class="col-sm-2 control-label">Username:</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" id="uname" name="username"
                           placeholder="input your username"><br>
                </div>


            </div>
            <div class="form-group">
                <label for="upass" class="col-sm-2 control-label">Password:</label>
                <div class="col-sm-10">
                    <input type="password" class="form-control" id="upass" name="password"
                           placeholder="input your password"><br>
                </div>
            </div>

           <div >
            <br> &nbsp &nbsp &nbsp <input type="radio" name="role" value="stu">student&nbsp
            <input id="tea" type="radio" name="role" value="tea">teacher<br/><br/>
            </div>

            <div align="center">
            <input id="rem" type="checkbox" value="true"><span>remember me</span> &nbsp &nbsp &nbsp &nbsp<a href="http://help.clouddream.net/newsitem/277741776" target="_blank">Login problem</a><br/>
            <br>
            </div>
      <div id="error_box" ></div>
            <div align="center">
               <input id="input_box" type="submit" class="btn btn-default btn-lg active" value="登录" onclick=" return miao()">
             </div>
          </form>

3.py:

  1. @app.route设置methods
  2. GET
  3. POST
    1. 读取表单数据
    2. 查询数据库
      1. 用户名密码对:
        1. 记住用户名
        2. 跳转到首页
      2. 用户名密码不对:
        1. 提示相应错误。
@app.route('/login/',methods=['GET','POST'])
def login():
    if request.method=='GET':
        return render_template('login.html')
    else:
        usern=request.form.get('username')
        passw=request.form.get('password')

        user=User.query.filter(User.username==usern).first()
        #判断用户名是否存在
        if user:
            session['user']=usern
            session.permanent=True
            if user.password==passw:
                return redirect(url_for('index'))
            else:
                return u' password error'
        else:
            return u' username  not existed'

十、登录后更新导航

1.用上下文处理器app_context_processor定义函数

  1. 获取session中保存的值
  2. 返回字典
#判断是否登陆,有则在导航栏显示用户名
@app.context_processor
def mycontext():
    usern=session.get('user')
    if usern:
        return {'username':usern}
    else:
        return {}

2.在父模板中更新导航,插入登录状态判断代码。、

  1. 注意用{% ... %}表示指令。
  2. {{ }}表示变量
    {% if username %}

    <li><a href="#"><h2>{{ username }}</h2></a></li>
    <li><a href="{{ url_for('logout') }}"><h2>注销</h2></a></li>
    {% else %}
    <li><a href="{{ url_for('login') }}"><h2>登录</h2></a></li>
    <li><a href="{{ url_for('zhuce') }}"><h2>注册</h2></a></li>
    {% endif %}
    <li><a href="#"><h2>联系我</h2></a></li>
</ul>


<img id="gui" onclick="mySwitch()" src="" width="1700px">

{% block aa %}{% endblock %}

3.完成注销功能。

  1. 清除session
  2. 跳转
#登出,清除session
@app.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('index'))

十一、完成发布功能

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'])
@loginFirst
def question():
#发布前登陆装饰器
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  # 返回一个函数

3.建立发布内容的对象关系映射。

class Question(db.Model):

4.完成发布函数。

  保存到数据库。

  重定向到首页。

#登陆前验证,进入评论进入问答页
@app.route('/question/',methods=['GET','POST'])
@loginFirst
def question():
    if request.method=='GET':
        return render_template('question.html')
    else:
        title=request.form.get('title')
        detail = request.form.get('detail')
        author_id = User.query.filter(User.username ==session.get('user')).first().id
        question = Question(title=title, detail=detail, author_id=author_id)
        db.session.add(question)
        db.session.commit()
        return redirect(url_for('index'))

十二、在首页显示问答列表

1. 在首页添加显示问答的列表,并定义好相应的样式。

<ul class="list-group">

    <li class="list-group-item" style=" 800px">

{#列表内容开始        #}
     <a class="wrap-img" href="#" target="_blank">
      <img src="http://www.bookmarkye.com/9.jpg" width="50px" >
    </a>
      <span class="glyphicon glyphicon-left" aria-hidden="true"></span>
        <a  href="#" target="_blank">{{ user }}</a>
        <br>
         <a  href="#">{{ title }}</a>
     <span class="badge">发布时间:{{time  }}</span>
              <p style="">{{ detail }}
    </p>


{#  列表内容结束      #}
    </li>


</ul>

2. 用字典向index.html传递参数。

@app.route('/')
def index():
    context={
        'user':'yubozhi',
        'title':'硬盘不是“背锅侠”',
        'time':'2017-11-29',
        'detail':'昔日上将悬梁,今天硬盘入党。说这话的是一位资深前媒体人。 光天化日之下,硬盘竟然坏了!众目睽睽之下,硬盘竟然坏了!!大庭广众之下,硬盘竟然坏了!!! 重要的事情说三遍!!! ...',

    }
    return render_template('basic.html',**context)

十三、从首页问答列表标题链接到详情页

1.主PY文件写视图函数,带id参数。 

代码如下:

复制代码
#进入每篇文章详情页
@app.route('/detail/<question_id>')
@loginFirst
def detail(question_id):
    quest=Question.query.filter(Question.id==question_id).first()

    context={
       'comments':Comment.query.order_by('-creat_time').all()

    }
    return render_template('detail.html',**context,ques=quest)
复制代码

2.首页标题的标签做带参数的链接。

代码如下:

复制代码
  {% for foo in questions %}


            <li class="list-group-item" style=" 800px">

              
                <a class="wrap-img" href="#" target="_blank">
                    <img src="{{ foo.author.image }}" width="50px">
                </a>
                <span class="glyphicon glyphicon-left" aria-hidden="true"></span>
                <a href="#" target="_blank">{{ foo.author.username }}</a>
                <br>
                <a href="{{ url_for('detail',question_id=foo.id) }}">{{ foo.title }}</a>

                <br>
                <span class="badge">发布时间:{{ foo.creat_time }}</span>
                <p style="">{{ foo.detail }}
                </p>
{#                {{ url_for('usercenter',user_id=foo.author.id) }}#}
{#                <a href="#" target="_blank"><br>评论({{ foo.comments.length }})</a>#}


            </li>
        {% endfor %}
复制代码

运行结果:

3.在详情页将数据的显示在恰当的位置。

  1.首页列表显示全部问答:

  1. 将数据库查询结果传递到前端页面 Question.query.all()
  2. 前端页面循环显示整个列表。
  3. 问答排序
@app.route('/')
def index():
    context={

        'questions':Question.query.order_by('-creat_time').all()

    }
    return render_template('basic.html',**context)

   2.完成问答详情页布局:

  1. 包含问答的全部信息
  2. 评论区
  3. 以往评论列表显示区。

代码如下:

复制代码
<div class="page-header">
    <h3>{{ ques.title }}<br><br>
    <small>作者:{{ ques.author.username }}&nbsp&nbsp&nbsp
    <span class="badge">{{ ques.creat_time }}</span>
        </small></h3>
</div>
            <p class="lead">{{ ques.detail }}</p>
            <hr>
            <form action="{{ url_for('comment') }}" method="post" style="">
                <div class="form-group">
                    <input type="text" value="{{ ques.id }}" name="question_id" hidden>
                    <input type="text" value="{{ user.id }}" name="author_id" hidden>
                    <textarea name="detail" class="form-control"  row="3" id="new-comment" placeholder="write your comment"></textarea>
                </div>
                <button type="submit" class="btn btn-default">发送</button>
            </form>
        <hr>
复制代码

   3.在首页点击问答标题,链接到相应详情页。

#进入每篇文章详情页
@app.route('/detail/<question_id>')
def detail(question_id):
    quest=Question.query.filter(Question.id==question_id).first()
    return render_template('detail.html',ques=question_id)

运行结果:

4.建立评论的对象关系映射:

代码如下:

复制代码
class Comment(db.Model):
    __tablename__ = 'comment'
    # 建立一个表log
    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'))
    creat_time = db.Column(db.DateTime,default=datetime.now)
    detail = db.Column(db.Text,nullable=False)
    question = db.relationship('Question', backref=db.backref('comments'))
    author=db.relationship('User',backref=db.backref('comments'))
复制代码

运行结果:

十四、实现发布评论,并显示评论区

代码如下:

复制代码
<h3>评论区:
{#    ({{ ques.comments.length }})#}
</h3><br>



<div class="basic_box" style="padding-bottom: 50px;">
    <ul class="list-group" style="margin-bottom: 10px">
      {% for qu in comments %}

            <li class="list-group-item" style=" 800px">


                <a class="wrap-img" href="#" target="_blank">
                    <img src="{{ qu.author.image }}" width="50px">
                </a>
                <span class="glyphicon glyphicon-left" aria-hidden="true"></span>

                <br>
                <a href="#">{{ qu.author_id }}</a>
                <span class="badge">评论时间:{{ qu.creat_time }}</span>
                <p style="">{{ qu.detail }}
                </p>


            </li>
     {% endfor %}
复制代码

运行结果:

十五、完成个人中心相关信息

1.个人中心的页面布局

html文件如下:

复制代码
{% extends 'basic.html' %}
{% block title %} 个人中心{% endblock %}

{% block aa %}


<div class="container">
    <div class="row clearfix">
        <div class="col-md-2 column">
        </div>
        <div class="col-md-6 column">


<h1><img src="{{ img }}" width="50px">{{usern }}</h1>

    <br>全部问答


<div class="basic_box" style="padding-bottom: 50px;">
    <ul class="list-group">

           {% for qu in ques %}

            <li class="list-group-item" style=" 800px">

<a class="wrap-img" href="#" target="_blank">
                    <img src="{{ qu.author.image }}" width="50px">
                </a>
                <span class="glyphicon glyphicon-left" aria-hidden="true"></span>
                <a href="{{ url_for('person',user_id=qu.author.id) }}" target="_blank">{{ qu.author.username }}</a>
                <br>
                <a href="{{ url_for('detail',question_id=qu.id) }}">{{qu.title }}</a>

                <br>
                <span class="badge">发布时间:{{ qu.creat_time }}</span>
                <p style="">{{ qu.detail }}
                </p>
            </li>
     {% endfor %}
    </ul>
</div>

<h1><img src="{{ img }}" width="50px">{{usern }}</h1>
    <br>全部评论

<div class="basic_box" style="padding-bottom: 50px;">
    <ul class="list-group" style="margin-bottom: 10px">
      {% for qu in users %}


            <li class="list-group-item" style=" 800px">


                <a class="wrap-img" href="#" target="_blank">
                    <img src="{{ qu.author.image }}" width="50px">
                </a>
                <span class="glyphicon glyphicon-left" aria-hidden="true"></span>

                <br>
                <a href="#">{{ qu.author.username }}</a>
                <span class="badge">评论时间:{{ qu.creat_time }}</span>
                <p style="">{{ qu.detail }}
                </p>


            </li>
     {% endfor %}
    </ul>
<br>
<br>
<h1><img src="{{ img }}" width="50px">{{usern }}</h1>
    <br>个人信息

<div class="basic_box" style="padding-bottom: 50px;">
    <ul class="list-group" style="margin-bottom: 10px">
{#      {% for qu in users %}#}


          <li class="list-group-item" style=" 800px"> 用户:{{ usern }}</li>
          <li class="list-group-item" style=" 800px"> 编号:{{ id }}</li>
          <li class="list-group-item" style=" 800px"> 问答数:{{ ques|length }}</li>
          <li class="list-group-item" style=" 800px"> 评论数:{{ comment|length }}</li>

{#      {% endfor %}#}
    </ul>

        </div>
        <div class="col-md-4 column">
        </div>
    </div>
</div>

</div>

</div>

{% endblock %}
复制代码

2.定义视图函数def usercenter(user_id):

复制代码
# 个人中心
@app.route('/person/<user_id>',methods=['GET','POST'])
def person(user_id):
    user = User.query.filter(User.id == user_id).first()

    context = {
        'img':user.image,
        'id':user_id,
        'usern':user.username,
        'ques': Question.query.filter(Question.author_id == user_id).order_by('-creat_time').all(),
        'users': Comment.query.filter(Comment.author_id == user_id).order_by('-creat_time').all(),
        'comment': user.comments
    }
    return render_template('person.html', **context)
复制代码

3.向前端页面传递参数

复制代码
  context = {
        'img':user.image,
        'id':user_id,
        'usern':user.username,
        'ques': Question.query.filter(Question.author_id == user_id).order_by('-creat_time').all(),
        'users': Comment.query.filter(Comment.author_id == user_id).order_by('-creat_time').all(),
        'comment': user.comments
    }
复制代码
复制代码
<h1><img src="{{ img }}" width="50px">{{usern }}</h1>

    <br>全部问答


<h1><img src="{{ img }}" width="50px">{{usern }}</h1>
    <br>全部评论


          <li class="list-group-item" style=" 800px"> 用户:{{ usern }}</li>
          <li class="list-group-item" style=" 800px"> 编号:{{ id }}</li>
          <li class="list-group-item" style=" 800px"> 问答数:{{ ques|length }}</li>
          <li class="list-group-item" style=" 800px"> 评论数:{{ comment|length }}</li>
复制代码

4.页面显示相应数据:发布的全部问答、发布的全部评论、个人信息

效果如下:

5.各个页面链接到个人中心

导航栏的昵称连接:

复制代码
 {% if username %}

        <li><a href="{{ url_for('person',user_id=user.id) }}"><h2>{{ username }}</h2></a></li>
        <li><a href="{{ url_for('logout') }}"><h2>注销</h2></a></li>
    {% else %}
        <li><a href="{{ url_for('login') }}"><h2>登录</h2></a></li>
        <li><a href="{{ url_for('zhuce') }}"><h2>注册</h2></a></li>
    {% endif %}
    <li><a href="{{ url_for('question') }}"><h2>问答</h2></a></li>
</ul>
复制代码

主页昵称连接:

  <a href="{{ url_for('person',user_id=foo.author.id) }}" target="_blank">{{ foo.author.username }}</a>

评论列表里面的昵称连接:

                <a href="{{ url_for('person',user_id=qu.author.id) }}">{{ qu.author.username }}</a>

文章中作者名字连接:

   作者:<a href="{{ url_for('person',user_id=ques.author.id) }}">{{ ques.author.username }}</a>

6.新页面user.html,用<ul ><li role="presentation"> 实现标签页导航。
<ul class="nav nav-tabs">
  <li role="presentation"><a href="#">Home</a></li>
  <li role="presentation"><a href="#">Profile</a></li>
  <li role="presentation"><a href="#">Messages</a></li>
</ul>

复制代码
<ul class="nav nav-tabs">


    <li class="nav"  role="presentation"><a href="#"><h3>全部问答</h3></a></li>
    <li class="nav"  role="presentation"><a href="#"><h3>全部评论</h3></a></li>
    <li class="nav"  role="presentation"><a href="#"><h3>个人中心</h3></a></li>

</ul>
复制代码

7.user.html继承base.html。
重写title,head,main块.
将上述<ul>放在main块中.
定义新的块user。

如下:

user.html

复制代码
{% extends 'basic.html' %}
{% block title %} 个人中心{% endblock %}

{% block aa %}
{% block user %}{% endblock %}
 </div>
{% endblock %}
复制代码

person.html

{% extends 'user.html' %}


{% block user %}
{% endblock %}

8.个人中心—视图函数带标签页面参数tag
@app.route('/usercenter/<user_id>/<tag>')
def usercenter(user_id, tag):
   if tag == ‘1':
       return render_template('usercenter1.html', **context)

复制代码
# 个人中心
@app.route('/person/<user_id>/<tag>',methods=['GET','POST'])
def person(user_id,tag):
    user = User.query.filter(User.id == user_id).first()

    context = {
        'img':user.image,
        'id':user_id,
        'usern':user.username,
        'ques': Question.query.filter(Question.author_id == user_id).order_by('-creat_time').all(),
        'users': Comment.query.filter(Comment.author_id == user_id).order_by('-creat_time').all(),
        'comment': user.comments,
        'user1':user
    }
    if tag=='1':
         return render_template('person.html', **context)
    elif tag=='2':
         return render_template('person2.html', **context)
    else:
         return render_template('person3.html', **context)
复制代码

9.个人中心—导航标签链接增加tag参数
<li role=“presentation”><a href=“{{ url_for(‘usercenter’,user_id = user.id,tag = ‘1’) }}">全部问答</a></li>

    <li class="nav" role="presentation"><a href="{{url_for('person',user_id=user1.id,tag=1)}}"><h3>全部问答</h3></a></li>
    <li class="nav" role="presentation"><a href="{{url_for('person',user_id=user1.id,tag=2)}}"><h3>全部评论</h3></a></li>
    <li class="nav" role="presentation"><a href="{{url_for('person',user_id=user1.id,tag=3)}}"><h3>个人中心</h3></a></li>

10.个人中心—有链接到个人中心页面的url增加tag参数
u <a href="{{ url_for('usercenter',user_id = session.get('userid'), tag=1) }}">{{ session.get('user') }}</a>

复制代码
   <a href="{{ url_for('person',user_id=qu.author.id,tag=1) }}" target="_blank">{{ qu.author.username }}</a>


   <a class="wrap-img" href="{{ url_for('person',user_id=foo.author.id,tag=1) }}" target="_blank">
 
   <a href="{{ url_for('person',user_id=foo.author.id,tag=1) }}" target="_blank">{{ foo.author.username }}</a>
复制代码

运行结果:

 

十六、实现导航条中的搜索功能

1.准备视图函数search()

# 查找
@app.route('/search/')
def search():
  pass

2.修改base.html 中搜索输入框所在的

<form action="{{ url_for('search') }}" method="get">

   <input name="q" type="text" placeholder="请输入关键字">

复制代码
  <li>
        <form action="{{url_for('search') }}" method="get"   >
        <input class="d1" name="q" type="text" placeholder="搜索从这里开始...">
        <button class="b" type="submit">搜索</button>
            </form>
    </li>
复制代码

3.完成视图函数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(
      
           Question.title.contains(qu)
       ).order_by('-creat_time')
   return render_template('basic.html',questions=ques)
复制代码

4.组合条件查询
from sqlalchemy import or_, and_

复制代码
# 查找
@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('-creat_time')
return render_template('basic.html',questions=ques)
复制代码

运行结果:

以下为搜索“我”的结果

十七、密码保护

1.更新User对象,设置对内的_password

class User(db.Model):

    __tablename__ = 'user' 

    _password = db.Column(db.String(200), nullable=False) #内部使用

代码如下:

复制代码
class User(db.Model):
    __tablename__ = 'user'
    # 建立一个表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)
    image = db.Column(db.String(100))
    say = db.Column(db.String(50))
复制代码

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(self):#外部使用
        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('/login/',methods=['GET','POST'])
def login():
    if request.method=='GET':
        return render_template('login.html')
    else:
        usern=request.form.get('username')
        passw=request.form.get('password')
        user=User.query.filter(User.username==usern).first()
        #判断用户名是否存在
        if user:

            # if user.password==passw:
            if user.check_password(passw):
                session['user'] = usern#字典键值
                session['userid'] = user.id
                session.permanent = True
                return redirect(url_for('index'))
            else:
                return u' password error'
        else:
            return u' username  not existed'
复制代码

运行结果:

 

十八、模型分离

1.新建models.py,将模型定义全部放到这个独立的文件中。

models.py

复制代码
from werkzeug.security import generate_password_hash,check_password_hash
from datetime import datetime
from exts import db



class User(db.Model):
    __tablename__ = 'user'
    # 建立一个表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)
    image = db.Column(db.String(100))
    say = db.Column(db.String(50))
   
    @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
#db.create_all()
# 建立一表question
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'))
    classify=db.Column(db.Enum('散文拾贝', '心灵鸡汤','笑谈风声', '随心所欲'),nullable=False)
    click = db.Column(db.INT, default=0)


# db.create_all()



# class Log(db.Model):
#     __tablename__ = 'log'
#     # 建立一个表log
#     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()
复制代码

2.新建exts.py,将db = SQLAlchemy()的定义放到这个独立的文件中。

exts.py

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

3.models.py和主py文件,都从exts.py中导入db。

from exts import db

4.在主py文件中,对db进行始化,db.init_app(app)。

主py

复制代码
from flask import Flask
from flask import render_template,request,redirect,url_for,session
import config
from sqlalchemy import  or_ ,and_
from functools import wraps
from models import Question,User,Comment
from exts import db
app = Flask(__name__)
app.config.from_object(config)
db.init_app(app)
复制代码

十九、数据迁移

1.pip install flask-migrate  #Flask-Migrate 是一个数据迁移框架,需要通过Flask-script库来操作.

2. pip install flask-script  #通过命令行来操作Flask

3. 新建模型更改文件:manage.py

from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from myqa import app
from exts import db

from models import Question, Comment

manager = Manager(app) #Manager只有一个参数:一个Flask实例
migrate = Migrate(app, db) #使用Migrate绑定app和db
manager.add_command('db', MigrateCommand) ##添加迁移脚本命令,命令行输入python manage.py db migrate

if __name__ == '__main__':
manager.run() #启动Manger实例接收命令行中的命令。
复制代码
from flask_script import Manager
from flask_migrate import Migrate,MigrateCommand
from untitled6 import  app
from  exts import  db

from  models import  User,Question,Comment

manager=Manager(app)#mangager只有一个参数:一个Flask实例

migrate=Migrate(app,db)#使用Migrate绑定app和db

#添加迁移脚本命令
manager.add_command('db',MigrateCommand)#加入命令,命令行输入python manage.py db migrate


if __name__ == '__main__':
    manager.run()#启动Manger实例接收命令行中的命令
复制代码

4. 要models.py发生改变后,在cmd命令行,进到manage.py所在的路径,执行:

(1)初始化迁移环境,只运行一次
python manage.py db init

(2)生成迁移文件,模型改变了就需要执行
python manage.py db migrate

(3)映射到数据库表中
python manage.py db upgrade

 

字段修改语句:

复制代码
class User(db.Model):
    __tablename__ = 'user'
    # 建立一个表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)
    image = db.Column(db.String(100))
    say = db.Column(db.String(50))
#加入该字段测试,试完删除 a=db.Column(db.String(50))
复制代码

数据库修改结果:

数据迁移生成文件及该flask框架整理布局:

二十、浅谈flask框架及其应用

为了理解 Flask 框架是如何抽象出Web开发中的共同部分,我们先来看看Web应用程序的一般流程。对于Web应用来说,当客户端想要获取动态资源时,就会发起一个HTTP请求(比如用浏览器访问一个 URL),Web应用程序会在后台进行相应的业务处理,(从数据库或者进行一些计算操作等)取出用户需要的数据,生成相应的HTTP响应(当然,如果访问静态资源,则直接返回资源即可,不需要进行业务处理)。整个处理过程如下图所示:

实际应用中,不同的请求可能会调用相同的处理逻辑。这里有着相同业务处理逻辑的 HTTP 请求可以用一类 URL 来标识。比如论坛站点中,对于所有的获取Topic内容的请求而言,可以用 topic/&lt;topic_id&gt;/ 这类URL来表示,这里的 topic_id 用以区分不同的topic。接着在后台定义一个 get_topic(topic_id) 的函数,用来获取topic相应的数据,此外还需要建立URL和函数之间的一一对应关系。这就是Web开发中所谓的路由分发,如下图所示:

写在最后:

此文章仅限flask框架的基本使用,要想学好这个框架,有一定的页面制作基础和python基础是不够的,还需要掌握请求-响应循环、请求调度、请求Hooks、Flask扩展等等网络技术,否则也只是小试牛刀,希望这篇文章能够给初学者提供一定的帮助。

原文地址:https://www.cnblogs.com/decadeyu/p/8145761.html