Опция хранения Django FileField

У меня есть эта модель:

class UserProfile(models.Model):
    #..........
    photo = models.ImageField(upload_to = get_upload_file_name,
                              storage = OverwriteStorage(),
                              blank = True, null = True,
                              height_field = 'photo_height',
                              width_field = 'photo_width')

И это моя функция хранения:

class OverwriteStorage(FileSystemStorage):
    def _save(self, name, content):
        self.delete(r'.*')
        return super(OverwriteStorage, self)._save(name, content)

    def get_available_name(self, name):
        return name

Как я могу сделать следующие 2 вещи:

  1. Всякий раз, когда пользователь загружает файл (то есть изображение), я хочу удалить старый, независимо от того, совпадает ли имя или нет. Я пытался удалить все, что соответствует регулярному выражению выше, но это не работает.

  2. Если пользователь загружает изображение с именем "me.jpg", я хочу переименовать его по-разному, например, в зависимости от имени пользователя. Так что я сделаю что-то вроде return super(OverwriteStorage, self)._save(SOMETHING_ELSE_HERE, content) Как это сделать? я могу передать дополнительный параметр в функцию OverwriteStorage?

И еще один третий вопрос: я создал ModelForm для этой формы. Таким образом, пользователь может загрузить изображение. Поэтому, когда кто-то нажимает "выбрать файл", появляется окно с окном для просмотра и выбора фотографии. Как я могу отображать только определенные файлы здесь? (например, только файлы.jpg и.jpeg)

Спасибо!

РЕДАКТИРОВАТЬ: get_upload_file_name функция

def get_upload_file_name(instance, filename):
    return "%s/%s/profile_photo/%s" % (instance.user.username[0].lower(), instance.user.username, filename)

EDIT2: я включил мои models.py

import datetime
import os
import urllib2, urlparse
import re

from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.utils.translation import ugettext_lazy as _
from imagekit.models import ImageSpecField
from imagekit.processors import ResizeToFill
from django.core.files.storage import FileSystemStorage
from django.contrib.staticfiles import finders
from django.conf import settings
from django.core.files.base import ContentFile
from django.forms import widgets

now = datetime.datetime.now()

def get_upload_file_name(instance, filename):
    now = datetime.datetime.now()

    file_name = str(now.year)  + '_' + \
                str(now.month) + '_' + \
                str(now.day)   + '_' + \
                str(now.hour)  + '_' + \
                str(now.minute)+ '_' + \
                str(now.second)+ '.' + \
                filename.split('.')[-1]

    return "%s/%s/profile_photo/%s" % (instance.user.username[0].lower(),
                                       instance.user.username,
                                       file_name)

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

class UserProfileManager(models.Manager):

    def create_user_profile(self, user):
        user_profile = self.create(user = user)
        return user_profile

class UserProfile(models.Model):

    ### it is now.year - 13 because legitimate persons on this website should be over 14 years old
    YEARS = tuple(
                  zip([format(x,'04d') for x in range(now.year-120, now.year-13)],
                      [format(x,'04d') for x in range(now.year-120, now.year-13)]
                      )
                  )
    MONTHS = (
              ('January','January'),('February','February'),('March','March'),('April','April'),
              ('May','May'), ('June','June'),('July','July'),('August','August'),
              ('September','September'),('October','October'),('November','November'), ('December', 'December')

             )
    GENDERS = (('M', 'Male'), ('F', 'Female'))

    user = models.OneToOneField(User, related_name = 'MoreAboutUser', unique=True, verbose_name=_('user'))
    year_of_birth = models.CharField(max_length=10, blank = True,  null = True, choices=YEARS)
    month_of_birth = models.CharField(max_length=10, blank = True,  null = True, choices=MONTHS)
    gender = models.CharField(max_length=1, blank = True,  null = True, choices=GENDERS)
    photo = models.ImageField(upload_to = get_upload_file_name,
                              blank = True, null = True,
                              height_field = 'photo_height',
                              width_field = 'photo_width',
                              #widget = widgets.FileInput(attrs={'accept': 'image/gif,image/png,image/jpeg'})
                              )
    photo_height = models.PositiveIntegerField(blank = True, default = 0)
    photo_width = models.PositiveIntegerField(blank = True, default = 0)
    creation_time = models.DateTimeField(auto_now_add = True, auto_now = False)
    update_time = models.DateTimeField(auto_now_add = False, auto_now = True)

    class Meta:
            verbose_name = _('user profile')
            verbose_name_plural = _('user profiles')

    def __unicode__(self):
        return self.user.username

    objects = UserProfileManager()

    def get_profile_photo_url(self):
        if self.photo and hasattr(self.photo, 'url'):
            return self.photo.url
        else:
            return '/static/images/generic_profile_photo.jpg'

def create_user_profile(sender, instance, created, **kwargs):

    if created:
        try:
            profile = UserProfile.objects.create_user_profile(user = instance)
            profile.save()
        except:
            pass

post_save.connect(create_user_profile, sender=User)

1 ответ

API хранилища ничего не знает о вашей модели, поэтому он не может принимать во внимание значения других полей - он не знает имени старого файла, хранящегося в этом поле, и не знает, какой пользователь владеет записью модели.

Вы, кажется, на правильном пути с предоставлением upload_to = get_upload_file_name вариант вашего ImageField; get_upload_file_name Функция (которую вы не опубликовали в своем вопросе) сможет создать пользовательское имя файла для изображения, так как оно получает ссылку на экземпляр модели, поэтому оно знает пользователя, которому принадлежит экземпляр модели.

Что касается удаления старого файла, вам, вероятно, потребуется реализовать это в вашей модели. save() метод. Слишком поздно в этот момент найти старое имя файла из существующего экземпляра модели, так как его photo поле будет уже обновлено новым значением; Тем не менее вы можете извлечь копию существующей записи из базы данных и удалить старый файл через нее. Вот пример реализации:

class UserProfile(models.Model):

    ...

    def save(self, *args, **kwargs):

        # grab a copy of the old record from the database
        oldrec = type(self).objects.get(pk=self.pk)

        # save the current instance, and keep the result
        result = super(UserProfile, self).save(*args, **kwargs)

        # if the path is the same, the file is overwritten already
        # and no deletion is necessary
        if oldrec.photo.path != self.photo.path:
            oldrec.photo.delete()

        # return the result received from the parent save() method
        return result

Чтобы указать приемлемые типы для выбора файлов, вам нужно будет предоставить accept приписать <input> тег, который будет отображаться для вашего поля; вам нужно переопределить виджет в пользовательской форме.

Другие вопросы по тегам