Python Django model ImageField每次上载文件时都会创建一个嵌套文件夹

Python Django model ImageField每次上载文件时都会创建一个嵌套文件夹,python,django,django-models,Python,Django,Django Models,我肯定我错过了一些愚蠢的事情,但我很困惑 我的个人资料有以下型号: class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) image = models.ImageField( default="default.jpg", upload_to="profile_pics/",

我肯定我错过了一些愚蠢的事情,但我很困惑

我的个人资料有以下型号:

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    image = models.ImageField(
        default="default.jpg",
        upload_to="profile_pics/",
        validators=[FileExtensionValidator(["jpeg", "jpg", "png"])],
    )

    def __str__(self):
        return f"{self.user.username} Profile"

    def save(self, *args, **kwargs):
        if self.image:
            self.image = make_thumbnail(self.image, size=(200, 200))
            super().save(*args, **kwargs)
        else:
            super().save(*args, **kwargs)
但是
profile\u pics
文件夹继续嵌套,因此我的文件夹结构开始如下所示:

我相信settings.py中的变量看起来很正常:

BASE_DIR = Path(__file__).resolve().parent.parent
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = "/media/"
我认为嵌套文件夹的问题源于我的配置文件类中的save方法,具体如下:

def save(self, *args, **kwargs):
    if self.image:
        self.image = make_thumbnail(self.image, size=(200, 200))
        super().save(*args, **kwargs)
    else:
        super().save(*args, **kwargs)
这是由我的信号触发的:

@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)


@receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
    instance.profile.save()
为什么要嵌套文件夹

我在博客帖子图像上使用相同的保存方法,但文件夹没有嵌套

我错过了什么

注意:如果有帮助,这是制作缩略图:

from io import BytesIO
from django.core.files import File
from PIL import Image

def make_thumbnail(image, size=(600, 600)):
    im = Image.open(image)
    if im.format == "JPEG":
        im.convert("RGB")
        im.thumbnail(size)
        thumb_io = BytesIO()
        im.save(thumb_io, "JPEG", quality=85)
        image = File(thumb_io, name=image.name)
    else:
        im.convert("RGBA")
        im.thumbnail(size)
        thumb_io = BytesIO()
        im.save(thumb_io, "PNG", quality=85)
        image = File(thumb_io, name=image.name)
    return image
编辑:

我无意中想到了这个解决方案,它避免了主要问题,尽管我不确定它的效率有多高:

def save(self, *args, **kwargs):
    if self.image:
        self.image = make_thumbnail(self.image, size=(200, 200))
        image_name = self.image.name
        ext = image_name.split(".")[-1]
        filename = "%s.%s" % (uuid.uuid4(), ext)
        clean_name = os.path.join("", filename)
        self.image.name = clean_name
        super().save(*args, **kwargs)
    else:
        super().save(*args, **kwargs)

django中有一种叫做“复制信号”的东西。这在项目导入定义信号的模块的任何地方都会发生,因为信号注册的运行次数与导入的次数相同

也许您可以通过传递一个唯一标识符作为dispatch\u uid参数来识别接收方函数来解决问题

from django.core.signals import request_finished

request_finished.connect(my_callback, dispatch_uid="my_unique_identifier")

资料来源:

我打赌你一定想适应自己的需要。作者没有问题,因为他“没有重复使用他所存的东西”

self.image
具有属性
name
。当您检查它是否存在时(
如果self.image
),它已经有了一个名称。然后,每次更新都会不断调整已调整大小的图像的大小,这也会不断将现有图像名称添加到
upload_to
path,因此每次迭代都会将
upload_上传到
+
self.image.name
。但是
self.image.name
已经是
/profile\u pics/…

要解决此问题,只需添加调整大小的

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    image = models.ImageField(
        default="default.jpg",
        upload_to="profile_pics/",
        validators=[FileExtensionValidator(["jpeg", "jpg", "png"])],
    )
    is_resized = models.BooleanField(default=False)

    def __str__(self):
        return f"{self.user.username} Profile"

    def save(self, *args, **kwargs):
        if self.image and not self.is_resized:
            self.is_resized = True
            self.image = make_thumbnail(self.image, size=(200, 200))
        super().save(*args, **kwargs)
只要记住在图像更改时将
的大小设置为
False

旁注,一般来说,信号是一种不好的做法。我也不认为在同一个物体上有两个信号是个好主意

如果你真的需要它们,考虑用一个信号替换它们。< /P>

@receiver(post_save, sender=User)
def handle_user_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)
    else:
        instance.profile.save()

不过如果在视图中运行调整缩略图大小会更好。

profile\u pics/
upload\u上传到
选项中删除斜杠后尝试。i、 e set
upload\u to='profile\u pics'
我试过了,但没有改变行为。我相信每次我登录时都会发生这种情况(嵌套)…你尝试过“博客照片/个人资料图片”吗?不,在模型中将上载更改为并不能解决这个问题。我肯定在别的地方做错了什么,但我真的不知道该去哪里找。。。每次我登录时肯定会创建一个新的嵌套文件夹。您可以显示您的登录视图吗?非常感谢,但是这个问题仍然存在:为什么它会创建一个“配置文件”\u图片“上一个文件夹中嵌套的文件夹?即使在我需要使用信号的时候,我也被难住了,尤其是因为你说博客照片表现正常。只是,请确保发出信号的文件已正确封装,或在登录时未被任何其他模块导入。是的,我只是进行了三次检查,blog_照片表现良好,即使它使用相同的功能来减小图像大小(我只是更新了我的问题以了解更多细节)。我很困惑。。。当我从模型中删除“def save()”时,图像会正确上传(但当然不会调整大小)。哦,我可以开始理解嵌套的原因了!神奇的眼睛,谢谢!为了确定,我应该在哪里设置大小是否再次调整为False?当这种情况发生时,我们不是又在同一窝里跑了吗?嗨,汤姆。我在save方法中想出了一种不向表中添加额外列的方法。我不知道你是否同意,我很高兴听到你的想法。我把它贴在了我最初的问题的末尾。也许我可以通过让图片保存在媒体文件夹而不是上传到的“profile_pics/”中来避免这一切。每当用户更改他的profile pic时,Set
的大小都会调整为
False
,所以你需要在每次放置(或其他)时进行检查请输入您的一个视图。还有其他解决方案,不添加柱,fe<代码>如果不是self.image.name.endswith('resized'):调整大小并将'resized'添加到名称中
。您的解决方案无效,因为它仍然会随着每次
save()
调整大小,这肯定不是您想要的。请接受答案。但请记住,如果您在那里制作缩略图,则不需要添加调整大小的
列,
如果请求.FILES
。但这超出了这个问题的范围。