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

如何在Django POST请求中添加多张Base64编码图片

问题描述

我需要发起POST请求创建Bot实例,同时传递多张Base64编码的photo_examples图片。默认的Base64ImageField仅支持单图上传,我已搭建好多图上传的结构,但提交指定格式的请求时,出现photo_examples的bot字段必填错误,相关代码及请求、错误信息如下:

models.py

class Bot(models.Model):
    """Bot Model."""
    name = models.CharField(
        'Название',
        max_length=100
    )
    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        verbose_name='Автор',
    )
    description = models.TextField(
        'Описание'
    )
    categories = models.ManyToManyField(
        Category,
        through='CategoryBot',
        related_name='bots',
        verbose_name='Категории',
    )
    main_photo = models.ImageField(
        'Фото',
        upload_to='uploads/main/%Y/%m/%d/',
    )
    price = models.DecimalField(
        'Цена',
        max_digits=8,
        decimal_places=0
    )
    is_special_offer = models.BooleanField(
        'Спецпредложение',
        default=False
    )

    class Meta:
        ordering = ('name',)
        verbose_name = 'Бот'
        verbose_name_plural = 'Боты'

    def __str__(self):
        return self.name


class Photo(models.Model):
    """Model of photos of examples of the bot's work."""
    photo_examples = models.ImageField(
        'Фото(образцы)',
        upload_to='uploads/examples/%Y/%m/%d/',
    )
    bot = models.ForeignKey(
        Bot,
        on_delete=models.CASCADE,
        verbose_name='Бот',
        related_name='photo_examples'
    )

    class Meta:
        ordering = ('bot',)
        verbose_name = 'Фото(образцы)'
        verbose_name_plural = 'Фото(образцы)'

    def __str__(self):
        return f' Фото бота {self.bot}'

serializers.py

class PhotoSerializer(serializers.ModelSerializer):
    """Serializer of photo bot examples"""
    photo_examples = Base64ImageField()

    class Meta:
        model = Photo
        fields = '__all__'


class CategoryNameSerializer(serializers.PrimaryKeyRelatedField):
    def to_representation(self, value):
        return value.name


class AbstractBotSerializer(serializers.ModelSerializer):
    """
    An abstract class that defines serialization and
deserialization behavior for bot-related data.
    """
    photo_examples = PhotoSerializer(many=True)
    author = serializers.StringRelatedField(read_only=True)
    discount_author = serializers.SerializerMethodField()
    discount_category = serializers.SerializerMethodField()
    amount_discounts_sum = serializers.SerializerMethodField()
    final_price = serializers.SerializerMethodField()
    categories = CategoryNameSerializer(
        required=False,
        queryset=Category.objects.all(),
        many=True,
    )

    class Meta:
        abstract = True
        fields = '__all__'

views.py

class BotViewSet(ModelViewSet):
    """The viewset for the bot.
    With the possibility of pagination, search by name
    and description, filtering by category"""
    queryset = Bot.objects.all()
    serializer_class = BotSerializer
    pagination_class = LimitPageNumberPagination
    filter_backends = (filters.SearchFilter, DjangoFilterBackend, )
    search_fields = ('name', 'description')
    permission_classes = (IsAuthorOrReadOnly, IsAuthor,)
    filterset_fields = ('categories', )

    def get_serializer_class(self):
        if self.action == 'retrieve':
            return BotReviewRatingSerializer
        return self.serializer_class

    def perform_create(self, serializer):
        discount_author = self.request.data.get("discount_author", 0)

        bot_instance = serializer.save(author=self.request.user)

        bot_discount, created = BotDiscount.objects.get_or_create(
            bot=bot_instance, defaults={"discount": discount_author}
        )

        if not created:
            bot_discount.discount = discount_author
            bot_discount.save()

期望POST请求格式

{
    "name": "My Bot New",
    "description": "This is my bot",
    "main_photo": "data:image/jpeg;base64,/9j/4A....A/9k=",
    "price": 500,
    "discount_author": 35,
    "categories": [6, 4],
    "photo_examples": [
        {
        "photo_examples": "data:image/jpeg;base64,/9j/4A....A/9k="
        },
        {
        "photo_examples": "data:image/jpeg;base64,/9j/4A....A/9k="
        }
    ]
}

当前错误信息

{
    "photo_examples": [
        {
            "bot": [
                "Required field"
            ]
        },
        {
            "bot": [
                "Required field."
            ]
        }
    ]
}

解决方法

1. 修改PhotoSerializer,标记bot字段为只读

由于Bot实例还未创建时无法提供有效的bot ID,需要让序列化器跳过对bot字段的前端验证,改为后端自动关联:

class PhotoSerializer(serializers.ModelSerializer):
    """Serializer of photo bot examples"""
    photo_examples = Base64ImageField()
    bot = serializers.PrimaryKeyRelatedField(read_only=True)  # 添加此行,设为只读

    class Meta:
        model = Photo
        fields = '__all__'

2. 在BotSerializer中重写create方法,处理嵌套图片数据

AbstractBotSerializer是抽象类,实际使用的BotSerializer需要继承它并手动处理嵌套的photo_examples数据,在Bot创建完成后关联图片:

class BotSerializer(AbstractBotSerializer):
    class Meta(AbstractBotSerializer.Meta):
        model = Bot

    def create(self, validated_data):
        # 从验证后的数据中取出图片列表,先移除避免干扰Bot创建
        photo_examples_data = validated_data.pop('photo_examples', [])
        # 调用父类方法创建Bot实例
        bot = super().create(validated_data)
        # 批量创建Photo实例并关联当前Bot
        for photo_data in photo_examples_data:
            Photo.objects.create(bot=bot, **photo_data)
        return bot

3. 保留原有视图逻辑

perform_create方法无需修改,它会在Bot实例创建完成后自动处理折扣相关逻辑,不影响图片关联流程。


经过以上修改,前端提交请求时无需传递bot字段,后端会在创建Bot实例后自动将所有示例图片与该Bot关联,解决必填字段错误的同时实现多图上传功能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 00:54:54