Flask 框架学习1

Flask 框架学习1,第1张

常用快捷键:

        自动导包:option+enter(mac)ctl+enter(windows)

        查看传参:ctr+ p

        新增文件:ctr + n

1、hello_world
from flask import Flask


# 创建应用对象
# __name__:如果程序直接在当前文件执行,值为__main__,如果从其他文件调用执行,表示模块名称
# static_url_path:静态资源路径默认为:./static
#template_folder: 模板路径,默认为:./templates
app = Flask(__name__)

# 使用路由路径,绑定视图函数
@app.route('/')
def hello_world():
    return 'hello world!'

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

这是flask框架最简单的一段代码,以及其中参数的含义

1、如果一个路由装饰多个函数,那么访问的是哪个函数?

        访问先被装饰的函数,相当于一个队列,先进先出

2、如果一个视图函数由多个路由装饰,那么优先访问哪个路径?

        所有的路径都能访问视图函数

2、methods

若一个路由装饰多个函数,可以通过methods,确认请求方式来确认访问的视图函数.methods 是一个列表,不同请求方式使用逗号分割

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'hello world!'

@app.route('/', methods=["post"])
def hello_world2():
    return 'hello world2!'

if __name__ == '__main__':
    app.run()
3、url_for

url_for 是属于app的方法,在flask模块中

格式:url_for('试图函数名字', key=value)

可以通过视图函数找到对应的路由路径,用于服务器内部资源定位,常与redirect连用

使用url_for传递参数时,接受的参数名需要与传递的参数名一致

from flask import Flask, url_for

app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'hello world点击按钮'.format(url_for('hello_world2'))

@app.route('/index')
def hello_world2():
    return 'hello world2!'

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

传参:

from flask import Flask, url_for

app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'hello world点击按钮'.format(url_for('hello_world2', money=100))

@app.route('/index/')
def hello_world2(money):
    return 'hello world2!,钱数:{}'.format(money)

if __name__ == '__main__':
    app.run()
4、redirect

重定向,可以通过路由找到视图函数,并自动跳转执行

格式:redirect('路径'),常与url_for一起使用

from flask import Flask, url_for, redirect

app = Flask(__name__)

@app.route('/')
def hello_world():
    return redirect(url_for('hello_world2', money=100))

@app.route('/index/')
def hello_world2(money):
    return 'hello world2!,钱数:{}'.format(money)

if __name__ == '__main__':
    app.run()
5、返回json数据

有两种方式:

a:通过jsonify(dict),flask模块中

b:json.dump() ,需要配合response对象设置,不推荐

from flask import Flask, url_for, redirect, jsonify

app = Flask(__name__)

@app.route('/')
def hello_world():
    dict={
        "name":"zhangsan",
        "age":19
    }

    return jsonify(dict)

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

在浏览器控制台可以看到响应头的conten-type是json格式

使用json.dumps()

from flask import Flask, url_for, redirect, jsonify, json, make_response

app = Flask(__name__)

@app.route('/')
def hello_world():
    dict={
        "name":"zhangsan",
        "age":19
    }

    return json.dumps(dict)

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

上面代码在浏览器返回的是text格式,若要返回json格式,还需要借助response对象

6、路由传参

格式:app.route("路径/<数据类型:变量名>")

数据类型:

        int:整数

        float:小数

        path:字符串

from flask import Flask, url_for, redirect, jsonify

app = Flask(__name__)

@app.route('/')
def hello_world(reasion):

    return "我今天是因为{}迟到了!".format(reasion)


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

但是没办法限制传参的个数,可以通过自定义转换器

之所以可以编写int、float、path是因为系统已经添加默认的转换器,如果要限制参数的具体个数等类型需要自定义

a、自定义类,继承自BaseConverter

b、重写__init__方法,接受两个参数,调用父类__init__方法初始化父类内容

c、将规则赋值到子类对象

d、将转换器添加到系统默认的转换器列表中

查看系统自带的转换器:app.url_map.converters

from flask import Flask, url_for, redirect, jsonify
from werkzeug.routing import BaseConverter

app = Flask(__name__)


class MyConvert(BaseConverter):
    def __init__(self, url_map, regex):
        super(MyConvert, self).__init__(url_map)
        self.regex = regex


app.url_map.converters['re'] = MyConvert


@app.route('/')
def hello_world(number):

    return "the phonenumber is {}".format(number)


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

 to_python 方法

from flask import Flask, url_for, redirect, jsonify
from werkzeug.routing import BaseConverter

app = Flask(__name__)


class MyConvert(BaseConverter):
    def __init__(self, url_map, regex):
        super(MyConvert, self).__init__(url_map)
        self.regex = regex

    # 重写to_python 方法
    def to_python(self, value):
        return 'xixi' + value


app.url_map.converters['re'] = MyConvert


@app.route('/')
def hello_world(number):

    return "the phonenumber is {}".format(number)


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

请求结果

在匹配完规则后,进入到视图函数之前执行该方法,可以过滤数据或编码 *** 作

7、调试模式

在运行程序的时候,可以通过app.run(debug=True)进入调试模式,修改代码之后,保存,自动重新启动

好处:

        a、程序改动之后,不需要重新启动既可以部署

        b、如果程序报错,有友好提示界面        


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

8、abort

主动停止程序,会配合@app.errorhandler使用,用来自定义错误界面

from flask import Flask, abort

app = Flask(__name__)

@app.route('/')
def hello_world():
    abort(403)
    return "hello world!!"

常与@app.errorhandler装饰器使用,可以自定义错误

from flask import Flask, abort

app = Flask(__name__)

@app.route('/')
def hello_world():
    abort(404)
    return "hello world!!"


@app.errorhandler(404)
def exception_404(e):
    return "找不到资源"


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

##########################################################
from flask import Flask, abort

app = Flask(__name__)

@app.route('/')
def hello_world():
    # abort(404)
    raise(Exception("big error"))

    return "hello world!!"


@app.errorhandler(404)
def exception_404(e):
    return "找不到资源"

@app.errorhandler(Exception)
def exception(e):
    return "出现大错误"


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

9、请求对象和返回对象

request:和请求对象相关,里面封装了请求有关的信息,比如请求方式、请求路径、请求参数

from flask import Flask, request

app = Flask(__name__)

@app.route('/')
def hello_world():
    print(request.method)
    print(request.url)
    print(request.args)
    return "hello world!"

if __name__ == '__main__':

    app.run(debug=True)

###################################
# 响应:
# 127.0.0.1 - - [02/May/2022 13:26:02] "GET
# http://127.0.0.1:5000/?name=zhagnsan&age=13
# ImmutableMultiDict([('name', 'zhagnsan'), ('age', '13')])
# GET /?name=zhagnsan&age=13 HTTP/1.1" 200 -

响应对象,在后端返回数据的时候,有两种主要方式

直接返回

        1、响应体        return “hello world”

        2、响应体+状态码        return  “hello world”,666

        3、响应体+状态码+响应头        return  “hello world”,666,{"Content-Type":"application/json"}

手动封装response对象返回

response = make_response(响应体)

response.status = "666"

response.hearder["Content-Type"] = "application/json"

10、请求勾子

在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如:

  • 在请求开始时,建立数据库连接;
  • 在请求开始时,根据需求进行权限校验;
  • 在请求结束时,指定数据的交互格式;

请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子

before_first_request:在处理第一个请求前执行

before_request:每次请求前执行,如果在某修饰的函数中返回了一个响应,视图函数将不再被调用

after_request:如果没有抛出错误,在每次请求后执行;接受一个参数:视图函数作出的响应;在此函数中可以对响应值在返回之前做最后一步修改处理;需要将参数中的响应在此参数中进行返回

teardown_request:每次请求后执行,接受一个参数:错误信息,如果有相关错误抛出

11、cookie和session

http 是一种无状态协议,浏览器请求服务器是无状态的,有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等

  • 在客户端存储信息使用Cookie
  • 在服务器端存储信息使用Session

设置cookie:当前浏览器访问服务器时,由服务器设置一些状态信息到浏览器中,存储在浏览器中

        response.set_cookie(key,value,max_age)        max_age: 过期时间

获取cookie:

        request.cookies.get(key)

from flask import Flask, make_response, request

app = Flask(__name__)

@app.route('/set_cookie')
def set_cookie():
    response = make_response()
    response.set_cookie('name','zhangsan')
    response.set_cookie('age',"13", 10)
    return response

@app.route('/get_cookie')
def get_cookie():
    name = request.cookies.get('name')
    age = request.cookies.get('age')
    return "name is {}, age is {}".format(name, age)

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

设置cookie

获取cookie

session: 当浏览器访问服务器的时候,由服务器创建并存储在服务器内部,依赖于cookie

        一般用来存储用户的隐私数据,比如:密码信息,登录信息,yhk信息等

seesion数据设置过程:

        1、当第一次请求的时候,在服务器端会给客户端设置响应的session数据,返回给客户端,存储在cookie中

        2、当第二次发送请求是,cookie中会携带sessionID到服务器,通过ID可以获取到session中的数据

        每一个用户向服务器请求的时候都会给其开辟对应的空间保存session数据

设置session:

        session[key] = value 

        设置session的编号生成依赖于SECRE_KEY

获取session:

        session.get(key)  若为空,直接返回空,推荐

        session[key]        若为空,报错。不推荐

from flask import Flask, session

app = Flask(__name__)
app.config['SECRET_KEY'] = 'jhfjafjkd'

@app.route('/set_session/')
def set_session(name):
    session['name'] = name
    return '设置session成功'

@app.route('/get_session')
def get_session():
    se = session.get('name')
    return 'session is {}'.format(se)

if __name__ == '__main__':
    app.run(debug=True)
 12、flask_script 动态指定IP和端口

在时机开发中,项目封板之后,如果部署的服务器发生变化,意味着IP和端口可能出现变化,如果直接修改源代码风险比较大,而且繁琐,可以通过flask_script解决,可以动态指定运行的IP和端口

安装:

pip install flask-script

创建Manager对象,关联app应用程序对象

运行:python xxx.py runserver -h 127.0.0.1 -p 8888

from flask import Flask
from flask_script import Manager

app = Flask(__name__)
# 设置调试模式
app.config["DEBUG"] = True

# 创建manager管理对象,关联app应用程序对象
manager = Manager(app)


@app.route('/')
def hello_world():
    return "hello world"

if __name__ == '__main__':
    # app.run(debug=True)
    manager.run()
13、模板渲染

Flask中页面的渲染是通过jinja2模块来实现的,依赖于render_template函数

模板中的注释:{# #}

我们在编写模板时,变量都是没有提示的,设置步骤:

使用格式:

        return render_temperate('模板文件',key=value)

        将HTML文件渲染撑响应体对象,可以携带value值到页面中

模板的默认位置:templates,若没有该文件,报错

from flask import Flask, render_template

app = Flask(__name__)

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

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

模板中的常见语法        

        获取变量:{{变量名称}}

        分支语句:{% if 条件A%}

                                语句1

                           {%elif 条件B%}

                                语句2

                           {%endif%}

        循环语句:

                {%for 变量 in 容器%}

                        语句

                 {%endfor%}

在HTML文件中若元祖或者列表取值时,可以使用list.0或者list[0],若是字典,可以dict.key

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def hello_world():
    num = 3
    str = '你好'
    tuple = (10, 11, 12, 13)
    list = [1, 2, 3, 4]
    dict = {
        "name":"张三",
        "age":17
    }
    return render_template('file01.html', num=num, str=str, tuple= tuple, list=list, dict=dict)

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



    
    Title


    this is a template
    整数:{{ num }}
    字符串:{{ str }}
    元祖:{{ tuple }}, 分开获取:{{ tuple.0 }}, {{ tuple[2] }}
    列表:{{ list }}, 分开获取: {{ list.0 }}, {{ list[1] }}
    字典:{{ dict }}, 分开获取: {{ dict.name }}, {{ dict.age }}

    遍历列表中的偶数
    {% for n in list %}
        {% if n %2==0 %}
            {{ n }}
        {% endif %}
    {% endfor %}
    获取元祖的编号
    {% for num in tuple %}
{#        索引:{{ loop.index0 }}, 值是:{{ num }} index0 表示从0开始,若是index,表示从1开始#}
        索引:{{ loop.index0 }}, 值是:{{ num }}
    {% endfor %}
    遍历字典
    {% for key in dict %}
{#        {{ key }} = {{ dict[key] }}  不能通过dict.key来取值,这表示dict中有个主键是key,此时key不是一个变量#}
        {{ key }} = {{ dict[key] }}
    {% endfor %}

过滤器

常见的过滤器有两种:

字符串过滤器:

upper:将字符串转成大写

lower:将字符串转成小写

reverse:将字符串反转

2、列表过滤器

first:第一个元素

last:最后一个元素

sum:取和

sort:排序

使用方法:变量名|过滤器

14、模板复用

宏:提前定义好模板代码块需要的时候调用即可

定义:{ %  macro 宏名(参数)   % }

          { %  endmacro   % }

{% macro input(name,value='',type='text') %}
    
{% endmacro %}

使用:

        当前文件:{{宏名(参数)}}

        其他文件:{ %import ”宏所在的文件“ as 别名% }

                          {% 别名.宏名(参数) %}

模板继承是为了重用模板中的公共内容,共性抽取,代码复用,继承之后可以扩展子模板独有的内容

{%extends "文件名"%}

包含:A完全用友B内容,一般对于不需要改变的内容使用包含

{%include  "文件名" ignore missing%}        #ignore missing如果文件不存在,不报错

模块的定义:

{%block 名称%}

{%endblock%}

继承




    
    Title



    {% extends 'file03fu.html' %}



15、flask表单

传统表单:有大量的HTML定义好的标签组成

缺点:一旦表单编写完成,如果需要增加额外的功能,就不能实现

传统表单




    
    Title






from flask import Flask, render_template, request

app = Flask(__name__)

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


@app.route('/register', methods=["post"])
def register():
    print(request.form)
    usrname  =request.form.get('username')
    password  =request.form.get('password')
    repassword  =request.form.get('repassword')
    if not all([usrname, password, repassword]):
        return '没有填写必要内容'
    if password != repassword:
        return "密码不一致"
    return '注册成功'

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

Flask_WTF:通过类的方式渲染页面,包含字段和验证函数

步骤:

  • 安装,导包
  • 自定义类,继承自FlaskForm
  • 编写字段,验证函数
  • 创建表单对象
  • 渲染到页面中

包含csrf验证机制

from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField
from wtforms.validators import DataRequired, EqualTo

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

# 自定义类,继承自FlaskForm
class MyForm(FlaskForm):
    username = StringField('用户名', validators=[DataRequired()])
    password = PasswordField('密码', validators=[DataRequired()])
    repassword = PasswordField('确认密码', validators=[DataRequired(), EqualTo('password', '两次输入的密码必须一致!')])
    submit = SubmitField('提交')

@app.route('/')
def hello_world():
    # 创建表单
    form = MyForm()

    return render_template('file05wtf.html', form=form) #将form对象传递到模板文件中

@app.route('/register', methods=["post"])
def register():
    # 根据创建的内容创建表单
    form = MyForm()
    # 验证提交的内容
    if form.validate_on_submit():
        return "注册成功"
    return "注册失败"

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

HTML文件:




    
    Title


{{ form.csrf_token }} {{ form.username.label }} {{ form.username }}
{{ form.password.label }} {{ form.password }}
{{ form.repassword.label }} {{ form.repassword }}
{{ form.submit }}
16、数据库

常用的数据库命令:

# 进入mysql,回车输入密码
mysql -uroot -p

# 退出登录
quit
exit
ctrl+d
# 查看所有数据库
show databases;
# 使用数据库
use 数据库名;
# 删除数据库
drop 数据库名;
# 查看当前数据库中的所有表
show tables;
# 删除表
drop table 表名;
# 数据库备份
mysqldump -uroot -p 数据库名 > python.sql
# 数据库恢复
mysql -uroot -p 数据库名 < python.sql

flask *** 作数据库是通过库战报Flask_Sqlalchemy

*** 作流程:

  • 安装,导入       

                pip install flask-sqlalchemy

                pip install flask-mysqldb   若报错可以安装pip install pymsql

  • 设置数据库的配置信息

                <协议mysql+pymsql>://<用户名>:<密码>@:<端口号>/<数据库名称>

                如果安装的是mysqld,协议使用mysql;如果安装的是pymysql,协议使用mysql+pymysql

  • 创建ORM对象SQLALchemy,关联app
  • 定义模型类
  • *** 作(方法)
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)

# 设置数据库信息
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://root:[email protected]:3306/pytest"
# 设置数据库的追踪数据变化,并压制警告信息
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

#创建SQLAlchemy对象
db = SQLAlchemy(app)
# 定义模型类 角色模型
class Role(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True, nullable=False)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True, nullable=False)
    # 外键
    role_id = db.Column(db.Integer, db.ForeignKey(Role.id))


@app.route('/')
def hello_world():
    return "hello world"

if __name__ == '__main__':
    db.create_all() # 将继承自db.Model全部新增
    #db.drop_all() 会将继承自db.Model的表全部删掉
    #db.session.add(obj) 添加数据,提交回话后才会插入到数据库
    #db.session.delete(obj) 删除数据,提交回话后才会更新数据库
    #db.session.commit() 提交会话
    #db.session.rollback() 回滚
    # 添加多条
      #us9 = User(name='li',email='[email protected]',password='4526342',role_id=ro2.id)
      #us10 = User(name='sun',email='[email protected]',password='235523',role_id=ro2.id)
      #db.session.add_all([us9,us10]) 添加的是一个列表
      #db.session.commit()
    app.run(debug=True)

执行成功后,数据库会新增两个表,role表和name表,若没有指定表名称,默认是类名的小写,在类中设置__tablename__=="表名"即可

User.query.all()    #查询所有用户数据
User.query.count()    #查询有多少个用户
User.query.filter_by(name='wang').all() # 精确查询,通过name查询
User.query.first()    #查询第1个用户
#查询id为4的用户
User.query.filter_by(id=4).first()
User.query.get(4)
User.query.filter(id==4).first() # filter用于模糊查询
#查询名字结尾字符为g的所有数据[开始/包含]
User.query.filter(User.name.endswith('g')).all()
User.query.filter(User.name.startswith('g')).all() # 以g开头的
User.query.filter(User.name.contains('g')).all()    # 包含g
#查询名字不等于wang的所有数据
User.query.filter(User.name != 'wang').all()
#查询名字和邮箱都以 li 开头的所有数据
User.query.filter(User.name.startswith('li'),User.email.startswith('li'))
#查询password是 `123456` 或者 `email` 以 `itheima.com` 结尾的所有数据
from sqlalchemy import or_
User.query.filter(or_(User.password=='123456', User.email.endswith('itheima.com'))).all()

#查询id为 [1, 3, 5, 7, 9] 的用户列表
from sqlalchemy import in_
User.query.filter_by(User.id.in_([1, 3, 5, 7, 9])).all()
# 查询name为liu的角色数据
user = User.query.filter_by(name="liu").first()
role = Role.query.filter(Role.id == user.role_id).first()
# 查询所有用户数据,并以邮箱排序
User.query.order_by(User.email).all()    #默认升序
User.query.order_by(User.email.desc()).all()     #降序返回
#每页3个,查询第2页的数据
paginate = User.query.paginate(2, 3, False) # False 有错误不输出
paginate.pages    # 返回页数
paginate.items    # 显示第二页的所有数据


#查询数据后删除
user = User.query.first()
db.session.delete(user)
db.session.commit()
#更新数据
user = User.query.first()
user.name = 'dong'
db.session.commit()
17、backref 反向引用

数据库查询 *** 作:

  1. 如果知道了用户对象,能不能快速查询出该用户所扮演的角色。例如:user.role
  2. 如果知道了角色对象,能不能快速查询出哪些用户扮演了该角色。例如:role.user

使用relationship关系属性,设置backref反向引用,一般建立在一方,外键在多方
一对多:A表中的一条数据对应B表中的一条数据,B表中的一条数据对应A表中的多条数据,此时A与B是多对一的关系

In [1]: from test_query import *

In [2]: user = User.query.filter(User.name=='liu').first()

In [3]: user.role
Out[3]: 


In [2]: admin = Role.query.filter(Role.name=='admin').first()

In [3]: admin
Out[3]: 

In [4]: admin.users
Out[4]: 
[,
 ,
 ,
 ]

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/868800.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存