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

Django FileField文件上传失败:未存入media目录与SQL数据库

Django课程文件上传问题排查与解决

问题描述

使用Django开发在线学习门户,教师为课程上传文件时,文件无法存入media/course_materials目录,且SQL数据库中未生成对应CourseMaterial记录(已测试小体积图片和Word文档)。视图调试输出显示文件已被正确接收:

return (<MultiValueDict: {'course_materials': [<InMemoryUploadedFile: filematerial.docx (application/vnd.openxmlformats-officedocument.wordprocessingml.document)>]}>
)

现有代码

Models代码

class Course(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField(default ="Description of Course")
    course_materials = models.ManyToManyField('CourseMaterial', blank=True)

    def __str__(self):
        return self.name
    
class CourseMaterial(models.Model):
    name = models.CharField(max_length=255)
    file = models.FileField(upload_to='course_materials/')

    def __str__(self):
        return self.name

Views代码

def edit_course(request, course_id):
    course = get_object_or_404(Course, id=course_id)
    if request.method == 'POST':
        form = CourseForm(request.POST, request.FILES.get, instance=course)
        if form.is_valid():
            print(request.FILES)
            course = form.save(commit=False)
            course.save()
            form.save_m2m()  # Save many-to-many relationships
            return redirect('teacher')  # Redirect to teacher dashboard after editing the course
    else:
        form = CourseForm(instance=course)
    
    context = {
        'form': form,
        'course': course,
    }
    return render(request, 'elearn/edit_course.html', context)

Forms代码

class CourseForm(forms.ModelForm):
    class Meta:
        model = Course
        fields = ['name', 'description', 'course_materials']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['course_materials'].queryset = CourseMaterial.objects.none()

HTML代码

<form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <div class="mb-3">
        <label for="id_name" class="form-label">Course Name</label>
        {{ form.name }}
    </div>
    <div class="mb-3">
        <label for="id_description" class="form-label">Description</label>
        {{ form.description }}
    </div>
    <div class="mb-3">
        <label for="id_course_materials" class="form-label">Course Materials</label>
        <ul>
            {% for material in course.course_materials.all %}
            <li>{{ material.name }}: <a href="{{ material.file.url }}">{{ material.file.name }}</a></li>
            {% endfor %}
        </ul>

        <input label="course_materials_input" type="file" name="course_materials" class="form-control-file">
    </div>
    <button type="submit" class="btn btn-primary">Save Changes</button>
</form>

Settings配置

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

问题原因及解决步骤

1. 表单逻辑错误:多对多字段无法直接处理文件上传

当前CourseForm绑定的course_materials是多对多字段,仅用于关联已存在的CourseMaterial实例,无法直接接收新上传的文件。需添加独立的文件上传字段:

class CourseForm(forms.ModelForm):
    # 添加独立的文件上传字段,避免与多对多字段重名
    new_material = forms.FileField(required=False, label="上传新材料")

    class Meta:
        model = Course
        fields = ['name', 'description', 'course_materials']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 改为显示所有已存在的材料,方便教师关联已有资源
        self.fields['course_materials'].queryset = CourseMaterial.objects.all()

2. 视图处理逻辑错误:未正确传递FILES参数与创建材料实例

  • 原视图中request.FILES.get参数传递错误,需直接传入request.FILES;
  • 需手动处理上传文件,创建CourseMaterial实例后关联到课程:
def edit_course(request, course_id):
    course = get_object_or_404(Course, id=course_id)
    if request.method == 'POST':
        # 正确传递request.FILES参数
        form = CourseForm(request.POST, request.FILES, instance=course)
        if form.is_valid():
            course = form.save(commit=False)
            course.save()
            form.save_m2m()

            # 处理新上传的文件
            uploaded_file = request.FILES.get('new_material')
            if uploaded_file:
                # 创建CourseMaterial实例
                material = CourseMaterial.objects.create(
                    name=uploaded_file.name,
                    file=uploaded_file
                )
                # 将新材料关联到当前课程
                course.course_materials.add(material)

            return redirect('teacher')
    else:
        form = CourseForm(instance=course)
    
    context = {
        'form': form,
        'course': course,
    }
    return render(request, 'elearn/edit_course.html', context)

3. HTML表单输入错误:字段名称与多对多字段冲突

原HTML中文件输入框名称course_materials与多对多字段重名,需改为与表单新增字段一致:

<div class="mb-3">
    <label for="id_course_materials" class="form-label">已有课程材料</label>
    {{ form.course_materials }}
    <ul>
        {% for material in course.course_materials.all %}
        <li>{{ material.name }}: <a href="{{ material.file.url }}">{{ material.file.name }}</a></li>
        {% endfor %}
    </ul>

    <label for="id_new_material" class="form-label">上传新材料</label>
    <input type="file" name="new_material" class="form-control-file" id="id_new_material">
</div>

4. 补充检查项

  • 确保media目录存在且具备读写权限,可通过命令创建并设置权限:mkdir media && chmod 755 media;
  • 确保项目URL配置中添加媒体文件访问路由:
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    # 其他路由配置
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 13:29:54