#36872: Django's template engine cannot handle asynchronous methods
---------------------------------+--------------------------------------
Reporter: Ricardo Robles | Owner: (none)
Type: Bug | Status: new
Component: Template system | Version: 6.0
Severity: Normal | Resolution:
Keywords: Template, Async | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
---------------------------------+--------------------------------------
Description changed by Ricardo Robles:
Old 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: <coroutine object Example.async_method at
> 0x7bdeeb9aa980></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.
New 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: <coroutine object Example.async_method at
0x7bdeeb9aa980></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):
obj = async_to_sync(lambda: 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.
--
--
Ticket URL: <https://code.djangoproject.com/ticket/36872#comment:3>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/django-updates/0107019be09a9ea9-1db435cb-8819-403e-ba93-d4f56bb92ba3-000000%40eu-central-1.amazonses.com.