用flask开发个人博客(27)—— 利用程序工厂函数创建Flask程序对象并注册蓝本

        什么是程序工厂函数?请先看__init__.py的代码:

from flask import Flask,render_template
from flask.ext.bootstrap import Bootstrap
from flask.ext.mail import Mail
from flask.ext.moment import Moment
from flask.ext.sqlalchemy import SQLAlchemy
from config import config

bootstrap=Bootstrap()
mail=Mail()
moment=Moment()
db=SQLAlchemy()

def create_app(config_name):
    app=Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)

    bootstrap.init_app(app)
    mail.init_app(app)
    moment.init_app(app)
    db.init_app(app)

    from .main import main as main_blueprint
    app.register_blueprint(main_blueprint)
    return app
        这里定义的这个create_app()函数就是工厂函数。为什么要使用工厂函数?其实在上一文已经提到过,主要是因为在可以实现app的动态配置,通过传给create_app()不同的参数,给app不同的配置。下面我们逐行分析下这个__init__.py文件:
1、8~11行分别创建了bootstrap等四个对象。

        对比之前对象的创建方式:

mail=Mail(app)
bootstrap=Bootstrap(app)
moment=Moment(app)
db=SQLAlchemy(app)
        此次创建并没有传入app的参数,而是分别采用Bootstrap等无参构造方式,然后再create_app())中利用init_app成员函数进行a与pp对象的关联:

bootstrap.init_app(app)
mail.init_app(app)
moment.init_app(app)
db.init_app(app)

2、接下来看倒数第2行和第3行,这里我们引入了一个蓝本的类对象main_blueprint       

        我们先看下app/main/__init__.py中对main的定义:

from flask import Blueprint
main=Blueprint('main',__name__)
from . import views,errors
        发现main是一个叫做Blueprint的类对象,在工厂函数中利用 app.register_blueprint()关联了这个类对象,接下来查看flask中对register_blueprint的定义:

@setupmethod
    def register_blueprint(self, blueprint, **options):
        """Register a blueprint on the application. For information about 
        blueprints head over to :ref:`blueprints`.

        The blueprint name is passed in as the first argument.	
        Options are passed as additional keyword arguments and forwarded to 
        `blueprints` in an "options" dictionary.

        :param subdomain: set a subdomain for the blueprint
        :param url_prefix: set the prefix for all URLs defined on the blueprint.
                            ``(url_prefix='/<lang code>')``
        :param url_defaults: a dictionary with URL defaults that is added to 
                            each and every URL defined with this blueprint
        :param static_folder: add a static folder to urls in this blueprint
        :param static_url_path: add a static url path to urls in this blueprint
        :param template_folder: set an alternate template folder
        :param root_path: set an alternate root path for this blueprint

        .. versionadded:: 0.7
        """
        first_registration = False
        if blueprint.name in self.blueprints:
            assert self.blueprints[blueprint.name] is blueprint, \
                'A blueprint\'s name collision occurred between %r and ' \
                '%r.  Both share the same name "%s".  Blueprints that ' \
                'are created on the fly need unique names.' % \
                (blueprint, self.blueprints[blueprint.name], blueprint.name)
        else:
            self.blueprints[blueprint.name] = blueprint
            self._blueprint_order.append(blueprint)
            first_registration = True
        blueprint.register(self, options, first_registration)

        发现register_blueprint()函数的作用就是把蓝本对象main注册到了其一个list中blueprints,我们可以做这样的猜想,当用户在输入URL中,程序内部会从app类中的蓝本list中找到这个蓝本对象main,然后再从main中找到对应的templates或者static资源路径,为了验证这个猜想,我们再看下blueprint的构造函数:
def __init__(self, name, import_name, static_folder=None,
                 static_url_path=None, template_folder=None,
                 url_prefix=None, subdomain=None, url_defaults=None,
                 root_path=None):
        _PackageBoundObject.__init__(self, import_name, template_folder,
                                     root_path=root_path)
        self.name = name
        self.url_prefix = url_prefix
        self.subdomain = subdomain
        self.static_folder = static_folder
        self.static_url_path = static_url_path
        self.deferred_functions = []
        if url_defaults is None:
            url_defaults = {}
        self.url_values_defaults = url_defaults

        我们使用Blueprint('main',__name__),也就是没有传入static_folder等参数,请留意deferred_functions 这个成员变量,这是一个list,将用于存储flask的url和视图函数的映射关系。我们看下在app/main/viem.py中用来添加路由和视图函数的方法:

from datetime import datetime
from flask import render_template,session,redirect,url_for

from . import main
from .forms import NameForm
from .. import db
from ..models import User

@main.route('/',methods=['GET','POST'])
def index():
    name=None
    form=NameForm()
    if form.validate_on_submit():
        user=User.query.filter_by(username=form.name.data).first()
        if user is None:
            user =User(username=form.name.data)
            db.session.add(user)
            session['konwn'] = False
            #if app.config['FLASKY_ADMIN']:
                #send_email(app.config['FLASKY_ADMIN'],'New User','mail/new_user',user=user)
        else:
            session['known'] = True
        session['name']= form.name.data
        form.name.data=''
        return redirect(url_for('main.index'))
    return render_template('index.html',form=form,name=session.get('name'),
            known=session.get('known',False))

        它调用蓝本的route函数添加路由与视图函数的映射,下面分别是route等函数的原型,它们存在调用关系:route -> add_url_rule() -> record()。仔细看下调用record的代码,传给它的参数是由lambda定义的一个匿名函数,返回参数的add_url_rule方法,而这个参数将来就会是flask程序对象。至此我们跟踪完了添加路由和视图函数的整个过程。

ef route(self, rule, **options):
        """Like :meth:`Flask.route` but for a blueprint.  The endpoint for the
        :func:`url_for` function is prefixed with the name of the blueprint.
        """
        def decorator(f):
            endpoint = options.pop("endpoint", f.__name__)
            self.add_url_rule(rule, endpoint, f, **options)
            return f
        return decorator

def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
    """Like :meth:`Flask.add_url_rule` but for a blueprint.  The endpoint for
    the :func:`url_for` function is prefixed with the name of the blueprint.
    """
    if endpoint:
        assert '.' not in endpoint, "Blueprint endpoints should not contain dots"
        self.record(lambda s:s.add_url_rule(rule, endpoint, view_func, **options))

def record(self, func):
        """Registers a function that is called when the blueprint is
        registered on the application.  This function is called with the
        state as argument as returned by the :meth:`make_setup_state`
        method.
        """
        if self._got_registered_once and self.warn_on_modifications:
            from warnings import warn
            warn(Warning('The blueprint was already registered once '
                         'but is getting modified now.  These changes '
                         'will not show up.'))
            self.deferred_functions.append(func)

Github位置:
https://github.com/HymanLiuTS/flaskTs
克隆本项目:
git clone git@github.com:HymanLiuTS/flaskTs.git
获取本文源代码:
git checkout FL26

































































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值