blueprints.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  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) 2011 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.blueprint.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. _PackageBoundObject.__init__(self, import_name, template_folder)
  76. self.name = name
  77. self.url_prefix = url_prefix
  78. self.subdomain = subdomain
  79. self.static_folder = static_folder
  80. self.static_url_path = static_url_path
  81. self.deferred_functions = []
  82. self.view_functions = {}
  83. if url_defaults is None:
  84. url_defaults = {}
  85. self.url_values_defaults = url_defaults
  86. def record(self, func):
  87. """Registers a function that is called when the blueprint is
  88. registered on the application. This function is called with the
  89. state as argument as returned by the :meth:`make_setup_state`
  90. method.
  91. """
  92. if self._got_registered_once and self.warn_on_modifications:
  93. from warnings import warn
  94. warn(Warning('The blueprint was already registered once '
  95. 'but is getting modified now. These changes '
  96. 'will not show up.'))
  97. self.deferred_functions.append(func)
  98. def record_once(self, func):
  99. """Works like :meth:`record` but wraps the function in another
  100. function that will ensure the function is only called once. If the
  101. blueprint is registered a second time on the application, the
  102. function passed is not called.
  103. """
  104. def wrapper(state):
  105. if state.first_registration:
  106. func(state)
  107. return self.record(update_wrapper(wrapper, func))
  108. def make_setup_state(self, app, options, first_registration=False):
  109. """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState`
  110. object that is later passed to the register callback functions.
  111. Subclasses can override this to return a subclass of the setup state.
  112. """
  113. return BlueprintSetupState(self, app, options, first_registration)
  114. def register(self, app, options, first_registration=False):
  115. """Called by :meth:`Flask.register_blueprint` to register a blueprint
  116. on the application. This can be overridden to customize the register
  117. behavior. Keyword arguments from
  118. :func:`~flask.Flask.register_blueprint` are directly forwarded to this
  119. method in the `options` dictionary.
  120. """
  121. self._got_registered_once = True
  122. state = self.make_setup_state(app, options, first_registration)
  123. if self.has_static_folder:
  124. state.add_url_rule(self.static_url_path + '/<path:filename>',
  125. view_func=self.send_static_file,
  126. endpoint='static')
  127. for deferred in self.deferred_functions:
  128. deferred(state)
  129. def route(self, rule, **options):
  130. """Like :meth:`Flask.route` but for a blueprint. The endpoint for the
  131. :func:`url_for` function is prefixed with the name of the blueprint.
  132. """
  133. def decorator(f):
  134. endpoint = options.pop("endpoint", f.__name__)
  135. self.add_url_rule(rule, endpoint, f, **options)
  136. return f
  137. return decorator
  138. def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
  139. """Like :meth:`Flask.add_url_rule` but for a blueprint. The endpoint for
  140. the :func:`url_for` function is prefixed with the name of the blueprint.
  141. """
  142. if endpoint:
  143. assert '.' not in endpoint, "Blueprint endpoint's should not contain dot's"
  144. self.record(lambda s:
  145. s.add_url_rule(rule, endpoint, view_func, **options))
  146. def endpoint(self, endpoint):
  147. """Like :meth:`Flask.endpoint` but for a blueprint. This does not
  148. prefix the endpoint with the blueprint name, this has to be done
  149. explicitly by the user of this method. If the endpoint is prefixed
  150. with a `.` it will be registered to the current blueprint, otherwise
  151. it's an application independent endpoint.
  152. """
  153. def decorator(f):
  154. def register_endpoint(state):
  155. state.app.view_functions[endpoint] = f
  156. self.record_once(register_endpoint)
  157. return f
  158. return decorator
  159. def app_template_filter(self, name=None):
  160. """Register a custom template filter, available application wide. Like
  161. :meth:`Flask.template_filter` but for a blueprint.
  162. :param name: the optional name of the filter, otherwise the
  163. function name will be used.
  164. """
  165. def decorator(f):
  166. self.add_app_template_filter(f, name=name)
  167. return f
  168. return decorator
  169. def add_app_template_filter(self, f, name=None):
  170. """Register a custom template filter, available application wide. Like
  171. :meth:`Flask.add_template_filter` but for a blueprint. Works exactly
  172. like the :meth:`app_template_filter` decorator.
  173. :param name: the optional name of the filter, otherwise the
  174. function name will be used.
  175. """
  176. def register_template(state):
  177. state.app.jinja_env.filters[name or f.__name__] = f
  178. self.record_once(register_template)
  179. def app_template_test(self, name=None):
  180. """Register a custom template test, available application wide. Like
  181. :meth:`Flask.template_test` but for a blueprint.
  182. .. versionadded:: 0.10
  183. :param name: the optional name of the test, otherwise the
  184. function name will be used.
  185. """
  186. def decorator(f):
  187. self.add_app_template_test(f, name=name)
  188. return f
  189. return decorator
  190. def add_app_template_test(self, f, name=None):
  191. """Register a custom template test, available application wide. Like
  192. :meth:`Flask.add_template_test` but for a blueprint. Works exactly
  193. like the :meth:`app_template_test` decorator.
  194. .. versionadded:: 0.10
  195. :param name: the optional name of the test, otherwise the
  196. function name will be used.
  197. """
  198. def register_template(state):
  199. state.app.jinja_env.tests[name or f.__name__] = f
  200. self.record_once(register_template)
  201. def app_template_global(self, name=None):
  202. """Register a custom template global, available application wide. Like
  203. :meth:`Flask.template_global` but for a blueprint.
  204. .. versionadded:: 0.10
  205. :param name: the optional name of the global, otherwise the
  206. function name will be used.
  207. """
  208. def decorator(f):
  209. self.add_app_template_global(f, name=name)
  210. return f
  211. return decorator
  212. def add_app_template_global(self, f, name=None):
  213. """Register a custom template global, available application wide. Like
  214. :meth:`Flask.add_template_global` but for a blueprint. Works exactly
  215. like the :meth:`app_template_global` decorator.
  216. .. versionadded:: 0.10
  217. :param name: the optional name of the global, otherwise the
  218. function name will be used.
  219. """
  220. def register_template(state):
  221. state.app.jinja_env.globals[name or f.__name__] = f
  222. self.record_once(register_template)
  223. def before_request(self, f):
  224. """Like :meth:`Flask.before_request` but for a blueprint. This function
  225. is only executed before each request that is handled by a function of
  226. that blueprint.
  227. """
  228. self.record_once(lambda s: s.app.before_request_funcs
  229. .setdefault(self.name, []).append(f))
  230. return f
  231. def before_app_request(self, f):
  232. """Like :meth:`Flask.before_request`. Such a function is executed
  233. before each request, even if outside of a blueprint.
  234. """
  235. self.record_once(lambda s: s.app.before_request_funcs
  236. .setdefault(None, []).append(f))
  237. return f
  238. def before_app_first_request(self, f):
  239. """Like :meth:`Flask.before_first_request`. Such a function is
  240. executed before the first request to the application.
  241. """
  242. self.record_once(lambda s: s.app.before_first_request_funcs.append(f))
  243. return f
  244. def after_request(self, f):
  245. """Like :meth:`Flask.after_request` but for a blueprint. This function
  246. is only executed after each request that is handled by a function of
  247. that blueprint.
  248. """
  249. self.record_once(lambda s: s.app.after_request_funcs
  250. .setdefault(self.name, []).append(f))
  251. return f
  252. def after_app_request(self, f):
  253. """Like :meth:`Flask.after_request` but for a blueprint. Such a function
  254. is executed after each request, even if outside of the blueprint.
  255. """
  256. self.record_once(lambda s: s.app.after_request_funcs
  257. .setdefault(None, []).append(f))
  258. return f
  259. def teardown_request(self, f):
  260. """Like :meth:`Flask.teardown_request` but for a blueprint. This
  261. function is only executed when tearing down requests handled by a
  262. function of that blueprint. Teardown request functions are executed
  263. when the request context is popped, even when no actual request was
  264. performed.
  265. """
  266. self.record_once(lambda s: s.app.teardown_request_funcs
  267. .setdefault(self.name, []).append(f))
  268. return f
  269. def teardown_app_request(self, f):
  270. """Like :meth:`Flask.teardown_request` but for a blueprint. Such a
  271. function is executed when tearing down each request, even if outside of
  272. the blueprint.
  273. """
  274. self.record_once(lambda s: s.app.teardown_request_funcs
  275. .setdefault(None, []).append(f))
  276. return f
  277. def context_processor(self, f):
  278. """Like :meth:`Flask.context_processor` but for a blueprint. This
  279. function is only executed for requests handled by a blueprint.
  280. """
  281. self.record_once(lambda s: s.app.template_context_processors
  282. .setdefault(self.name, []).append(f))
  283. return f
  284. def app_context_processor(self, f):
  285. """Like :meth:`Flask.context_processor` but for a blueprint. Such a
  286. function is executed each request, even if outside of the blueprint.
  287. """
  288. self.record_once(lambda s: s.app.template_context_processors
  289. .setdefault(None, []).append(f))
  290. return f
  291. def app_errorhandler(self, code):
  292. """Like :meth:`Flask.errorhandler` but for a blueprint. This
  293. handler is used for all requests, even if outside of the blueprint.
  294. """
  295. def decorator(f):
  296. self.record_once(lambda s: s.app.errorhandler(code)(f))
  297. return f
  298. return decorator
  299. def url_value_preprocessor(self, f):
  300. """Registers a function as URL value preprocessor for this
  301. blueprint. It's called before the view functions are called and
  302. can modify the url values provided.
  303. """
  304. self.record_once(lambda s: s.app.url_value_preprocessors
  305. .setdefault(self.name, []).append(f))
  306. return f
  307. def url_defaults(self, f):
  308. """Callback function for URL defaults for this blueprint. It's called
  309. with the endpoint and values and should update the values passed
  310. in place.
  311. """
  312. self.record_once(lambda s: s.app.url_default_functions
  313. .setdefault(self.name, []).append(f))
  314. return f
  315. def app_url_value_preprocessor(self, f):
  316. """Same as :meth:`url_value_preprocessor` but application wide.
  317. """
  318. self.record_once(lambda s: s.app.url_value_preprocessors
  319. .setdefault(None, []).append(f))
  320. return f
  321. def app_url_defaults(self, f):
  322. """Same as :meth:`url_defaults` but application wide.
  323. """
  324. self.record_once(lambda s: s.app.url_default_functions
  325. .setdefault(None, []).append(f))
  326. return f
  327. def errorhandler(self, code_or_exception):
  328. """Registers an error handler that becomes active for this blueprint
  329. only. Please be aware that routing does not happen local to a
  330. blueprint so an error handler for 404 usually is not handled by
  331. a blueprint unless it is caused inside a view function. Another
  332. special case is the 500 internal server error which is always looked
  333. up from the application.
  334. Otherwise works as the :meth:`~flask.Flask.errorhandler` decorator
  335. of the :class:`~flask.Flask` object.
  336. """
  337. def decorator(f):
  338. self.record_once(lambda s: s.app._register_error_handler(
  339. self.name, code_or_exception, f))
  340. return f
  341. return decorator