'Show message in Django without needing a request

I have a view which uses threading e.g

from .utils import my_heavy_function
def my_view(request):

    if request.method == "POST":
       form = my_model_form()

        if form.is_valid():
            
            #create thread
            thr = threading.Thread(target=my_heavy_function,args=(form,))
            thr.start()
            messages.success(request, "Processing ...")

            return redirect("my_template")
    else:
        form = my_model_form()
    return render(request, "my_app/my_template.html")

and it works like a charm; it process the my_heavy_function in the background while making the user able to continue using the webpage. I just need a way to show a message when my_heavy_function is done.

Is there a way to make Django display a message even when a new-request is not called but based on some other condition? E.g on a page when a file is done loading etc.

(I have on purposed not used Django-Q, Celery or back-ground-tasks for this threading since I find it being overkill)



Solution 1:[1]

It's not very clear what do you want to do. If you want to notify user without it doing an HTTP request, your only bet in web technologies is to setup a WebSocket so you can push things from server.

If it's ok for user to get the message next time they open a page, you can put something in the DB when your heavy task is done. And on each request you check if you have something in DB, you'll do messages.add_message and remove that row from DB.

Solution 2:[2]

If you want to show the message with a result of an action, that has finished after a response to the request has been returned, you have this option to show it synchronically.

The way it works is that next time the user requests a resource, this implementation will check for any messages for this user, that has been created meanwhile (eg. after your async code finished execution and added a message.

This solution uses a superstructure to the synchronous Django messaging framework with a simple Memcache container.

  1. Install memcached as your cache backend.

    docker run -p 11211:11211 --name local-memcache -d memcached memcached -m 64

  2. Then go and pip install django-async-messages django-pymemcache

  3. Add this to your middleware in settings.py file:

    'async_messages.middleware.AsyncMiddleware'

Ensure it comes after 'django.contrib.messages.middleware.MessageMiddleware'

  1. Then add this to your settings.py file:

    CACHES = { 'default': { 'BACKEND': 'djpymemcache.backend.PyMemcacheCache', 'LOCATION': [ '127.0.0.1:11211', ], }, }

  2. Where you want to use this async messaging, go from async_messages import message_user Substitute your classical messages.add_message(... for message_user(request.user, "your message") where first agrument is a user object

  3. go to the django-async-messages package because it is slightly obsolete and needs a small update. Locate the middleware.py file in the package (likely in venv/Lib/site-packages/async_messages/middleware.py)

  4. Change it from this

    from django.contrib import messages

    from async_messages import get_messages

    class AsyncMiddleware(object):

     def process_response(self, request, response):
         """
         Check for messages for this user and, if it exists,
         call the messages API with it
         """
         if hasattr(request, "session") and hasattr(request, "user") and request.user.is_authenticated():
             msgs = get_messages(request.user)
             if msgs:
                 for msg, level in msgs:
                     messages.add_message(request, level, msg)
         return response
    

to this:

from django.contrib import messages

from async_messages import get_messages
from django.utils.deprecation import MiddlewareMixin

class AsyncMiddleware(MiddlewareMixin):

    def process_response(self, request, response):
        """
        Check for messages for this user and, if it exists,
        call the messages API with it
        """
        if hasattr(request, "session") and hasattr(request, "user") and request.user.is_authenticated:
            msgs = get_messages(request.user)
            if msgs:
                for msg, level in msgs:
                    messages.add_message(request, level, msg)
        return response

That is it - you have asynchronous messaging!

Ps - because you just edited a package which would change when deploying - I exctracted the package and with above changes I included it directly in my project structure.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Arman Ordookhani
Solution 2 David Louda