Además de las buenas sugerencias de linqq anteriores, también puede anular la funcionalidad predeterminada si es necesario. Hay un par de formas:
Se puede anular create_global_jinja_loader
en una aplicación Flask subclasificada (que devuelve DispatchingJinjaLoader
definida en el matraz/templating.py). Esto no es recomendable, pero funcionaría. La razón por la cual esto se desaconseja es que el DispatchingJinjaLoader
tiene suficiente flexibilidad para admitir la inyección de cargadores personalizados.Y si atornillas tu propio cargador, podrá apoyarse en funcionalidad sana y predeterminada.
Por lo tanto, lo que se recomienda es que uno "anule la función jinja_loader
" en su lugar. Aquí es donde entra la falta de documentación. La estrategia de carga de Patching Flask requiere un conocimiento que no parece estar documentado, así como una buena comprensión de Jinja2.
Hay dos componentes que necesita para comprender:
- El entorno Jinja2
- La plantilla cargador Jinja2
Estos son creados por frasco, con parámetros por defecto, de forma automática. (Por cierto, puede especificar su propio Jinja2 options anulando app.jinja_options
, pero tenga en cuenta que perderá dos extensiones que Flask incluye de manera predeterminada, autoescape
y with
, a menos que usted mismo las haya especificado. Eche un vistazo a frasco/app.py para ver la forma en que hacen referencia a aquellos.)
el medio ambiente contiene todos esos procesadores de contexto (por ejemplo, para que pueda hacer var|tojson
en una plantilla), funciones de ayuda (url_for
, etc.) y variables (g
, session
, app
). También contiene una referencia a un cargador de plantillas, en este caso, el mencionado DispatchingJinjaLoader
autoejecutivo. Entonces, cuando llamas al render_template
en tu aplicación, encuentra o crea el entorno Jinja2, configura todas esas cosas y llama al get_template
, que a su vez llama al get_source
dentro del DispatchingJinjaLoader
, que intenta algunas estrategias que se describen más adelante.
Si todo va según lo planeado, esa cadena se resolverá al encontrar un archivo y devolverá su contenido (y algunos other data). Además, tenga en cuenta que esta es la misma ruta de ejecución que toma {% extend 'foo.htm' %}
.
DispatchingJinjaLoader
hace dos cosas: Primero comprueba si el cargador global de la aplicación, que es app.jinja_loader
puede localizar el archivo. En su defecto, comprueba todos los planos de aplicación (en orden de registro, AFAIK) para blueprint.jinja_loader
en un intento de localizar el archivo. Rastreo de esa cadena hasta el final, aquí es la definición de jinja_loader (en matraz/helpers.py, _PackageBoundObject
, la clase base tanto de la aplicación Flask y modelos):
def jinja_loader(self):
"""The Jinja loader for this package bound object.
.. versionadded:: 0.5
"""
if self.template_folder is not None:
return FileSystemLoader(os.path.join(self.root_path,
self.template_folder))
Ah! Entonces ahora vemos. Obviamente, los espacios de nombres de ambos entrarán en conflicto con los mismos nombres de directorio. Como el cargador global se llama primero, siempre ganará. (FileSystemLoader
es uno de varios cargadores Jinja2 estándar.) Sin embargo, lo que esto significa es que no hay una manera verdaderamente simple de reordenar el Blueprint y el cargador de plantillas de toda la aplicación.
Por lo tanto, tenemos que modificar el comportamiento de DispatchingJinjaLoader
. Por un tiempo, pensé que no había una forma buena y no desalentada de hacer esto. Sin embargo, al parecer, si anulas app.jinja_options['loader']
, podemos obtener el comportamiento que deseamos. Entonces, si subclasificamos DispatchingJinjaLoader
, y modificamos una función pequeña (supongo que sería mejor volver a implementarla por completo, pero esto funciona por ahora), tenemos el comportamiento que queremos.En total, una estrategia razonable sería el siguiente (no probado, pero debería funcionar con aplicaciones Frasco modernas):
from flask.templating import DispatchingJinjaLoader
from flask.globals import _request_ctx_stack
class ModifiedLoader(DispatchingJinjaLoader):
def _iter_loaders(self, template):
bp = _request_ctx_stack.top.request.blueprint
if bp is not None and bp in self.app.blueprints:
loader = self.app.blueprints[bp].jinja_loader
if loader is not None:
yield loader, template
loader = self.app.jinja_loader
if loader is not None:
yield loader, template
Esto modifica la estrategia del cargador original en dos formas: Intento de cargar con el proyecto básico (y sólo el plano que se está ejecutando actualmente, no todos los planos) primero, y si eso falla, solo entonces cargue desde la aplicación. Si le gusta el comportamiento all-blueprint, puede hacer un poco de copy-pasta desde el matraz/templating.py.
Para unirlo todo, usted tiene que fijar jinja_options
en el objeto Frasco:
app = Flask(__name__)
# jinja_options is an ImmutableDict, so we have to do this song and dance
app.jinja_options = Flask.jinja_options.copy()
app.jinja_options['loader'] = ModifiedLoader(app)
La primera vez que se necesita un entorno de plantilla (y por lo tanto crea una instancia), lo que significa la primera render_template vez que se llama, su cargador debe ser utilizado.
Después de haber problema similar. Desearía que esto se manejara de manera diferente de la caja. Cambiar la ubicación de la carpeta estática funciona bien con los archivos de publicación, pero la plantilla get se reemplaza si el mismo archivo ya existe. –
Como jay chan había señalado, la forma más sencilla es almacenar plantillas dentro de la carpeta de plantillas principal para que admin.html se almacene en 'myapp/templates/admin/index.html' –
¿Por qué no cambias' template_folder' a '" admin/pages "' o '" main/pages "'? –