flask入门

异常汇总

Flask抛出RuntimeError: Working outside of application context.错误

详情:https://www.jianshu.com/p/b12290ba46c7

app.app_context().push()  # 推送应用上下文环境

Session Cookie的HttpOnly和secure属性
一、属性说明: 1 secure属性
当设置为true时,表示创建的 Cookie 会被以安全的形式向服务器传输,也就是只能在 HTTPS 连接中被浏览器传递到服务器端进行会话验证,如果是 HTTP 连接则不会传递该信息,所以不会被窃取到Cookie 的具体内容。
2 HttpOnly属性
如果在Cookie中设置了"HttpOnly"属性,那么通过程序(JS脚本、Applet等)将无法读取到Cookie信息,这样能有效的防止XSS攻击。
 
对于以上两个属性, 首先,secure属性是防止信息在传递的过程中被监听捕获后信息泄漏,HttpOnly属性的目的是防止程序获取cookie后进行攻击。
其次,GlassFish2.x支持的是servlet2.5,而servlet2.5不支持Session Cookie的"HttpOnly"属性。不过使用Filter做一定的处理可以简单的实现HttpOnly属性。GlashFish3.0(支持servlet3.0)默认开启Session Cookie的HttpOnly属性。
也就是说两个属性,并不能解决cookie在本机出现的信息泄漏的问题(FireFox的插件FireBug能直接看到cookie的相关信息)。

扩展库

命令行扩展

  • click
  • flask-script

ORM扩展

  • flask-SQLAlchemy 

session扩展

  • flask-session

基于Python的信号库 Blinker: https://www.jianshu.com/p/829da3cd70b6

拦截器、中间件或钩子函数

from flask import request


def load_middleware(app):
    @app.before_request
    def before():
        print("before 中间件", request.url)

    @app.after_request
    def after(resp):
        print("after 中间件", resp)
        return resp

flask四大内置对象

  • request
  • session
  • g
  • config

会话技术

  • 跨请求共享数据
  • 出现的原因
    • web开发中http都是短连接
    • http请求都是无状态的
    • 请求从request到response就结束了
  • Cookie
    • 客户端会话技术
    • 数据存储在客户端
  • session
    • 服务端会话技术
    • 数据存储在服务器
    • flask中
      • 将session存储在cookie中
      • 对数据进行序列化
      • 还进行了base64编码
      • 还进行了zlib压缩
      • 还进行了签名
  • Token

Cookie

# curl -c ./cookie.txt -d "username=Tom" "http://localhost:5000/login"
@blue.route('/login', methods=["GET", "POST"])
def login():
    if request.method == "GET":
        return render_template('my/login.html')
    elif request.method == "POST":
        username = request.form.get("username")
        response = Response("登录成功%s\n" % username)
        response.set_cookie("username", username)
        return response

# curl -b ./cookie.txt "http://localhost:5000/mine"
@blue.route('/mine')
def mine():
    username = request.cookies.get("username")
    return "欢迎回来:%s\n" % username

Session

# curl -c ./cookie.txt -d "username=Tom" "http://localhost:5000/login"
@blue.route('/login', methods=["GET", "POST"])
def login():
    if request.method == "GET":
        return render_template('my/login.html')
    elif request.method == "POST":
        username = request.form.get("username")
        response = Response("登录成功%s\n" % username)
        session["username"] = username
        session["password"] = "110"
        return response


# curl -b ./cookie.txt "http://localhost:5000/mine"
@blue.route('/mine')
def mine():
    username = session.get("username")
    return "欢迎回来:%s\n" % username

session持久化

Flask-Session

from flask import Flask, session
from flask.ext.session import Session

app = Flask(__name__)
# Check Configuration section for more details
SESSION_TYPE = 'redis'
app.config.from_object(__name__)
Session(app)

@app.route('/set/')
def set():
    session['key'] = 'value'
    return 'ok'

@app.route('/get/')
def get():
    return session.get('key', 'not set')

You may also set up your application later using init_app() method:

sess = Session()
sess.init_app(app)

flask config对象

settings.py

def get_db_url(db_info):
    if db_info.get("ENGINE", "sqlite") == "sqlite":
        return 'sqlite:////tmp/test.db'
    engine = db_info.get("ENGINE", "sqlite")
    driver = db_info.get("DRIVER", "sqlite")
    user = db_info.get("USER", "")
    password = db_info.get("PASSWORD", "")
    host = db_info.get("HOST", "")
    port = db_info.get("PORT", "")
    name = db_info.get("NAME", "/tmp/test.db")
    return "{}+{}://{}:{}@{}:{}/{}".format(engine, driver, user, password, host, port, name)


class Config(object):
    DEBUG = False
    TESTING = False
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    JSON_AS_ASCII = False
    RESTFUL_JSON = dict(ensure_ascii=False)


class DevelopConfig(Config):
    DEBUG = False
    # db_info = {
    #     "ENGINE": "mysql",
    #     "DRIVER": "pymysql",
    #     "USER": "test",
    #     "PASSWORD": "abcABC123...",
    #     "HOST": "localhost",
    #     "PORT": "3306",
    #     "NAME": "helloFlask",
    # }
    SQLALCHEMY_DATABASE_URI = get_db_url({})


class TestConfig(Config):
    DEBUG = False
    db_info = {
        "ENGINE": "mysql",
        "DRIVER": "pymysql",
        "USER": "test",
        "PASSWORD": "abcABC123...",
        "HOST": "localhost",
        "PORT": "3306",
        "NAME": "helloFlask",
    }
    SQLALCHEMY_DATABASE_URI = get_db_url(db_info)


class ProductConfig(Config):
    DEBUG = False
    db_info = {
        "ENGINE": "mysql",
        "DRIVER": "pymysql",
        "USER": "test",
        "PASSWORD": "abcABC123...",
        "HOST": "localhost",
        "PORT": "3306",
        "NAME": "helloFlask",
    }
    SQLALCHEMY_DATABASE_URI = get_db_url(db_info)


envs = {
    "dev": DevelopConfig,
    "testing": TestConfig,
    "product": ProductConfig,
    "default": DevelopConfig
}

初始化配置

from flask import Flask

def create_app():
    app = Flask(__name__)

    # 加载配置
    app.config.from_object(envs.get("dev"))
    return app

html获取配置

<ul>
    {% for con in config %}
        <li>{{con}} = {{ config|con }}</li>
    {% endfor %}
</ul>

函数中获取配置

from flask import  current_app

def load_middleware(app):
    @app.before_request
    def before():
        config = current_app.config
        keys = current_app.config.keys()
        for key in keys:
            print(key, "=", config[key])

Flask的g对象,范围是什么?

作者:胡伟强
链接:https://www.zhihu.com/question/25033592/answer/103317523

问题: https://www.zhihu.com/question/25033592

1. app context 和 request context
request context 很好理解,看名字就知道是请求上下文。
而 app context 却有点误导性,它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对 app 的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的应用,它是伴 request 而生,随 request 而灭的。2. g 和 session
从第一点可以明白 app context 的生命周期其实也只有一个 request 那么长。
既然这样,那么像g这样的属于app context 的变量当然也是只有一个 request 那么长的生命周期了。
这就是为什么你的代码在另一个 request 中读取 g.count 会报错的原因。
要达到你想要的效果,你可以使用其session。
稍微读一下源码你就会发现 session 也是一个 request context 的变量,但它把数据保存到了 cookie 中并发送到了客户端,客户端再次请求的时候又带上了cookie。

flask,flask-restful接口返回值无法显示中文

作者:是凸凸不是凹凹
链接:https://www.jianshu.com/p/c24d3de00ac4

flask 接口无法显示中文,添加:

app= Flask(__name__)
app.config['JSON_AS_ASCII'] =False

flask-restful 接口无法显示中文,添加:

app= Flask(__name__)
app.config.update(RESTFUL_JSON=dict(ensure_ascii=False))

flask-restful

from flask import Flask
from flask_restful import Resource, Api, fields, marshal_with, reqparse, marshal

app = Flask(__name__)
api = Api(app)

user_fields = {
    "id": fields.Integer,
    'custom_name': fields.String(attribute="name"),
    "desc": fields.String(default="default_desc")
}

single_users_fields = {
    "status": fields.Integer,
    "msg": fields.String,
    "data": fields.Nested(user_fields)
}

multi_users_fields = {
    "status": fields.Integer,
    "msg": fields.String,
    "data": fields.List(fields.Nested(user_fields))
}

parser = reqparse.RequestParser()
parser.add_argument('name', required=True, type=str, help='name cannot be converted')


class User(object):
    pass


class HelloResource1(Resource):
    @marshal_with(user_fields, envelope='resource')
    def get(self):  # curl "http://localhost:5000/hello1?name=Tom"
        args = parser.parse_args()
        name = args['name']

        ret = []
        for _ in range(2):
            user = User()
            user.id = _
            user.name = name
            ret.append(user)
        return ret


class HelloResource2(Resource):
    def get(self):  # curl "http://localhost:5000/hello2"
        user = User()
        user.id = 1
        user.name = "Tom"
        ret = {
            "status": 200,
            "msg": "success",
            "data": marshal(user, user_fields)
        }
        return ret


class HelloResource3(Resource):
    @marshal_with(single_users_fields)
    def get(self):
        user = User()
        user.id = 1
        user.name = "Tom"
        ret = {
            "status": 200,
            "msg": "success",
            "data": marshal(user, user_fields)
        }
        return ret


class HelloResource4(Resource):
    @marshal_with(multi_users_fields)
    def get(self):
        user = User()
        user.id = 1
        user.name = "Tom"
        ret = {
            "status": 200,
            "msg": "success",
            "data": [marshal(user, user_fields)]
        }
        return ret


api.add_resource(HelloResource1, '/hello1')
api.add_resource(HelloResource2, '/hello2')
api.add_resource(HelloResource3, '/hello3')
api.add_resource(HelloResource4, '/hello4', '/')

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

flask-sqlalchemy 简单笔记

https://www.jianshu.com/p/a52cf3907f29

$ cat app.py 
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    books = db.relationship('Book', backref='user',lazy=True)

    def __repr__(self):
        return '<User username:%r email:%s>' % (self.username, self.email)

class Book(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(256), unique=True, nullable=False,default="")
    user_id = db.Column(db.Integer,db.ForeignKey('user.id'))

    def __repr__(self):
        return '<Book name:%s user_id:%d>' % (self.name, self.user_id)

python3交互式

from app import *

db.drop_all()
db.create_all()
admin = User(username='admin', email='admin@example.com') guest = User(username='guest', email='guest@example.com') db.session.add(admin) db.session.add(guest) db.session.commit()
boy
= Book(name='boy', user=admin) girl = Book(name='girl', user=guest) db.session.add(boy) db.session.add(girl) db.session.commit()

迭代

>>> for user in User.query.all():
...     print(user)
... 
<User username:'admin' email:admin@example.com>
<User username:'guest' email:guest@example.com>

获取第一个

>>> user = user.query.filter_by().first()
>>> print(user)
<User username:'admin' email:admin@example.com>

关联查询

>>> user.books
[<Book name:boy user_id:1>]
>>> book = user.books[0]
>>> book.user
<User username:'admin' email:admin@example.com> 

sqlalchemy session 执行 delete 时 synchronize_session 策略

原文地址:https://sanyuesha.com/2017/06/07/sqlalchemy-delete-synchronize-session/

session.query(Tag).filter(Tag.id.in_([1,2,3,4])).delete(synchronize_session='evaluate')

sqlalchemy 利用 session 执行 delete 时有一个 synchronize_session 参数用来说明 session 删除对象时需要执行的策略,共三个选项:

False

don’t synchronize the session. This option is the most efficient and is reliable once the session is expired, which typically occurs after a commit(), or explicitly using expire_all(). Before the expiration, objects may still remain in the session which were in fact deleted which can lead to confusing results if they are accessed via get() or already loaded collections.

不同步 session,如果被删除的 objects 已经在 session 中存在,在 session commit 或者 expire_all 之前,这些被删除的对象都存在 session 中。

不同步可能会导致获取被删除 objects 时出错。

fetch

performs a select query before the delete to find objects that are matched by the delete query and need to be removed from the session. Matched objects are removed from the session.

删除之前从 db 中匹配被删除的对象并保存在 session 中,然后再从 session 中删除,这样做是为了让 session 的对象管理 identity_map 得知被删除的对象究竟是哪些以便更新引用关系。

evaluate

Evaluate the query’s criteria in Python straight on the objects in the session. If evaluation of the criteria isn’t implemented, an error is raised.
The ‘evaluate’ strategy performs a scan of all matching objects within the Session; if the contents of the Session are expired, such as via a proceeding Session.commit() call, this will result in SELECT queries emitted for every matching object.

默认值。根据当前的 query criteria 扫描 session 中的 objects,如果不能正确执行则抛出错误,这句话也可以理解为,如果 session 中原本就没有这些被删除的 objects,扫描当然不会发生匹配,相当于匹配未正确执行。

注意这里报错只会在特定 query criteria 时报错,比如 in 操作。

session.query(Tag).filter(Tag.id.in_([1,2,3])).delete()

sqlalchemy.exc.InvalidRequestError: Could not evaluate current criteria in Python. Specify 'fetch' or False for the synchronize_session parameter.

pip3 install psycopg2安装失败,解决办法

 

sudo apt-get install python-psycopg2
sudo apt-get install libpq-dev python3-dev
pip3 install psycopg2

 

 

 

 

 

posted @ 2020-05-11 23:00  逐梦客!  阅读(248)  评论(0)    收藏  举报