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'}) }
问题原因
- save方法执行逻辑错误:先调用
super().save()保存模型,之后修改barcode字段但未再次保存,导致字段变更无法写入数据库。 - ImageField赋值方式错误:Django的
ImageField需要接收文件对象(或符合存储规范的路径),直接赋值PIL的Image实例无法被识别。 - 文件路径配置冗余:
upload_to参数指定了media/barcodes,结合默认的MEDIA_ROOT会导致路径重复(如media/media/barcodes),不符合Django媒体文件管理规范。 - 视图中重复保存表单:
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
相关产品推荐
相关产品推荐

