2012-04-01 16 views
13

Varsayılan Django admin ile basit bir fotoğraf galerisi oluşturmaya çalışıyorum. Her galeri için örnek bir fotoğraf kaydetmek istiyorum, ancak dosya adını saklamak istemiyorum. Dosya adı yerine, modelin kimliğini kaydetmek istiyorum (N.jpg). Ama ilk kez kaydetmek istediğim nesnenin kimliği mevcut değil. Modelde bir sonraki otomatik artışını nasıl bilebilirim veya bir şekilde model verilerini super.save ile karşıya yüklemeden önce ve self.id olduğunda dosya yükledikten sonra nasıl kaydedebilirim? Soğuk bir çözüm var mı? BöyleDjango admin dosya yükleme geçerli model numarasıyla eşleme

şey:

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) 

Ve belki farklı bir alanda dosya adı saklayın.

+4

Bu neden bir downvote hak ediyorum? Kesinlikle, bazılarından daha iyi bir kalite meselesi. – hop

+0

Bir sonraki kaydın kimliğini önceden bilmenin güvenilir bir yolu yoktur. Kayıt başlangıçta oluşturulduktan sonra kimliği alabilirsiniz, ancak bu da yarış koşullarına tabidir. Tavsiyem - dosyalarınızı adlandırmak için id'in yanı sıra başka bir şey seçin. – Brandon

+0

Örneğin, geçerli zaman damgası + microseconds – ilvar

cevap

9

Resim dosyası, Galeri örneği önünde kaydediliyor. Yani devlet taşıyan kendisi/Galeri örneği w sinyalleri kullanarak iki evreye tasarruf bölmek zorunda: Ben tip yerine FileField ait ImageField kullanmanızı öneririz

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) 

gibi

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 görünüyor alanın sadece resim yükleme için olup olmadığını kontrol etme. Ayrıca, ben de aynı sorun koştu

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

Çok teşekkür ederim! :) Benim tek yorumum: gönderen = Birisi başka biri bu çözümü kullanmaya çalışacaksa model nesnenin nesnesidir. –

+0

@KBalazs yardımcı olur, sadece kodu düzeltin, düzenlemeyi kontrol edin lütfen – okm

31

gibi (çünkü mime gereksiz olan) bir dosya adı uzantısına normalleştirmek isteyebilirsiniz. Okm'un cevabı beni doğru yola gönderdi ama bana göre save() yöntemini geçersiz kılarak aynı işlevselliği elde etmek mümkün.

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) 

Bu kesinlikle bilgileri doğru şekilde kaydeder.

+0

django 1.7'de bu bozuk mu? – Kukosk

+1

@Kukosk Django 1.7'de çalışıyor! – Ajoy

+3

Güzel çözüm! Bir süre sonra kwargs 'force_insert = True' ve ikinci kaydetme IntegrityError: (1062, "Duplicate entry") içerdiğinden ünite testlerini kırdığını fark ettim. Eğer kwargs.pop ('force_insert') 'i eklerseniz problemi çözer. – jurer

0

Django 1.7'de, önerilen çözümler benim için işe yaramadı, bu yüzden FileField alt sınıflarımı eski dosyaları kaldıran bir depolama alt sınıfı ile birlikte yazdım.

Depolama:

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 

ve benim upload_to callables şuna benzer:

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