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

Django中使用CreateView创建实体时生成并保存条形码图片问题

解决Django中ItemEntity模型条形码图片无法关联到ImageField的问题

问题描述

我正在开发一个Django应用,需要为ItemEntity模型生成条形码并保存为图片,关联到模型的barcode字段。目前图片能成功生成并保存到指定文件夹,但查看实体时始终显示默认图片。

现有代码

models.py

class ItemEntity(models.Model):
    sub_type = models.ForeignKey(ItemSubType, on_delete=models.PROTECT)
    location = models.ForeignKey(WarehouseBin, on_delete=models.PROTECT)
    supplyer = models.ForeignKey(ItemSupplyers, on_delete=models.PROTECT)
    quantity = models.IntegerField()
    unit = models.ForeignKey(Units, on_delete=models.PROTECT)
    creation_date = models.DateTimeField(default=timezone.now)
    expiration_date = models.DateTimeField()
    time_of_consumption = models.DateTimeField(null=True)    
    barcode = models.ImageField(default='default_barcode.jpg', upload_to='media/barcodes')

    def __str__(self):
        return self.sub_type.name + " " + str(self.quantity) + " " + str(self.unit) 

    def save(self):
        super().save()
        
        datas=self.pk,self.location,self.sub_type # Datas to give to my barcode generator code
        self.img = Image.new('RGB',(1,1))   # Declaring an empty image variable
        BarcodeGenerator(datas,self) # Barcode generator the barcode it generates is put intu self.img variable

        self.img.save("/home/makiverem/projects/pina/media/media/barcodes/"+str(self.pk)+".jpg") # Saving the image to media/media/barcodes
        #I MISS SOMETHING FROM HERE I THINK
        self.barcode=self.img #Making the models barcode field equal to self.img

views.py

class ItemEntityCreateView(CreateView):
    model=ItemEntity
    form_class = ItemEntityForm


    def get_form_kwargs(self, *args, **kwargs):
        kwargs = super(ItemEntityCreateView, self).get_form_kwargs(*args, **kwargs)
        return kwargs

    def get_initial(self):
            return {
                "sub_type": self.kwargs.get('pk'),
                #"unit": self.kwargs.get('pk') #kérdés mi lenne a jobb megoldás itt urlben több argumentumot átadni?
            }
            
    def get_success_url(self):
        return reverse('whouse_itemsubtype_detail', kwargs={'pk' : self.object.sub_type.id})

    def form_valid(self,form):
        if form.is_valid():
            messages.success(request= self.request, message='Sikeres Módosítás.')
            form.save()
            return super().form_valid(form)
        else:
            messages.error(request= self.request, message='Sikertelen Módosítás.')
            return super().form_invalid(form) 

forms.py

class ItemEntityForm(ModelForm):

    def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
                if self.instance.pk == None:
                    self.fields['location'].queryset = WarehouseBin.objects.all().exclude(id__in=ItemEntity.objects.all().prefetch_related('location').values_list('location'))
                else:
                    self.fields['location'].queryset = WarehouseBin.objects.all().exclude(id__in=ItemEntity.objects.all().prefetch_related('location').values_list('location'))|(WarehouseBin.objects.all().filter(id= ItemEntity.objects.all().get(pk=self.instance.pk).location.id))
                    
    class Meta:    

        model = ItemEntity
        fields = ('sub_type', 'location', 'supplyer', 'quantity', 'unit', 'expiration_date') #

        
        labels = {
            'sub_type': 'Példány altípusa:',
            'location': 'Hely:',
            'supplyer': 'Beszállító',
            'quantity': 'Mennyiség:',
            'unit': 'Egység:',
            'expiration_date': 'Szavatossági idő:',
        }  

        widgets= {
            'expiration_date' : DateInput(attrs={'type': 'date'})
        }

问题原因

  1. save方法执行逻辑错误:先调用super().save()保存模型,之后修改barcode字段但未再次保存,导致字段变更无法写入数据库。
  2. ImageField赋值方式错误:Django的ImageField需要接收文件对象(或符合存储规范的路径),直接赋值PIL的Image实例无法被识别。
  3. 文件路径配置冗余:upload_to参数指定了media/barcodes,结合默认的MEDIA_ROOT会导致路径重复(如media/media/barcodes),不符合Django媒体文件管理规范。
  4. 视图中重复保存表单:form_valid中手动调用form.save()后又调用super().form_valid(form),会触发两次模型的save方法,造成冗余操作。

修复方案

1. 修改models.py的save方法及字段配置

首先导入必要模块:

from io import BytesIO
from django.core.files.uploadedfile import SimpleUploadedFile

然后调整ItemEntity模型:

class ItemEntity(models.Model):
    # 其他字段保持不变
    barcode = models.ImageField(default='default_barcode.jpg', upload_to='barcodes')  # 去掉冗余的media/

    # __str__方法不变

    def save(self, *args, **kwargs):
        # 新建对象时先保存一次获取pk(条形码文件名依赖pk)
        if not self.pk:
            super().save(*args, **kwargs)
        
        # 生成条形码
        datas = (self.pk, self.location, self.sub_type)
        self.img = Image.new('RGB', (1,1))
        BarcodeGenerator(datas, self)  # 假设该函数已将条形码写入self.img

        # 将PIL Image转换为Django可识别的文件对象
        buffer = BytesIO()
        self.img.save(buffer, format='JPEG')
        buffer.seek(0)

        # 构造文件名并赋值给barcode字段
        filename = f"{self.pk}.jpg"
        self.barcode.save(
            filename,
            SimpleUploadedFile(filename, buffer.read(), content_type='image/jpeg'),
            save=False  # 暂时不保存,最后统一提交
        )

        # 最后保存模型,将barcode字段的变更写入数据库
        super().save(*args, **kwargs)

2. 优化views.py的form_valid方法

移除重复的form.save()调用:

def form_valid(self, form):
    messages.success(self.request, 'Sikeres Módosítás.')
    return super().form_valid(form)

def form_invalid(self, form):
    messages.error(self.request, 'Sikertelen Módosítás.')
    return super().form_invalid(form)

关键修改说明

  • 调整save顺序:新建对象时先保存获取pk,确保条形码文件名唯一;生成图片后关联字段,最后统一保存。
  • 用BytesIO处理图片:将PIL生成的图片转换为内存文件对象,交给Django自动处理存储,避免手动操作文件路径出错。
  • 修正upload_to路径:仅保留barcodes,让Django自动拼接MEDIA_ROOT路径,避免重复嵌套。
  • 移除重复保存:super().form_valid(form)已包含表单保存逻辑,无需手动调用form.save()。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 11:18:38