Comment faire pour qu'un formulaire Django conserve un fichier après avoir échoué la validation

form = AddItemForm(request.POST, request.FILES)

if form.is_valid()

   do_stuff

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

maintenant form aura l'information d'erreur avec les valeurs originales des champs, mais il ne conserve pas un fichier sélectionné Comment conserver le fichier sélectionné si la validation du formulaire échoue?

19
demandé sur bragboy 2010-06-23 03:48:04

5 réponses

le problème avec ce que vous voulez faire est que, pour des raisons de sécurité, les navigateurs ne permettront pas à une boîte de saisie de fichier d'avoir une valeur pré-sélectionnée sur la charge de page. Ceci est vrai même si cela préserve simplement la valeur d'une instance antérieure de la même page. Il n'y a rien que Django puisse faire pour changer cela.

Si vous voulez éviter de demander à l'utilisateur de choisir à nouveau et re-télécharger le fichier, vous aurez besoin d'enregistrer le fichier téléchargé, même si la validation échoue, et ensuite remplacer le fichier d'entrée terrain avec quelque chose pour indiquer que vous avez déjà données. Vous voulez aussi probablement aussi un bouton qui exécute JavaScript pour réactiver le champ Fichier si l'utilisateur veut mettre dans un fichier différent. Je ne pense pas que Django Vienne avec une machine pour ça, donc tu devras le coder toi-même.

10
répondu Walter Mundt 2010-09-07 19:50:33

django-fichier-retransmettre

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')
    },
}

utilisation

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

class ModelAdmin(AdminResubmitMixin, ModelAdmin):
    pass

ou

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, 
        }
9
répondu un1t 2012-08-22 11:08:57

Ok, donc j'ai tout d'abord révisé et implémenté la solution de Narendra avant de réaliser qu'elle ne pouvait pas fonctionner car javascript ne peut pas définir les valeurs input type="file". Voir: http://www.w3schools.com/jsref/prop_fileupload_value.asp

Mais voici une solution qui fonctionne:

  1. séparer le reste du formulaire des fichiers à télécharger.
  2. toujours sauvegarder le fichier (ou exécuter la validation pour voir si le fichier doit être sauvegardé) et sauvegarder sur temporaire modèle.
  3. dans le template, avoir un champ caché indiquant l'id de l'instance du model temporaire donc si vous changez le fichier le changement se propage. Vous pouvez obtenir plus de fichiers enregistrés sur votre serveur, mais vous pouvez nettoyer l'extérieur.
  4. lorsque le formulaire moins tous les fichiers peuvent être sauvegardés, sauvegardez-le, liez les fichiers sauvegardés au modèle original et supprimez le modèle de fichier téléchargé intermédiaire.

Ok, voici un croquis de la procédure, ce qui fonctionne pour moi d'une exemple où vous tentez de sauvegarder un pdf d'un livre avec une adresse e-mail (choisie car les e-mails ne valident pas parfois) d'un auteur.

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>
3
répondu dr jimbob 2011-05-02 19:23:59

Django ne sauvera le fichier sur le disque que si vous utilisez une forme de modèle et que vous enregistrez avec succès une nouvelle instance de ce modèle qui a un champ de fichier.

dans votre cas, ce que vous devez faire est d'obtenir le fichier de la requête.Dossiers dictionnaire, et le sauver sur le disque sur votre propre. Il devrait ressembler à quelque chose comme ça.

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

Maintenant que vous avez le fichier enregistré sur le disque, il suffit de se rappeler le chemin d'accès au fichier, de sorte que vous pouvez l'ouvrir à nouveau lorsque l'utilisateur renvoie la forme ratée.

2
répondu Apreche 2010-06-23 00:25:46

un peu délicat, mais voici la logique.

  1. avec votre fichier HTML, vous avez un autre champ caché.
  2. écrire du JavaScript pour conserver le chemin du fichier sélectionné + le nom dans ce champ caché.
  3. pendant le chargement de la page, votre JavaScript devrait vérifier la valeur du champ caché et la définir à File field, s'il en a un.

au premier chargement, le champ caché est vide, donc rien ne se passe.

Sur la sélection du fichier, il enregistre le chemin+le nom dans champ caché.

Lors de la seconde charge, le champ caché n'est pas vide, donc il va définir le chemin du fichier

de cette façon, tout le travail sale se passe dans le navigateur, et à moins que vous ayez un formulaire valide, Vous n'avez pas à sauvegarder le fichier sur le serveur.

1
répondu Narendra Kamma 2010-09-09 12:35:00