blueprints.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. # -*- coding: utf-8 -*-
  2. """
  3. flask.blueprints
  4. ~~~~~~~~~~~~~~~~
  5. Blueprints are the recommended way to implement larger or more
  6. pluggable applications in Flask 0.7 and later.
  7. :copyright: (c) 2015 by Armin Ronacher.
  8. :license: BSD, see LICENSE for more details.
  9. """
  10. from functools import update_wrapper
  11. from .helpers import _PackageBoundObject, _endpoint_from_view_func
  12. class BlueprintSetupState(object):
  13. """Temporary holder object for registering a blueprint with the
  14. application. An instance of this class is created by the
  15. :meth:`~flask.Blueprint.make_setup_state` method and later passed
  16. to all register callback functions.
  17. """
  18. def __init__(self, blueprint, app, options, first_registration):
  19. #: a reference to the current application
  20. self.app = app
  21. #: a reference to the blueprint that created this setup state.
  22. self.blueprint = blueprint
  23. #: a dictionary with all options that were passed to the
  24. #: :meth:`~flask.Flask.register_blueprint` method.
  25. self.options = options
  26. #: as blueprints can be registered multiple times with the
  27. #: application and not everything wants to be registered
  28. #: multiple times on it, this attribute can be used to figure
  29. #: out if the blueprint was registered in the past already.
  30. self.first_registration = first_registration
  31. subdomain = self.options.get('subdomain')
  32. if subdomain is None:
  33. subdomain = self.blueprint.subdomain
  34. #: The subdomain that the blueprint should be active for, ``None``
  35. #: otherwise.
  36. self.subdomain = subdomain
  37. url_prefix = self.options.get('url_prefix')
  38. if url_prefix is None:
  39. url_prefix = self.blueprint.url_prefix
  40. #: The prefix that should be used for all URLs defined on the
  41. #: blueprint.
  42. self.url_prefix = url_prefix
  43. #: A dictionary with URL defaults that is added to each and every
  44. #: URL that was defined with the blueprint.
  45. self.url_defaults = dict(self.blueprint.url_values_defaults)
  46. self.url_defaults.update(self.options.get('url_defaults', ()))
  47. def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
  48. """A helper method to register a rule (and optionally a view function)
  49. to the application. The endpoint is automatically prefixed with the
  50. blueprint's name.
  51. """
  52. if self.url_prefix:
  53. rule = self.url_prefix + rule
  54. options.setdefault('subdomain', self.subdomain)
  55. if endpoint is None:
  56. endpoint = _endpoint_from_view_func(view_func)
  57. defaults = self.url_defaults
  58. if 'defaults' in options:
  59. defaults = dict(defaults, **options.pop('defaults'))
  60. self.app.add_url_rule(rule, '%s.%s' % (self.blueprint.name, endpoint),
  61. view_func, defaults=defaults, **options)
  62. class Blueprint(_PackageBoundObject):
  63. """Represents a blueprint. A blueprint is an object that records
  64. functions that will be called with the
  65. :class:`~flask.blueprints.BlueprintSetupState` later to register functions
  66. or other things on the main application. See :ref:`blueprints` for more
  67. information.
  68. .. versionadded:: 0.7
  69. """
  70. warn_on_modifications = False
  71. _got_registered_once = False
  72. def __init__(self, name, import_name, static_folder=None,
  73. static_url_path=None, template_folder=None,
  74. url_prefix=None, subdomain=None, url_defaults=None,
  75. root_path=None):
  76. _PackageBoundObject.__init__(self, import_name, template_folder,
  77. root_path=root_path)
  78. self.name = name
  79. self.url_prefix = url_prefix
  80. self.subdomain = subdomain
  81. self.static_folder = static_folder
  82. self.static_url_path = static_url_path
  83. self.deferred_functions = []
  84. if url_defaults is None:
  85. url_defaults = {}
  86. self.url_values_defaults = url_defaults
  87. def record(self, func):
  88. """Registers a function that is called when the blueprint is
  89. registered on the application. This function is called with the
  90. state as argument as returned by the :meth:`make_setup_state`
  91. method.
  92. """
  93. if self._got_registered_once and self.warn_on_modifications:
  94. from warnings import warn
  95. warn(Warning('The blueprint was already registered once '
  96. 'but is getting modified now. These changes '
  97. 'will not show up.'))
  98. self.deferred_functions.append(func)
  99. def record_once(self, func):
  100. """Works like :meth:`record` but wraps the function in another
  101. function that will ensure the function is only called once. If the
  102. blueprint is registered a second time on the application, the
  103. function passed is not called.
  104. """
  105. def wrapper(state):
  106. if state.first_registration:
  107. func(state)
  108. return self.record(update_wrapper(wrapper, func))
  109. def make_setup_state(self, app, options, first_registration=False):
  110. """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState`
  111. object that is later passed to the register callback functions.
  112. Subclasses can override this to return a subclass of the setup state.
  113. """
  114. return BlueprintSetupState(self, app, options, first_registration)
  115. def register(self, app, options, first_registration=False):
  116. """Called by :meth:`Flask.register_blueprint` to register a blueprint
  117. on the application. This can be overridden to customize the register
  118. behavior. Keyword arguments from
  119. :func:`~flask.Flask.register_blueprint` are directly forwarded to this
  120. method in the `options` dictionary.
  121. """
  122. self._got_registered_once = True
  123. state = self.make_setup_state(app, options, first_registration)
  124. if self.has_static_folder:
  125. state.add_url_rule(self.static_url_path + '/<path:filename>',
  126. view_func=self.send_static_file,
  127. endpoint='static')
  128. for deferred in self.deferred_functions:
  129. deferred(state)
  130. def route(self, rule, **options):
  131. """Like :meth:`Flask.route` but for a blueprint. The endpoint for the
  132. :func:`url_for` function is prefixed with the name of the blueprint.
  133. """
  134. def decorator(f):
  135. endpoint = options.pop("endpoint", f.__name__)
  136. self.add_url_rule(rule, endpoint, f, **options)
  137. return f
  138. return decorator
  139. def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
  140. """Like :meth:`Flask.add_url_rule` but for a blueprint. The endpoint for
  141. the :func:`url_for` function is prefixed with the name of the blueprint.
  142. """
  143. if endpoint:
  144. assert '.' not in endpoint, "Blueprint endpoints should not contain dots"
  145. self.record(lambda s:
  146. s.add_url_rule(rule, endpoint, view_func, **options))
  147. def endpoint(self, endpoint):
  148. """Like :meth:`Flask.endpoint` but for a blueprint. This does not
  149. prefix the endpoint with the blueprint name, this has to be done
  150. explicitly by the user of this method. If the endpoint is prefixed
  151. with a `.` it will be registered to the current blueprint, otherwise
  152. it's an application independent endpoint.
  153. """
  154. def decorator(f):
  155. def register_endpoint(state):
  156. state.app.view_functions[endpoint] = f
  157. self.record_once(register_endpoint)
  158. return f
  159. return decorator
  160. def app_template_filter(self, name=None):
  161. """Register a custom template filter, available application wide. Like
  162. :meth:`Flask.template_filter` but for a blueprint.
  163. :param name: the optional name of the filter, otherwise the
  164. function name will be used.
  165. """
  166. def decorator(f):
  167. self.add_app_template_filter(f, name=name)
  168. return f
  169. return decorator
  170. def add_app_template_filter(self, f, name=None):
  171. """Register a custom template filter, available application wide. Like
  172. :meth:`Flask.add_template_filter` but for a blueprint. Works exactly
  173. like the :meth:`app_template_filter` decorator.
  174. :param name: the optional name of the filter, otherwise the
  175. function name will be used.
  176. """
  177. def register_template(state):
  178. state.app.jinja_env.filters[name or f.__name__] = f
  179. self.record_once(register_template)
  180. def app_template_test(self, name=None):
  181. """Register a custom template test, available application wide. Like
  182. :meth:`Flask.template_test` but for a blueprint.
  183. .. versionadded:: 0.10
  184. :param name: the optional name of the test, otherwise the
  185. function name will be used.
  186. """
  187. def decorator(f):
  188. self.add_app_template_test(f, name=name)
  189. return f
  190. return decorator
  191. def add_app_template_test(self, f, name=None):
  192. """Register a custom template test, available application wide. Like
  193. :meth:`Flask.add_template_test` but for a blueprint. Works exactly
  194. like the :meth:`app_template_test` decorator.
  195. .. versionadded:: 0.10
  196. :param name: the optional name of the test, otherwise the
  197. function name will be used.
  198. """
  199. def register_template(state):
  200. state.app.jinja_env.tests[name or f.__name__] = f
  201. self.record_once(register_template)
  202. def app_template_global(self, name=None):
  203. """Register a custom template global, available application wide. Like
  204. :meth:`Flask.template_global` but for a blueprint.
  205. .. versionadded:: 0.10
  206. :param name: the optional name of the global, otherwise the
  207. function name will be used.
  208. """
  209. def decorator(f):
  210. self.add_app_template_global(f, name=name)
  211. return f
  212. return decorator
  213. def add_app_template_global(self, f, name=None):
  214. """Register a custom template global, available application wide. Like
  215. :meth:`Flask.add_template_global` but for a blueprint. Works exactly
  216. like the :meth:`app_template_global` decorator.
  217. .. versionadded:: 0.10
  218. :param name: the optional name of the global, otherwise the
  219. function name will be used.
  220. """
  221. def register_template(state):
  222. state.app.jinja_env.globals[name or f.__name__] = f
  223. self.record_once(register_template)
  224. def before_request(self, f):
  225. """Like :meth:`Flask.before_request` but for a blueprint. This function
  226. is only executed before each request that is handled by a function of
  227. that blueprint.
  228. """
  229. self.record_once(lambda s: s.app.before_request_funcs
  230. .setdefault(self.name, []).append(f))
  231. return f
  232. def before_app_request(self, f):
  233. """Like :meth:`Flask.before_request`. Such a function is executed
  234. before each request, even if outside of a blueprint.
  235. """
  236. self.record_once(lambda s: s.app.before_request_funcs
  237. .setdefault(None, []).append(f))
  238. return f
  239. def before_app_first_request(self, f):
  240. """Like :meth:`Flask.before_first_request`. Such a function is
  241. executed before the first request to the application.
  242. """
  243. self.record_once(lambda s: s.app.before_first_request_funcs.append(f))
  244. return f
  245. def after_request(self, f):
  246. """Like :meth:`Flask.after_request` but for a blueprint. This function
  247. is only executed after each request that is handled by a function of
  248. that blueprint.
  249. """
  250. self.record_once(lambda s: s.app.after_request_funcs
  251. .setdefault(self.name, []).append(f))
  252. return f
  253. def after_app_request(self, f):
  254. """Like :meth:`Flask.after_request` but for a blueprint. Such a function
  255. is executed after each request, even if outside of the blueprint.
  256. """
  257. self.record_once(lambda s: s.app.after_request_funcs
  258. .setdefault(None, []).append(f))
  259. return f
  260. def teardown_request(self, f):
  261. """Like :meth:`Flask.teardown_request` but for a blueprint. This
  262. function is only executed when tearing down requests handled by a
  263. function of that blueprint. Teardown request functions are executed
  264. when the request context is popped, even when no actual request was
  265. performed.
  266. """
  267. self.record_once(lambda s: s.app.teardown_request_funcs
  268. .setdefault(self.name, []).append(f))
  269. return f
  270. def teardown_app_request(self, f):
  271. """Like :meth:`Flask.teardown_request` but for a blueprint. Such a
  272. function is executed when tearing down each request, even if outside of
  273. the blueprint.
  274. """
  275. self.record_once(lambda s: s.app.teardown_request_funcs
  276. .setdefault(None, []).append(f))
  277. return f
  278. def context_processor(self, f):
  279. """Like :meth:`Flask.context_processor` but for a blueprint. This
  280. function is only executed for requests handled by a blueprint.
  281. """
  282. self.record_once(lambda s: s.app.template_context_processors
  283. .setdefault(self.name, []).append(f))
  284. return f
  285. def app_context_processor(self, f):
  286. """Like :meth:`Flask.context_processor` but for a blueprint. Such a
  287. function is executed each request, even if outside of the blueprint.
  288. """
  289. self.record_once(lambda s: s.app.template_context_processors
  290. .setdefault(None, []).append(f))
  291. return f
  292. def app_errorhandler(self, code):
  293. """Like :meth:`Flask.errorhandler` but for a blueprint. This
  294. handler is used for all requests, even if outside of the blueprint.
  295. """
  296. def decorator(f):
  297. self.record_once(lambda s: s.app.errorhandler(code)(f))
  298. return f
  299. return decorator
  300. def url_value_preprocessor(self, f):
  301. """Registers a function as URL value preprocessor for this
  302. blueprint. It's called before the view functions are called and
  303. can modify the url values provided.
  304. """
  305. self.record_once(lambda s: s.app.url_value_preprocessors
  306. .setdefault(self.name, []).append(f))
  307. return f
  308. def url_defaults(self, f):
  309. """Callback function for URL defaults for this blueprint. It's called
  310. with the endpoint and values and should update the values passed
  311. in place.
  312. """
  313. self.record_once(lambda s: s.app.url_default_functions
  314. .setdefault(self.name, []).append(f))
  315. return f
  316. def app_url_value_preprocessor(self, f):
  317. """Same as :meth:`url_value_preprocessor` but application wide.
  318. """
  319. self.record_once(lambda s: s.app.url_value_preprocessors
  320. .setdefault(None, []).append(f))
  321. return f
  322. def app_url_defaults(self, f):
  323. """Same as :meth:`url_defaults` but application wide.
  324. """
  325. self.record_once(lambda s: s.app.url_default_functions
  326. .setdefault(None, []).append(f))
  327. return f
  328. def errorhandler(self, code_or_exception):
  329. """Registers an error handler that becomes active for this blueprint
  330. only. Please be aware that routing does not happen local to a
  331. blueprint so an error handler for 404 usually is not handled by
  332. a blueprint unless it is caused inside a view function. Another
  333. special case is the 500 internal server error which is always looked
  334. up from the application.
  335. Otherwise works as the :meth:`~flask.Flask.errorhandler` decorator
  336. of the :class:`~flask.Flask` object.
  337. """
  338. def decorator(f):
  339. self.record_once(lambda s: s.app._register_error_handler(
  340. self.name, code_or_exception, f))
  341. return f
  342. return decorator
  343. def register_error_handler(self, code_or_exception, f):
  344. """Non-decorator version of the :meth:`errorhandler` error attach
  345. function, akin to the :meth:`~flask.Flask.register_error_handler`
  346. application-wide function of the :class:`~flask.Flask` object but
  347. for error handlers limited to this blueprint.
  348. .. versionadded:: 0.11
  349. """
  350. self.record_once(lambda s: s.app._register_error_handler(
  351. self.name, code_or_exception, f))