Keats / tera

A template engine for Rust based on Jinja2/Django
http://keats.github.io/tera/
MIT License
3.43k stars 279 forks source link

Partial Render / Pre Render #832

Open Wicpar opened 1 year ago

Wicpar commented 1 year ago

Hi, I am using tera to generate user provided templates as HTML with support for internationalization. Most info is known upfront except a few fields that need to be set after an asynchronous process has changed. At the moment I have to save all templates inside the database with all the required data to then render it entirely once the missing data is available.

A nice feature would be to pre-render the entire template to string except a few (known or unknown) variables that remain to be set, maybe with a special renderer PreRenderer that simply precompiles the AST if possible. Ideally it would also know and keep the pre-rendrerd context that is still needed to complete it.

Keats commented 1 year ago

That's not planned and won't be added, it would be too messy since changes in any templates can affect other templates

Wicpar commented 1 year ago

the idea is to be able to export a new standalone template ouside of the tera context that can be re-rendered as a one-off, it will not affect templates directly in the main Tera context. I don't see how that would be messy. On the contrary it would be an opportunity to refactor parts to be less interdependent. I'd be glad to contribute it when i have the time to work on it.

Keats commented 1 year ago

How do you envision this to work? I don't see how that would be clean but maybe I don't understand it

Wicpar commented 1 year ago

For a simple version essentially take the template hierarchy, flatten it into a special template that only takes non dependent AST nodes, then iterate over each node in a pre-render step replacing it with a text node if it could be resolved, or keeping it as-is if there is some context missing, then in a last step serialize it back into a textual template that can be rendered as a one-off. This requires two things: a way to flatten multiple template asts into one, and a way to serialize back ast nodes to a string template.

If it still makes no sense i'll explain it better once implemented, it will be necessary anyway for my usecase.

Keats commented 1 year ago

I wouldn't spent time implementing it, Tera v2 doesn't have an AST anymore (well it has a temporary one) so that would be completely different work that cannot be reused. The issue is that you need to track all the dependencies and and figure out what is an error vs what needs to be loaded and you still have to render it anyway so it's going to be slower than rendering in one go. Eg

// user is defined, user.name isn't. Is that something that should be loaded later or an error?
{% set something = user.name %}

// a is defined, b isn't. Is that normal or missing data? We need to keep the `if` since we are not sure.
{% if a and b %}...{% endif %}
Wicpar commented 1 year ago

it is slower, but it's better than having 50MB of templates per user request in the DB, which would represent 99% of the user's data consumption while it could be a few kilobytes. The issue of what is needed can be resolved by tracking dependencies for each node, or by having a special context that signifies the missing data that should remain as a template. AST or not the principle is to replace the resolvable parts with the resolved data to get a one-off template that can be serialized and deserialized. I get the difficulties, it's just that it's not viable to replicate raw templates everywhere in the DB that need to be recompiled entierly every time with each render.