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及其子类

------------------------------------------------------------------------------------------------------------------------------

posted @ 2020-04-04 16:45  Alpha205  阅读(229)  评论(0编辑  收藏  举报