2010-05-25 13 views
10

Estoy tratando de escribir un conjunto de etiquetas de plantilla que le permiten especificar fácilmente los archivos js y css dentro de los archivos de la plantilla. Algo similar a {% requires global.css %} y más adelante en la solicitud, {% get_required_css %}.Django templatetag "orden de procesamiento"

Tengo esto funcionando principalmente, pero hay un par de problemas. Comenzaremos con los problemas de 'timing'.

Cada etiqueta de plantilla se compone de dos pasos, call/init y render. Cada llamada/init ocurre antes de que se invoque cualquier procedimiento de renderizado. Para garantizar que todos los archivos estén en cola antes de que se muestre el {% get_required_css %}, necesito compilar mi lista de archivos necesarios en los procedimientos call/init.

Por lo tanto, necesito recopilar todos los archivos en un paquete por solicitud. El dict context obviamente es el lugar para esto, pero desafortunadamente, el call/init no tiene acceso a la variable de contexto.

¿Esto tiene sentido? ¿Alguien ve una forma de evitar esto (sin recurrir a un objeto global hack-y request)?

Otra posibilidad de almacenar estos en un dict local, pero aún deberían estar vinculados a la solicitud de alguna manera ... posiblemente algún tipo de etiqueta {% start_requires %}? Pero no tengo ni idea de cómo hacer ese trabajo tampoco.

+0

Después de revisar brevemente las capacidades de 'middleware', estoy considerando usar eso para insertar el html generado. El problema con eso es que es más difícil conseguir que cualquiera de las diversas aplicaciones de compresión estática funcione sin problemas. –

+0

Si alguien está interesado, tengo una aplicación en github que es una solución bastante poco elegante para este problema específico: http://github.com/pappy74/django-requires_js_css. Es un poco similar a la respuesta de Jack a continuación, pero empaquetada como una aplicación bonita y ordenada. –

Respuesta

2

He encontrado una manera de hacer esto que se adapte mejor a sus necesidades. Tendrá un poco más de carga en el servidor, pero el almacenamiento en caché adecuado puede ayudar a aliviar la mayor parte de eso. A continuación, describí una forma que debería funcionar si el CSS incluye lo mismo para cada ruta. Tendrá que crear una vista única para incluir todos estos archivos, pero puede optimizar su CSS utilizando este método, haciendo solo una llamada CSS para cada página.

import md5 
class LoadCss(template.Node): 
    def __init__(self, tag_name, css): 
     self.css = css 
     self.tag_name = tag_name 
    def render(self, context): 
     request = context['request'] 
     md5key = md5.new(request.path).hexdigest() 
     if md5key not in request.session: 
      request.session[md5key] = list() 
     ## This assumes that this method is being called in the correct output order. 
     request.session[md5key].append(self.css) 
     return '<!-- Require %s -->' % self.css 
def do_load_css(parser, token): 
    tag_name, css = token.split_contents() 
    return LoadCss(tag_name, key) 
register.tag('requires', do_load_css) 

class IncludeCss(template.Node): 
    def __init__(self, tag_name): 
     self.tag_name = tag_name 
    def render(self, context): 
     request = context['request'] 
     md5key = md5.new(request.path).hexdigest() 
     return '<link rel="stylesheet" href="/path/to/css/view/%s">' % md5key 
def do_include_css(parser, token): 
    return IncludeCss(token) 
register.tag('get_required_css', do_include_css) 

views.py:

from django.conf import settings 
from django.views.decorators.cache import cache_page 
import os 

@cache_page(60 * 15) ## 15 Minute cache. 
def css_view(request, md5key): 
    css_requires = request.session.get(md5key, list()) 
    output = list() 
    for css in css_requires: 
     fname = os.path.join(settings.MEDIA_ROOT, 'css', css) ## Assumes MEDIA_ROOT/css/ is where the CSS files are. 
     f = open(fname, 'r') 
     output.append(f.read()) 
    HttpResponse(''.join(output), mimetype="text/css") 

Esto le permite almacenar la información en el contexto de CSS, a continuación, en la sesión, y servir a la salida de una vista (con el almacenamiento en caché para que sea Más rápido). Esto, por supuesto, tendrá un poco más de sobrecarga del servidor.

Si necesita cambiar el CSS en algo más que la ruta, simplemente puede modificar las líneas md5 para adaptarlas a sus necesidades. Tiene acceso al objeto de solicitud completo y al contexto, por lo que casi todo debe estar allí.

Cuidado: En segundo examen, esto puede causar una condición de carrera si el navegador obtiene el CSS antes de la sesión ha sido rellenada. No creo que Django funcione de esa manera, pero no tengo ganas de buscarlo ahora mismo.

+0

Así fue como lo implementé inicialmente, pero el problema es el de ordenar. Con esto, * debes * asegurarte de que todos tus 'require' sean analizados antes de que se 'analice' get_required '.Esto es particularmente evidente cuando una etiqueta de plantilla de inclusión en sí misma 'requiere' un archivo css. Si realmente puede agregar los archivos durante __init__, puede solucionar el problema. –

+0

Agregué otra forma posible de lograr esto que no depende de los pedidos internos. Es un poco intrincado, pero debería funcionar. –

Cuestiones relacionadas