2012-04-01 13 views
13

के साथ अपलोड करें मैं डिफ़ॉल्ट Django व्यवस्थापक के साथ एक साधारण फोटो गैलरी बनाने की कोशिश कर रहा हूं। मैं प्रत्येक गैलरी के लिए नमूना फोटो सहेजना चाहता हूं, लेकिन मैं फ़ाइल नाम नहीं रखना चाहता हूं। फ़ाइल नाम के बजाय, मैं मॉडल की आईडी को सहेजना चाहता हूं (N.jpg)। लेकिन पहली बार जब मैं ऑब्जेक्ट को सहेजना चाहता हूं तो आईडी मौजूद नहीं है। मैं मॉडल में अगली ऑटो वृद्धि कैसे कर सकता हूं, या super.save के साथ अपलोड से पहले मॉडल डेटा को सहेज सकता हूं और फ़ाइल अपलोड करने के बाद self.id मौजूद है? क्या कोई अच्छा समाधान है?Django व्यवस्थापक फ़ाइल वर्तमान मॉडल आईडी

कुछ इस तरह:

def upload_path_handler(instance, filename): 
    ext = filename extension 
    return "site_media/images/gallery/{id}.{ext}".format(id=instance.nextincrement, ext=ext) 

class Gallery(models.Model): 
    name = models.TextField() 
    image = models.FileField(upload_to=upload_path_handler) 

और शायद एक अलग क्षेत्र में फ़ाइल नाम की दुकान।

+4

क्यों यह एक downvote लायक हैं? यह निश्चित रूप से कुछ की तुलना में बेहतर गुणवत्ता का सवाल है। – hop

+0

समय से पहले अगले रिकॉर्ड की आईडी जानने का कोई विश्वसनीय तरीका नहीं है। रिकॉर्ड शुरू होने के बाद आप आईडी प्राप्त कर सकते हैं, लेकिन यह भी दौड़ की स्थिति के अधीन है। मेरी सलाह - आईडी के अलावा अपनी फाइलों को नाम देने के लिए कुछ और चुनें। – Brandon

+0

उदाहरण के लिए, वर्तमान टाइमस्टैम्प + माइक्रोसेकंड – ilvar

उत्तर

9

छवि फ़ाइल गैलरी इंस्टेंस से पहले सहेजी जाती है। तो अगर आप अपने आप w/गैलरी उदाहरण संकेतों का उपयोग कर राज्य को ले जाने से दो चरणों के लिए बचत विभाजित करने के लिए है:

from django.db.models.signals import post_save, pre_save 
from django.dispatch import receiver 

_UNSAVED_FILEFIELD = 'unsaved_filefield' 

@receiver(pre_save, sender=Image) 
def skip_saving_file(sender, instance, **kwargs): 
    if not instance.pk and not hasattr(instance, _UNSAVED_FILEFIELD): 
     setattr(instance, _UNSAVED_FILEFIELD, instance.image) 
     instance.image = None 

@receiver(post_save, sender=Image) 
def save_file(sender, instance, created, **kwargs): 
    if created and hasattr(instance, _UNSAVED_FILEFIELD): 
     instance.image = getattr(instance, _UNSAVED_FILEFIELD) 
     instance.save()   
     # delete it if you feel uncomfortable... 
     # instance.__dict__.pop(_UNSAVED_FILEFIELD) 

upload_path_handler लग रहा है

तरह
def upload_path_handler(instance, filename): 
    import os.path 
    fn, ext = os.path.splitext(filename) 
    return "site_media/images/gallery/{id}{ext}".format(id=instance.pk, ext=ext) 

मैं type- के लिए FileField के बजाय ImageField उपयोग करने का सुझाव यह जांचना कि फ़ील्ड केवल छवि अपलोड करने के लिए है या नहीं। इसके अलावा, आप की तरह

def normalize_ext(image_field): 
    try: 
     from PIL import Image 
    except ImportError: 
     import Image 
    ext = Image.open(image_field).format 
    if hasattr(image_field, 'seek') and callable(image_field.seek): 
     image_field.seek(0) 
    ext = ext.lower() 
    if ext == 'jpeg': 
     ext = 'jpg' 
    return '.' + ext 
+0

आपको बहुत बहुत धन्यवाद! :) मेरी एकमात्र टिप्पणी है: प्रेषक = छवि मॉडल ऑब्जेक्ट का ऑब्जेक्ट है यदि कोई और इस समाधान का उपयोग करने का प्रयास करेगा। –

+0

@KBalazs खुशी है कि यह मदद करता है, बस कोड को ठीक, संपादित करें कृपया – okm

31

मैं एक ही समस्या में पड़ गए फ़ाइल नाम एक्सटेंशन को सामान्य बनाने में (जो माइम प्रकार की वजह से अनावश्यक है) कर सकते हैं। ओकेएम के जवाब ने मुझे सही रास्ते पर भेजा लेकिन मुझे लगता है कि save() विधि को ओवरराइड करके समान कार्यक्षमता प्राप्त करना संभव है।

def save(self, *args, **kwargs): 
    if self.pk is None: 
     saved_image = self.image 
     self.image = None 
     super(Material, self).save(*args, **kwargs) 
     self.image = saved_image 

    super(Material, self).save(*args, **kwargs) 

यह निश्चित रूप से जानकारी को सही ढंग से बचाता है।

+0

इस Django 1.7 में टूट गया है की जाँच? – Kukosk

+1

@ कुकोस्क यह Django 1.7 में काम करता है! – Ajoy

+3

अच्छा समाधान! थोड़ी देर के बाद मैंने देखा है कि यह इकाई परीक्षण पर टूट जाता है, के रूप में kwargs शामिल 'force_insert = सच' और दूसरा IntegrityError में परिणाम को बचाने: (1062, "डुप्लीकेट प्रवेश")। अगर ब्लॉक के अंत में kwargs.pop ('force_insert') जोड़ना समस्या हल करता है। – jurer

0

Django 1.7 में, प्रस्तावित समाधान मेरे लिए काम नहीं लगता था, तो मैं एक भंडारण उपवर्ग जो पुरानी फ़ाइलों को हटा के साथ मेरी FileField उपवर्गों लिखा था।

संग्रहण:

class OverwriteFileSystemStorage(FileSystemStorage): 
    def _save(self, name, content): 
     self.delete(name) 
     return super()._save(name, content) 

    def get_available_name(self, name): 
     return name 

    def delete(self, name): 
     super().delete(name) 

     last_dir = os.path.dirname(self.path(name)) 

     while True: 
      try: 
       os.rmdir(last_dir) 
      except OSError as e: 
       if e.errno in {errno.ENOTEMPTY, errno.ENOENT}: 
        break 

       raise e 

      last_dir = os.path.dirname(last_dir) 

FileField:

def tweak_field_save(cls, field): 
    field_defined_in_this_class = field.name in cls.__dict__ and field.name not in cls.__bases__[0].__dict__ 

    if field_defined_in_this_class: 
     orig_save = cls.save 

     if orig_save and callable(orig_save): 
      assert isinstance(field.storage, OverwriteFileSystemStorage), "Using other storage than '{0}' may cause unexpected behavior.".format(OverwriteFileSystemStorage.__name__) 

      def save(self, *args, **kwargs): 
       if self.pk is None: 
        orig_save(self, *args, **kwargs) 

        field_file = getattr(self, field.name) 

        if field_file: 
         old_path = field_file.path 
         new_filename = field.generate_filename(self, os.path.basename(old_path)) 
         new_path = field.storage.path(new_filename) 
         os.makedirs(os.path.dirname(new_path), exist_ok=True) 
         os.rename(old_path, new_path) 
         setattr(self, field.name, new_filename) 

        # for next save 
        if len(args) > 0: 
         args = tuple(v if k >= 2 else False for k, v in enumerate(args)) 

        kwargs['force_insert'] = False 
        kwargs['force_update'] = False 

       orig_save(self, *args, **kwargs) 

      cls.save = save 


def tweak_field_class(orig_cls): 
    orig_init = orig_cls.__init__ 

    def __init__(self, *args, **kwargs): 
     if 'storage' not in kwargs: 
      kwargs['storage'] = OverwriteFileSystemStorage() 

     if orig_init and callable(orig_init): 
      orig_init(self, *args, **kwargs) 

    orig_cls.__init__ = __init__ 

    orig_contribute_to_class = orig_cls.contribute_to_class 

    def contribute_to_class(self, cls, name): 
     if orig_contribute_to_class and callable(orig_contribute_to_class): 
      orig_contribute_to_class(self, cls, name) 

     tweak_field_save(cls, self) 

    orig_cls.contribute_to_class = contribute_to_class 

    return orig_cls 


def tweak_file_class(orig_cls): 
    """ 
    Overriding FieldFile.save method to remove the old associated file. 
    I'm doing the same thing in OverwriteFileSystemStorage, but it works just when the names match. 
    I probably want to preserve both methods if anyone calls Storage.save. 
    """ 

    orig_save = orig_cls.save 

    def new_save(self, name, content, save=True): 
     self.delete(save=False) 

     if orig_save and callable(orig_save): 
      orig_save(self, name, content, save=save) 

    new_save.__name__ = 'save' 
    orig_cls.save = new_save 

    return orig_cls 


@tweak_file_class 
class OverwriteFieldFile(models.FileField.attr_class): 
    pass 


@tweak_file_class 
class OverwriteImageFieldFile(models.ImageField.attr_class): 
    pass 


@tweak_field_class 
class RenamedFileField(models.FileField): 
    attr_class = OverwriteFieldFile 


@tweak_field_class 
class RenamedImageField(models.ImageField): 
    attr_class = OverwriteImageFieldFile 

और मेरे upload_to callables इस तरह दिखेगा:

def user_image_path(instance, filename): 
    name, ext = 'image', os.path.splitext(filename)[1] 

    if instance.pk is not None: 
     return os.path.join('users', os.path.join(str(instance.pk), name + ext)) 

    return os.path.join('users', '{0}_{1}{2}'.format(uuid1(), name, ext)) 
संबंधित मुद्दे