Web后端学习笔记 Flask(1)基础知识
基础的准备
1. 使用的python版本 python3.6
2. pycharm编辑器
3. 安装python虚拟环境:
python中的虚拟环境:
python中的虚拟环境相当于一个抽屉,在这个抽屉中安装的任何python包都不会影响到其他的抽屉。
通过pip install virtualenv来安装python的虚拟环境,如果安装出现下面的错误(Read time out,就是下载连接超时,更换到国内的镜像源即可)
更换到国内镜像源有两种方法:
a. 通过安装命令指定镜像源
b. 同构修改python中的配置文件,永久的修改下载镜像源
这里选择通过安装命令临时修改镜像源:
pip install virtualenv -i https://pypi.douban.com/simple/
安装virtualenvwrapper,这个软件包可以使得管理虚拟环境更加的简单,不用再到某个目录下通过virtualenv来创建虚拟环境,并且激活的时候也不不用到特定的目录下去
pip install virtualenvwrapper-win -i https://pypi.douban.com/simple/
virtualenvwrapper的基本使用:
1. 创建虚拟环境
mkvirtualenv flask-env
2. 切换到某个虚拟环境(进入虚拟环境)
workon flask-env
3. 退出当前虚拟环境
deactivate
4. 删除某个虚拟环境
rmvirtualenv flask-env
5. 列出所有虚拟环境
lsvirtualenv
6. 进入虚拟环境所在的目录
cdvirtualenv
7. 修改虚拟环境的路径,
在环境变量->系统变量中添加一个参数WORKON_HOME,将这个参数的值设定为你需要的路径
8. 创建虚拟环境的时候指定python的版本‘
mkvirtualenv --python=D:/Python36/python.exe flask-env
URL组成部分详解:
URL是Uniform Resource Locator的简写,统一资源定位符。
一个URL由以下几部分组成:
scheme://host:port/path/?query-string=xxx#anchor
scheme:代表的是访问的协议,一般为http或者https以及ftp等。
host:主机名,域名,比如www.douban.com。
port:端口号。当你访问一个网站的时候,浏览器默认使用80端口。
path:查找路径,比如:www.jianshu.com/trending/now,后面的trending/now就是path。
query-string:查询字符串,比如:www.baidu.com/s?wd=python,后面的wd=python就是查询字符串。
anchor:锚点,后台一般不用管,前端用来做页面定位的。比如:
Web服务器:负责处理http请求,响应静态文件,常见的有Nginx
应用服务器:负责处理逻辑的服务器,例如编写的python代码,是不能直接通过nginx这种web服务器来处理的,只能通过应用服务器来处理,常见的应用服务器有uwsgi、tomcat等。
web应用框架:一般使用某种语言,封装了常用的web功能的框架就是web应用框架,flask、Django框架都是web应用框架。
Flask简介:
flask是一款非常流行的python web框架,它是一个微框架,具有简洁的特点,且flask和相关的依赖设计的非常优秀。
打开创建的虚拟环境:创建第一个flask程序
from flask import Flask
app = Flask(__name__)
# __name__参数的所用:
# 1. 可以规定模板和静态文件的查找路径
# 2. 以后如果flask的插件,比如flask-migrate, flask-sqlalchemy如果报错,
# 那么flask可以通过这个参数找到具体的错误位置
# @app.route()的作用是将url映射到对应的视图函数上面
@app.route('/')
def hello_world():
return 'Hello vv World!'
if __name__ == '__main__':
app.run(port=8000) # app.run()是flask中的一个测试服务器
flask的debug模式
在pycharm中,在程序中开始debug=True之后,需要在IDE中下面的位置:
点击Edit Configurations, 然后再勾选FLASK_DEBUG,这样就可以开启debug模式
1. 开始debug模式,在代码中如果抛出了异常,在浏览器的页面中可以看到具体错误信息以及错误代码
2. 如果不开启debug模式,则看不到相应的错误信息
3. 开启debug模式之后,再修改完代码后,不用再重新手动运行代码,按下CTRL+S,flask会自动将修改后的代码加载。此时只需要重新刷新网页即可。
flask中的配置文件
一般再开发中,我们会将整个项目的配置放到一个文件中,这样会便于整个项目的管理,可以在flask目录下新建一个config.py文件,用于存放整个项目的所有配置:
1. 首先import config
app.config.from_object(config)加载配置文件
2. 在不想import config.py文件的时候,可以通过另一种方式导入:
参数silent=false:如果加载的配置文件不存在,就会报错,如果silent=true,则即使加载的文件不存在,也不会报错
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
---------------------------------------------------------------------------------------------
URL与视图函数的映射
如何在url中传递参数:
在flask中,通过装饰器app.route()将url与视图函数映射起来
from flask import Flask
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/list/')
def article_list():
return "article list"
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
如果需要在URL中传递参数,则可以按照如下的方式进行:同时视图函数也必须定义同名的形参
@app.route('/article/<article_id>/') # url最好以/结尾
def article_detail(article_id):
return "The article id is %s" % article_id
如果传递的参数的数据类型是某一个确定的类型,则可以进行指定:
@app.route('/article/<int:article_id>/') # url最好以/结尾
def article_detail(article_id):
return "The article id is %s" % article_id
其他的数据类型还有:
还有一种特殊的数据类型: uuid 通用唯一识别码(Universally Unique Identifier)
这种字符串中的每一个能够保证唯一性,所以可以作为数据库中的主键进行使用,在python中可以通过uuid模块生成uuid
import uuid
print(uuid.uuid4())
为了保证UUID的唯一性,规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。UUID的复杂特性在保证了其唯一性的同时,意味着只能由计算机生成。UUID是不能人工指定的,除非你冒着UUID重复的风险。UUID的复杂性决定了“一般人“不能直接从一个UUID知道哪个对象和它关联。
例如,在某网站需要访问博客或者用户,那么可以按照一般的方法,定义两个视图函数
# .../blog/<id>/ 访问博客
#.../user/<id>/ 访问某一位作者
还可以按照另一种方式实现,将两个url放到一个当中,可以通过any()实现
@app.route('/<any(blog, user):url_pass>/<id>/') # url_pass用于存放blog或者user
def get_detail(url_pass, id):
if url_pass == "blog":
return "You get blog detail %s" % id
else:
return "You get user detail %s" % id
此时:
http://127.0.0.1:5000/blog/12/
http://127.0.0.1:5000/user/12/
两个链接均可以映射到同一个视图函数
总结:
传递参数的数据类型可以总结为:
int, float, string, path, uuid, any
获取客户端发送的参数
在get方法中,用户会通过查询字符串的方式向服务端传送数据,在服务端,flask可以按照以下的方式获取参数
from flask import Flask
from flask import request
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/day/')
def get_day():
username = request.args.get("user")
passwd = request.args.get("password")
return "User: %s password: %s" % (username, passwd)
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
则通过url获取的参数如下:
所以接受用户传递的参数有两种方式:
1. 通过path的形式,将参数嵌入到路径当中,进行参数传递
2. 使用查询字符串的方式,即通过“?key=value”的形式
采用第一种形式有利于搜索引擎优化(SEO),而第二种方式不利于搜索引擎优化
flask: url_for使用详解:
app.route()用于将url映射到视图函数,而url_for则正好与之相反,可以通过视图函数找到对应的url
from flask import Flask
from flask import url_for
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
@app.route('/')
def hello_world():
print(url_for("my_list")) # 视图函数对应的url 输出/list/
return 'Hello World!'
@app.route('/list/')
def my_list(page):
return "My list is here %d" % page
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
还可以获取url中具有参数的视图函数的url:
from flask import Flask
from flask import url_for
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
@app.route('/')
def hello_world():
print(url_for("my_list", page=1)) # 视图函数对应的url 输出/list/
return 'Hello World!'
@app.route('/list/<page>')
def my_list(page):
return "My list is here %d" % page
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
url_for的优点:
1. 如果修改了url,就不用去手动拼接替换url
2. url_for会自动处理特殊字符,例如\,会自动对\进行编码,不用去手动处理
url_for中的next参数,相当于给url中添加一个查询字符串,字符串的形式为 ?next="xxx"
自定义url转换器
例如,在一个url中传递的参数中有手机号码,为了对手机号码这一参数进行严格的限制,需要自定义url转换器:
所有上述介绍的转换器的定义都在一个称为BaseConverter的包中:
from werkzeug.routing import BaseConverter
在这个包中,不同的转换器都是继承自基类BaseConverter
例如:
class UnicodeConverter(BaseConverter):
"""This converter is the default converter and accepts any string but
only one path segment. Thus the string can not include a slash.
This is the default validator.
Example::
Rule('/pages/<page>'),
Rule('/<string(length=2):lang_code>')
:param map: the :class:`Map`.
:param minlength: the minimum length of the string. Must be greater
or equal 1.
:param maxlength: the maximum length of the string.
:param length: the exact length of the string.
"""
def __init__(self, map, minlength=1, maxlength=None, length=None):
BaseConverter.__init__(self, map)
if length is not None:
length = "{%d}" % int(length)
else:
if maxlength is None:
maxlength = ""
else:
maxlength = int(maxlength)
length = "{%s,%s}" % (int(minlength), maxlength)
self.regex = "[^/]" + length
class NumberConverter(BaseConverter):
"""Baseclass for `IntegerConverter` and `FloatConverter`.
:internal:
"""
weight = 50
def __init__(self, map, fixed_digits=0, min=None, max=None, signed=False):
if signed:
self.regex = self.signed_regex
BaseConverter.__init__(self, map)
self.fixed_digits = fixed_digits
self.min = min
self.max = max
self.signed = signed
def to_python(self, value):
if self.fixed_digits and len(value) != self.fixed_digits:
raise ValidationError()
value = self.num_convert(value)
if (self.min is not None and value < self.min) or (
self.max is not None and value > self.max
):
raise ValidationError()
return value
def to_url(self, value):
value = self.num_convert(value)
if self.fixed_digits:
value = ("%%0%sd" % self.fixed_digits) % value
return str(value)
@property
def signed_regex(self):
return r"-?" + self.regex
class IntegerConverter(NumberConverter):
"""This converter only accepts integer values::
Rule("/page/<int:page>")
By default it only accepts unsigned, positive values. The ``signed``
parameter will enable signed, negative values. ::
Rule("/page/<int(signed=True):page>")
:param map: The :class:`Map`.
:param fixed_digits: The number of fixed digits in the URL. If you
set this to ``4`` for example, the rule will only match if the
URL looks like ``/0001/``. The default is variable length.
:param min: The minimal value.
:param max: The maximal value.
:param signed: Allow signed (negative) values.
.. versionadded:: 0.15
The ``signed`` parameter.
"""
regex = r"\d+"
num_convert = int
class FloatConverter(NumberConverter):
"""This converter only accepts floating point values::
Rule("/probability/<float:probability>")
By default it only accepts unsigned, positive values. The ``signed``
parameter will enable signed, negative values. ::
Rule("/offset/<float(signed=True):offset>")
:param map: The :class:`Map`.
:param min: The minimal value.
:param max: The maximal value.
:param signed: Allow signed (negative) values.
.. versionadded:: 0.15
The ``signed`` parameter.
"""
regex = r"\d+\.\d+"
num_convert = float
def __init__(self, map, min=None, max=None, signed=False):
NumberConverter.__init__(self, map, min=min, max=max, signed=signed)
系统中已经预定义的转换器如下所示:
#: the default converter mapping for the map.
DEFAULT_CONVERTERS = {
"default": UnicodeConverter,
"string": UnicodeConverter,
"any": AnyConverter,
"path": PathConverter,
"int": IntegerConverter,
"float": FloatConverter,
"uuid": UUIDConverter,
}
所以,可以按照转换器的定义方法,自定义手机号码转换器,定义一个视图函数,并且将电话号码作为参数传入到url中:
from flask import Flask
from werkzeug.routing import BaseConverter
import re
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
# 自定义转换器
class TelephoneConverter(BaseConverter):
regex = r'1[0-9]{9}[0-9]'
app.url_map.converters["tel"] = TelephoneConverter # 将自定义的转换器添加到预定义的转换器中
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/telephone/<tel:phone>/')
def user_phone(phone):
return "The user phone is %s" % phone
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
总结:
1. 实现自定义转换器,必须实现一个类,继承自基类BaseConverter
2. 在自定义的类中,必须重写正则表达式regex
3. 将自定义的类映射到app.url_map.converters["tel"] = xxx
转换器除了对传入到url中的参数的数据类型可以限制外,还具有真正的转换功能,例如,对传入到url中的参数进行解析并返回,此时,如果对url传入某种格式的参数,那个url对应的视图函数此时接受到的参数,实际上是经过Converter转换过的参数,可以直接使用。例如,对于 /blog/ai+c/表示需要查找分类为AI和C语言的所有博客:此时,就可以利用Converter对传入的参数ai+c进行转换,使得视图函数得到的参数为[ai, c],需要实现这种功能,需要在Converter中实现to_python()方法:Converter中还有一个方法,即to_url(),这个方法实现的功能正好与to_python()方法相反,通过实现to_url方法,能够获取到带参数的视图函数对应的url
from flask import Flask, url_for
from werkzeug.routing import BaseConverter
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
# 自定义转换器
class TelephoneConverter(BaseConverter):
regex = r'1[0-9]{9}[0-9]'
# 自定义转换器
class ListConverter(BaseConverter):
def to_python(self, value): # 需要实现转换功能,必须实现to_python方法,将url中的参数经过解析传递给视图函数
if "+" in value:
res = value.split("+")
else:
res = [value]
return res
def to_url(self, value): # 会在调用url_for的时候生成符合要求的url形式
return "+".join(value)
app.url_map.converters["tel"] = TelephoneConverter # 将自定义的转换器添加到预定义的转换器中
app.url_map.converters["list"] = ListConverter # List 转换器
@app.route('/')
def hello_world():
print(url_for("get_blog", category=["aa", "bb"]))
return 'Hello World!'
@app.route('/telephone/<tel:phone>/')
def user_phone(phone):
return "The user phone is %s" % phone
@app.route('/blog/<list:category>/')
def get_blog(category):
res = ""
for x in category:
res = res + x + "|"
return "The blog %s" % res[:-1]
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
转换的结果:
例如,输入的url中包含参数,/a+b/,转换器可以对这个参数提前进行解析,使得传递给视图函数的是解析过的参数[a, b]
to_url的效果:
例如视图函数get_blog( ),通过url_for生成他的url是,可以将传入的参数进行转换,在嵌入到url中,得到的url中包含/a+b/
其他的小细节:
1. 在局域网中让其他电脑能访问到我的网站:
修改host="0.0.0.0"
2. 指定端口号
flask项目,默认使用的是5000端口,可以通过port=8000来设置端口号
3. url唯一性
在定义url的时候,要在最后加上/,同时搜索引擎会将加/和不加/的url视为两个url,但实际上这是同一个url,因此这样不利于SEO
4. get请求与post请求
get: 只会在服务器上获取资源,不会影响服务器的状态,这种请求方式推荐使用get请求。get请求会将请求参数放到url当中 。
post: 会给服务器提交一些数据以及文件,会对服务器的状态产生影响。这种条件下推荐post请求
5. flask中,默认的请求方式为GET请求,可以通过methods参数进行设置
重定向:
重定向分为永久重定向和暂时重定向,在页面上体现的操作就是浏览器会从一个页面跳转到另一个页面。比如在访问一个网站的过程中,用户访问了一个需要登陆的页面,当时用户当前没有登陆,所以需要给用户跳转到登陆界面,让其重新登陆才可以。
永久重定向:http的状态码是301,多用于旧的网址被废弃,用户需要转到一个新的网址。例如在浏览器访问www.jingdong.com,会被永久重定向到www.jd.com,因为网址www.jingdong.com已被废弃。
暂时重定向:http的状态码是302,表示页面暂时性的跳转了,比如访问一个需要权限的网址,如果用户当前没有登陆,应该暂时重定向到登陆页面。
在flask中,通过redirect(locaton, code=302)来实现重定向的。
from flask import Flask, url_for, request, redirect
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
@app.route('/')
def hello_world():
return 'Hello Wold!'
@app.route('/login/') # 登陆页面
def login():
return "Login page"
@app.route('/profile/') # 用户详情页
def profile():
name = request.args.get("name")
if name:
return "User: %s" % name
else:
return redirect(url_for("login"), code=302)
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
视图函数Response返回值详解:
视图函数应该返回什么样的值?
视图函数的返回值会被自动转化为一个响应对象,flask的转化过程如下:
1. 如果返回值是Response对象,则直接返回
2. 返回值是一个字符串,那么flask会创建一个werkzeug.wrappers.Response对象,Response将该字符串作为主体,状态码为200,MIME类型为text/html.然后返回该Response对象
3. 如果返回的是一个元组,元组中的数据类型是(response,status,headers)。status值会覆盖默认的200状态码,headers可以是一个列表或者字典,作为额外的消息头。
4. 如果以上条件都不满足,flask会假设返回值是一个合法的WSGI应用程序,并通过Response.force_type()转换为一个请求对象。
flask = werkzeug(处理网络请求相关的) + sqlalchemy(处理数据库相关) + jinja2(html模板相关)
自定义响应:
Restful-API都是通过JSON的形式进行传递的,如果后台跟前台进行交互的时候,所有的url都是发送JSON数据,则可以定义一个叫做JSONResponse的类来代替Flask自带的Response类。
自定义响应必须满足以下三个条件:
1. 必须继承自Response类
2. 必须实现force_type(cls, rv, environ=None)
3.必须指定app.response_class为自定义的Response
4. 如果视图函数返回的数据不是字符串,也不是元组,也不是Response对象,那么就会将返回值传递给force_type,然后再将response_type的返回值返回给前端。
from flask import Flask, Response, jsonify
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
# 将视图函数中返回的字典,转换为JSON对象,然后返回
class JSONResponse(Response):
@classmethod
def force_type(cls, response, environ=None):
"""
这个方法只有视图函数返回,非字符串,非元组,非Response对象的时候才会调用
:param response:
:param environ:
:return:
"""
if isinstance(response, dict):
# jsonify将字典转换为json对象,且包装成Response对象
response = jsonify(response)
return super(JSONResponse, cls).force_type(response, environ)
app.response_class = JSONResponse
@app.route('/')
def hello_world():
return 'Hell Wold!'
@app.route('/list/')
def list_1():
return {"name": "lili", "age": 26} # 此时返回的是字典,所以会调用上面的JSONResponse方法
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
视图函数可以返回的类型:
1. 可以返回字符串,flask底层会将这个字符串包装成Response对象
2. 可以返回元组,元组的形式是:(响应体,状态码,头部信息),返回的元组其实在底层也是封装成Response对象
3. 可以返回Response及其子类
------------------------------------------------------------------------------------------------------------------------------