#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: &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.

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: &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):
                     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.

Reply via email to