Python Flask框架学习笔记

一、URL与视图

1、Flask简介

flask是一款非常流行的Python Web框架,出生于2010年,作者是Armin Ronacher,本来这个项目只是作者在愚人节的一个玩笑,后来由于非常受欢迎,进而成为一个正式的项目。

flask自2010年发布第一个版本以来,大受欢迎,深得开发者的喜爱,目前在Github上的Star数已经超过55.5k了,有超Django之趋势。flask能如此流行的原因,可以分为以下几点:

  • 微框架、简洁、只做他需要做的,给开发者提供了很大的扩展性。
  • Flask和相应的插件写得很好,用起来很爽。
  • 开发效率非常高,比如使用SQLAlchemy的ORM操作数据库可以节省开发者大量书写sql的时间。

Flask的灵活度非常之高,他不会帮你做太多的决策,一些你都可以按照自己的意愿进行更改。比如:

  • 使用Flask开发数据库的时候,具体是使用SQLAlchemy还是MongoEngine,选择权完全掌握在你自己的手中。区别于Django,Django内置了非常完善和丰富的功能,并且如果你想替换成你自己想要的,要么不支持,要么非常麻烦。
  • 把默认的Jinija2模板引擎替换成其他模板引擎都是非常容易的。

2、安装Flask

在终端输入命令 pip install flask 即可安装。
在这里插入图片描述

3、新建第一个flask程序

新建项目中框架选择需pycharm专业版才有这功能

在这里插入图片描述

4、运行flask项目

在这里插入图片描述
在这里插入图片描述

在浏览器中输入http://127.0.0.1:5000就能看到hello world了。需要说明一点的是,app.run这种方式只适合于开发,如果在生产环境中,应该使用Gunicorn或者uWSGI来启动。如果是在终端运行的,可以按ctrl+c来让服务停止。
在这里插入图片描述

5、设置为DEBUG模式

默认情况下flask不会开启DEBUG模式,开启DEBUG模式后,flask会在每次保存代码的时候自动的重新载入代码,此时网页直接刷新就能看到效果,并且如果代码有错误,会在终端进行提示。
在这里插入图片描述
在这里插入图片描述
需要注意的是,只能在开发环境下开启DEBUG模式,因为DEBUG模式会带来非常大的安全隐患。

6、配置文件

Flask项目的配置,都是通过app.config对象来进行配置的。比如要配置一个项目的SECRET_KEY,那么可以使用app.config[‘SECRET_KEY’] = "xxx"来进行设置

常用有这几种方法:

1、在py文件中直接硬编码:

缺点:需要一个个写,涉及文件多的话显得很累赘
在这里插入图片描述

2、将所有配置项写成一个配置文件,然后使用者进行模块导入

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Flask项目内置了许多的配置项,所有的内置配置项,可以点这查看~

7、URL与视图函数的映射

在这里插入图片描述
在这里插入图片描述
@app.route()是什么?

  • 在Python中,只要是带着@的,基本上就是装饰器,装饰器的本质是扩展原本函数功能的一种函数
  • 而这里的app.route(‘URL’)就是在Flask框架中非常重要的一个装饰器,它的作用是在程序运行时,装饰一个视图函数,用给定的URL规则和选项注册它,这里不理解也无所谓,能用即可。

从之前的helloworld.py文件中,我们已经看到,一个URL要与执行函数进行映射,使用的是@app.route装饰器。@app.route装饰器中,可以指定URL的规则来进行更加详细的映射,比如现在要映射一个文章详情的URL,文章详情的URL是/article/id/,id有可能为1、2、3…,那么可以通过以下方式:

@app.route('/article/<id>/')
def article(id):
   return '%s article detail' % id

其中,尖括号是固定写法,语法为,variable默认的数据类型是字符串。如果需要指定类型,则要写成converter:variable,其中converter就是类型名称,可以有以下几种:

  • string: 默认的数据类型,接受没有任何斜杠/的字符串。
  • int: 整形
  • float: 浮点型。
  • path: 和string类似,但是可以传递斜杠/。
  • uuid: uuid类型的字符串。
  • any:可以指定多种路径,这个通过一个例子来进行说明:
@app.route('/<any(article,blog):url_path>/')
def item(url_path):
  return url_path

以上例子中,item这个函数可以接受两个URL,一个是/article/,另一个是/blog/。并且,一定要传url_path参数,当然这个url_path的名称可以随便。

如果不想定制子路径来传递参数,也可以通过传统的?=的形式来传递参数,例如:/article?id=xxx,这种情况下,可以通过request.args.get(‘id’)来获取id的值。如果是post方法,则可以通过request.form.get(‘id’)来进行获取

8、视图转URL(url_for)

一般我们通过一个URL就可以执行到某一个函数。如果反过来,我们知道一个函数,怎么去获得这个URL呢?url_for函数就可以帮我们实现这个功能。url_for()函数接收两个及以上的参数,他接收函数名作为第一个参数,接收对应URL规则的命名参数,如果还出现其他的参数,则会添加到URL的后面作为查询参数。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

9、指定指定(methods)HTTP方法:

在@app.route()中可以传入一个关键字参数methods来指定本方法支持的HTTP方法,默认情况下,只能使用GET请求,看以下例子:
在这里插入图片描述
以上装饰器将让login的URL既能支持GET又能支持POST。

10、页面跳转和重定向(redirect):

重定向分为永久性重定向和暂时性重定向,在页面上体现的操作就是浏览器会从一个页面自动跳转到另外一个页面。比如用户访问了一个需要权限的页面,但是该用户当前并没有登录,因此我们应该给他重定向到登录页面。

  • 永久性重定向:http的状态码是301,多用于旧网址被废弃了要转到一个新的网址确保用户的访问,最经典的就是京东网站,你输入www.jingdong.com的时候,会被重定向到www.jd.com,因为jingdong.com这个网址已经被废弃了,被改成jd.com,所以这种情况下应该用永久重定向。
  • 暂时性重定向:http的状态码是302,表示页面的暂时性跳转。比如访问一个需要权限的网址,如果当前用户没有登录,应该重定向到登录页面,这种情况下,应该用暂时性重定向。

在flask中,重定向是通过flask.redirect(location,code=302)这个函数来实现的,location表示需要重定向到的URL,应该配合之前讲的url_for()函数来使用,code表示采用哪个重定向,默认是302也即暂时性重定向,可以修改成301来实现永久性重定向。
在这里插入图片描述
两个例子都为如果没有登录或者找不到该用户,即给你重定向到首页或者登录页面!
在这里插入图片描述

在这里插入图片描述

二、Jinja模板

1、模板简介

模板是一个web开发必备的模块。因为我们在渲染一个网页的时候,并不是只渲染一个纯文本字符串,而是需要渲染一个有富文本标签的页面。这时候我们就需要使用模板了。在Flask中,配套的模板是Jinja2,Jinja2的作者也是Flask的作者。这个模板非常的强大,并且执行效率高。以下对Jinja2做一个简单介绍!

2、Flask渲染Jinja模板(render_template)

要渲染一个模板,通过render_template方法即可,以下将用一个简单的例子进行讲解:

from flask import Flask,render_template
app = Flask(__name__)

@app.route('/about/')
def about():
    return render_template('about.html')

当访问/about/的时候,about()函数会在当前目录下的templates(默认不建议修改)文件夹下寻找about.html模板文件。如果想更改模板文件地址,应该在创建app的时候,给Flask传递一个关键字参数template_folder,指定具体的路径,再看以下例子:

from flask import Flask,render_template
app = Flask(__name__,template_folder=r'C:\templates')

@app.route('/about/')
def about():
    return render_template('about.html')

以上例子将会在C盘的templates文件夹中寻找模板文件。还有最后一点是,如果模板文件中有参数需要传递,应该怎么传呢,我们再来看一个例子:

from flask import Flask,render_template
app = Flask(__name__)

@app.route('/about/')
def about():
    # return render_template('about.html',user='zhiliao')
    return render_template('about.html',**{'user':'zhiliao'})

以上例子介绍了两种传递参数的方式,因为render_template需要传递的是一个关键字参数,所以第一种方式是顺其自然的。但是当你的模板中要传递的参数过多的时候,把所有参数放在一个函数中显然不是一个好的选择,因此我们使用字典进行包装,并且加两个*号,来转换成关键字参数。

渲染模板例子:
在这里插入图片描述

3、Jinja2模版概述

​ 视图函数的主要作用是,处理业务逻辑,返回响应内容

​ flask是使用jinja2这个模板引擎来渲染模板

使用模板的好处

  • 视图函数只负责业务逻辑和数据处理
  • 模板取到视图函数的数据结果进行展示
  • 代码结构清晰,耦合度低

模板传参

  1. 再使用render_template渲染模板的时候,可以传递关键字参数,以后直接在模板中使用即可
  2. 如果参数过多,可以将所有的参数放到一个字典或列表中。将字典打散成关键字参数可以在参数前面加**
from flask import Flask,render_template

app = Flask(__name__)

student = {
    'name': 'zhangsan',
    'age':8,
    'gender':'男'
}
student_list = [
    {'name': 'zhangsan','age':18,'gender':'男'},
    {'name': 'lisi','age':68,'gender':'女'},
    {'name': 'wangwu','age':16,'gender':'男'}
]

student_dict = {
    'a':{'name': 'zhangsan','age':18,'gender':'男'},
    'b':{'name': 'lisi','age':28,'gender':'女'},
    'c':{'name': 'wangwu','age':19,'gender':'男'}
}
@app.route('/test1')
def test1():
    return render_template('01.html', **student) # 为了方便在模板中使用,可以把字典打散

@app.route('/test2')
def test2():
    return render_template('02.html', stu_list = student_list)

@app.route('/test3')
def test3():
    return render_template('03.html', stu_dict = student_dict)

if __name__ == '__main__':
    app.run()


01.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>第一个模板</title>
</head>
<body>
    学生姓名:{{ name }}
    学生年龄:{% if age >= 18 %}
                    已经成年
            {% else %}
                    未成年
            {% endif %}
    学生性别:{{ gender }}
</body>
</html>

结果
在这里插入图片描述
02.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>第二个模板</title>
</head>
<body>
   {{ stu_list }}

    <table border="1px">
        <tr>
            <td>序号</td>
            <td>姓名</td>
            <td>年龄</td>
            <td>性别</td>
        </tr>

        {% for stu in stu_list %}
            <tr>
                <td>{{ loop.index }}</td>
                <td>{{ stu.name }}</td> <!-- 由于stu是字典,有三种写法得到key的value-->
                {% if stu.age >= 60 %}
                    <td>已退休</td>
                {% elif stu.age >= 18 %}
                    <td>已成年</td>
                {% else %}
                    <td>未成年</td>
                {% endif %}
{#                <td>{{ stu.get('age') }}</td>#}
                <td>{{ stu['gender'] }}</td>
            </tr>
        {% endfor %}
        
    </table>
</body>
</html>

结果
在这里插入图片描述
03.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>第三个模板</title>
</head>
<body>
   {{ stu_dict }}

    <table border="1px">
        <tr>
            <td>序号</td>
            <td>姓名</td>
            <td>年龄</td>
            <td>性别</td>
        </tr>

        {% for stu_key,stu in stu_dict.items() %}
            <tr>
                <td>{{ loop.index }},key:{{ stu_key }}</td>
                <td>{{ stu.name }}</td> <!-- 由于stu是字典,有三种写法得到key的value-->
                {% if stu.age >= 60 %}
                    <td>已退休</td>
                {% elif stu.age >= 18 %}
                    <td>已成年</td>
                {% else %}
                    <td>未成年</td>
                {% endif %}
{#                <td>{{ stu.get('age') }}</td>#}
                <td>{{ stu['gender'] }}</td>
            </tr>
        {% endfor %}
        
    </table>
</body>
</html>

结果
在这里插入图片描述
语法

  1. 控制结构(逻辑代码){%%}
  2. 变量取值{{}}
  3. 注释{##}

更多参考原文章

4、模板过滤器

过滤器是通过管道符号(|)进行使用的,例如:{{ name|length }},将返回name的长度。过滤器相当于是一个函数,把当前的变量传入到过滤器中,然后过滤器根据自己的功能,再返回相应的值,之后再将结果渲染到页面中。Jinja2中内置了许多过滤器,在这里可以看到所有的过滤器,现对一些常用的过滤器进行讲解:

  1. abs(value):返回一个数值的绝对值。 例如:-1|abs。

  2. default(value,default_value,boolean=false):如果当前变量没有值,则会使用参数中的值来代替。name|default(‘xiaotuo’)——如果name不存在,则会使用xiaotuo来替代。boolean=False默认是在只有这个变量为undefined的时候才会使用default中的值,如果想使用python的形式判断是否为false,则可以传递boolean=true。也可以使用or来替换。

  3. escape(value)或e:转义字符,会将<、>等符号转义成HTML中的符号。例如:content|escape或content|e。

  4. first(value):返回一个序列的第一个元素。names|first。

  5. format(value,*arags,**kwargs):格式化字符串。例如以下代码:

{{ "%s" - "%s"|format('Hello?',"Foo!") }}

将输出:Helloo? - Foo!

  1. last(value):返回一个序列的最后一个元素。示例:names|last。

  2. length(value):返回一个序列或者字典的长度。示例:names|length。

  3. join(value,d=u’'):将一个序列用d这个参数的值拼接成字符串。

下边做实验
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  1. safe(value):如果开启了全局转义,那么safe过滤器会将变量关掉转义。示例:content_html|safe。

  2. int(value):将值转换为int类型。

  3. float(value):将值转换为float类型。

  4. lower(value):将字符串转换为小写。

  5. upper(value):将字符串转换为小写。

  6. replace(value,old,new): 替换将old替换为new的字符串。

  7. truncate(value,length=255,killwords=False):截取length长度的字符串。

  8. striptags(value):删除字符串中所有的HTML标签,如果出现多个空格,将替换成一个空格。

  9. trim:截取字符串前面和后面的空白字符。

  10. string(value):将变量转换成字符串。

  11. wordcount(s):计算一个长字符串中单词的个数。

也可以自定义过滤器,但是用的不多~

5、控制语句

所有的控制语句都是放在{% … %}中,并且有一个语句{% endxxx %}来进行结束,Jinja中常用的控制语句有if/for…in…,现对他们进行讲解:

  1. if:if语句和python中的类似,可以使用>,<,<=,>=,==,!=来进行判断,也可以通过and,or,not,()来进行逻辑合并操作,以下看例子:
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

  1. for…in…:for循环可以遍历任何一个序列包括列表、字典、元组。并且可以进行反向遍历,以下将用几个例子进行解释:
  • 普通的遍历:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 遍历字典:
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 如果序列中没有值的时候,进入else:
<ul>
{% for user in users %}
	<li>{{ user.username|e }}</li>
{% else %}
	<li><em>no users found</em></li>
{% endfor %}
</ul>

并且Jinja中的for循环还包含以下变量,可以用来获取当前的遍历状态:
在这里插入图片描述

6、测试器

测试器主要用来判断一个值是否满足某种类型,并且这种类型一般通过普通的if判断是有很大的挑战的。语法是:if…is…,先来简单的看个例子:

{% if variable is escaped%}
    value of variable: {{ escaped }}
{% else %}
    variable is not escaped
{% endif %}

以上判断variable这个变量是否已经被转义了,Jinja中内置了许多的测试器,看以下列表:
在这里插入图片描述

7、宏和import语句

一、宏:
模板中的宏跟python中的函数类似,可以传递参数,但是不能有返回值,可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来当成一个变量,以下将用一个例子来进行解释:

{% macro input(name, value='', type='text') %}
	<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}">
{% endmacro %}

以上例子可以抽取出了一个input标签,指定了一些默认参数。那么我们以后创建input标签的时候,可以通过他快速的创建:

<p>{{ input('username') }}</p>
<p>{{ input('password', type='password') }}</p>

二、import语句:
在真实的开发中,会将一些常用的宏单独放在一个文件中,在需要使用的时候,再从这个文件中进行导入。import语句的用法跟python中的import类似,可以直接import…as…,也可以from…import…或者from…import…as…,假设现在有一个文件,叫做forms.html,里面有两个宏分别为input和textarea,如下:

{% macro input(name, value='', type='text') %}
    <input type="{{ type }}" value="{{ value|e }}" name="{{ name }}">
{% endmacro %}

{% macro textarea(name, value='', rows=10, cols=40) %}
    <textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols
    }}">{{ value|e }}</textarea>
{% endmacro %}

三、导入宏的例子:

  1. import…as…形式:
{% import 'forms.html' as forms %}
<dl>
 <dt>Username</dt>
 <dd>{{ forms.input('username') }}</dd>
 <dt>Password</dt>
 <dd>{{ forms.input('password', type='password') }}</dd>
</dl>
<p>{{ forms.textarea('comment') }}</p>
  1. from…import…as…/from…import…形式:
{% from 'forms.html' import input as input_field, textarea %}
<dl>
 <dt>Username</dt>
 <dd>{{ input_field('username') }}</dd>
 <dt>Password</dt>
 <dd>{{ input_field('password', type='password') }}</dd>
</dl>
<p>{{ textarea('comment') }}</p>

另外需要注意的是,导入模板并不会把当前上下文中的变量添加到被导入的模板中,如果你想要导入一个需要访问当前上下文变量的宏,有两种可能的方法:

显式地传入请求或请求对象的属性作为宏的参数。

与上下文一起(with context)导入宏。

与上下文中一起(with context)导入的方式如下:

{% from '_helpers.html' import my_macro with context %}

8、include和set语句

include和set语句

一、include语句:
include语句可以把一个模板引入到另外一个模板中,类似于把一个模板的代码copy到另外一个模板的指定位置,看以下例子:

{% include 'header.html' %}
	主体内容
{% include 'footer.html' %}
  • 创建header.html
<h3>网页头</h3>
  • 创建footer.html
<h3>网页尾部</h3>
  • 创建测试文件test.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {% include 'header.html' %}
    <p>网页内容</p>
    {% include 'footer.html'%}
</body>
</html>
  • python文件
from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
def index():
    return render_template('index.html')


@app.route("/test")
def test():
    return render_template('test.html')


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

结果:

在这里插入图片描述

二、赋值(set)语句:
有时候我们想在在模板中添加变量,这时候赋值语句(set)就派上用场了,先看以下例子:

{% set name='zhiliao' %}

那么以后就可以使用name来代替zhiliao这个值了,同时,也可以给他赋值为列表和元组:

{% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}

赋值语句创建的变量在其之后都是有效的,如果不想让一个变量污染全局环境,可以使用with语句来创建一个内部的作用域,将set语句放在其中,这样创建的变量只在with代码块中才有效,看以下示例:

{% with %}
    {% set foo = 42 %}
    {{ foo }}           foo is 42 here
{% endwith %}

也可以在with的后面直接添加变量,比如以上的写法可以修改成这样:

{% with foo = 42 %}
    {{ foo }}
{% endwith %}

这两种方式都是等价的,一旦超出with代码块,就不能再使用foo这个变量了。

9、模版继承(extends)

Flask中的模板可以继承,通过继承可以把模板中许多重复出现的元素抽取出来,放在父模板中,并且父模板通过定义block给子模板开一个口,子模板根据需要,再实现这个block,假设现在有一个base.html这个父模板,代码如下:

在这里插入图片描述
以上父模板中,抽取了所有模板都需要用到的元素html、body等,并且对于一些所有模板都要用到的样式文件style.css也进行了抽取,同时对于一些子模板需要重写的地方,比如title、head、body都定义成了block,然后子模板可以根据自己的需要,再具体的实现。以下再来看子模板的代码:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

效果如下:
在这里插入图片描述

10、转义

转义的概念是,在模板渲染字符串的时候,字符串有可能包括一些非常危险的字符比如<、>等,这些字符会破坏掉原来HTML标签的结构,更严重的可能会发生XSS跨域脚本攻击,因此如果碰到<、>这些字符的时候,应该转义成HTML能正确表示这些字符的写法,比如>在HTML中应该用<来表示等。

但是Flask中默认没有开启全局自动转义,针对那些以.html、.htm、.xml和.xhtml结尾的文件,如果采用render_template函数进行渲染的,则会开启自动转义。并且当用render_template_string函数的时候,会将所有的字符串进行转义后再渲染。而对于Jinja2默认没有开启全局自动转义,作者有自己的原因:

  1. 渲染到模板中的字符串并不是所有都是危险的,大部分还是没有问题的,如果开启自动转义,那么将会带来大量的不必要的开销。
  2. Jinja2很难获取当前的字符串是否已经被转义过了,因此如果开启自动转义,将对一些已经被转义过的字符串发生二次转义,在渲染后会破坏原来的字符串。

在没有开启自动转义的模式下(比如以.conf结尾的文件),对于一些不信任的字符串,可以通过{{ content_html|e }}或者是{{ content_html|escape }}的方式进行转义。在开启了自动转义的模式下,如果想关闭自动转义,可以通过{{ content_html|safe }}的方式关闭自动转义。而{%autoescape true/false%}…{%endautoescape%}可以将一段代码块放在中间,来关闭或开启自动转义,例如以下代码关闭了自动转义:

{% autoescape false %}
  <p>autoescaping is disabled here
  <p>{{ will_not_be_escaped }}
{% endautoescape %}

11、数据类型和运算符

一、数据类型:
Jinja支持许多数据类型,包括:字符串、整型、浮点型、列表、元组、字典、True/False。

二、运算符:

  • +号运算符:可以完成数字相加,字符串相加,列表相加。但是并不推荐使用+运算符来操作字符串,字符串相加应该使用~运算符。
  • -号运算符:只能针对两个数字相减。
  • /号运算符:对两个数进行相除。
  • %号运算符:取余运算。
  • *号运算符:乘号运算符,并且可以对字符进行相乘。
  • 号运算符:次幂运算符,比如23=8。
  • in操作符:跟python中的in一样使用,比如{{1 in [1,2,3]}}返回true。
  • ~号运算符:拼接多个字符串,比如{{“Hello” ~ “World”}}将返回HelloWorld。

12、静态文件的配置

Web应用中会出现大量的静态文件来使得网页更加生动美观。类似于CSS样式文件、JavaScript脚本文件、图片文件、字体文件等静态资源。在Jinja中加载静态文件非常简单,只需要通过url_for全局函数就可以实现,看以下代码:

<link href="{{ url_for('static',filename='about.css') }}">

url_for函数默认会在项目根目录下的static文件夹中寻找about.css文件,如果找到了,会生成一个相对于项目根目录下的/static/about.css路径。当然我们也可以把静态文件不放在static文件夹中,此时就需要具体指定了,看以下代码:

app = Flask(__name__,static_folder='C:\static')

那么访问静态文件的时候,将会到/static这个文件夹下寻找。
在这里插入图片描述

三、视图高级

一、类视图

之前我们接触的视图都是函数,所以一般简称视图函数。其实视图也可以基于类来实现,类视图的好处是支持继承,但是类视图不能跟函数视图一样,写完类视图还需要通过app.add_url_rule(url_rule,view_func)来进行注册。以下将对两种类视图进行讲解:

一、标准类视图:
标准类视图是继承自flask.views.View,并且在子类中必须实现dispatch_request方法,这个方法类似于视图函数,也要返回一个基于Response或者其子类的对象。以下将用一个例子进行讲解:

from flask.views import View
class PersonalView(View):
    def dispatch_request(self):
        return "知了课堂"

类视图通过add_url_rule方法和url做映射

app.add_url_rule('/users/',view_func=PersonalView.as_view('personalview'))

二、基于调度方法的视图:
Flask还为我们提供了另外一种类视图flask.views.MethodView,对每个HTTP方法执行不同的函数(映射到对应方法的小写的同名方法上),以下将用一个例子来进行讲解:

class LoginView(views.MethodView):
    # 当客户端通过get方法进行访问的时候执行的函数
    def get(self):
        return render_template("login.html")

    # 当客户端通过post方法进行访问的时候执行的函数
    def post(self):
        email = request.form.get("email")
        password = request.form.get("password")
        if email == 'xx@qq.com' and password == '111111':
            return "登录成功!"
        else:
            return "用户名或密码错误!"

# 通过add_url_rule添加类视图和url的映射,并且在as_view方法中指定该url的名称,方便url_for函数调用
app.add_url_rule('/myuser/',view_func=LoginView.as_view('loginview'))

如果用类视图,我们怎么使用装饰器呢?比如有时候需要做权限验证的时候,比如看以下例子:

from flask import session
def login_required(func):
    def wrapper(*args,**kwargs):
        if not session.get("user_id"):
            return 'auth failure'
        return func(*args,**kwargs)
    return wrapper

装饰器写完后,可以在类视图中定义一个属性叫做decorators,然后存储装饰器。以后每次调用这个类视图的时候,就会执行这个装饰器。示例代码如下:

class UserView(views.MethodView):
    decorators = [user_required]
    ...

二、蓝图和子域名

一、蓝图
之前我们写的url和视图函数都是处在同一个文件,如果项目比较大的话,这显然不是一个合理的结构,而蓝图可以优雅的帮我们实现这种需求。以下看一个使用蓝图的文件的例子:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

静态文件默认到static文件夹中查找,模板文件默认到templates文件夹下查找,一般不建议修改默认路径~
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

二、子域名
子域名在许多网站中都用到了,比如一个网站叫做xxx.com,那么我们可以定义一个子域名cms.xxx.com来作为cms管理系统的网址,子域名的实现一般也是通过蓝图来实现,在之前章节中,我们创建蓝图的时候添加了一个url_prefix=/user作为url前缀,那样我们就可以通过/user/来访问user下的url。但使用子域名则不需要。另外,还需要配置SERVER_NAME,比如app.config[SERVER_NAME]=‘example.com:9000’。并且在注册蓝图的时候,还需要添加一个subdomain的参数,这个参数就是子域名的名称,先来看一下蓝图的实现(admin.py):

from flask import Blueprint
bp = Blueprint('admin',__name__,subdomain='admin')

@bp.route('/')
def admin():
    return 'Admin Page'

这个没有多大区别,接下来看主app的实现:

from flask import Flask
import admin

# 配置`SERVER_NAME`
app.config['SERVER_NAME'] = 'example.com:8000'
# 注册蓝图,指定了subdomain
app.register_blueprint(admin.bp)

if __name__ == '__main__':
    app.run(host='0.0.0.0',port=8000,debug=True)

写完以上两个文件后,还是不能正常的访问admin.example.com:8000这个子域名,因为我们没有在host文件中添加域名解析,你可以在最后添加一行127.0.0.1 admin.example.com,就可以访问到了。另外,子域名不能在127.0.0.1上出现,也不能在localhost上出现。

四、SQLAlchemy

MySQL数据库:
在网站开发中,数据库是网站的重要组成部分。只有提供数据库,数据才能够动态的展示,而不是在网页中显示一个静态的页面。数据库有很多,比如有SQL Server、Oracle、PostgreSQL以及MySQL等等。MySQL由于价格实惠、简单易用、不受平台限制、灵活度高等特性,目前已经取得了绝大多数的市场份额。因此我们在Flask中,也是使用MySQL来作为数据存储。

1、mysql安装

1、在MySQL的官网下载MySQL数据库安装文件:https://dev.mysql.com/downloads/mysql/
在这里插入图片描述

2、安装步骤略

2、navicat数据库操作软件:

安装完MySQL数据库以后,就可以使用MySQL提供的终端客户端软件来操作数据库。如下:
在这里插入图片描述
这个软件所有的操作都是基于sql语言,对于想要熟练sql语言的同学来讲是非常合适的。但是对于在企业中可能不是一款好用的工具。在企业中我们推荐使用mysql workbench以及navicat这种图形化操作的软件。而mysql workbench是mysql官方提供的一个免费的软件,正因为是免费,所以在一些功能上不及navicat。navicat for mysql是一款收费的软件。官网地址如下:https://www.navicat.com.cn/products。使用的截图如下:
在这里插入图片描述

3、MySQL驱动程序安装:

我们使用Django来操作MySQL,实际上底层还是通过Python来操作的。因此我们想要用Flask来操作MySQL,首先还是需要安装一个驱动程序。在Python3中,驱动程序有多种选择。比如有pymysql以及mysqlclient等。这里我们就使用mysqlclient来操作。mysqlclient安装非常简单。只需要通过pip install mysqlclient即可安装。
在这里插入图片描述

4、Flask-SQLAlchemy连接数据库

1、SQLAlchemy和Flask-SQLAlchemy的区别:
SQLAlchemy:是一个独立的ORM框架,可以独立于Flask存在,也可以在其他项目中使用,比如在Django中。
Flask-SQLAlchemy:对SQLAlchemy的一个封装,能够更适合在flask中使用。(所以此处没有必要从SQLAlchemy学起)

2、安装和验证:

  1. 安装连接数据库的库:pip install pymysql
  2. 安装:pip install flask-sqlalchemy

3、连接数据库:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# 数据库的配置变量
HOSTNAME = '127.0.0.1'   #默认
PORT     = '3306'        #默认
DATABASE = 'xt_flask'   #数据库名按实际情况填写
USERNAME = 'root'    #按实际情况填写
PASSWORD = 'root'   #按实际情况填写
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)
app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

db = SQLAlchemy(app)

@app.route('/')
def hello_world():
    # 写一个测试代码来验证是否连接成功
    engine = db.get_engine()
    with engine.connect() as conn:
        result = conn.execute("select 1")
        print(result.fetchone())
    return 'Hello World!'


if __name__ == '__main__':
    app.run()

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5、ORM模型映射到数据库

1、要使用ORM来操作数据库,首先需要创建一个类来与对应的表进行映射。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# 数据库的配置变量
HOSTNAME = '127.0.0.1'
PORT     = '3306'
DATABASE = 'zl_flask'
USERNAME = 'root'
PASSWORD = 'root'
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)
app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

db = SQLAlchemy(app)

class Article(db.Model):
    __tablename__ = "article"
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    title = db.Column(db.String(200),nullable=False)
    content = db.Column(db.Text,nullable=False)

db.create_all()

在这里插入图片描述

6、ORM增删改查操作

2、添加数据

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# 数据库的配置变量
HOSTNAME = '127.0.0.1'
PORT     = '3306'
DATABASE = 'zl_flask'
USERNAME = 'root'
PASSWORD = 'root'
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)
app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

db = SQLAlchemy(app)

class Article(db.Model):
    __tablename__ = "article"
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    title = db.Column(db.String(200),nullable=False)
    content = db.Column(db.Text,nullable=False)

db.create_all()

@app.route("/article")
def article_view():
    # 1. 添加数据
	insert table article values(xx)
    article = Article(title="钢铁是怎样炼成的",content="xxx")
    db.session.add(article)
    # # 做一个提交操作
    db.session.commit()

在这里插入图片描述
3、查询数据

参考以上代码~
在这里插入图片描述
4、修改数据

参考以上代码~

    # 修改数据
     article = Article.query.filter_by(id=1)[0]       #先做查询后修改
     article.content = "yyy"
     db.session.commit()
	 return "数据操作成功"

刷新网页,然后刷新navicat
在这里插入图片描述
5、删除数据

参考以上代码~

    # 删除数据
    Article.query.filter_by(id=1).delete()     #删除数据不用先取零提取出来
    db.session.commit()
    return "数据操作成功"

在这里插入图片描述

在这里插入图片描述

7、表关系

表之间的关系存在三种:一对一、一对多、多对多。而SQLAlchemy中的ORM也可以模拟这三种关系。因为一对一其实在SQLAlchemy中底层是通过一对多的方式模拟的,所以先来看下一对多的关系:

1、外键
在Mysql中,外键可以让表之间的关系更加紧密。而SQLAlchemy同样也支持外键。通过ForeignKey类来实现,并且可以指定表的外键约束。

2、一对多

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# 数据库的配置变量
HOSTNAME = '127.0.0.1'
PORT     = '3306'
DATABASE = 'zl_flask'
USERNAME = 'root'
PASSWORD = 'root'
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)
app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

db = SQLAlchemy(app)

migrate = Migrate(app,db)


class User(db.Model):
    __tablename__ = "user"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(200),nullable=False)
    # password = db.Column(db.String(200),nullable=False)
    

    # db.backref
    # 1. 在反向引用的时候,如果需要传递一些其他的参数,那么就需要用到这个函数,否则不需要使用,只要在relationship的backref参数上,设置反向引用的名称就可以了。
    # 2. uselist=False:代表反向引用的时候,不是一个列表,而是一个对象。
    user = db.relationship("User",backref=db.backref("extension",uselist=False))


class Article(db.Model):
    __tablename__ = "article"
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    title = db.Column(db.String(200),nullable=False)
    content = db.Column(db.Text,nullable=False)

    # 外键:
    # 1. 外键的数据类型一定要看,所引用的字段的类型,如该例子中外键为Integer整数型
    # 2. db.ForeignKey("表名.字段名 ")
    # 3. 外键是属于数据库层面的,不推荐直接在ORM中使用
    author_id = db.Column(db.Integer,db.ForeignKey("user.id"))

    # relationship:
    # 1. 第一个参数是模型的名字,必须要和模型的名字保持一致
    # 2. backref(back reference):代表反向引用,代表对方访问我的时候的字段名称
    author = db.relationship("User",backref="articles")


# 暂时还没有讲到ORM迁移数据库的版本管理,所以现在只能先删除所有表,再创建
 db.drop_all()
 db.create_all()  #模型映射到数据库


@app.route("/otm")
def one_to_many():
    article1 = Article(title="111",content="xxx")
    article2 = Article(title="222", content="yyy")
    user = User(username="zhiliao")
    article1.author = user
    article2.author = user
    db.session.add(article1,article2)
    db.session.commit()

    print(user.articles)
    return "one to many数据操作成功"

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

3、一对一

from flask import Flask
from flask_sqlalchemy import SQLAlchemy


app = Flask(__name__)

# 数据库的配置变量
HOSTNAME = '127.0.0.1'
PORT     = '3306'
DATABASE = 'zl_flask'
USERNAME = 'root'
PASSWORD = 'root'
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)
app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

db = SQLAlchemy(app)

migrate = Migrate(app,db)


class User(db.Model):
    __tablename__ = "user"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(200),nullable=False)
    # password = db.Column(db.String(200),nullable=False)


class UserExtension(db.Model):
    __tablename__ = "user_extension"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    school = db.Column(db.String(100))
    user_id = db.Column(db.Integer,db.ForeignKey("user.id"))

    # db.backref
    # 1. 在反向引用的时候,如果需要传递一些其他的参数,那么就需要用到这个函数,否则不需要使用,只要在relationship的backref参数上,设置反向引用的名称就可以了。
    # 2. uselist=False:代表反向引用的时候,不是一个列表,而是一个对象。既为一对一
    user = db.relationship("User",backref=db.backref("extension",uselist=False))
    # 最核心,指定访问这个extension对象,还有uselist=False 这个参数!


class Article(db.Model):
    __tablename__ = "article"
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    title = db.Column(db.String(200),nullable=False)
    content = db.Column(db.Text,nullable=False)

    # 外键:
    # 1. 外键的数据类型一定要看,所引用的字段的类型
    # 2. db.ForeignKey("表名.字段名 ")
    # 3. 外键是属于数据库层面的,不推荐直接在ORM中使用
    author_id = db.Column(db.Integer,db.ForeignKey("user.id"))

    # relationship:
    # 1. 第一个参数是模型的名字,必须要和模型的名字保持一致
    # 2. backref(back reference):代表反向引用,代表对方访问我的时候的字段名称
    author = db.relationship("User",backref="articles")


# 暂时还没有讲到ORM迁移数据库的版本管理,所以现在只能先删除所有表,再创建
# db.drop_all()
# db.create_all()   #模型映射到数据库



@app.route("/oto")
def one_to_one():
    user = User(username="zhiliao")
    extension = UserExtension(school="清华大学")
    user.extension = extension   #这地方不能是列表,只能是一个对象
    db.session.add(user)
    db.session.commit()
    return "one to one"

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

五、ORM迁移

1、ORM迁移与表的映射

Flask-Migrate插件
在实际的开发环境中,经常会发生数据库修改的行为。一般我们修改数据库不会直接手动的去修改,而是去修改ORM对应的模型,然后再把模型映射到数据库中。这时候如果有一个工具能专门做这种事情,就显得非常有用了,而flask-migrate就是做这个事情的。flask-migrate是基于Alembic进行的一个封装,并集成到Flask中,而所有的迁移操作其实都是Alembic做的,他能跟踪模型的变化,并将变化映射到数据库中。

使用Flask-Migrate需要安装,命令如下:

pip install flask-migrate

初始化动作只需要做一次~
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2、项目重构

如果将所有代码都写在一个文件中,这样会导致文件会越来越乱。所以需要进行一下项目重构,设置为以下的目录结构:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

app.py

from flask import Flask
from flask_migrate import Migrate
from models import Article,User,UserExtension
import config
from exts import db

app = Flask(__name__)
app.config.from_object(config)
# 把app绑定到db上
db.init_app(app)

migrate = Migrate(app,db)

@app.route("/otm")
def one_to_many():
    article1 = Article(title="111",content="xxx")
    article2 = Article(title="222", content="yyy")
    user = User(username="zhiliao")
    article1.author = user
    article2.author = user
    db.session.add(article1,article2)
    db.session.commit()

    print(user.articles)
    return "one to many数据操作成功"


@app.route("/oto")
def one_to_one():
    user = User(username="zhiliao")
    extension = UserExtension(school="清华大学")
    user.extension = extension
    db.session.add(user)
    db.session.commit()
    return "one to one"


@app.route("/article")
def article_view():
    # 1. 添加数据
    # insert table article values(xx)
    # article = Article(title="钢铁是怎样炼成的",content="xxx")
    # db.session.add(article)
    # # 做一个提交操作
    # db.session.commit()

    # 2. 查询数据
    # filter_by:返回一个类列表的对象
    # article = Article.query.filter_by(id=1)[0]
    # print(article.title)

    # 3. 修改数据
    # article = Article.query.filter_by(id=1)[0]
    # article.content = "yyy"
    # db.session.commit()

    # 4. 删除数据
    Article.query.filter_by(id=1).delete()
    db.session.commit()
    return "数据操作成功"


@app.route('/')
def hello_world():
    # 写一个测试代码来验证是否连接成功
    engine = db.get_engine()
    with engine.connect() as conn:
        result = conn.execute("select 1")
        print(result.fetchone())
    return 'Hello World!'


if __name__ == '__main__':
    app.run()

config.py

# 数据库的配置变量
HOSTNAME = '127.0.0.1'
PORT     = '3306'
DATABASE = 'zl_flask'
USERNAME = 'root'
PASSWORD = 'root'
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)
SQLALCHEMY_DATABASE_URI = DB_URI
SQLALCHEMY_TRACK_MODIFICATIONS = True

exts.py

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

models.py

from exts import db

class User(db.Model):
    __tablename__ = "user"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(200),nullable=False)
    # password = db.Column(db.String(200),nullable=False)


class UserExtension(db.Model):
    __tablename__ = "user_extension"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    school = db.Column(db.String(100))
    user_id = db.Column(db.Integer,db.ForeignKey("user.id"))

    # db.backref
    # 1. 在反向引用的时候,如果需要传递一些其他的参数,那么就需要用到这个函数,否则不需要使用,只要在relationship的backref参数上,设置反向引用的名称就可以了。
    # 2. uselist=False:代表反向引用的时候,不是一个列表,而是一个对象。
    user = db.relationship("User",backref=db.backref("extension",uselist=False))


class Article(db.Model):
    __tablename__ = "article"
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    title = db.Column(db.String(200),nullable=False)
    content = db.Column(db.Text,nullable=False)

    # 外键:
    # 1. 外键的数据类型一定要看,所引用的字段的类型
    # 2. db.ForeignKey("表名.字段名 ")
    # 3. 外键是属于数据库层面的,不推荐直接在ORM中使用
    author_id = db.Column(db.Integer,db.ForeignKey("user.id"))

    # relationship:
    # 1. 第一个参数是模型的名字,必须要和模型的名字保持一致
    # 2. backref(back reference):代表反向引用,代表对方访问我的时候的字段名称
    author = db.relationship("User",backref="articles")

测试重构结果
在这里插入图片描述

在这里插入图片描述

测试ORM迁移结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

六、cookie和session

1、cookie和session概念

  1. cookie:在网站中,http请求是无状态的。也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户。cookie的出现就是为了解决这个问题,第一次登录后服务器返回一些数据(cookie)给浏览器,然后浏览器保存在本地,当该用户发送第二次请求的时候,就会自动的把上次请求存储的cookie数据自动的携带给服务器,服务器通过浏览器携带的数据就能判断当前用户是哪个了。cookie存储的数据量有限,不同的浏览器有不同的存储大小,但一般不超过4KB。因此使用cookie只能存储一些小量的数据。

  2. session: session和cookie的作用有点类似,都是为了存储用户相关的信息。不同的是,cookie是存储在本地浏览器,而session存储在服务器,不同的服务器,不同的框架,不同的语言有不同的实现。虽然实现不一样,但是他们的目的都是服务器为了方便存储数据的。session的出现,是为了解决cookie存储数据不安全的问题的。

  3. cookie和session结合使用:web开发发展至今,cookie和session的使用已经出现了一些非常成熟的方案。在如今的市场或者企业里,一般有两种存储方式:

1、存储在服务端:通过cookie存储一个session_id,然后具体的数据则是保存在session中。如果用户已经登录,则服务器会在cookie中保存一个session_id,下次再次请求的时候,会把该session_id携带上来,服务器根据session_id在session库中获取用户的session数据。就能知道该用户到底是谁,以及之前保存的一些状态信息。这种专业术语叫做server side session。存储在服务器的数据会更加的安全,不容易被窃取。但存储在服务器也有一定的弊端,就是会占用服务器的资源,但现在服务器已经发展至今,一些session信息还是绰绰有余的。

2、将session数据加密,然后存储在cookie中。这种专业术语叫做client side session。flask采用的就是这种方式,但是也可以替换成其他形式。

2、flask中使用cookie和session

1、cookies:在Flask中操作cookie,是通过response对象来操作,可以在response返回之前,通过response.set_cookie来设置,这个方法有以下几个参数需要注意:

  • key:设置的cookie的key。
  • value:key对应的value。
  • max_age:改cookie的过期时间,如果不设置,则浏览器关闭后就会自动过期。
  • expires:过期时间,应该是一个datetime类型。
  • domain:该cookie在哪个域名中有效。一般设置子域名,比如cms.example.com。
  • path:该cookie在哪个路径下有效。

2、session:Flask中的session是通过from flask import session。然后添加值key和value进去即可。并且,Flask中的session机制是将session信息加密,然后存储在cookie中。专业术语叫做client side session。

完整的测试代码:
在这里插入图片描述

3、set_cookie

from flask import Flask,Response

app = Flask(__name__)

@app.route("/set_cookie")
def set_cookie():
    response = Response("cookie 设置")
    response.set_cookie("user_id","xxx")
    return response

@app.route('/')
def hello_world():
    return 'Hello World!'


if __name__ == '__main__':
    app.run()

在这里插入图片描述
在这里插入图片描述

4、get_cookie

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5、set_session

from flask import Flask,Response,request,session

app = Flask(__name__)
app.config['SECRET_KEY'] = "12asdfadfdsfasd3"


@app.route("/set_cookie")
def set_cookie():
    response = Response("cookie 设置")
    response.set_cookie("user_id","xxx")
    return response


@app.route("/get_cookie")
def get_cookie():
    user_id = request.cookies.get("user_id")
    print("user_id:",user_id)
    return "获取cookie"


@app.route("/set_session")
def set_session():
    # 在flask中,session是先把数据经过加密,然后用session_id作为key,存放到cookie中
    # 因为session会经过加密再存储到cookie中,所以我们的敏感信息,会存放到session中
    session['username'] = "zhiliao"
    return "session设置成功"

@app.route('/')
def hello_world():
    return 'Hello World!'


if __name__ == '__main__':
    app.run()

在这里插入图片描述

6、get_session

from flask import Flask,Response,request,session

app = Flask(__name__)
app.config['SECRET_KEY'] = "12asdfadfdsfasd3"


@app.route("/set_cookie")
def set_cookie():
    response = Response("cookie 设置")
    response.set_cookie("user_id","xxx")
    return response


@app.route("/get_cookie")
def get_cookie():
    user_id = request.cookies.get("user_id")
    print("user_id:",user_id)
    return "获取cookie"


@app.route("/set_session")
def set_session():
    # 在flask中,session是先把数据经过加密,然后用session_id作为key,存放到cookie中
    # 因为session会经过加密再存储到cookie中,所以我们的敏感信息,会存放到session中
    session['username'] = "zhiliao"
    return "session设置成功"


@app.route("/get_session")
def get_session():
    username = session.get('username')
    print("username:",username)
    return "get session"


@app.route('/')
def hello_world():
    return 'Hello World!'


if __name__ == '__main__':
    app.run()

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

七、Flask-WTF表单验证

Flask-WTF是简化了WTForms操作的一个第三方库。WTForms表单的两个主要功能是验证用户提交数据的合法性以及渲染模板。当然还包括一些其他的功能:CSRF保护,文件上传等。安装Flask-WTF默认也会安装WTForms,因此使用以下命令来安装Flask-WTF:

pip install flask-wtf

1、表单验证

代码框架预览:
在这里插入图片描述

app.py

from flask import Flask,request,render_template
from forms import LoginForm

app = Flask(__name__)


@app.route('/login',methods=['GET','POST'])
def login():
    if request.method == 'GET':
        return render_template("login.html")
    else:
        form = LoginForm(request.form)
        if form.validate():
            return "登录成功!"
        else:
            return "邮箱或密码错误!"


@app.route('/')
def hello_world():
    return 'Hello World!'


if __name__ == '__main__':
    app.run()

forms.py

# pip install flask-wtf
import wtforms
from wtforms.validators impor length,email


class LoginForm(wtforms.Form):
    email = wtforms.StringField(validators=[length(min=5,max=20),email()])   #指定账号字符长度
    password = wtforms.StringField(validators=[length(min=6,max=20)])        #指定密码字符长度

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<form action="/login" method="post">
    <table>
        <tbody>
            <tr>
                <td>邮箱:</td>
                <td>
                    <input type="text" name="email">
                </td>
            </tr>
            <tr>
                <td>密码:</td>
                <td><input type="text" name="password"></td>
            </tr>
            <tr>
                <td></td>
                <td><button>登录</button></td>
            </tr>
        </tbody>
    </table>
</form>
</body>
</html>

报错解决

pip install email_validator

在这里插入图片描述

正确访问界面
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

八、Restful API规范

restful api是用于在前端与后台进行通信的一套规范。使用这个规范可以让前后端开发变得更加轻松。以下将讨论这套规范的一些设计细节。

一、协议:

采用http或者https协议。

二、数据传输格式:

数据之间传输的格式应该都使用json,而不使用xml。

三、url链接:

url链接中,不能有动词,只能有名词。并且对于一些名词,如果出现复数,那么应该在后面加s。

比如:获取文章列表,应该使用articles,而不应该使用get_article

四、HTTP请求的方法:

GET:从服务器上获取资源。
POST:在服务器上新创建一个资源。
PUT:在服务器上更新资源。(客户端提供所有改变后的数据)
PATCH:在服务器上更新资源。(客户端只提供需要改变的属性)
DELETE:从服务器上删除资源。
示例如下:
GET /users/:获取所有用户。
POST /user/:新建一个用户。
GET /user/id/:根据id获取一个用户。
PUT /user/id/:更新某个id的用户的信息(需要提供用户的所有信息)。
PATCH /user/id/:更新某个id的用户信息(只需要提供需要改变的信息)。
DELETE /user/id/:删除一个用户。

五、状态码:

状态码 原生描述 描述
200 OK 服务器成功响应客户端的请求。
400 INVALID REQUEST 用户发出的请求有错误,服务器没有进行新建或修改数据的操作
401 Unauthorized 用户没有权限访问这个请求
403 Forbidden 因为某些原因禁止访问这个请求
404 NOT FOUND 用户发送的请求的url不存在
406 NOT Acceptable 用户请求不被服务器接收(比如服务器期望客户端发送某个字段,但是没有发送)。
500 Internal server error 服务器内部错误,比如出现了bug

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要基于PythonFlask建立一个学习笔记系统,你可以按照以下步骤进行: 1. 安装PythonFlask:首先,确保你的计算机上安装了Python。然后,使用pip命令安装Flask框架。在命令行中执行以下命令: ``` pip install Flask ``` 2. 创建Flask应用:在你选择的目录下创建一个新的文件夹,用于存放你的应用程序。进入该文件夹,并创建一个名为app.py的Python脚本文件。 3. 导入依赖:在app.py文件中,导入必要的模块和库: ```python from flask import Flask, render_template, request ``` 4. 初始化Flask应用:在app.py文件中,初始化Flask应用: ```python app = Flask(__name__) ``` 5. 创建路由和视图函数:根据你的学习笔记系统的需求,创建不同的路由和对应的视图函数。例如,你可以创建一个用于显示学习笔记列表的路由: ```python @app.route('/notes') def notes(): # 在这里编写代码来获取学习笔记列表,并将其传递给模板 return render_template('notes.html', notes=notes_list) ``` 6. 创建模板:在你的应用程序文件夹中创建一个名为templates的文件夹,并在其中创建一个名为notes.html的模板文件。在模板文件中,你可以使用Flask提供的模板语法来显示学习笔记列表: ```html <h1>学习笔记列表</h1> <ul> {% for note in notes %} <li>{{ note }}</li> {% endfor %} </ul> ``` 7. 运行应用:在命令行中,使用以下命令运行你的应用: ``` python app.py ``` 现在,你的基于PythonFlask学习笔记系统就可以运行了。你可以根据需要添加更多的路由和视图函数,以及其他功能如添加、编辑和删除学习笔记等。同时,你可以探索Flask的文档和教程,以深入了解如何构建更复杂的应用程序。祝你成功!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

且听风吟tmj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值