Opened 2 hours ago

#36872 new Bug

Django's template engine cannot handle asynchronous methods

Reported by: Ricardo Robles Owned by:
Component: Template system Version: 6.0
Severity: Normal Keywords: Template, Async
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: yes

Description

Currently, the Django template is not designed to execute asynchronous methods; it is only designed to execute synchronous methods.

Here's an example of how to reproduce the error:

from django.template import engines

django_engine = engines['django']

class Example:
    def sync_method(self):
        return "Synchronous Method Result"
    async def async_method(self):
        return "Asynchronous Method Result"

html_string = """
<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
</head>
<body>
    <h1>sync: {{ example.sync_method }}!</h1>
    <p>async: {{ example.async_method }}</p>
</body>
</html>
"""

template = django_engine.from_string(html_string)
rendered_html = template.render({'example': Example()})

print(rendered_html)

This will return this error:

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
</head>
<body>
    <h1>sync: Synchronous Method Result!</h1>
    <p>async: &lt;coroutine object Example.async_method at 0x7bdeeb9aa980&gt;</p>
</body>
</html>

I had thought that a solution to this error might be to modify the resolve method of the FilterExpression class
https://github.com/django/django/blob/main/django/template/base.py#L785

If we add this:

class FilterExpression:
    ...

    def resolve(self, context, ignore_failures=False):
        if self.is_var:
            try:
                obj = self.var.resolve(context)

                # My proposal begins
                if asyncio.iscoroutine(obj):
                    try:
                        loop = asyncio.get_event_loop()
                    except RuntimeError:
                        loop = asyncio.new_event_loop()
                        asyncio.set_event_loop(loop)

                    if loop.is_running():
                        obj = loop.run_until_complete(obj)
                    else:
                        obj = asyncio.run(obj)
                # My proposal ends

            except VariableDoesNotExist:
                ...

Now it renders correctly:

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
</head>
<body>
    <h1>sync: Synchronous Method Result!</h1>
    <p>async: Asynchronous Method Result</p>
</body>
</html>

I use Django ASGI a lot at work, and there are many features like this that would be very useful. I look forward to your feedback.

Change History (0)

Note: See TracTickets for help on using tickets.
Back to Top