2015-12-01 8 views
10

Próbuję zaadaptować urządzenie Jinja2 WithExtension, aby utworzyć ogólne rozszerzenie do zawijania bloku (a następnie kilka bardziej złożonych).Jak analizować i wstrzykiwać dodatkowe węzły w rozszerzeniu Jinja?

Moim celem jest wspieranie następujących szablonów:

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

I wrapper.html.j2 wyglądać coś takiego:

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

wierzę moim przykładem jest większość drogi tam pojawia się parametr WithExtension, aby przeanalizować blok, a następnie dołączyć reprezentację AST niektórych węzłów {% assign .. %} do kontekstu węzłów, które analizuje.

Więc pomyślałem, że chcę tego samego, te przypisania, po którym następuje blok włączający, który spodziewałbym się mieć możliwość uzyskania dostępu do tych zmiennych podczas analizowania AST i przejścia przez blok, który był zawijany jako zmienna content.

Mam następujący dotąd:

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 

jednak przewraca w moim nodes.Include linii, po prostu dostać assert frame is None, 'no root frame allowed'. Uważam, że muszę przekazać AST do nodes.Template zamiast nazwy szablonu, ale tak naprawdę nie wiem, jak analizować w dodatkowych węzłach w celu uzyskania AST raczej niż wyjście łańcuchowe (tj. Renderowania) - ani czy jest to właściwe podejście . Czy jestem na właściwej linii, jakieś pomysły na to, jak powinienem to zrobić?

+0

Czy możesz dodać więcej szczegółów na temat wymaganych wyników? Czy chcesz zakończyć znacznikami zawartymi w treści 'wrapper.html.j2'? Czy możesz podać przykład zawartości dla 'wrapper.html.j2'? – tutuDajuju

+0

Och, whoops, w upraszczaniu mojego przykładu zrobiłem to bezsensowne, będę aktualizować przykłady – Steve

Odpowiedz

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 parsowanie dostać stąd https://github.com/Suor/django-cacheops/blob/master/cacheops/jinja2.py


Dodatkowo, powyższe może być przedłużony do obsługi dodatkowych znaczników z domyślnym szablonem określonym jako Wrapper:

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

Uprozniłem mój przykład trochę, potrzebuję jakiegokolwiek kwargs oprócz 'szablonu' przekazanego do hello_wrapper.js - w przeciwnym razie to wygląda dobrze: D – Steve

+0

naprawiono, j2! = js :) – vadimchin

+0

Nie jestem pewien, czy podążam, w jaki sposób argumenty i kwargs przechodzą do 'CallBlock' w' parsse'? Zakładam, że muszą zostać przekazane do wywołania 'render' w' _wrap'? – Steve

0

Lepszym rozwiązaniem jest użycie makr.Zdefiniuj go:

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

I korzystać później z call tagu:

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

Makro może mieć argumenty, takie jak funkcja Python i mogą być importowane:

{% from macros import wrapper %} 

zobacz dokumentację dla macro, call i import znaczniki, aby uzyskać więcej szczegółów.