【问题标题】:Django FileField storage optionDjango FileField 存储选项
【发布时间】:2014-03-28 11:42:16
【问题描述】:

我有这个模型:

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。所以用户可以上传图片。因此,当有人按下“选择文件”时,会弹出一个 Windows 窗口以浏览和选择照片。我怎样才能在这里只显示某些文件? (例如,仅 .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)

【问题讨论】:

    标签: python django django-models filefield imagefield


    【解决方案1】:

    存储 API 对您的模型一无所知,因此它不能考虑其他字段的值 - 它不知道存储在该字段中的旧文件的名称,也不知道哪个用户拥有该模型记录。

    为您的ImageField 提供upload_to = get_upload_file_name 选项,您似乎走在了正确的轨道上; 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
    

    要为您的文件选择指定可接受的类型,您需要为您的字段呈现的<input> 标记提供accept 属性;您需要以自定义表单覆盖小部件。

    【讨论】:

    • 对,我现在修改了get_upload_file_name,可以重命名输入文件了。但是,我不确定您的意思是在替换的情况下我应该做什么。但是我想到了一个想法:为什么不在 get_file_name 函数中删除旧图像呢? :) 对于小部件,我收到一个错误 :( 'widget' unexepcted
    • get_upload_file_name 函数中,您仍然无法确定模型实例是否会被保存(例如,如果它未通过验证,或者它的 save 方法从未被调用)。过早删除文件可能会给您留下一个缺少文件的陈旧记录。理想情况下,您应该只在保存新文件 之后删除旧文件(确保旧文件没有相同的名称,因此您实际上不会删除 new i> 文件)。您能否发布完整的 photo = models.ImageField(...) 定义,以便我可以帮助您解决意外的 widget 参数。
    • 我已经包含了我的 models.py。也许您也可以指导我如何删除旧图像。我理解这个想法,但我不知道该怎么做
    • 你是如何为你的模特准备表格的?您有UserProfile 模型的自定义表单吗?您需要一个来覆盖 photo 表单字段的小部件,显然您不能从模型字段本身覆盖它。
    猜你喜欢
    • 1970-01-01
    • 2013-03-19
    • 1970-01-01
    • 2020-05-10
    • 1970-01-01
    • 2011-02-10
    • 2019-02-17
    • 2017-08-02
    • 2011-09-08
    相关资源
    最近更新 更多