【问题标题】:Show message in Django without needing a request在 Django 中显示消息而不需要请求
【发布时间】:2021-03-06 11:03:09
【问题描述】:

我有一个使用线程的视图,例如

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")

它就像一个魅力;它在后台处理my_heavy_function,同时使用户能够继续使用该网页。我只需要在my_heavy_function 完成后显示一条消息。

有没有办法让 Django 显示一条消息,即使没有调用新请求而是基于其他条件?例如,当文件加载完成时在页面上等。

(我故意不使用 Django-Q、Celery 或后台任务来处理这个线程,因为我觉得这太过分了)

【问题讨论】:

  • 你不能直接在 django 中这样做。为什么? Django 是一个网络框架,它接受来自网络的请求并给出响应以连接到浏览器。

标签: django django-messages


【解决方案1】:

不清楚你想做什么。如果您想在不发出 HTTP 请求的情况下通知用户,那么您对 ​​Web 技术的唯一选择就是设置一个 WebSocket,以便您可以从服务器推送内容。

如果用户下次打开页面时可以收到消息,则可以在完成繁重的任务后将某些内容放入数据库中。对于每个请求,您都会检查 DB 中是否有某些内容,您将执行 messages.add_message 并从 DB 中删除该行。

【讨论】:

    【解决方案2】:

    如果您想显示带有操作结果的消息,该操作在返回请求响应后完成,您可以选择同步显示它。

    它的工作方式是,下次用户请求资源时,此实现将检查同时创建的该用户的任何消息(例如,在您的异步代码完成执行并添加消息之后。

    此解决方案使用一个简单的 Memcache 容器对同步 Django 消息传递框架使用上层结构。

    1. 安装 memcached 作为你的缓存后端。

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

    2. 那就去pip install django-async-messages django-pymemcache

    3. 将此添加到您的middleware settings.py 文件中:

      'async_messages.middleware.AsyncMiddleware'

    确保它出现在'django.contrib.messages.middleware.MessageMiddleware'之后

    1. 然后将其添加到您的 settings.py 文件中:

      缓存 = { '默认': { 'BACKEND': 'djpymemcache.backend.PyMemcacheCache', '地点': [ '127.0.0.1:11211', ], }, }

    2. 你想在哪里使用这个异步消息,去from async_messages import message_user 将您的经典 messages.add_message(... 替换为 message_user(request.user, "your message") 其中第一个 agrument 是用户对象

    3. 转到django-async-messages 包,因为它有点过时了,需要小幅更新。 在包中找到middleware.py文件(可能在venv/Lib/site-packages/async_messages/middleware.py中)

    4. 从这里改变它

      从 django.contrib 导入消息

      从 async_messages 导入 get_messages

      类 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
      

    到这里:

    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
    

    就是这样 - 你有异步消息传递!

    Ps - 因为您刚刚编辑了一个在部署时会更改的包 - 我提取了该包,并通过上述更改将其直接包含在我的项目结构中。

    【讨论】:

      【解决方案3】:

      要让消息正常工作,您需要编辑 settings.py

      settings.py

      INSTALLED_APPS = [
      ...
      'django.contrib.messages',
      ...
      ]
      
      
      

      django.contrib.messages

      是的,您可以通过 Django 中的视图在模板中显示消息。

      将此添加到您的模板中: index.html

      {% if messages %}
      <ul class="messages">
          {% for message in messages %}
          <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
          {% endfor %}
      </ul>
      {% endif %}
      

      现在,只要有新消息,它就会显示在您的模板中。 在你的代码中你可以使用try,除了这样的块

      view.py

      from .utils import my_heavy_function
      def my_view(request):
      
          if request.method == "POST":
             form = my_model_form()
      
              if form.is_valid():
                  thr = threading.Thread(target=my_heavy_function,args=(form,))
                  thr.start()
                  # Need to save form ??? form.save() ??????
                  messages.add_message(request, Messages.INFO, "form saved")
              
              try:
                  # run function here.
                  messages.add_message(request, message.INFO, "Sucess! ")
                  return redirect("my_template")
              except Exception as e:
                  # For testing print(e)
                  messages.add_message(request, message.INFO, "Error! ")
                  return redirect('my_template")
                  
          else:
              form = my_model_form()
          return render(request, "my_app/my_template.html")
      
      

      更多信息: https://docs.djangoproject.com/en/3.1/ref/contrib/messages/

      【讨论】:

      • 我完全知道在返回请求时会在视图中显示一条消息。我正在寻找一种在 not 返回请求时显示消息的方法。以您提供的方式,在调用 redirect 时显示消息,如所述 my_heavy_function 不返回请求,但我需要某种方式在函数完成计算时而不是在用户刷新时显示消息页面
      • 检查编辑。我向你展示了你在哪里运行你的函数。这是 MVT 架构中的一个视图。发送的请求必须与响应一起返回。否则你的应用会崩溃。如果您尝试添加不是对请求的响应的其他消息。那么你应该使用 Vanilla JavaScript ,在你的视图中添加这个逻辑是没有意义的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-05
      • 2021-03-29
      • 1970-01-01
      相关资源
      最近更新 更多