You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django视图函数create_page中执行分发的信号?

技术指导:表单前置文件上传与Tus信号处理

现有代码的核心问题

  • 在视图函数内部定义@receiver会导致重复注册信号接收器:每次请求create_page视图都会新增一个接收器,后续上传的文件会触发所有已注册的接收器,造成数据混乱。
  • 无法保证files列表与当前请求的绑定:多用户并发请求时,不同用户的上传文件会混入同一个列表,导致文件关联错误。
  • attach.file = f直接赋值文件名不符合Django FileField要求:需要处理文件路径或使用File对象完成关联。

修正方案步骤

1. 移除视图内信号接收器,改用独立接收器+会话关联

把信号接收器移到signals.py中,通过**用户会话(session)**绑定当前请求的上传文件,避免跨请求污染:

修改signals.py

from django.dispatch import receiver
from django.contrib.sessions.models import Session
from .models import Attach
from django.conf import settings

@receiver(tus_upload_finished_signal)
def handle_tus_upload(sender, **kwargs):
    # 从metadata中获取前端传入的session_key
    session_key = kwargs['metadata'].get('session_key')
    if not session_key:
        return
    
    # 从会话中读取已上传文件列表,追加新文件信息
    session = Session.objects.get(session_key=session_key)
    upload_files = session.get('tus_upload_files', [])
    upload_files.append({
        'filename': kwargs['filename'],
        'file_path': kwargs['upload_file_path'],
        'file_size': kwargs['file_size']
    })
    session['tus_upload_files'] = upload_files
    session.save()

2. 前端上传时传递session_key

前端发起Tus上传请求时,需将当前用户的session_key放入metadata,确保后端能关联到对应请求会话:

// 示例tus-js-client上传代码
const upload = new tus.Upload(file, {
    endpoint: '/tus/upload/',
    metadata: {
        filename: file.name,
        session_key: '{{ request.session.session_key }}' // Django模板渲染会话key
    },
    // 其他上传配置
});

3. 重构create_page视图,从会话读取文件并关联Post

def create_page(request):
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.save()

            # 从会话获取当前用户的上传文件
            upload_files = request.session.get('tus_upload_files', [])
            for file_info in upload_files:
                from django.core.files import File
                attach = Attach()
                attach.post = post
                attach.filename = file_info['filename']
                # 为FileField赋值文件对象
                with open(file_info['file_path'], 'rb') as f:
                    attach.file.save(file_info['filename'], File(f), save=False)
                attach.file_size = file_info['file_size']
                attach.save()
            
            # 清空会话中的上传记录,避免重复关联
            if 'tus_upload_files' in request.session:
                del request.session['tus_upload_files']
            return redirect('index_page')
    else:
        form = PostForm()
    return render(request, 'core/_form.html', {'form': form})

4. 确保Attach模型字段适配需求

# models.py示例
from django.db import models
from .models import Post

class Attach(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='attachments')
    file = models.FileField(upload_to='attachments/') # 配置文件存储路径
    filename = models.CharField(max_length=255, blank=True)
    file_size = models.IntegerField(blank=True, null=True)
    uploaded_at = models.DateTimeField(auto_now_add=True)

额外注意事项

  • 临时文件清理:Tus上传的临时文件在关联到Attach后,需通过定时任务清理未被关联的冗余文件。
  • 权限验证:在TusUpload的patch方法中,需验证当前用户权限,防止恶意上传。
  • 分布式部署适配:多服务器部署时,需将session存储在共享介质(如Redis),避免跨节点会话不一致。

内容的提问来源于stack exchange,提问作者afi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 13:57:20