def before_init_app(self, app: FlaskUnchained): app.jinja_env.add_extension('jinja2.ext.i18n') babel.locale_selector_func = self.get_locale if app.config.get('ENABLE_URL_LANG_CODE_PREFIX', BabelBundleConfig.ENABLE_URL_LANG_CODE_PREFIX): app.url_value_preprocessor(self.lang_code_url_value_preprocessor) app.url_defaults(self.set_url_defaults)
def after_init_app(self, app: FlaskUnchained): """ Configure the JSON encoder for Flask to be able to serialize Enums, LocalProxy objects, and SQLAlchemy models. """ self.set_json_encoder(app) app.before_first_request(self.register_model_resources)
def register_blueprint(self, app: FlaskUnchained, blueprint: Blueprint, **options): if app.config.get('ENABLE_URL_LANG_CODE_PREFIX'): url_prefix = (options.get('url_prefix', (blueprint.url_prefix or '')) .rstrip('/')) options = dict(**options, url_prefix=self.get_url_rule(url_prefix), register_with_babel=False) app.register_blueprint(blueprint, **options)
def process_objects(self, app: FlaskUnchained, blueprints: List[Blueprint]): for blueprint in reversed(blueprints): # rstrip '/' off url_prefix because views should be declaring their # routes beginning with '/', and if url_prefix ends with '/', routes # will end up looking like '/prefix//endpoint', which is no good url_prefix = (blueprint.url_prefix or '').rstrip('/') app.register_blueprint(blueprint, url_prefix=url_prefix)
def process_objects(self, app: FlaskUnchained, routes: Iterable[Route]): for route in _reduce_routes(routes): if route.should_register(app): if route.module_name and route in self.bundle.endpoints[ route.endpoint]: import warnings warnings.warn(f'Duplicate route found: {route}') continue self.bundle.endpoints[route.endpoint].append(route) if route._controller_cls: key = f'{route._controller_cls.__name__}.{route.method_name}' self.bundle.controller_endpoints[key].append(route) # build up a list of bundles with views: bundle_module_names = [ ] # [tuple(top_bundle_module_name, hierarchy_module_names)] for bundle in app.unchained.bundles.values(): hierarchy = [ bundle_super.module_name for bundle_super in bundle._iter_class_hierarchy() if bundle_super._has_views ] if hierarchy: bundle_module_names.append((bundle.module_name, hierarchy)) # for each route, figure out which bundle hierarchy it's from, and assign the # route to the top bundle for that hierarchy bundle_route_endpoints = set() for endpoint, routes in self.bundle.endpoints.items(): for route in routes: if not route.module_name: continue # FIXME would be nice for routes to know which bundle they're from... for top_level_bundle_module_name, hierarchy in bundle_module_names: for bundle_module_name in hierarchy: if route.module_name.startswith(bundle_module_name): self.bundle.bundle_routes[ top_level_bundle_module_name].append(route) bundle_route_endpoints.add(endpoint) break # get all the remaining routes not belonging to a bundle self.bundle.other_routes = itertools.chain.from_iterable([ routes for endpoint, routes in self.bundle.endpoints.items() if endpoint not in bundle_route_endpoints ]) # we register non-bundle routes with the app here, and # the RegisterBundleBlueprintsHook registers the bundle routes for route in self.bundle.other_routes: app.add_url_rule(route.full_rule, defaults=route.defaults, endpoint=route.endpoint, methods=route.methods, view_func=route.view_func, **route.rule_options)
def before_init_app(self, app: FlaskUnchained): """ Configure the Jinja environment and template loader. """ from .templates import (UnchainedJinjaEnvironment, UnchainedJinjaLoader) app.jinja_environment = UnchainedJinjaEnvironment app.jinja_options = { **app.jinja_options, 'loader': UnchainedJinjaLoader(app) } app.jinja_env.globals['url_for'] = url_for
def before_init_app(self, app: FlaskUnchained): """ Configure the Jinja environment and template loader. """ from .templates import (UnchainedJinjaEnvironment, UnchainedJinjaLoader) app.jinja_environment = UnchainedJinjaEnvironment app.jinja_options = { **app.jinja_options, 'loader': UnchainedJinjaLoader(app) } app.jinja_env.globals['url_for'] = url_for for name in ['string', 'str']: app.url_map.converters[name] = StringConverter
def run_hook(self, app: FlaskUnchained, bundles: List[Bundle]): for bundle_ in reversed(bundles): for bundle in bundle_._iter_class_hierarchy(reverse=False): if (bundle.template_folder or bundle._static_folders or bundle._has_views()): bp = BundleBlueprint(bundle) for route in self.bundle.bundle_routes.get( bundle.module_name, []): bp.add_url_rule(route.full_rule, defaults=route.defaults, endpoint=route.endpoint, methods=route.methods, view_func=route.view_func, **route.rule_options) app.register_blueprint(bp)
def set_json_encoder(self, app: FlaskUnchained): from flask_unchained.bundles.sqlalchemy import BaseModel from flask_unchained import unchained from werkzeug.local import LocalProxy class JSONEncoder(app.json_encoder): def default(self, obj): if isinstance(obj, LocalProxy): obj = obj._get_current_object() if isinstance(obj, enum.Enum): return obj.name elif isinstance(obj, _LazyString): return str(obj) api_bundle = unchained.api_bundle if isinstance(obj, BaseModel): model_name = obj.__class__.__name__ serializer_cls = api_bundle.serializers_by_model.get( model_name) if serializer_cls: return serializer_cls().dump(obj) elif (obj and isinstance(obj, (list, tuple)) and isinstance(obj[0], BaseModel)): model_name = obj[0].__class__.__name__ serializer_cls = api_bundle.many_by_model.get( model_name, api_bundle.serializers_by_model.get(model_name)) if serializer_cls: return serializer_cls(many=True).dump(obj) return super().default(obj) app.json_encoder = JSONEncoder
def process_objects(self, app: FlaskUnchained, routes: Iterable[Route]): for route in _reduce_routes(routes): # FIXME maybe validate routes first? (eg for duplicates?) # Flask doesn't complain; it will match the first route found, # but maybe we should at least warn the user? if route.should_register(app): self.bundle.endpoints[route.endpoint].append(route) if route._controller_cls: key = f'{route._controller_cls.__name__}.{route.method_name}' self.bundle.controller_endpoints[key].append(route) bundle_names = [( bundle.module_name, [ bundle_super.module_name for bundle_super in bundle._iter_class_hierarchy( include_self=False) if bundle_super._has_views() ], ) for bundle in app.unchained.bundles.values()] bundle_route_endpoints = set() for endpoint, routes in self.bundle.endpoints.items(): for route in routes: module_name = route.module_name for top_level_bundle_name, hierarchy in bundle_names: for bundle_name in hierarchy: if module_name and module_name.startswith(bundle_name): self.bundle.bundle_routes[ top_level_bundle_name].append(route) bundle_route_endpoints.add(endpoint) break self.bundle.other_routes = itertools.chain.from_iterable([ routes for endpoint, routes in self.bundle.endpoints.items() if endpoint not in bundle_route_endpoints ]) for route in self.bundle.other_routes: app.add_url_rule(route.full_rule, defaults=route.defaults, endpoint=route.endpoint, methods=route.methods, view_func=route.view_func, **route.rule_options)
def register_routes(self, app: FlaskUnchained): bp = Blueprint('api-docs', __name__, url_prefix=app.config.API_REDOC_URL_PREFIX.rstrip('/'), template_folder=os.path.join( os.path.dirname(os.path.abspath(__file__)), 'templates')) # Serve json spec at `API_REDOC_URL_PREFIX/openapi.json` by default bp.add_url_rule(app.config.API_OPENAPI_JSON_PATH, endpoint='openapi_json', view_func=self._openapi_json) # Serve ReDoc at `API_REDOC_URL_PREFIX/` by default bp.add_url_rule(app.config.API_REDOC_PATH, endpoint='openapi_redoc', view_func=self._openapi_redoc) app.register_blueprint(bp, register_with_babel=False)
def after_init_app(self, app: FlaskUnchained): if app.config.GRAPHENE_URL: app.add_url_rule(app.config.GRAPHENE_URL, view_func=GraphQLView.as_view( 'graphql', schema=self.root_schema, graphiql=app.config.GRAPHENE_ENABLE_GRAPHIQL, pretty=app.config.GRAPHENE_PRETTY_JSON, batch=False, )) if app.config.GRAPHENE_BATCH_URL: app.add_url_rule(app.config.GRAPHENE_BATCH_URL, view_func=GraphQLView.as_view( 'graphql', schema=self.root_schema, graphiql=app.config.GRAPHENE_ENABLE_GRAPHIQL, pretty=app.config.GRAPHENE_PRETTY_JSON, batch=True, ))
def init_app(self, app: FlaskUnchained): self.app = app self.name = app.config.ADMIN_NAME self.subdomain = app.config.ADMIN_SUBDOMAIN self._set_admin_index_view(app.config.ADMIN_INDEX_VIEW, url=app.config.ADMIN_BASE_URL) self.base_template = app.config.ADMIN_BASE_TEMPLATE self.template_mode = app.config.ADMIN_TEMPLATE_MODE self._init_extension() # Register views for view in self._views: app.register_blueprint(view.create_blueprint(self), register_with_babel=False) app.context_processor( lambda: dict(admin_base_template=self.base_template, admin_view=self.index_view, h=helpers, get_url=url_for))
def register_routes(self, app: FlaskUnchained): redoc_url_prefix = app.config.get('API_REDOC_URL_PREFIX', '/api-docs').rstrip('/') template_folder = os.path.join( os.path.dirname(os.path.abspath(__file__)), 'templates') bp = Blueprint('api-docs', __name__, url_prefix=redoc_url_prefix, template_folder=template_folder) # Serve json spec at 'url_prefix/openapi.json' by default json_path = app.config.get('OPENAPI_JSON_PATH', 'openapi.json') bp.add_url_rule(json_path, endpoint='openapi_json', view_func=self._openapi_json) # Serve ReDoc at `url_prefix/' by default redoc_path = app.config.get('OPENAPI_REDOC_PATH', '/') bp.add_url_rule(redoc_path, endpoint='openapi_redoc', view_func=self._openapi_redoc) app.register_blueprint(bp, register_with_babel=False)
def init_app(self, app: FlaskUnchained): # NOTE: the order of these `self.get_*` calls is important! self.confirm_serializer = self._get_serializer(app, 'confirm') self.hashing_context = self._get_hashing_context(app) self.login_manager = self._get_login_manager( app, app.config.SECURITY_ANONYMOUS_USER) self.login_serializer = self._get_serializer(app, 'login') self.principal = self._get_principal(app) self.pwd_context = self._get_pwd_context(app) self.remember_token_serializer = self._get_serializer(app, 'remember') self.reset_serializer = self._get_serializer(app, 'reset') self.context_processor( lambda: dict(security=_SecurityConfigProperties())) # FIXME: should this be easier to customize for end users, perhaps by making # FIXME: the function come from a config setting? identity_loaded.connect_via(app)(self._on_identity_loaded) app.extensions['security'] = self
def init_app(self, app: FlaskUnchained): app.extensions['mail'] = self
def add_url_rule(self, app: FlaskUnchained, rule: str, **kwargs): if app.config.ENABLE_URL_LANG_CODE_PREFIX: app.add_url_rule(self.get_url_rule(rule), register_with_babel=False, **kwargs)
def init_app(self, app: FlaskUnchained): self.app = app app.extensions['api'] = self self.spec = APISpec(app, plugins=app.config.API_APISPEC_PLUGINS)