Flask操作指南

    /home/user/Projects/flask-tutorial
    ├── flaskr/    #一个包含应用代码和文件的 Python 包。
    │   ├── __init__.py
    │   ├── db.py
    │   ├── schema.sql
    │   ├── auth.py
    │   ├── blog.py
    │   ├── templates/
    │   │   ├── base.html
    │   │   ├── auth/
    │   │   │   ├── login.html
    │   │   │   └── register.html
    │   │   └── blog/
    │   │       ├── create.html
    │   │       ├── index.html
    │   │       └── update.html
    │   └── static/
    │       └── style.css
    ├── tests/    #一个包含测试模块的文件夹。
    │   ├── conftest.py
    │   ├── data.sql
    │   ├── test_factory.py
    │   ├── test_db.py
    │   ├── test_auth.py
    │   └── test_blog.py
    ├── venv/    #一个 Python 虚拟环境,用于安装 Flask 和其他依赖的包。
    ├── setup.py
    └── MANIFEST.in
    
    假设使用 git 来进行版本控制,那么使用 .gitignore 来设置应当忽略 的文件
项目布局
Setuptools是一个通常被用来分发Python库和扩展的外部扩展库。    
    setup.py
        from setuptools import find_packages, setup
        setup(
            name='flaskr',
            version='1.0.0',
            packages=find_packages(),
            include_package_data=True,
            zip_safe=False,     #zip_safe可以用来强迫或者避免zip Archive的创建。
            install_requires=[  #依赖作为一个list被声明在install_requires参数中,这个list中的每一项应该从PyPI中下载下来,
                'flask',        #缺省下载最近的版本,但是可以提供最小和最大的版本值。
                'SQLAlchemy>=0.6',
            ],
        )    
    
        packages 告诉 Python 包所包括的文件夹(及其所包含的 Python 文件)。find_packages() 自动找到这些文件夹,这样就不用手动写出来。 
        为了包含其他文件夹,如静态文件和模板文件所在的文件夹,需要设置 include_package_data =True 。
        include_package_data告诉setuptools去寻找一个MAINFEST.in文件,并且下载所有匹配包数据的项,
            可以用这个来伴随你的python模块分发静态文件和模版文件。
    MANIFEST.in
        include flaskr/schema.sql
        graft flaskr/static
        graft flaskr/templates
        global-exclude *.pyc
        
        这告诉 Python 复制所有 static 和 templates 文件夹中的文件, schema.sql 文件,但是排除所有字节文件。
描述项目
该类的实例将会成为 WSGI 应用。
    class Flask(_PackageBoundObject):
        def __init__(
                self,
                import_name,    
                static_url_path=None,
                static_folder='static',
                static_host=None,
                host_matching=False,
                subdomain_matching=False,
                template_folder='templates',
                instance_path=None,
                instance_relative_config=False,
                root_path=None
            )
            import_name: 让Flask了解属于您的应用程序的内容,如果您使用单个模块,'__name__'始终是正确的值。 
                但是,如果您使用的是软件包,通常建议在那里对软件包的名称进行硬编码。

            static_url_path:可用于为Web上的静态文件指定不同的路径。 默认为'static_folder'文件夹的名称。

            static_folder: 包含静态文件的文件夹,应该在'static_url_path'中提供。 默认为应用程序根路径中的'static'文件夹。
            
            static_host: 添加静态路由时使用的主机。 默认为无。
            
            host_matching:
            
            subdomain_matching: 
            
            template_folder:
            
            instance_path:
            
            instance_relative_config:
            
            root_path:
导入 Flask 类
不管你使用何种方式载入配置,都可以使用 Flask 对象的 config 属性来操作配置的值。
    config 实质上是一个字典的子类,可以像字典一样操作:
        app = Flask(__name__)
        app.config['TESTING'] = True
        
        一次更新多个配置值:
            app.config.update(
                TESTING=True,
                SECRET_KEY=b'_5#y2L"F4Q8z\n\xec]/'
            )
    使用类和类的继承来配置:
        class Config(object):
            DEBUG = False
            TESTING = False
            DATABASE_URI = 'sqlite:///:memory:'

        class ProductionConfig(Config):
            DATABASE_URI = 'mysql://user@localhost/foo'

        class DevelopmentConfig(Config):
            DEBUG = True

        class TestingConfig(Config):
            TESTING = True
        
        app.config.from_object('configmodule.ProductionConfig')
    
    
    app.config.from_envvar('YOURAPPLICATION_SETTINGS')
    
    app.config.from_pyfile('application.cfg', silent=True)
    
    实例文件夹
        显式定义使用 instance_path 参数:
            app = Flask(__name__, instance_path='/path/to/instance/folder')
            
            
    app.config.from_json(filename, silent=False)

    app.config.from_mapping(*mapping, **kwargs)
配置管理
@app.route('/')
    实际上是调用了
    @setupmethod
    def add_url_rule(self, rule, endpoint=None, view_func=None,
                     provide_automatic_options=None, **options):
        
        endpoint:已注册URL规则的端点。 Flask默认将视图函数的名称视为端点
            self.view_functions[endpoint] = view_func
        
        provide_automatic_options:控制是否应自动添加'OPTIONS'方法。 
            这也可以通过在添加规则之前设置'view_func.provide_automatic_options = False'来控制。

        options:'methods','redirect_to','host','alias','build_only','subdomain'
            #如果使用了 GET 方法, Flask 会自动添加 HEAD 方法支持,OPTIONS 也会自动实现。
        
    创建一个Rule实例 Rule(rule, methods=methods, **options),并将其放入app.url_map中,
        然后设置视图函数app.view_functions[endpoint] = view_func
    #rule必须以'/'开头
    @app.route('/projects/')
    def projects():
        return 'The project page'

    @app.route('/about')
    def about():
        return 'The about page'    
    projects 的 URL 尾部有一个斜杠,看起来就如同一个文件夹。 
        访问一个没有斜杠结尾的 URL 时 Flask 会自动进行重定向,帮你在尾部加上一个斜杠。
    about 的 URL 没有尾部斜杠,因此其行为表现与一个文件类似。
        如果访问这个 URL 时添加了尾部斜杠就会得到一个 404 错误。
    
    url_for() 函数用于构建指定函数的 URL。它把函数名称作为第一个 参数。
        它可以接受任意个关键字参数,每个关键字参数对应 URL 中的变量。
        未知变量 将添加到 URL 中作为查询参数。
        url_for('login')           -->  /login
        url_for('login', next='/') -->  /login?next=/
路由
class UserAPI(MethodView):

    def get(self, user_id):
        if user_id is None:
            # 返回一个包含所有用户的列表
            pass
        else:
            # 显示一个用户
            pass

    def post(self):
        # 创建一个新用户
        pass

    def delete(self, user_id):
        # 删除一个用户
        pass

    def put(self, user_id):
        # update a single user
        pass
    
    
    def register_api(view, endpoint, url, pk='id', pk_type='int'):
        view_func = view.as_view(endpoint)
        app.add_url_rule(url, defaults={pk: None},
                         view_func=view_func, methods=['GET',])
        app.add_url_rule(url, view_func=view_func, methods=['POST',])
        app.add_url_rule('%s<%s:%s>' % (url, pk_type, pk), view_func=view_func,
                         methods=['GET', 'PUT', 'DELETE'])

    register_api(UserAPI, 'user_api', '/users/', pk='user_id')
    
    
    装饰视图
        #定义装饰器
        import functools
        def user_required(func):
            @functools.wraps(func)
            def inner(*args,**kwargs):
                if not g.user:
                    abort(401)
                return func(*args,**kwargs)

            return inner
        #添加装饰器
        class UserAPI(MethodView):
            decorators = [user_required]    
基于类的视图
render_template() 方法可以渲染模板,提供模板名称和context 作为参数传递给模板的变量就行了。
        Flask 会在 templates 文件夹内寻找模板。
            情形 1 : 一个模块:
                /application.py
                /templates
                    /hello.html
            情形 2 : 一个包:
                /application
                    /__init__.py
                    /templates
                        /hello.html
        自动转义默认开启。因此,如果 name 包含 HTML ,那么会被自动转义。
            如果你可以 信任某个变量,且知道它是安全的 HTML ,那么可以使用 Markup 类把它标记为安全的,
            或者在模板 中使用 |safe 过滤器。
渲染模版
HTML 表单中设置 enctype="multipart/form-data" 属性
    已上传的文件被储存在内存或文件系统的临时位置。
        你可以通过请求对象 files 属性来访问上传的文件。每个上传的文件都储存在这个 字典型属性中。
    @app.route('/upload', methods=['GET', 'POST'])
    def upload_file():
        if request.method == 'POST':
            f = request.files['the_file']
            f.save('/var/www/uploads/uploaded_file.txt')
            #文件上传之前其在客户端系统中的名称,可以使用 filename 属性。 f.filename

        #如果想要把客户端的文件名作为服务器上的文件名, 可以通过 Werkzeug 提供的 secure_filename() 函数
            from werkzeug.utils import secure_filename
            f.save(current_app.config['UPLOAD_FOLDER'] + secure_filename(f.filename))
            注意:
                secure_filename()函数只返回ASCII字符,非ASCII字符会被过滤掉。
                所以会导致中文出错,可以将中文文件名转换成英文,可以使用pypinyin来转换(使用pip安装)
                from pypinyin import lazy_pinyin
                name , ext = f.filename.split('.')
                new_filename= '_'.join(lazy_pinyin(name)) + '.' + ext
                f.save((os.path.join(current_app.config['UPLOAD_FOLDER'], new_filename))
文件上传
from flask import request
    username = request.cookies.get('username')   #获取cookies
    
    resp = make_response(render_template(...))
    resp.set_cookie('username', 'the username')  #设置cookies
    
    注意, cookies 设置在响应对象上。通常只是从视图函数返回字符串, Flask 会把它们 转换为响应对象。
    如果你想显式地转换,那么可以使用 make_response() 函数,然后再修改它。
Cookies
Request的常用操作
    Flask之request - 道友请多指教 - 博客园 http://www.cnblogs.com/95lyj/p/9508723.html
Request的常用操作
注册出错处理器
        1.使用 errorhandler() 装饰函数来注册
        @app.errorhandler(404)
        def page_not_found(error):
            return render_template('page_not_found.html'), 404
            
        注意 render_template() 后面的 404 ,这表示页面对就的出错 代码是 404 ,即页面不存在。缺省情况下 200 表示:一切正常。
        2.使用 register_error_handler() 来注册
        app.register_error_handler(400, handle_bad_request)
    
    @app.errorhandler(werkzeug.exceptions.BadRequest)
    def handle_bad_request(e):
        return 'bad request!', 400
    
    BadRequest ,和它们的 HTTP 代码是可替换的。 ( BadRequest.code == 400 )
    app.error_handler_spec = 
        {None: {400: {<class 'werkzeug.exceptions.BadRequest'>: <function handle_bad_request>}}}
    
    我们可以自定义一个 HTTPException 子类:
        class InsufficientStorage(werkzeug.exceptions.HTTPException):
            code = 507
            description = 'Not enough storage space.'

        app.register_error_handler(InsuffcientStorage, handle_507) 
错误处理
视图函数的返回值会自动转换为一个响应对象。
    如果返回值是一个字符串,那么会被转换 为一个包含作为响应体的字符串、一个 200 OK 出错代码 和一个 text/html 类型的响应对象。
    以下是转换的规则:
        1.如果视图返回的是一个响应对象,那么就直接返回它。
        2.如果返回的是一个字符串,那么根据这个字符串和缺省参数生成一个用于返回的 响应对象。
        3.如果返回的是一个元组,那么元组中的项目可以提供额外的信息。
            元组中必须至少 包含一个项目,且项目应当由 (response, status, headers) 或者 (response, headers) 组成。 
            status 的值会重载状态代码, 
            headers 是一个由额外头部值组成的列表或字典。
        
        4.如果以上都不是,那么 Flask 会假定返回值是一个有效的 WSGI 应用并把它转换为 一个响应对象。
    
    if len_rv == 3:
        rv, status, headers = rv
    elif len_rv == 2:
        if isinstance(rv[1], (Headers, dict, tuple, list)):
            rv, headers = rv
        else:
            rv, status = rv
    app.response_class(rv, status=status, headers=headers)
    
    如果想要在视图内部掌控响应对象的结果,那么可以使用 make_response() 函数。
        
        如设置cookies、添加响应头
    
响应
除了请求对象之外还有一种称为 session 的对象,允许你在不同请求 之间储存信息。
    这个对象相当于用密钥签名加密的 cookie ,即用户可以查看你的 cookie ,但是如果没有密钥就无法修改它。
    
    使用会话之前你必须设置一个密钥。
        from flask import Flask, session, escape, request
        app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
        或者使用随机字符串,更安全
        import os; 
        app.secret_key = os.urandom(16)
Session
Blueprint 是一种组织一组相关视图及其他代码的方式。
    蓝图方式是把它们注册到蓝图,然后在工厂函数中 把蓝图注册到应用。
    和普通应用一样,蓝图一般都放在一个文件夹中。虽然多个蓝图可以共存于同一个文 件夹中,但是最好不要这样做。
    
    创建蓝图
        from flask import Blueprint
        simple_page = Blueprint('admin', __name__,static_folder='static',
                        template_folder='templates',url_prefix='/admin')
            创建了一个名称为 'admin' 的 Blueprint 。
            __name__ 这个参数 指定与蓝图相关的逻辑 Python 模块或包。 
            url_prefix 会添加到所有与该蓝图关联的 URL 前面。
        
        资源文件夹
            第二个参数:
                如果这个参数指向的是实际的 Python 包 (文件系统中的一个文件夹),那么它就是资源文件夹。
                如果是一个模块,那么这个 模块包含的包就是资源文件夹。
            
            可以通过 Blueprint.root_path 属性来查 看蓝图的资源文件夹 (返回绝对路径)
            
            可以使用 open_resource() 函数快速打开这个文件夹中的资源
                with simple_page.open_resource('static/style.css') as f:
                    code = f.read()
        静态文件
            静态文件 URL 就是 /admin/static 
        
        模版文件
            如果你有一个名为 admin 的蓝图,该蓝图指定的模版文件是 index.html ,
                admin/
                    templates/
                        admin/
                            index.html
                    __init__.py        
            当你需要渲染模板的时候就可以使用 admin/index.html 来找到模板。
    
    注册蓝图
        使用 app.register_blueprint() 导入并注册 蓝图。新的代码放在工厂函数的尾部返回应用之前。
            def create_app():
                app = ...
                # existing code omitted

                from . import auth
                app.register_blueprint(admin.simple_page)

                return app
    蓝图视图
        @simple_page.route('/register', methods=('GET', 'POST'))
        def register():
            ...
            return render_template('admin/register.html')

        当 Flask 收到一个指向 /admin/register 的请求时就会调用 register 视图并把其返回值作为响应。

    使用url_for 创建 URL
        和通常一样使用 url_for() 函数,只是要把蓝图名称作为端点的前缀,并且用一个点( . )来分隔
            url_for('simple_page.index')    
蓝图
from flask import current_app,request,session,g
    
    current_app = LocalProxy(_find_app)
        #_app_ctx_stack.top.app

    request = LocalProxy(partial(_lookup_req_object, 'request'))
    session = LocalProxy(partial(_lookup_req_object, 'session'))
        #getattr(_request_ctx_stack.top, 'request')
        #getattr(_request_ctx_stack.top, 'session')

    g = LocalProxy(partial(_lookup_app_object, 'g'))
        #getattr(_app_ctx_stack.top, 'g')

    app  __call__   方法:
        def wsgi_app(self, environ, start_response):
            ctx = RequestContext(self, environ)
            try:
                ctx.push()
                    
                    #1 _app_ctx_stack = LocalStack()
                        _app_ctx_stack._local.__storage__ = {
                            self.__ident_func__():{
                                'stack' : [Appcontext(),]
                            }
                        
                        }
                    #2 _request_ctx_stack = LocalStack()
                        _request_ctx_stack._local.__storage__ = {
                            self.__ident_func__():{
                                'stack' : [RequestContext(),]
                            }
                        
                        }
                    
                    app_ctx = AppContext(self.app)
                    app_ctx.push()
                        _app_ctx_stack.push(self)
                    
                    ctx._implicit_app_ctx_stack = [app_ctx]
                    
                    _request_ctx_stack.push(self)
            
                response = self.full_dispatch_request()
            
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            
            return response(environ, start_response)
current_app、request、session、g
@app.before_first_request
        app.before_first_request_funcs.append(f)
    
    @app.url_value_preprocessor   #在请求匹配到且能够执行基于url值的代码的时候立即执行
        app.url_value_preprocessors.setdefault(None, []).append(f)
        func(request.endpoint, request.view_args)

    @app.before_request
        app.before_request_funcs.setdefault(None, []).append(f)  #如果有返回值,则跳过视图函数,按反顺序执行after_request(rv)
    
    @app.after_request
        app.after_request_funcs.setdefault(None, []).append(f)   #按照添加的顺序反向执行

    @app.teardown_request
    
    @app.errorhandler
    
    @app.url_defaults
        app.url_default_functions.setdefault(None, []).append(f)
        实例:
            @app.url_defaults
            def add_language_code(endpoint, values):
                if 'lang_code' in values or not g.lang_code:
                    return
                if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
                    values['lang_code'] = g.lang_code
                #url_map 的 is_endpoint_expecting函数用于找出往endpoint函数传递该参数是否有意义。

            @app.route('/<lang_code>/')
            def index():
                pass


            # 此时会调用add_language_code函数,endpoint就是传入url_for的第一个参数,就是处理url的函数index, values是包括url_for函数所有后面参数的dict,添加values的值,用于生成url
            >>> url_for('index', lang_code='Zh')

            # 假设此时g.lang_code已经存在,且值为‘Zh’
            >>> url_for('index')
            '/Zh'

    @blueprint.before_app_first_request
    
    @blueprint.before_app_request
    
    @blueprint.before_request
    
    @blueprint.after_request
    
    @blueprint.after_app_request
    
    @blueprint.teardown_app_request
    
    @blueprint.teardown_request
装饰器

 

转载于:https://www.cnblogs.com/fufenghao/p/10847399.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值