Django Rest Framework:按账户等级生成不同尺寸图片的实现疑问
实现按会员等级生成不同分辨率图片的方案
1. 调整模型结构
首先修改Images模型,拆分原图与缩略图字段(避免覆盖原图),同时添加信号确保用户创建时自动生成Profile:
# models.py import os from django.db import models from django.utils import timezone from django.contrib.auth.models import User from django.db.models.signals import post_save from django.dispatch import receiver from PIL import Image def user_directory_path(instance, filename): return 'images/{0}'.format(filename) class Images(models.Model): title = models.CharField(max_length=250) # 存储上传的原图 original_image = models.ImageField(upload_to=user_directory_path) # 200px缩略图字段,兼容旧数据设为可空 thumbnail_200 = models.ImageField(upload_to=user_directory_path, blank=True, null=True) # 400px缩略图字段,仅Premium/Enterprise用户生成 thumbnail_400 = models.ImageField(upload_to=user_directory_path, blank=True, null=True) created = models.DateTimeField(default=timezone.now) author = models.ForeignKey(User, on_delete=models.PROTECT, related_name='author') def save(self, *args, **kwargs): # 第一步:先保存原图 super().save(*args, **kwargs) # 获取当前用户的会员等级 try: membership = self.author.profile.membership except Profile.DoesNotExist: membership = 'BASIC' # 打开原图准备生成缩略图 img = Image.open(self.original_image.path) name, ext = os.path.splitext(os.path.basename(self.original_image.name)) img_dir = os.path.dirname(self.original_image.path) # 生成200px缩略图(所有等级用户都需要) img_200 = img.copy() img_200.thumbnail((200, 200)) thumbnail_200_path = os.path.join(img_dir, f"{name}_200{ext}") img_200.save(thumbnail_200_path) self.thumbnail_200.name = f"images/{name}_200{ext}" # 为Premium/Enterprise用户生成400px缩略图 if membership in ['PREMIUM', 'ENTERPRISE']: img_400 = img.copy() img_400.thumbnail((400, 400)) thumbnail_400_path = os.path.join(img_dir, f"{name}_400{ext}") img_400.save(thumbnail_400_path) self.thumbnail_400.name = f"images/{name}_400{ext}" # 第二步:保存缩略图字段的更新 super().save(update_fields=['thumbnail_200', 'thumbnail_400']) class Profile(models.Model): MEMBERSHIP = ( ('BASIC', 'Basic'), ('PREMIUM', 'Premium'), ('ENTERPRISE', 'Enterprise') ) user = models.OneToOneField(User, on_delete=models.CASCADE) membership = models.CharField(max_length=10, choices=MEMBERSHIP, default='BASIC') def __str__(self): return f'{self.user.username} {self.membership} Profile' # 信号:用户创建时自动生成对应Profile @receiver(post_save, sender=User) def create_user_profile(sender, instance, created, **kwargs): if created: Profile.objects.create(user=instance) @receiver(post_save, sender=User) def save_user_profile(sender, instance, **kwargs): instance.profile.save()
2. 动态调整序列化器返回字段
根据当前请求用户的会员等级,返回对应权限的图片链接:
# serializers.py from rest_framework import serializers from blog.models import Images, Profile class ImagesSerializer(serializers.ModelSerializer): class Meta: model = Images fields = ('author', 'title') def to_representation(self, instance): representation = super().to_representation(instance) request = self.context.get('request') if not request: return representation # 获取当前用户的会员等级 try: membership = request.user.profile.membership except Profile.DoesNotExist: membership = 'BASIC' # 按等级返回对应图片链接 if membership == 'BASIC': if instance.thumbnail_200: representation['thumbnail_200'] = request.build_absolute_uri(instance.thumbnail_200.url) elif membership in ['PREMIUM', 'ENTERPRISE']: if instance.thumbnail_200: representation['thumbnail_200'] = request.build_absolute_uri(instance.thumbnail_200.url) if instance.thumbnail_400: representation['thumbnail_400'] = request.build_absolute_uri(instance.thumbnail_400.url) representation['original_image'] = request.build_absolute_uri(instance.original_image.url) return representation
3. 视图集传递请求上下文
确保序列化器能获取到request对象,用于生成绝对链接和判断会员等级:
# views.py from rest_framework import viewsets from blog.models import Images from blog.serializers import ImagesSerializer class ImagesViewSet(viewsets.ModelViewSet): queryset = Images.objects.all() serializer_class = ImagesSerializer def get_queryset(self): user = self.request.user return Images.objects.filter(author=user) def get_serializer_context(self): # 把request传递到序列化器上下文 context = super().get_serializer_context() context['request'] = self.request return context
4. 数据库迁移
修改模型后执行迁移命令更新数据库:
python manage.py makemigrations python manage.py migrate
关键说明
- 原图完整保留,缩略图以
原文件名_尺寸.扩展名格式存储,避免文件覆盖; - 所有等级用户都会生成200px缩略图,Premium/Enterprise用户额外生成400px缩略图并返回原图链接;
- 通过信号自动创建用户Profile,避免因Profile不存在导致的错误;
- 序列化器动态返回字段,确保不同等级用户仅能获取自身权限内的图片链接。
内容的提问来源于stack exchange,提问作者Robert Bielicki
相关产品推荐
相关产品推荐

