2012-04-01 29 views
13

Tôi đang cố tạo một thư viện ảnh đơn giản với quản trị viên Django mặc định. Tôi muốn lưu một bức ảnh mẫu cho mỗi bộ sưu tập, nhưng tôi không muốn giữ lại tên. Thay vì tên tệp, tôi muốn lưu id của mô hình (N.jpg). Nhưng lần đầu tiên tôi muốn lưu đối tượng, id không tồn tại. Làm cách nào tôi có thể biết số gia tăng tự động tiếp theo trong mô hình hoặc bằng cách nào đó lưu dữ liệu mô hình trước khi tải lên với super.save và sau khi tải tệp lên khi có self.id? Có một giải pháp mát mẻ?Tải lên tệp quản trị Django với id mô hình hiện tại

Something như thế này:

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) 

Và có lẽ lưu trữ các tên tập tin trong một lĩnh vực khác nhau.

+4

Tại sao điều này sẽ xứng đáng với một downvote? Nó chắc chắn là một câu hỏi có chất lượng tốt hơn so với một số. – hop

+0

Không có cách nào đáng tin cậy để biết id của hồ sơ tiếp theo trước thời hạn. Bạn có thể nhận được id sau khi bản ghi được tạo ban đầu, nhưng điều này cũng phải tuân theo điều kiện chủng tộc. Lời khuyên của tôi - chọn một thứ khác ngoài id để đặt tên tệp của bạn. – Brandon

+0

Ví dụ: dấu thời gian hiện tại + micro giây – ilvar

Trả lời

9

Tệp hình ảnh được lưu trước phiên bản Thư viện. Vì vậy, bạn phải chia tiết kiệm đến hai giai đoạn bằng cách sử dụng tín hiệu w Gallery dụ/bản thân mang tiểu bang:

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) 

Các upload_path_handler trông giống như

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) 

tôi đề nghị sử dụng ImageField thay vì FileField cho loại- kiểm tra xem trường có dành cho tải lên hình ảnh không. Ngoài ra, bạn có thể muốn mở rộng tên tệp bình thường (không cần thiết vì mimetype) như

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

Cảm ơn bạn rất nhiều! :) Nhận xét duy nhất của tôi là: người gửi = Hình ảnh là đối tượng của đối tượng mô hình nếu ai đó khác sẽ cố gắng sử dụng giải pháp này. –

+0

@KBalazs vui vì nó giúp, chỉ cần sửa mã, kiểm tra chỉnh sửa xin vui lòng – okm

31

Tôi đã gặp sự cố tương tự. Câu trả lời của Okm đã gửi cho tôi trên con đường bên phải nhưng có vẻ như tôi có thể có được cùng một chức năng bằng cách chỉ ghi đè phương thức 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) 

Điều này chắc chắn lưu thông tin chính xác.

+0

là điều này bị hỏng trong django 1.7? – Kukosk

+1

@Kukosk Điều này hoạt động ở Django 1.7! – Ajoy

+3

Giải pháp tuyệt vời! Sau một thời gian tôi nhận thấy rằng nó phá vỡ trên các bài kiểm tra đơn vị, như kwargs chứa 'force_insert = True' và thứ hai lưu kết quả trong IntegrityError: (1062, "Duplicate entry"). Thêm kwargs.pop ('force_insert') vào cuối khối nếu giải quyết được vấn đề. – jurer

0

Trong django 1.7, các giải pháp được đề xuất dường như không hoạt động đối với tôi, vì vậy tôi đã viết các lớp con FileField của mình cùng với một lớp con lưu trữ loại bỏ các tệp cũ.

Bảo quản:

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 

và callables upload_to của tôi trông như thế này:

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)) 
Các vấn đề liên quan