In aggiunta ai buoni suggerimenti di linqq sopra, è anche possibile sovrascrivere la funzionalità predefinita, se necessario. Ci sono un paio di modi:
si può ignorare create_global_jinja_loader
in un'applicazione Flask sottoclasse (che restituisce un DispatchingJinjaLoader
definito nel pallone/templating.py). Questo non è raccomandato, ma funzionerebbe. Il motivo per cui questo è scoraggiato è che il DispatchingJinjaLoader
ha abbastanza flessibilità per supportare l'iniezione di caricatori personalizzati.E se avviti il tuo caricatore, sarà in grado di appoggiarsi a funzionalità predefinite e sensate.
Quindi, si consiglia di sostituire "la funzione jinja_loader
". È qui che manca la documentazione. La strategia di caricamento di Patching Flask richiede alcune conoscenze che non sembrano essere documentate, così come una buona conoscenza di Jinja2.
Ci sono due componenti è necessario comprendere:
- L'ambiente Jinja2
- Il modello di pala Jinja2
Questi sono creati da Flask, con ragionevoli valori predefiniti, automaticamente. (Puoi specificare il tuo , ignorando app.jinja_options
- ma tieni a mente che perderai due estensioni che Flask include per impostazione predefinita - autoescape
e with
- a meno che non le specifichi tu stesso. boccetta/app.py per vedere come fanno riferimento quelli.)
L'ambiente contiene tutti questi processori di contesto (ad esempio, in modo da poter fare var|tojson
in un modello), funzioni di supporto (url_for
, ecc) e variabili (g
, session
, app
). Contiene anche un riferimento a un caricatore di template, in questo caso il già citato e auto-istanziato DispatchingJinjaLoader
. Quindi, quando chiami render_template
nella tua app, trova o crea l'ambiente Jinja2, configura tutti quei gadget e chiama get_template
su di esso, che a sua volta chiama get_source
all'interno dello DispatchingJinjaLoader
, che tenta alcune strategie descritte in seguito.
Se tutto va secondo i piani, tale catena si risolverà nel trovare un file e restituirà il suo contenuto (e alcuni other data). Inoltre, si noti che questo è lo stesso percorso di esecuzione utilizzato da {% extend 'foo.htm' %}
.
DispatchingJinjaLoader
fa due cose: prima controlla se il caricatore globale dell'applicazione, che è app.jinja_loader
, può individuare il file. In caso contrario, controlla tutti i progetti di applicazione (in ordine di registrazione, AFAIK) per blueprint.jinja_loader
nel tentativo di individuare il file. Tracciare la catena fino alla fine, ecco definizione di jinja_loader (nella beuta/helpers.py, _PackageBoundObject
, la classe di base sia della domanda Flask e Blueprints):
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! Quindi ora vediamo. Ovviamente, gli spazi dei nomi di entrambi saranno in conflitto sugli stessi nomi di directory. Dal momento che il caricatore globale viene chiamato per primo, vincerà sempre. (FileSystemLoader
è uno dei diversi caricatori Jinja2 standard.) Tuttavia, ciò significa che non esiste un modo veramente semplice per riordinare Blueprint e il caricatore di modelli dell'applicazione.
Quindi, è necessario modificare il comportamento di DispatchingJinjaLoader
. Per un po 'ho pensato che non ci fosse un buon modo non scoraggiato ed efficiente per far questo. Tuttavia, apparentemente se si sostituisce lo stesso app.jinja_options['loader']
, è possibile ottenere il comportamento desiderato. Quindi, se eseguiamo la sottoclasse di DispatchingJinjaLoader
e modifichiamo una piccola funzione (suppongo che potrebbe essere meglio reimplementarla interamente, ma questo funziona per ora), abbiamo il comportamento che vogliamo.In totale, una strategia ragionevole potrebbe essere il seguente (non testato, ma dovrebbe funzionare con le moderne applicazioni Flask):
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
Questa modifica la strategia del caricatore originale in due modi: tentare di caricare dal progetto (e SOLO il modello correntemente in esecuzione, non tutti i progetti) prima, e se fallisce, solo allora carica dall'applicazione. Se ti piace il comportamento all-blueprint, puoi fare un po 'di copia-pasta da flask/templating.py.
Per legare tutto insieme, è necessario impostare jinja_options
sull'oggetto Flask:
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 prima volta che è necessario un ambiente modello (e quindi un'istanza), vale a dire la prima render_template volta che viene chiamato, il tuo caricatore dovrebbe essere usato
Avendo problema simile. Vorrei che questo fosse gestito diversamente fuori dagli schemi. La modifica della posizione della cartella statica funziona correttamente con la pubblicazione dei file, ma il modello get viene sovrascritto se esiste già lo stesso file. –
Come jay chan ha sottolineato il modo più semplice è quello di archiviare i modelli all'interno della cartella dei modelli principale in modo che admin.html sia archiviato in 'myapp/templates/admin/index.html' –
Perché non cambi semplicemente' template_folder'? a '" admin/pages "' o '" main/pages "'? –