Django 모델 ImageField는 파일을 업로드 할 때마다 중첩 폴더를 만듭니다.

Nov 29 2020

나는 어리석은 것을 놓치고 있다고 확신하지만 혼란 스럽습니다.

내 프로필에 다음 모델이 있습니다.

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_pics폴더가 계속 중첩되므로 폴더 구조가 다음과 같이 보이기 시작합니다.

settings.py의 내 변수는 정상적으로 보입니다.

BASE_DIR = Path(__file__).resolve().parent.parent
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = "/media/"

중첩 폴더의 문제는 내 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)

내 신호에 의해 트리거됩니다.

@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()

폴더가 중첩되는 이유는 무엇입니까?

블로그 게시물 이미지에 동일한 저장 방법을 사용하고 있는데 폴더가 중첩되지 않습니다.

내가 무엇을 놓치고 있습니까?

추신 : 도움이되는 경우 make_thumbnail입니다.

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)

답변

2 TomWojcik Nov 30 2020 at 01:13

이 게시물 을 귀하의 필요 에 맞게 조정 하고 싶었을 것입니다. 저자는 "저축 한 것을 재사용하지 않기"때문에 문제가 없습니다.

self.image속성이 name있습니다. 존재하는지 확인하면 ( if self.image) 이미 이름이있는 것입니다. 그런 다음 업데이트 할 때마다 이미 크기가 조정 된 이미지의 크기를 계속 조정하고 이미 기존 이미지 이름을 upload_to경로에 계속 추가 하므로 반복 할 때마다 upload_to+ self.image.name. 그러나 self.image.name이미 /profile_pics/...입니다.

이 문제를 해결하려면 is_resized열을 추가하기 만하면 됩니다.

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)

이미지가 변경 될 때마다 로 설정하는 is_resized것을 False잊지 마십시오.

참고로, 일반적으로 신호는 나쁜 습관입니다. 나는 또한 동일한 객체에서 작동하는 두 개의 신호를 갖는 것이 좋은 생각이라고 생각하지 않습니다.

정말로 필요한 경우 단일 신호로 교체하는 것이 좋습니다.

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

보기에서 축소판 크기를 조정하면 더 나을 것입니다.

2 AadeshDhakal Nov 29 2020 at 22:21

장고에는 '중복 신호'라는 것이 있습니다. 이는 신호 등록이 가져온 횟수만큼 실행되기 때문에 프로젝트가 신호를 정의하는 모듈을 가져 오는 모든 곳에서 발생합니다.

수신자 함수를 식별하기 위해 고유 식별자를 dispatch_uid 인수로 전달하여 문제를 해결할 수 있습니다.

from django.core.signals import request_finished

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

출처: https://docs.djangoproject.com/en/3.1/topics/signals/