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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 14:25:48