Skip to content

Django - 文件上传

许多 Web 应用需要允许用户上传文件,例如头像、文档或其他媒体文件。Django 提供了内置机制来安全高效地处理文件上传。

Django 的模型使用特定字段来处理文件:

  • FileField:用于处理任何类型的文件上传。
  • ImageField:FileField 的特殊版本,它还会验证上传的文件是否为有效的图像。它需要安装 Pillow 库(pip install Pillow)。

要处理文件上传,你必须在 settings.py 中配置两个设置:

import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# ... other settings ...
# Base URL from which media files will be served.
# Example: 'http://localhost:8000/media/' or '/media/'
MEDIA_URL = '/media/'
# Absolute filesystem path to the directory where uploaded files will be stored.
# Example: BASE_DIR / 'mediafiles'
MEDIA_ROOT = os.path.join(BASE_DIR, 'mediafiles')
  • MEDIA_URL:通过浏览器访问上传文件使用的基础 URL。必须以斜杠结尾。
  • MEDIA_ROOT:在你的服务器文件系统上,Django 存储上传文件的目录的绝对路径。确保 Web 服务器进程对该目录有写入权限。

Django 的开发服务器不会自动提供媒体文件服务。你需要将一个 URL 模式添加到你的项目的 urls.py 中:

myproject/urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('myapp/', include('myapp.urls')), # Include your app's urls
# ... other project urls
]
# Add this pattern for serving media files during development ONLY
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

重要: static() 提供的此模式不适合生产环境。在生产环境中,你的 Web 服务器(Nginx、Apache)应该配置为直接从 MEDIA_ROOT 提供媒体文件服务。

让我们创建一个简单的个人资料模型和表单来处理图片上传。

确保 Pillow 已安装在你激活的虚拟环境中:

pip install Pillow
from django.db import models
class Profile(models.Model):
name = models.CharField(max_length=100)
# 'upload_to' specifies a subdirectory within MEDIA_ROOT
picture = models.ImageField(upload_to='profile_pictures/')
def __str__(self):
return self.name

upload_to='profile_pictures/' 参数告诉 Django 将此字段上传的图片保存到 MEDIA_ROOT/profile_pictures/ 目录中。

定义模型后,运行 python manage.py makemigrations 和 python manage.py migrate。

使用 ModelForm 简化了处理模型数据。

from django import forms
from .models import Profile
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['name', 'picture'] # Include the fields you want in the form

如果你不使用 ModelForm,你需要显式使用 forms.ImageField()。

此视图处理显示空表单(GET 请求)和处理提交的表单(POST 请求)。

from django.shortcuts import render, redirect
from .forms import ProfileForm
from .models import Profile
def upload_profile(request):
if request.method == 'POST':
# IMPORTANT: Pass request.FILES to the form when handling file uploads
form = ProfileForm(request.POST, request.FILES)
if form.is_valid():
form.save() # ModelForm's save handles creating the Profile instance
# Redirect to a success page or the profile list, etc.
return redirect('profile_success') # Assumes a URL named 'profile_success'
# If form is not valid, it will be re-rendered with errors below
else: # GET request
form = ProfileForm()
# Pass the form instance (either empty or with errors) to the template
context = {'form': form}
return render(request, 'myapp/upload_profile.html', context)
# A simple success view (optional)
def profile_success(request):
profiles = Profile.objects.all()
context = {'profiles': profiles}
return render(request, 'myapp/profile_success.html', context)

至关重要的是,在处理 POST 请求中的文件上传时,必须将 request.FILES 作为第二个参数传递给表单构造函数。

5. 创建模板(myapp/templates/myapp/upload_profile.html)

Section titled “5. 创建模板(myapp/templates/myapp/upload_profile.html)”

模板需要一个带有 enctype="multipart/form-data" 属性的 form 标签来启用文件上传。

<!DOCTYPE html>
<html>
<head>
<title>Upload Profile</title>
</head>
<body>
<h2>Upload Your Profile</h2>
<form method="post" enctype="multipart/form-data"> {# Essential for file uploads #}
{% csrf_token %}
{{ form.as_p }} {# Render the form fields as paragraphs #}
<button type="submit">Upload</button>
</form>
</body>
</html>
from django.urls import path
from . import views
urlpatterns = [
path('upload/', views.upload_profile, name='upload_profile'),
path('success/', views.profile_success, name='profile_success'),
# You might want a view to display profiles, e.g.:
# path('profiles/', views.profile_list_view, name='profile_list'),
]

确保在你的项目的 urls.py 中包含这些应用 URL。

现在,导航到 /myapp/upload/(或你配置的 URL)将显示表单。成功提交后,文件将保存在 MEDIA_ROOT/profile_pictures/ 中,并在数据库中创建一条 Profile 记录。

要上传除图片之外的文件,只需在模型中使用 models.FileField,在表单中使用 forms.FileField(如果你不使用 ModelForm)。过程的其余部分(设置、视图逻辑、模板 enctype)保持不变。