mercredi 13 août 2014

Django : Afficher les chemins des modèles comme la superposition - Stack Overflow


I know that django-debug-toolbar has a Templates view that will list all templates invoked on a page request. That's helpful, but I'm looking for something that will go farther and visually show a label on each block of a page, specifying what template was used to draw that block (sort of similar to HTML debug tools like Web Developer's Extension or Firebug).


Is there a package out there that does this already? If not, is there a way for a template to display its own name? With that, I could probably write a custom template tag that would do this in debug mode.




Update: The final solution we used was a modification of Stephan's solution below. I've packaged up the working versions as a custom template loader in this gist:


https://gist.github.com/shacker/9261885




I haven't tested this, but you could probably write your own Template class.


from django.template.base import Template

class DebugTemplate(Template):
def _render(self, context):
rendered_template = Template._render(self, context)
if settings.DEBUG:
overlay = Template("overlay.html")
context = Context({"rendered_template": rendered_template})
return overlay.render(context)
return rendered_template

Your overlay template would put whatever surrounding html you would want, and use the {{rendered_template}} variable tag to put in the actual contents.


You would then have to make your own loader, to use this Template class instead of the built in Django one. I don't believe there is a setting to specify what Template class to use, but you can specify loaders.


In django/template/loader.py you can find the BaseLoader, and in django/template/loaders/ you can find the loaders themselves. You could create a DebugLoader:


from django.template.loader import BaseLoader

class DebugLoader(BaseLoader):
is_usable = True

def __init__(self, loaders):
self._loaders = loaders
self._cached_loaders = []

@property
def loaders(self):
# Resolve loaders on demand to avoid circular imports
if not self._cached_loaders:
# Set self._cached_loaders atomically. Otherwise, another thread
# could see an incomplete list. See #17303.
cached_loaders = []
for loader in self._loaders:
cached_loaders.append(find_template_loader(loader))
self._cached_loaders = cached_loaders
return self._cached_loaders

def load_template(self, template_name, template_dirs=None):
source = None

for loader in self.loaders:
try:
source, display_name = loader.load_template_source(template_name, template_dirs)
except TemplateDoesNotExist:
pass
else:
break

if not source:
raise TemplateDoesNotExist

origin = make_origin(display_name, loader.load_template_source, template_name, template_dirs)

try:
self.get_template_from_string(source, origin, template_name)
return template, None
except TemplateDoesNotExist:
return source, display_name

def get_template_from_string(self, source, origin, template_name):
return DebugTemplate(source, origin, name)

So, I took parts of that from the CachedLoader, and I didn't put in all the imports. List it in the loaders setting the same as you would the CachedLoader (not sure how it would work with the cached loader itself).


It should get the templates the way each of the other loaders would, then use the DebugTemplate instead of the default Template.


Like I said, I did not test this. This is just what I could come up with, without putting too much effort into it. Let me know any clarification you might need.




A simpler way:


#project/shortcuts.py
from django.template import RequestContext
from django.shortcuts import render_to_response
from django.template.base import Template,Context
from django.conf import settings

def render_template(request, template, data=None):
"Wrapper around render_to_response that fills in template name for you"
if settings.DEBUG:
temp = Template("template_debug.html")
context = Context({"rendered_template": template})
data['template_debug'] = temp.render(context)
response = render_to_response(template,
data,context_instance=RequestContext(request))
return response

#templates/template_debug.html
<div style="position:absolute; top:0px; left:0px; background-color:#000; color:#fff">
{{ rendered_template }}
</div>

Now you have to do two things:
1) Use render_template instead of render_to_response - added advantage of getting request context.
2) put {{ template_debug }} in any template that you want to see where it is getting rendered.



I know that django-debug-toolbar has a Templates view that will list all templates invoked on a page request. That's helpful, but I'm looking for something that will go farther and visually show a label on each block of a page, specifying what template was used to draw that block (sort of similar to HTML debug tools like Web Developer's Extension or Firebug).


Is there a package out there that does this already? If not, is there a way for a template to display its own name? With that, I could probably write a custom template tag that would do this in debug mode.




Update: The final solution we used was a modification of Stephan's solution below. I've packaged up the working versions as a custom template loader in this gist:


https://gist.github.com/shacker/9261885



I haven't tested this, but you could probably write your own Template class.


from django.template.base import Template

class DebugTemplate(Template):
def _render(self, context):
rendered_template = Template._render(self, context)
if settings.DEBUG:
overlay = Template("overlay.html")
context = Context({"rendered_template": rendered_template})
return overlay.render(context)
return rendered_template

Your overlay template would put whatever surrounding html you would want, and use the {{rendered_template}} variable tag to put in the actual contents.


You would then have to make your own loader, to use this Template class instead of the built in Django one. I don't believe there is a setting to specify what Template class to use, but you can specify loaders.


In django/template/loader.py you can find the BaseLoader, and in django/template/loaders/ you can find the loaders themselves. You could create a DebugLoader:


from django.template.loader import BaseLoader

class DebugLoader(BaseLoader):
is_usable = True

def __init__(self, loaders):
self._loaders = loaders
self._cached_loaders = []

@property
def loaders(self):
# Resolve loaders on demand to avoid circular imports
if not self._cached_loaders:
# Set self._cached_loaders atomically. Otherwise, another thread
# could see an incomplete list. See #17303.
cached_loaders = []
for loader in self._loaders:
cached_loaders.append(find_template_loader(loader))
self._cached_loaders = cached_loaders
return self._cached_loaders

def load_template(self, template_name, template_dirs=None):
source = None

for loader in self.loaders:
try:
source, display_name = loader.load_template_source(template_name, template_dirs)
except TemplateDoesNotExist:
pass
else:
break

if not source:
raise TemplateDoesNotExist

origin = make_origin(display_name, loader.load_template_source, template_name, template_dirs)

try:
self.get_template_from_string(source, origin, template_name)
return template, None
except TemplateDoesNotExist:
return source, display_name

def get_template_from_string(self, source, origin, template_name):
return DebugTemplate(source, origin, name)

So, I took parts of that from the CachedLoader, and I didn't put in all the imports. List it in the loaders setting the same as you would the CachedLoader (not sure how it would work with the cached loader itself).


It should get the templates the way each of the other loaders would, then use the DebugTemplate instead of the default Template.


Like I said, I did not test this. This is just what I could come up with, without putting too much effort into it. Let me know any clarification you might need.



A simpler way:


#project/shortcuts.py
from django.template import RequestContext
from django.shortcuts import render_to_response
from django.template.base import Template,Context
from django.conf import settings

def render_template(request, template, data=None):
"Wrapper around render_to_response that fills in template name for you"
if settings.DEBUG:
temp = Template("template_debug.html")
context = Context({"rendered_template": template})
data['template_debug'] = temp.render(context)
response = render_to_response(template,
data,context_instance=RequestContext(request))
return response

#templates/template_debug.html
<div style="position:absolute; top:0px; left:0px; background-color:#000; color:#fff">
{{ rendered_template }}
</div>

Now you have to do two things:
1) Use render_template instead of render_to_response - added advantage of getting request context.
2) put {{ template_debug }} in any template that you want to see where it is getting rendered.


0 commentaires:

Enregistrer un commentaire