我有一个用户个人资料模型,其可选的头像看起来像
#models.py
class UserProfile(models.Model):
avatar = models.ImageField(upload_to=avatars null=True, blank=True)
.
.
.
然后是一个表格:
#forms.py
class UserProfileForm(forms.ModelForm):
avatar = forms.ImageField(required=False,....)
class Meta:
model = UserProfile
最后,包含
的视图#views.py
def edit_profile(....)
profile_obj = request.user.userprofile
form = UserProfile(data=request.POST, files=request.FILES, instance=profile_obj)
if form.is_valid():
form.save()
现在,当用于编辑用户的个人资料和头像的模板被渲染时,它包含一个Clear
复选框,当选中该选项时,用户将移除他们的头像照片。它将myuser.avatar
留在状态<ImageFieldFile: None>
中,但它不会删除站点本身存储区域中的文件(即jpg,png或其他)。我已经读到这是Django 1.6
中的设计,这一切都很好,但是我如何覆盖这个功能,以便确实删除文件?
从shell中没有问题:
from myapp.models import UserProfile
user1==UserProfile.objects.all()[0]
user1.avatar.delete()
也删除jpeg
。
修改
我尝试使用如下信号:
#models.py
.
.
.
@receiver(post_delete, sender=UserProfile)
def avatar_post_delete_handler(sender, **kwargs):
print 'DEBUG: avatar delete triggered'
avatar = kwargs['instance']
storage, path = avatar.original_image.storage, avatar.original_image.path
storage.delete(path)
但这甚至没有触发,我猜是因为我没有删除UserProfile
对象
当用户选择清除复选框时,完整地,而不仅仅是头像。
答案 0 :(得分:6)
像这样扩展ImageField并改为使用它:
class ImageField(models.ImageField):
def save_form_data(self, instance, data):
if data is not None:
file = getattr(instance, self.attname)
if file != data:
file.delete(save=False)
super(ImageField, self).save_form_data(instance, data)
如果您将旧文件替换为旧文件,则会删除旧文件,或将其标记为清除。 Here解释原因。
修改强>
还有一个应用django-smartfields,其中包含此功能以及更多功能,例如自动重新调整大小,自动转换图像和视频等等。它使用字段描述符和模型以更复杂的方式实现它定制。但它使用起来非常简单:
from smartfields import fields
class UserProfile(models.Model):
avatar = fields.ImageField(upload_to='avatars', blank=True)
它还会在以下情况下删除文件:
答案 1 :(得分:0)
根本不是最好的解决方案,但一种骇人的方式可能是为每个用户仅用他们的用户名存储文件,然后用空pre_save
捕获instance.avatar.name
信号并在用户所在的位置文件存在于预期位置的磁盘上,将其删除。呸。
class UserProfile(models.Model):
avatar = models.ImageField(upload_to=avatars null=True, blank=True)
和
#save the avatar for each user as their username
def update_filename(instance, filename):
path = "avatars"
format = instance.user.username
return os.path.join(path, format)
和
#if current instance has empty avatar.name
#and the file exists on disk where expected for user
#deduce user has clicked clear, delete file for user.
@receiver(pre_save, sender=UserProfile)
def avatar_pre_save_handler(sender, instance, **kwargs):
avatar_filepath = settings.MEDIA_ROOT +'/avatars/'+ instance.user.username
if not instance.avatar.name and os.path.isfile(avatar_filepath):
os.remove(avatar_filepath)
答案 2 :(得分:0)
另一种可能的方式(不需要特殊的文件命名)是覆盖表单保存方法,然后使用旧化身作为kwarg调用它:
#forms.py
class UserProfileForm(forms.ModelForm):
def save(self, commit=True, *args, **kwargs):
instance = super(UserProfileForm, self).save(commit=False)
old_avatar_name = kwargs.pop('old_avatar_name', None)
new_avatar_name = None
if self.cleaned_data['avatar']:
new_avatar_name = self.cleaned_data['avatar'].name
if old_avatar_name != new_avatar_name:
old_avatar_filepath = settings.MEDIA_ROOT +'/'+ old_avatar_name
if os.path.isfile(old_avatar_filepath):
os.remove(old_avatar_filepath)
if commit:
instance.save()
return instance
然后在视图中:
def edit_profile(request,....):
.
.
.
try:
profile_obj = request.user.userprofile
except ObjectDoesNotExist:
return HttpResponseRedirect(reverse('profiles_create_profile'))
if profile_obj.avatar.name:
avatar_kwargs={'old_avatar_name': profile_obj.avatar.name}
else:
avatar_kwargs={}
.
.
.
if form.is_valid():
form.save(**avatar_kwargs)
答案 3 :(得分:0)
使用下面的Mixin
class ImageDeleteMixin(object):
def delete(self, *args, **kwargs):
if self.avatar:
storage, path = self.avatar.storage, self.avatar.path
super(ImageDeleteMixin, self).delete(*args, **kwargs)
storage.delete(path)
else:
super(ImageDeleteMixin, self).delete(*args, **kwargs)
def save(self, *args, **kwargs):
if self.id:
old_instance = self.__class__._default_manager.get(pk=self.pk)
if (
old_instance.avatar != self.avatar and old_instance.avatar and
old_instance.avatar.path
):
storage, path = old_instance.avatar.storage, old_instance.avatar.path
super(ImageDeleteMixin, self).save(*args, **kwargs)
storage.delete(path)
return
return super(ImageDeleteMixin, self).save(*args, **kwargs)
class UserProfile(ImageDeleteMixin, models.Model):
avatar = models.ImageField(upload_to=avatars null=True, blank=True)
答案 4 :(得分:-1)
根据您的使用情况,您可以挂钩Django信号: https://docs.djangoproject.com/en/dev/ref/signals/#post-delete
或者,在视图中,当“已检查”变量被发送回服务器时,删除头像!