Как заставить форму Django сохранить файл после неудачной проверки - PullRequest
20 голосов
/ 23 июня 2010
form = AddItemForm(request.POST, request.FILES)

if form.is_valid()

   do_stuff

return render_to_response(blah.html, {'form':form})

Теперь форма будет содержать информацию об ошибке вместе с исходными значениями полей, но она не сохранит выбранный файл Как сохранить выбранный файл, если форма не проходит проверку?

Ответы [ 5 ]

10 голосов
/ 22 августа 2012

Попробуйте django-file-resubmit

insallation

pip install django-file-resubmit

settings.py

INSTALLED_APPS = {
    ...
    'sorl.thumbnail',
    'file_resubmit',
    ...
}

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
    },
    "file_resubmit": {
        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        "LOCATION": project_path('data/cache/file_resubmit')
    },
}

использование

from django.contrib import admin
from file_resubmit.admin import AdminResubmitMixin

class ModelAdmin(AdminResubmitMixin, ModelAdmin):
    pass

или

from django.forms import ModelForm
from file_resubmit.admin import AdminResubmitImageWidget, AdminResubmitFileWidget

class MyModelForm(forms.ModelForm)

    class Meta:
        model = MyModel
        widgets = {
            'picture': AdminResubmitImageWidget,
            'file': AdminResubmitFileWidget, 
        }
10 голосов
/ 07 сентября 2010

Проблема с тем, что вы хотите сделать, заключается в том, что по соображениям безопасности браузеры не позволяют окну ввода файла иметь предварительно выбранное значение при загрузке страницы.Это верно, даже если это просто сохранение значения из предыдущего экземпляра той же страницы.Django ничего не может сделать, чтобы изменить это.

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

3 голосов
/ 02 мая 2011

Хорошо, поэтому я сначала проголосовал и реализовал решение Narendra, прежде чем понял, что оно не может работать, поскольку javascript не может установить значения input type = "file". Увидеть: http://www.w3schools.com/jsref/prop_fileupload_value.asp

Но вот решение, которое работает:

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

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

models.py

class Book(models.Model):
    pdf = models.FileField("PDF", upload_to="books/")
    author_email = models.EmailField("Author Email")

class TempPDF(models.Model):
    pdf = models.FileField("PDF", upload_to="books/")

forms.py

from project_name.app_name.models import Book, TempPDF
from django.forms import ModelForm
from django.contrib.admin.widgets import AdminFileWidget

class BookForm(ModelForm):
    class Meta:
        model = Book
        exclude = ['pdf',]

class TempPDFForm(ModelForm):
    class Meta:
        model = TempPDF
        widgets = dict(pdf = AdminFileWidget) 
        # The AdminFileWidget will give a link to the saved file as well as give a prompt to change the file.
        # Note: be safe and don't let malicious users upload php/cgi scripts that your webserver may try running.

views.py

def new_book_form(request):
    if request.method == 'POST': 
        ## Always try saving file.  
        try: 
            temp_pdf_inst = TempPDF.objects.get(id=request.POST.has_key('temp_pdf_id'))
        except:  ## should really catch specific errors, but being quick
            temp_pdf_inst = None
        temp_pdf_form = TempPDFForm(request.POST, request.FILES, instance=temp_pdf_inst, prefix='temp_pdf')
        if temp_pdf_form.is_valid() and len(request.FILES) > 0:
            temp_pdf_inst = temp_pdf_form.save()
            temp_pdf_id = temp_pdf_inst.id
        book_form = BookForm(request.POST, prefix='book')

        if book_form.is_valid(): # All validation rules pass
            book = book_form.save(commit=False)
            book.pdf = temp_pdf_inst.pdf
            book.save()
            if temp_pdf_inst != None:
                temp_pdf_inst.delete()
            return HttpResponseRedirect('/thanks/') # Redirect after POST
    else:
        book_form = BookForm() # An unbound form
        temp_pdf_form = TempPDFForm()
        temp_pdf_id = None 
    return render_to_response('bookform_template.html',
                              dict(book_form = book_form,
                                   temp_pdf_form = temp_pdf_form,
                                   temp_pdf_id = temp_pdf_id)
                              )

bookform_template.html

<table>
  {{ book_form }}
  {{ temp_pdf_form }}
  <input type="hidden" name="temp_pdf_id" value="{{ temp_pdf_id }}">
</table>
2 голосов
/ 23 июня 2010

Django будет сохранять файл на диск самостоятельно, только если вы используете modelForm и вы успешно сохраните новый экземпляр этой модели, который имеет fileField.

В вашем случае вам нужно получить файл из словаря request.FILES и сохранить его на диске самостоятельно. Это должно выглядеть примерно так.

input_file = request.FILES['fileformfieldname']
new_file = open('/path/to/file.xxx')
new_file.write(input_file.read())

Теперь, когда у вас есть файл, сохраненный на диск, вам просто нужно запомнить путь к файлу, чтобы вы могли открыть его снова, когда пользователь повторно отправит сбойную форму.

1 голос
/ 09 сентября 2010

Немного сложно, но здесь есть логика.

  1. Наряду с вашим HTML-файлом ввода есть еще одно скрытое поле.
  2. Напишите некоторый JavaScript-код для хранения выбранного пути файла + именискрытое поле.
  3. Во время загрузки страницы ваш JavaScript должен проверить значение скрытого поля и установить его в поле «Файл», если оно есть.

При первой загрузке скрытое поле пустое, поэтому ничего не происходит.
При выборе файла он сохраняет путь + имя в скрытом поле.
При второй загрузке скрытое поле не пустое, поэтому будет установлен путь к файлу

Таким образом, вся грязная работапроисходит в браузере, и если у вас нет действительной формы, вам не нужно сохранять файл на сервере.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...