bordeltabernacle
bordeltabernacle

Reputation: 1653

Django - render template tags before rendering template

I have a problem I'm not quite sure how to approach. I have a template that will render a section of text that will itself contain a template tag to be dynamically updated.

<ul>
  {% for section in sections %}
    <li>{{ section.content | safe }}</li>
  {% endfor %}
</ul>

will render:

{{ document.customer }} Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis sit amet tortor eu velit accumsan ornare vel in purus. 
{{ document.customer }} Aenean rhoncus elit tempus finibus fermentum. Nullam accumsan ullamcorper felis, eu vestibulum neque laoreet ac.

I then need to dynamically update the {{ document.customer }} tag within section.content. This same tag renders fine in the template, but not as part of the individual sections also being rendered. How would I go about rendering the template variable within the section within the template?

I've tried replacing the above with {% include "display_lld_sections.html" with sections=sections document=document %} but this doesn't make a difference, as I assume it's just moving the issue to a different template. And I can't see that writing a custom tag is going to help either as it's still going to only work on the sections rather then the content within the sections, am I right?

I have also tried using a with statement:

<ul>
  {% for section in sections %}
    {% with document=document %}
      <li>{{ section.content | safe }}</li>
    {% endwith %}
  {% endfor %}
</ul>

I also started to rewrite my view:

rendered_sections = {}
for section in sections:
    x = render(request, 'lld/lld_sections.html',{'document': document})
    rendered_sections[section.name] = x

Though this is not, I suspect the way to approach this. Is this something that will need to being done using code in my view or model, rather than something that will be done in my template?

Upvotes: 0

Views: 1778

Answers (2)

spectras
spectras

Reputation: 13552

It depends on the level of versatility you need.

  • If the content can actually include any kind of template feature, then you'll have no choice but to make it a template. See GwynBleidD's answer then. There are several drawbacks though:

    • The main drawback is that those templates will not be cached by Django, so they will have to be compiled everytime, which could have a significant performance impact.
    • Also, if the content may come from an untrusted source, remember that making it a template will give it access to a lot of internal data (the context, anything exposed by a context processor such as you site's settings, all kind of tags, etc). What if I make a section with content: Hehe, I got your {{ settings.DATABASES.default.PASSWORD }}?
  • If, however, you just need replacement of a small set of known variables, you could just do a simple string replacement in your view, looking for {{ document.customer }} and replacing with the value.

    If you go that path, maybe you should simplify it a bit. I would do something like this:

    lookup = re.compile(r'{{ *(\w+) *}}')
    def interpolate(text, context):
        replacer = lambda m: context.get(m.group(1), '')
        return lookup.sub(replacer, text)
    
    def in_your_view(self, whatever):
        # some stuff
        section_context = {
            "customer": document.customer,
            "foo": "bar",
        }
        for section in sections:
            section.interpolated = interpolate(section.section, section_context)
    

    This will replace {{ customer }} with the customer and {{foo}} with bar, without doing any template stuff. It is intentionally limited so it will not give access to any data but things from the context. And it does not have the performance impact of parsing a template.

    Now you just output ̀{{ section.interpolated }} in your view and you're done.

Upvotes: 1

GwynBleidD
GwynBleidD

Reputation: 20569

You should wrap section.content into template and include it using include tag like this:

{% include section.content_template %}

Let's say, if your section is django model instance, add this property into that model:

    @property
    def content_template(self):
        from django.template.backends.django import Template
        return Template(self.content)

You can of course dynamically choose engine for you templates, but in other engines, including Template objects might not work.

Upvotes: 1

Related Questions