2015-12-01 12 views
10

Sto tentando di adattare il Jinja2 WithExtension per produrre un'estensione generica per il wrapping di un blocco (seguito da alcuni più complessi).Come si analizzano e si iniettano nodi aggiuntivi in ​​un'estensione Jinja?

Il mio obiettivo è quello di sostenere la seguente nei modelli:

{% wrap template='wrapper.html.j2' ... %} 
    <img src="{{ url('image:thumbnail' ... }}"> 
{% endwrap %} 

E per wrapper.html.j2 a guardare come qualcosa di simile a:

<div> 
    some ifs and stuff 
    {{ content }} 
    more ifs and stuff 
</div> 

Credo che il mio esempio è la maggior parte del modo in cui In questo caso, WithExtension sembra analizzare il blocco e quindi aggiungere la rappresentazione AST di alcuni nodi {% assign .. %} nel contesto dei nodi che sta analizzando.

Quindi ho pensato che volevo la stessa cosa, quei compiti, seguiti da un blocco di inclusione, che mi sarei aspettato di poter accedere a quelle variabili quando l'AST è analizzato, e di passare attraverso il blocco che era avvolto come una variabile content.

Ho il seguente finora:

class WrapExtension(Extension): 
    tags = set(['wrap']) 

    def parse(self, parser): 
     node = nodes.Scope(lineno=next(parser.stream).lineno) 
     assignments = [] 
     while parser.stream.current.type != 'block_end': 
      lineno = parser.stream.current.lineno 
      if assignments: 
       parser.stream.expect('comma') 
      target = parser.parse_assign_target() 
      parser.stream.expect('assign') 
      expr = parser.parse_expression() 
      assignments.append(nodes.Assign(target, expr, lineno=lineno)) 
     content = parser.parse_statements(('name:endwrap',), drop_needle=True) 
     assignments.append(nodes.Name('content', content)) 
     assignments.append(nodes.Include(nodes.Template('wrapper.html.j2'), True, False)) 
     node.body = assignments 
     return node 

Tuttavia, cade sopra a mia linea nodes.Include, ho semplicemente capito assert frame is None, 'no root frame allowed'. Credo di dover passare AST a nodes.Template anziché a un nome di modello, ma non so davvero come analizzare in nodi aggiuntivi per l'obiettivo di ottenere AST piuttosto che l'output di stringa (cioè i rendering) - né se questo sia l'approccio giusto . Sono sulla buona strada, qualche idea su come dovrei fare questo?

+0

Potete aggiungere ulteriori dettagli sul risultato richiesto? Intendi finire con il markup contenuto nel contenuto di 'wrapper.html.j2'? Puoi dare un esempio di contenuto per 'wrapper.html.j2'? – tutuDajuju

+0

Oh, whoop, nel semplificare il mio esempio l'ho reso non sensato, aggiornerò gli esempi – Steve

risposta

4

templatetags/wrap.py

class WrapExtension(jinja2.ext.Extension): 
    tags = set(['wrap']) 
    template = None 

    def parse(self, parser): 
     tag = parser.stream.current.value 
     lineno = parser.stream.next().lineno 
     args, kwargs = self.parse_args(parser) 
     body = parser.parse_statements(['name:end{}'.format(tag)], drop_needle=True) 

     return nodes.CallBlock(self.call_method('wrap', args, kwargs), [], [], body).set_lineno(lineno) 

    def parse_args(self, parser): 
     args = [] 
     kwargs = [] 
     require_comma = False 

     while parser.stream.current.type != 'block_end': 
      if require_comma: 
       parser.stream.expect('comma') 

      if parser.stream.current.type == 'name' and parser.stream.look().type == 'assign': 
       key = parser.stream.current.value 
       parser.stream.skip(2) 
       value = parser.parse_expression() 
       kwargs.append(nodes.Keyword(key, value, lineno=value.lineno)) 
      else: 
       if kwargs: 
        parser.fail('Invalid argument syntax for WrapExtension tag', 
           parser.stream.current.lineno) 
       args.append(parser.parse_expression()) 

      require_comma = True 

     return args, kwargs 

    @jinja2.contextfunction 
    def wrap(self, context, caller, template=None, *args, **kwargs): 
     return self.environment.get_template(template or self.template).render(dict(context, content=caller(), **kwargs)) 

base.html.j2

<h1>dsd</h1> 
{% wrap template='wrapper.html.j2' %} 
    {% for i in range(3) %} 
     im wrapped content {{ i }}<br> 
    {% endfor %} 
{% endwrap %} 

wrapper.html.j2

Hello im wrapper 
<br> 
<hr> 
{{ content|safe }} 
<hr>   

args/Kwargs parsing arrivare da qui https://github.com/Suor/django-cacheops/blob/master/cacheops/jinja2.py


Inoltre, quanto sopra può essere esteso per supportare ulteriori tag con un modello predefinito specificato come l'involucro:

templatetags/example.py

class ExampleExtension(WrapExtension): 
    tags = set(['example']) 
    template = 'example.html.j2' 

base.html.j2

{% example otherstuff=True, somethingelse=False %} 
    {% for i in range(3) %} 
     im wrapped content {{ i }}<br> 
    {% endfor %} 
{% endexample %} 
+0

Ho semplificato un po 'il mio esempio, ho bisogno di qualsiasi kwargs oltre a 'template' passato a hello_wrapper.js - altrimenti questo ha un bell'aspetto: D – Steve

+0

corretto, j2! = js :) – vadimchin

+0

Non sono sicuro di seguirlo, in che modo args e kwargs vengono passati in 'CallBlock' in' parse'? Suppongo che debbano finire per essere passati alla chiamata 'render' in' _wrap'? – Steve

0

Il modo migliore per gestire questo è l'utilizzo di macro.Definire con:

{% macro wrapper() -%} 
<div> 
    some ifs and stuff 
    {{ caller() }} 
    more ifs and stuff 
</div> 
{%- endmacro %} 

e utilizzare in seguito con un tag call:

{% call wrapper() %} 
    <img src="{{ url('image:thumbnail' ... }}"> 
{% endcall %} 

Macro può avere argomenti come la funzione di pitone e potrebbero essere importati:

{% from macros import wrapper %} 

vedere la documentazione per macro, call e import tag per maggiori dettagli.

Problemi correlati