blueprints.py 18 KB

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