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फ़ोल्डर घोंसला बनाए रखता है, इसलिए मेरी फ़ोल्डर संरचना इस तरह दिखना शुरू होती है:

सेटिंग्स में मेरे चर सामान्य दिखेंगे, मेरा मानना ​​है:

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

मेरा मानना ​​है कि नेस्टिंग फोल्डर के साथ समस्या मेरी प्रोफाइल क्लास में विशेष रूप से इसे बचाने के तरीके के साथ उत्पन्न होती है:

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

Django में एक चीज़ है जिसे 'डुप्लिकेट सिग्नल' कहा जाता है। यह हर जगह होता है आपकी परियोजना मॉड्यूल को आयात करती है जहां आप संकेतों को परिभाषित करते हैं, क्योंकि सिग्नल पंजीकरण आयात किए जाने के दौरान कई बार चलता है।

हो सकता है कि आप अपने रिसीवर फ़ंक्शन को पहचानने के लिए 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/