Django+imagekit上传图片时width字段违反非空约束的问题
问题描述
使用版本:Django5.2.12,django-imagekit6.1.0
我的PostImage模型代码如下:
class PostImage(models.Model): post = models.ForeignKey(Post, on_delete=models.CASCADE, null=True, related_name="images") image = ProcessedImageField(verbose_name=_("image"), storage=post_image_storage, upload_to=upload_to_post_image_directory, width_field="width", height_field="height", blank=False, null=True, format="JPEG", options={"quality": 100}, processors=[ResizeToFit(width=1024, upscale=False)]) width = models.PositiveIntegerField(editable=False, null=False, blank=False) height = models.PositiveIntegerField(editable=False, null=False, blank=False) hash = models.CharField(_("hash"), max_length=64, blank=False, null=True) thumbnail = ProcessedImageField(verbose_name=_("thumbnail"), storage=post_image_storage, upload_to=upload_to_post_image_directory, blank=False, null=True, format="JPEG", options={"quality": 50}, processors=[ResizeToFit(width=1024, upscale=False)]) media = GenericRelation(PostMedia) image_view_type = ( ("regularimage", "regularimage"), ("gridimages", "gridimages"), ) viewtype = models.CharField(max_length=24, choices=image_view_type, default="")
创建PostImage对象时触发错误:
null value in column "width" violates not null constraint.
因为后续逻辑需要用到width和height的计算值,不想把这两个字段设为null=True。测试时设为null=True后能创建对象,但width和height字段依然没有值。
错误栈详情:
django.db.utils.IntegrityError: null value in column "width" of relation "myapi_posts_postimage" violates not-null constraint my-api-webserver-0 | DETAIL: Failing row contains (1, posts/9957dc49-639b-490e-87f5-54cab35c0229/582a8f62-86d5-4c3d-a1..., null, null, 01d2d1ce86aa7007f1cecad2259394163b95987a850e6c2d6a48c322a2dc8df0, posts/9957dc49-639b-490e-87f5-54cab35c0229/33d944aa-ded1-49cb-a9..., regularimage, 1).
解决方案
这个问题是django-imagekit的ProcessedImageField未自动填充width/height字段导致的,可按以下方法解决:
调整字段定义顺序
将width和height字段移到image字段前面。Django模型字段的定义顺序会影响赋值逻辑,imagekit需要先存在对应的宽高字段,才能正确写入计算值。调整后的核心代码:class PostImage(models.Model): post = models.ForeignKey(Post, on_delete=models.CASCADE, null=True, related_name="images") width = models.PositiveIntegerField(editable=False, null=False, blank=False) height = models.PositiveIntegerField(editable=False, null=False, blank=False) image = ProcessedImageField(verbose_name=_("image"), storage=post_image_storage, upload_to=upload_to_post_image_directory, width_field="width", height_field="height", blank=False, null=True, format="JPEG", options={"quality": 100}, processors=[ResizeToFit(width=1024, upscale=False)]) # 剩余字段保持不变确保触发imagekit处理逻辑
如果是手动创建对象,不要直接赋值image后就save,而是先通过imagekit的工具方法处理图片再保存:from imagekit.utils import process_image # 假设image_file是上传的文件对象 post_image = PostImage(post=target_post) # 手动处理图片 processed_file = process_image(image_file, processors=[ResizeToFit(width=1024, upscale=False)], format="JPEG", options={"quality": 100}) # 保存处理后的图片,此时width/height会自动填充 post_image.image.save(processed_file.name, processed_file) post_image.save()排查自定义存储干扰
暂时替换post_image_storage为Django默认存储,测试是否是自定义存储的save方法干扰了imagekit的处理流程。如果是,需要调整自定义存储的逻辑,确保不阻断imagekit的图片处理步骤。用信号手动填充宽高
若以上方法无效,通过Django的pre_save信号在保存前手动计算图片尺寸:from django.db.models.signals import pre_save from django.dispatch import receiver from PIL import Image @receiver(pre_save, sender=PostImage) def fill_image_dimensions(sender, instance, **kwargs): if instance.image and not instance.width: with instance.image.open() as img_file: img = Image.open(img_file) instance.width, instance.height = img.size
内容的提问来源于stack exchange,提问作者Earthling
相关产品推荐
相关产品推荐

