【新闻推荐系统】(task4)Flask框架基础与入门

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: HTTP,Hypertext Transfer Protocol,超文本传输协议HTTP URL实例:http://www.bit.edu.cn

零、HTTP与Web服务器简介

0.1 HTTP协议

HTTP,Hypertext Transfer Protocol,超文本传输协议

HTTP URL实例:http://www.bit.edu.cn

HTTP URL的理解:

URL是通过HTTP协议存取资源的Internet路径,一个URL对应一个数据资源

(1)HTTP协议对资源的操作

通过URL和命令管理资源,操作独立无状态,网络通道及服务器成为了黑盒子:

(2)Web服务器

0.2 Flask简介

Flask是一个轻量级的可定制的web框架,使用Python语言编写,较其他同类型框架更为灵活、轻便、安全且容易上手。可快速实现一个网站或Web服务。

可以很好地结合MVC模式进行开发,开发人员分工合作,小型团队在短时间内就可以完成功能丰富的中小型网站或Web服务的实现。

一般情况下,它不会指定数据库和模板引擎等对象,用户可以根据需要自己选择各种数据库。

百度百科: https://baike.baidu.com/item/Flask/1241509

维基百科: https://zh.wikipedia.org/zh-hans/Flask

0.3 django和flask框架的区别:

Flask:小巧、灵活,让程序员自己决定定制哪些功能,非常适用于小型网站。对于普通的工人来说将毛坯房装修为城市综合体还是很麻烦的,使用Flask来开发大型网站也一样,开发的难度较大,代码架构需要自己设计,开发成本取决于开发者的能力和经验。

Django:大而全,功能极其强大,是Python web框架的先驱,用户多,第三方库极其丰富。非常适合企业级网站的开发,但是对于小型的微服务来说,总有“杀鸡焉有宰牛刀”的感觉,体量较大,非常臃肿,定制化程度没有Flask高,也没有Flask那么灵活。

如果想搞懂Python web开发WSGI协议原理以及实现过程、灵活定制组件,完全DIY应用、想实现微服务则选择Flask;如果关注产品的最终交付、想快速开发一个大的应用系统,则用Django,想实现的功能都有。

一、 准备工作

1.1 环境配置

为了保持全局环境的干净,指定不同的依赖版本,我们可以利用virtualenv来构建虚拟的环境,类似于anaconda。

pip install virtualenv

通过上述指令安装virtualenv,之后将在文件夹中创建新的虚拟环境。

mkdir newproj
cd newproj
virtualenv venv

要在Linux激活相应的环境。

venv/bin/activate

接下来就可以在这个环境中安装 Flask,当然如果你也可以直接选择使用下述指令直接安装Flask。

pip install Flask

1.2 测试安装

为了测试装的Flask是否能正常使用,可以在编译器中输入一下代码:

# 从框架导入Flask类
from flask import Flask
# 初始化
app = Flask(__name__)
# 将根URL映射到hello world函数上 
@app.route('/')
# 定义视图函数
def hello_world():
   return 'Hello World'
if __name__ == '__main__':
    # 运行
   app.run()

运行上述代码,在浏览器中打开localhost:5000,将显示**“Hello World”**消息。

python Hello.py

上述代码中,Flask将(__name__)作为参数,即Flask在当前模块运行,route()函数是一个装饰器,将请求的url映射到对应的函数上。上述代码将’/'与hello_world()函数进行绑定,因此在请求localhost:5000时,网页显示 Hello World 结果。

程序的启动是用过Flask类的run()方法在本地启动服务器应用程序。

app.run(host, port, debug, options)

其中参数是可选的。

image.png

二、主要内容

2.1 路由

在Flask中,路由是指用户请求的URL,与视图函数之间的映射。Flask通过利用路由表将URL映射到对应的视图函数,根据视图函数的执行结果,返回给WSGI服务器。路由表的内容是由开发者进行填充,主要有一下两个方式。

  • route装饰器:使用Flask应用实例的route装饰器将一个URL规则绑定到 一个视图函数上。
@app.route('/test')
 def test():
   return 'this is response of test function.'

通过装饰器的方式,Flask框架会将URL规则*/test绑定到视图函数test()*上。

add_url_rule() :该方法直接会在路由表中注册映射关系。其实route装饰器内部也是通过调用add_url_rule()方法实现的路由注册。

 def test():
   return 'this is response of test function.'
 app.add_url_rule('/test',view_func=test)

更多参考:

(1)视图装饰器的解读:https://dormousehole.readthedocs.io/en/latest/patterns/viewdecorators.html

(2)Flask视图和模板简介:https://www.jianshu.com/p/69ecf785a2ff

2.1.1 指定HTTP方法

默认情况下,Flask的路由支持HTTP的GET请求,如果需要视图函数支持HTTP的其他方法,可以通过methods关键字参数进行设置。关键字参数methods的类型为list,可以同时指定多种HTTP方法。

@app.route('/user', methods = ['POST', 'GET'])
def get_users():
  if request.method == 'GET':
    return ... # 返回用户列表
  else:
    return ... # 创建新用户 

2.1.2 匹配动态URL

动态URL用于当需要将同一类URL,映射到同一个视图函数处理,比如,使用同一个视图函数 来显示不同用户的个人信息。那么可以将URL中的可变部分使用一对小括号<>声明为变量, 并为视图函数声明同名的参数:

@app.route('/user/<uname>')
def get_userInfo(uname):
  return '%s\'s Informations' % uname

除了上述方式来设置参数,还可以在URL参数前添加转换器来转换参数类型:

@app.route('/user/<int:uname>')
def get_userInfo(uname):
    return '%s\'s Informations' % uname

使用该方法时,请求的参数必须是属于int类型,否则将会出现404错误。目前支持的参数类型转换器有:

image.png

比如不用转换器时,if的判断要逐个转类型:

# 从框架导入Flask类
from flask import Flask
# 初始化
app = Flask(__name__)
# 将根URL映射到hello world函数上 
@app.route('/user/<id>')
# 定义视图函数
def index(id):
    # 这里的id是字符串
    if id == '1':
        return 'python'
    if id == str(2):
        return 'django'
    if int(id) == 3:
        return 'flask'
    return 'Hello World'
if __name__ == '__main__':
    # 运行
   app.run()

用了转换器后,直接对route的参数转换类型:

# 从框架导入Flask类
from flask import Flask
# 初始化
app = Flask(__name__)
# 将根URL映射到hello world函数上 
@app.route('/user/<int:id>')
# 定义视图函数
def index(id):
    # 这里的id是字符串
    if id == 1:
        return 'python'
    if id == 2:
        return 'django'
    if id == 3:
        return 'flask'
    return 'Hello World'
if __name__ == '__main__':
    # 运行
   app.run()

2.1.3 匹配动态URL

为了满足一个视图函数可以解决多个问题,因此每个视图函数可以配置多个路由规则。

@app.route('/user')
@app.route('/user/<uname>')
@app.route('/user/<int:uname>')
def get_userInfo(uname=None):
    if uname:
      return '%s\'s Informations' % uname
    else:
        return 'this is all informations of users'

.1.4 URL构建方法

在很多时候,在一个实用的视图中需要指向其他视图的连接,为了防止路径出现问题,我们可以让Flask框架帮我们计算链接URL。简单地给url_for()函数传入一个访问点,它返回将是一个可靠的URL地址:

@app.route('/')
def hello():
    return 'Hello world!'
@app.route('/user/<uname>')
def get_userInfo(uname=None):
    if uname: return '%s\'s Informations' % uname
    else: return 'this is all informations of users'
@app.route('/test')
def test_url_for():
    print(url_for('hello'))  # 输出:/

添加URL变量 : 如果指定访问点对应的视图函数接收参数,那么关键字参数将生成对应的参数URL。下面的 示例将生成 /user/zhangsan:

@app.route('/')
def hello():
    return 'Hello world!'
@app.route('/user/<uname>')
def get_userInfo(uname=None):
    if uname:
        return '%s\'s Informations' % uname
    else:
        return 'this is all informations of users'
@app.route('/test')
def test_url_for():
    print(url_for('get_userInfo', uname='zhangsan'))  # 输出:/user/zhangsan
    print(url_for('test_url_for', num=2))  # 输出:/test?num=2

2.2 请求,响应及会话

对于一个完整的HTTP请求,包括:

(1)来自客户端的请求对象(Request);

(2)服务器端的响应对象(Respose)和会话对象(Session)等。

在Flask框架中,当然也具有这些对象,这些对象不仅可以在请求函数中使用,同时也可以在模板中使用。那我们来简单看看这些对象具体怎么使用。

2.2.1 请求对象 request

request包括前端发送过来的所有请求数据。

在Flask包中,可以直接引入request对象,其中包含Form,args ,Cookies ,files 等属性。

Form 是一个字典对象,包含表单当中所有参数及其值的键和值对;

args 是解析查询字符串的内容,它是问号(?)之后的URL的一部分,当使用get请求时,通过URL传递参数时可以通过args属性获取;

Cookies 是用来保存Cookie名称和值的字典对象;

files 属性和上传文件有关的数据。

(1)登陆栗子1

用request对象获取前端输入的账号密码,并且打印出对应信息。

# -*- coding: utf-8 -*-
"""
Created on Sat Dec 25 10:29:49 2021
@author: 86493
"""
from flask import Flask, render_template, request
app = Flask(__name__)
# 默认为GET请求
@app.route('/index', methods = ['GET', 'POST'])
def index():
    if request.method == 'GET':
        return render_template('index.html')
    if request.method == 'POST':
        name = request.form.get('name')
        password = request.form.get('password')
        print(name, password)
        return 'this is post'
if __name__ == '__main__':
    app.run()

其中我们的html文件为:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method = "post">
    账号:
    <input type="text" name="name">
    <br>
    密码:
    <input type="password" name="password">
    <br>
    <input type="submit" name="submit">
</form>
</body>
</html>

控制台的结果为:

127.0.0.1 - - [25/Dec/2021 10:38:06] "POST /index HTTP/1.1" 200 -
andyguo 123456

(2)登陆栗子2

我们以一个登陆的例子看看如何搭配使用这些属性。

from flask import request, session, make_response
@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == 'POST':
        if request.form['username'] == 'admin':
            session['username'] = request.form['username']
            response = make_response('Admin login successfully!')
            response.set_cookie('login_time', time.strftime('%Y-%m-%d %H:%M:%S'))
            return 'Admin login successfully!'
        else:
            return 'No such user!'
    elif request.method == 'GET':
        if request.args.get("username") == 'admin':
            session['username'] = request.form['username']
            return 'Admin login successfully!'
        else:
            return 'No such user!'
app.secret_key = '123456'

上述代码中,可以根据method属性判断当前请求的类型,通过form属性可以获取表单信息,并通过session来存储用户登陆信息。特别提醒,使用session时一定要设置一个密钥app.secret_key,并且密钥要尽量复杂。

我们可以使用make_response的方法就是用来构建response对象的第二个参数代表响应状态码,缺省就是”200”。response对象的详细使用可参阅Flask的官方API文档。通过创建的response对象可以使用response.set_cookie()函数,来设置Cookie项,之后这个项值会被保存在浏览器中,等下次请求时可以从request对象中获取到cookies对象。

由于现在前后端的交互会采用json的数据格式进行传输,因此让前端请求的数据是json类型的时候,可以使用get_data()方法来获取。

from flask import Flask, jsonify, request
@app.route('/login', methods=["POST"])
def login():
    request_str = request.get_data()
    request_dict = json.loads(request_str)

获取json数据之后,可以使用flask中的jsonify对象来处理json类型数据。

2.2.2 响应对象 response

(1)引栗

# -*- coding: utf-8 -*-
"""
Created on Sat Dec 25 11:00:13 2021
@author: 86493
"""
from flask import Flask, make_response, json
# 前后端数据的交互这里用json格式数据
app = Flask(__name__)
@app.route('/index')
def index():
    data = {
        'name': '张三'
    }
    # 把python的字典转为json的字符串
    response = make_response(json.dumps(data, ensure_ascii = False)) 
    # json的数据格式
    response.mimetype = 'application/json'
    return response
if __name__ == '__main__':
    app.run()

运行上面代码后,返回页面'name': '张三',现在的response是json数据(如下图):

image.png

当然为了减少代码量,可以使用直接jsonify(data),但是要在开头添加上属性app.config

from flask import Flask, make_response, json, jsonify
# 前后端数据的交互这里用json格式数据
app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False 
@app.route('/index')
def index():
    data = {
        'name': '张三'
    }
    # 把python的字典转为json的字符串
    # response = make_response(json.dumps(data, ensure_ascii = False)) 
    # json的数据格式
    # response.mimetype = 'application/json'
    # return response
    return jsonify(data)
if __name__ == '__main__':
    app.run()

如果视图函数想向前端返回数据,必须是Response的对象, 主要将返回数据的几种方

式:

(2)视图函数 return 多个值

@app.route("/user_one")
def user_one():
    return "userInfo.html", "200 Ok", {"name": "zhangsan"; "age":"20"}

当return多个值的时候,第一个是字符串,也是网页的内容;"200 Ok"表示状态码及解析;{“name”: “zhangsan”; “age”:“20”} 表示请求头。其中前面两个值是必须要的并且顺序不能改变,请求头不是必须要的,这样Flask会自动将返回的值转换成一个相应对象。如果返回一个字符串,则Response将该字符串作为主体,状态码为200,然后返回该Response对象。

(3)使用Response创建

可以通过直接创建Response对象,配置其参数。

from flask import Response
@app.route("/user_one")
def user_one():
    response = Response("user_one")
    response.status_code = 200
    response.status = "200 ok"
    response.data = {"name": "zhangsan"; "age":"20"}
    return response

(4)使用make_response函数

make_response 函数可以传递三个参数 第一个是一个字符串,第二个传状态码,第三个传请求头。

@app.route("/user_one")
def user_one():
    response = make_response('user_one', 200, {"name": "zhangsan"; "age":"20"})
    return response

由于现在前后端交互往往采用的是json的数据格式,因此可以将数据通过 jsonify 函数将其转化成json格式,再通过response对象发送给前端。

@app.route('/hot_list', methods=["GET"])
def hot_list():
    if request.method == "GET":
        user_id = request.args.get('user_id')
        page_id = request.args.get('page_id')
        if user_id is None or page_id is None:
            return make_response(jsonify({"code": 2000, "msg": "user_id or page_id is none!"}), 200)

2.3 重定向与错误处理

2.3.1 重定向

【写法一】先举一个小栗子,如果想让自己的网页地址输入后跳转到百度页面(重定向):

# -*- coding: utf-8 -*-
"""
Created on Sat Dec 25 10:42:44 2021
@author: 86493
"""
# 重定向 302 
from flask import Flask, redirect
app = Flask(__name__)
@app.route('/index')
def index():
    return redirect('https://www.baidu.com')
if __name__ == '__main__':
    app.run()

【写法二】另一种重定向写法:重定向到另一个hello函数中(如下),则页面输出this is hello fun

# -*- coding: utf-8 -*-
"""
Created on Sat Dec 25 10:42:44 2021
@author: 86493
"""
# 重定向 302 
from flask import Flask, redirect, url_for
app = Flask(__name__)
@app.route('/index') 
def index():
    return redirect(url_for('hello'))
@app.route('/')
def hello():
    return "this is hello fun"
if __name__ == '__main__':
    app.run()

重定向:当一个请求过来后可能还需要在请求另一个视图函数才能达到目的,那么就可以调用redirect(location, code=302, Response=None)函数指定重定向页面。

from flask import Flask, redirect, url_for
app = Flask(__name__)
@app.route("/demo")
def demo():
    url = url_for("demo2")  # 路由反转,根据视图函数名获取路由地址
    return redirect(url)
@app.route("/demo2")
def demo2():
    return "this is demo2 page"
@app.route("/")
def index():
    # 使用方法:redirect(location, code=302, Response=None) 
    return redirect("/demo", 301) 

常用重定向状态码如下,更多状态码可以回顾:HTTP状态码(HTTP Status Code)

image.png

2.3.2 错误处理

当请求或服务器出现错误的时候,我们希望遇到特定错误代码时重写错误页面,可以使用 errorhandler() 装饰器:

from flask import render_template
@app.errorhandler(404)
def page_not_found(error):
    return render_template('page_not_found.html'), 404

当遇到404错误时,会调用page_not_found()函数,返回元组数据,第一个元素是”page_not_found.html”的模板页,第二个元素代表错误代码,返回值会自动转成 response 对象。

2.4 SQLAlchemy

官网对应SQLAlchemy部分

SQLAlchemy 是一个功能强大的Python ORM 工具包,为应用程序开发人员提供了SQL的全部功能和和ORM操作。其中ORM(Object Relation Mapping)指的是将对象参数映射到底层RDBMS表结构的技术,ORM API提供了执行CRUD操作的方法,不需要程序员编写原始SQL语句。

2.4.1 安装

通过下面指令可以进行安装:

pip install SQLalchemy

在连接数据库时,我们使用pymysql框架进行连接,因此还需要使用下面指令下载pymysql框架:

pip install pymysql

2.4.2 创建连接

from sqlalchemy import create_engine
def mysql_db(host='127.0.0.1',dbname='3306'):
    engine = create_engine("mysql+pymysql://root:123456@{}:49168/{}?charset=utf8".format(host,dbname))
    print(engine)  # Engine(mysql+pymysql://root:***@127.0.0.1:49168/3306?charset=utf8)

通过create_engine函数已经创建了Engine,在Engine内部实际上会创建一个Pool(连接池)和Dialect(方言),并且可以发现此时Engine并不会建立连接,只会等到执行到具体的语句时才会连接到数据库。上述代码默认本地已经存在并开启mysql服务。

对于 create_engine 函数可以有以下参数

create_engine("mysql://user:password@hostname/dbname?charset=utf8",
                       echo=True,
                       pool_size=8,
                       pool_recycle=60*30)

第一个参数是和框架表明连接数据库所需的信息,“数据库+数据库连接框架://用户名:密码@IP地址:端口号/数据库名称?连接参数”;关于配置键可以参考官方文档。

echo是设置当前ORM语句是否转化为SQL打印;

pool_size是用来设置连接池大小,默认值为5;

pool_recycle设置连接失效的时间,超过时间连接池会自动断开。

2.4.3 创建数据库表类

由于SQLAlchemy 是对象关系映射,在操作数据库表时需要通过操作对象实现,因此就需要创建一个数据库表类。

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
    __tablename__ = 'UserInfo'
    index = Column(Integer(), primary_key=True)
    user_id = Column(Integer(), unique=True)
    username = Column(String(30))
    passwd = Column(String(500))
    def __init__(self,index, user_id, username, passwd):
        self.index = index
        self.user_id = user_id
        self.username = username
        self.passwd = passwd

通过declarative_base()函数,可以将python类和数据库表进行关联映射,并通过 __tablename__ 属性将数据库模型类和表进行管理。其中Column() 表示数据表中的列,Integer()和String()表示数据库的数据类型。

2.4.4 操作数据库

创建完连接之后,我们需要借助sqlalchemy中的session来创建程序与数据库之间的会话。换句话来说,需要通过session才能利用程序对数据库进行CURD,增加(Create)、检索(Retrieve)、更新(Update)和删除(Delete)。这里我们可以通过 sessionmaker()函数来创建会话。

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
def mysql_db(host='127.0.0.1',dbname='test'):
    engine = create_engine("mysql+pymysql://root:123456@{}:49168/{}?charset=utf8mb4".format(host,dbname))
    session = sessionmaker(bind=engine)
    Base.metadata.create_all(engine)
    return engine, session()

session常用的方法如下:

  • flush:预提交,提交到数据库文件,还未写入数据库文件中
  • commit:提交了一个事务
  • rollback:回滚
  • close:关闭session连接

(1)增加数据

增加一个用户:

engine, session = mysql_db()
user = User("100","zhangsan","11111")
session.add(user)
session.commit()

注意一点,session.add()不会直接提交到数据库,而是在 commit 时才会提交到数据库。add操作会把user加入当前session维护的持久空间(可以从session.dirty看到)中。

也可以通过add_all() 进行批量提交。

engine, session = mysql_db()
user1 = User("101","lisi","11111")
user2 = User("102","wangwu","22222")
session.add_all([user1,user2])
session.commit()

(2)查询数据

engine, session = mysql_db()
users = session.query(User).filter_by(passwd='11111').all()
for item in users:
    print(item.username,item.passwd)

通过上面代码可以查询获取数据,通过 session.query() 我们查询返回了一个Query对象,此时没有去数据库查询,只有等到.count() .first() .all() 具体函数时才会去数据库执行。还可以使用 filter() 方法查询,与 filter_by() 的区别如下:

image.png

(3)修改数据

通过 query 中的 update() 方法:

session.query(User).filter_by(username="zhangsan").update({'passwd': "123456"})

或者

users = session.query(User).filter_by(username="zhangsan").first()
users.username = "zhangsan-test"
session.add(users)
session.commit()

(4)删除数据

通过 query 中的 delete() 方法:

session.query(User).filter(User.username == "zhangsan-test").delete()
session.commit()

或者 通过 session.delete() 方法

users = session.query(User).filter(User.username == "lisi").first()
if users:
    session.delete(users)
    session.commit()
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
前端开发 JavaScript 数据库连接
一、Flask入门介绍
一、Flask入门介绍
41 1
|
1月前
|
Python
Flask学习笔记(二):基于Flask框架上传图片到服务器端并原名保存
关于如何使用Flask框架上传图片到服务器端并以其原名保存的教程。
74 1
|
1月前
|
Python
Flask学习笔记(三):基于Flask框架上传特征值(相关数据)到服务器端并保存为txt文件
这篇博客文章是关于如何使用Flask框架上传特征值数据到服务器端,并将其保存为txt文件的教程。
31 0
Flask学习笔记(三):基于Flask框架上传特征值(相关数据)到服务器端并保存为txt文件
|
1月前
|
JSON 测试技术 数据库
Python的Flask框架
【10月更文挑战第4天】Python的Flask框架
|
1月前
|
存储 安全 数据库
Flask框架中,如何实现用户身份验证和会话管理?
【10月更文挑战第4天】Flask框架中,如何实现用户身份验证和会话管理?
|
1月前
|
存储 SQL 数据库
使用Python和Flask框架创建Web应用
【10月更文挑战第3天】使用Python和Flask框架创建Web应用
26 1
|
2月前
|
JSON 测试技术 数据库
Python的Flask框架
Python的Flask框架
|
2月前
|
安全 数据安全/隐私保护 Python
基于Flask框架实现一个简易后台用户登录系统
基于Flask框架实现一个简易后台用户登录系统
|
30天前
|
Unix 中间件 数据库
bottle flask 框架对比
Flask:Flask是一个轻量级的Web应用框架, 使用Python编写。
30 0
|
2月前
|
JSON 测试技术 数据库
Python的Flask框架
Python的Flask框架