Загрузка файла django - изображение удаляется при обновлении редактируемого шаблона
Я использую django, чтобы позволить пользователю загружать изображения вместе с текстом для описания заголовка изображения.
Пользователь должен иметь возможность редактировать заголовок изображения и изменять файл изображения, используя шаблон редактирования. Это работает - в некоторой степени.
Я могу отобразить файл изображения и заголовок изображения в шаблоне редактирования для редактирования. У меня возникает проблема, когда я не включаю файл изображения во входной файл загрузки (пользователь хочет изменить только заголовок изображения, а не файл изображения), код, который у меня есть, меняет заголовок изображения, но файл изображения удаляется. из базы данных (физический файл образа не удаляется из файловой системы).
Я могу успешно обновить детали изображения, но пользователь должен включить новый файл изображения или тот же файл изображения в поле ввода загрузки файла для заголовка изображения, чтобы детали изображения были успешно обновлены.
Кажется, пользователь должен включить файл изображения в поле ввода загрузки файла. Я пытаюсь заставить мой код работать так же, как администратор django, где пользователю не нужно включать файл изображения каждый раз, когда пользователь меняет заголовок изображения.
Как разрешить пользователю обновлять детали изображения без необходимости повторно загружать файл изображения каждый раз, когда пользователь хочет изменить заголовок изображения?
Вот мой код models.py:
РЕДАКТИРОВАТЬ # 2 - добавлена FillableModelWithLanguageVersion & LanguageVersion
# START: ATTACHMENT DETAILS MODEL.
def _get_document_upload_location(instance, filename):
"""
Using a function instead of a lambda to make migrations happy. DO NOT remove or rename this function in the future, as it will break migrations.
@param instance: model instance that owns the FileField we're generating the upload filename for.
@param filename: Original file name assigned by django.
"""
return 'attachments/%d/%s' % (instance.user.id, uuid.uuid4())
class AttachmentDetails(models.Model, FillableModelWithLanguageVersion):
user = models.ForeignKey(User)
language_version = models.ForeignKey('LanguageVersion')
attachment_document = models.FileField(upload_to=_get_document_upload_location, null=True, blank=True)
attachment_title = models.CharField(null=False, blank=False, max_length=250)
attachment_timestamp_added = models.DateTimeField(auto_now_add=True, auto_now=False)
attachment_timestamp_updated = models.DateTimeField(auto_now=True, auto_now_add=False)
def __unicode__(self):
return unicode(self.user)
class Meta:
verbose_name = ('Attachment Detail')
verbose_name_plural = ('Attachment Details')
@staticmethod
def view_link():
return reverse(settings.MENU_DETAIL_LINK_ATTACHMENT_DETAILS)
@property
def language_name(self):
return LANGUAGES[self.language_version.language_code].name
@property
def language_name_english(self):
return LANGUAGES[self.language_version.language_code].name_english
@property
def language_name_native(self):
return LANGUAGES[self.language_version.language_code].name_native
# delete file when AttachmentDetails model is deleted
@receiver(post_delete,sender=AttachmentDetails,dispatch_uid='delete_attachment_details')
def delete_attachment_details(sender, **kwargs):
attachment_details = kwargs['instance']
attachment_details.attachment_document.delete(save=False)
# FINISH: ATTACHMENT DETAILS MODEL.
class FillableModelWithLanguageVersion(object):
def fill(self, fields):
self.language_version = LanguageVersion.objects.get(user=self.user, language_code=fields['language_code'])
for field, value in fields.iteritems():
if field == 'language_code':
continue
setattr(self, field, value)
class LanguageVersion(models.Model):
"""Language version selection for a user"""
user = models.ForeignKey(User)
language_code = models.CharField(max_length=32)
language_code_disabled = models.BooleanField(default=False)
language_version_timestamp_added = models.DateTimeField(auto_now_add=True, auto_now=False)
language_version_timestamp_updated = models.DateTimeField(auto_now=True, auto_now_add=False) # the date the language version is updated when the user changes their subscription type.
def __unicode__(self):
return unicode(self.language_code)
class Meta:
unique_together = ('user', 'language_code')
verbose_name = ('Language Versions')
verbose_name_plural = ('Language Versions')
Вот мой код views.py:
def attachment_details_edit(request, attachment_details_id):
try:
attachment_details = AttachmentDetails.objects.get(pk=attachment_details_id, user=request.user)
except AttachmentDetails.DoesNotExist:
return redirect(settings.MENU_DETAIL_LINK_ATTACHMENT_DETAILS)
language_versions = LanguageVersion.objects.filter(user=request.user).select_related('language_version')
available_languages = get_available_language_details(language_versions, request.user.userprofile.language_preference)
attachment_details_num = request.user.attachmentdetails_set.count()
language_code = attachment_details.language_version.language_code
language_code_disabled = attachment_details.language_version.language_code_disabled
language_preference = request.user.userprofile.language_preference
if language_code_disabled:
return redirect(settings.MENU_DETAIL_LINK_ATTACHMENT_DETAILS)
if request.method == 'GET':
language_code = attachment_details.language_version.language_code
form = AttachmentDetailsForm(
available_languages,
language_preference=request.user.userprofile.language_preference,
file_required=True,
initial=dict(
model_to_dict(attachment_details),
language_code=language_code
)
)
elif request.method == 'POST':
form = AttachmentDetailsForm(
available_languages,
language_preference,
False, # file_required
request.POST,
request.FILES
)
if form.is_valid():
cd = form.cleaned_data
if cd['attachment_document'] is not None:
# delete the existing uploaded attachment when user updates the existing attachment with a replacement attachment.
print 'removing previously uploaded file'
attachment_details.attachment_document.delete(save=False)
attachment_details.fill(cd)
attachment_details.save()
messages.success(request, _('successfully updated.'))
return redirect(settings.MENU_DETAIL_LINK_ATTACHMENT_DETAILS)
У меня есть соответствующий пост здесь.
редактировать
Вот мой код forms.py:
class AttachmentDetailsForm(forms.ModelForm):
required_css_class = 'required'
def __init__(self, available_languages, language_preference, file_required, *args, **kwargs):
"""
available_languages should be a valid choices list
"""
super(AttachmentDetailsForm, self).__init__(*args, **kwargs)
self.fields['language_code'] = forms.ChoiceField(choices=available_languages, initial=language_preference, label=_('Language'),)
self.fields['attachment_document'] = forms.FileField(label=_('Attachment'), required=file_required)
class Meta:
model = AttachmentDetails
fields = (
'attachment_title',
)
labels = {
'attachment_title': _('Attachment Title'),
}
def clean_attachment_document(self):
if self.cleaned_data['attachment_document'] is not None:
file_name = self.cleaned_data['attachment_document'].name.lower()
extension = file_name.split('.')[-1]
if extension not in settings.ALLOWED_ATTACHMENT_EXTENSIONS:
raise forms.ValidationError(_("Only file types .bmp, .gif, .jpg, .jpeg are permitted."))
# use the following if condition when the file size of the attachment is to be measured in MB - MB calculation.
#if self.cleaned_data['attachment_document'].size > settings.MAX_ATTACHMENT_FILE_SIZE_MB * 1024 * 1024:
# raise forms.ValidationError('Maximum permitted attachment size is: %d MB.' % settings.MAX_ATTACHMENT_FILE_SIZE_MB)
# use the following if condition when the file size of the attachment is to be measured in kB - kB calculation.
if self.cleaned_data['attachment_document'].size > settings.MAX_ATTACHMENT_FILE_SIZE_KB * 1024:
raise forms.ValidationError('Maximum permitted attachment size is: %d kB.' % settings.MAX_ATTACHMENT_FILE_SIZE_KB)
return self.cleaned_data['attachment_document']
2 ответа
Ваша проблема исходит от fill
метод вашей модели. Это всегда устанавливает attachment_document
, Чтобы предотвратить это, попробуйте это:
if form.is_valid():
cd = form.cleaned_data
attachment_document = cd.pop('attachment_document')
if attachment_document:
# delete the existing uploaded attachment when user updates the existing attachment with a replacement attachment.
print 'removing previously uploaded file'
attachment_details.attachment_document.delete(save=False)
attachment_details.attachment_document = attachment_document
attachment_details.fill(cd)
attachment_details.save()
messages.success(request, _('successfully updated.'))
return redirect(settings.MENU_DETAIL_LINK_ATTACHMENT_DETAILS)
Не пробовал, но похоже, что вы не установили аргумент экземпляра при публикации отредактированной формы. Вы можете попробовать это следующим образом, а затем просто вызвать метод save в форме:
elif request.method == 'POST':
form = AttachmentDetailsForm(
available_languages,
language_preference,
False, # file_required
request.POST,
request.FILES,
instance=attachment_details
)
if form.is_valid():
form.save()
messages.success(request, _('successfully updated.'))
return redirect(settings.MENU_DETAIL_LINK_ATTACHMENT_DETAILS)