如何在Django REST Framework中实现图片上传与服务功能?
嘿,我来帮你搞定DRF通用视图结合图片上传的问题!其实这事儿没那么复杂,咱们一步步拆解来做:
第一步:定义带图片字段的模型
首先得有个存储图片的模型,用ImageField指定上传路径就行,记得先装Pillow处理图片哦:
# your_app/models.py from django.db import models class Image(models.Model): title = models.CharField(max_length=100, help_text="给图片加个标题吧") image = models.ImageField(upload_to='uploads/images/%Y/%m/%d/') # 按年月分类存储,更整洁 uploaded_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f"{self.title} - {self.uploaded_at.strftime('%Y-%m-%d')}"
然后安装依赖:
pip install pillow
还要在项目的settings.py里配置媒体文件的存储路径和访问URL:
# settings.py from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent # 媒体文件配置 MEDIA_ROOT = BASE_DIR / 'media' MEDIA_URL = '/media/'
最后在项目根路由里加媒体文件的访问路由(开发环境用,生产环境别这么搞):
# project/urls.py from django.conf import settings from django.conf.urls.static import static from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('api/', include('your_app.urls')), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
第二步:创建序列化器
序列化器要能处理文件上传,还能返回图片的完整访问URL,这样前端直接就能用:
# your_app/serializers.py from rest_framework import serializers from .models import Image class ImageSerializer(serializers.ModelSerializer): # 自定义字段返回完整图片URL image_url = serializers.SerializerMethodField() class Meta: model = Image fields = ['id', 'title', 'image', 'image_url', 'uploaded_at'] def get_image_url(self, obj): # 从上下文里拿request,用来生成绝对URL request = self.context.get('request') if obj.image and request: return request.build_absolute_uri(obj.image.url) return obj.image.url if obj.image else None # 可选:添加图片验证,限制大小和格式 def validate_image(self, value): # 限制2MB以内 if value.size > 2 * 1024 * 1024: raise serializers.ValidationError("图片大小不能超过2MB哦") # 只允许图片格式 allowed_types = ['image/jpeg', 'image/png', 'image/webp'] if value.content_type not in allowed_types: raise serializers.ValidationError("只能上传JPG、PNG或WebP格式的图片") return value
第三步:用通用视图实现上传+服务功能
这里推荐用ModelViewSet,一次搞定上传、列表、详情、更新、删除所有功能,比单独写多个通用视图省事多了:
# your_app/views.py from rest_framework import viewsets from rest_framework.permissions import IsAuthenticated # 可选:权限控制 from .models import Image from .serializers import ImageSerializer class ImageViewSet(viewsets.ModelViewSet): queryset = Image.objects.all().order_by('-uploaded_at') # 按上传时间倒序 serializer_class = ImageSerializer # 可选:只有登录用户才能操作,注释掉就是公开访问 # permission_classes = [IsAuthenticated] # 把request传到序列化器的上下文里,用来生成绝对URL def get_serializer_context(self): context = super().get_serializer_context() context['request'] = self.request return context
然后在app的路由里注册这个ViewSet:
# your_app/urls.py from django.urls import path, include from rest_framework.routers import DefaultRouter from .views import ImageViewSet router = DefaultRouter() router.register(r'images', ImageViewSet, basename='image') urlpatterns = [ path('', include(router.urls)), ]
这样你就拥有了这些API接口:
POST /api/images/:上传图片(form-data格式,传title和image字段)GET /api/images/:获取所有图片列表(带完整image_url)GET /api/images/<id>/:获取单张图片的详情PUT/PATCH /api/images/<id>/:更新图片的标题或替换图片DELETE /api/images/<id>/:删除图片
如果只需要部分功能,比如只上传和列表,也可以单独用通用视图:
# 示例:单独用CreateAPIView和ListAPIView from rest_framework.generics import CreateAPIView, ListAPIView class ImageUploadView(CreateAPIView): queryset = Image.objects.all() serializer_class = ImageSerializer def get_serializer_context(self): return {'request': self.request} class ImageListView(ListAPIView): queryset = Image.objects.all().order_by('-uploaded_at') serializer_class = ImageSerializer def get_serializer_context(self): return {'request': self.request}
对应的路由改成:
urlpatterns = [ path('images/upload/', ImageUploadView.as_view(), name='image-upload'), path('images/', ImageListView.as_view(), name='image-list'), ]
第四步:测试一下
直接用DRF自带的可视化界面访问http://localhost:8000/api/images/,就能上传图片、查看列表了。或者用curl命令测试上传:
curl -X POST -H "Content-Type: multipart/form-data" -F "title=我的风景照" -F "image=@/Users/me/Desktop/photo.jpg" http://localhost:8000/api/images/
生产环境注意事项
- 别用
static()处理媒体文件,用Nginx或Apache配置静态资源服务 - 可以用第三方存储服务,比如阿里云OSS、AWS S3,Django有对应的库支持
- 记得给
MEDIA_ROOT目录设置正确的读写权限
内容的提问来源于stack exchange,提问作者Mehi




