[Django]-Django Form File Field disappears on form error

32👍

Unfortunately, this is a problem (really a security feature) imposed by browsers, and is not solvable, as such. Browsers will not let you specify an initial value for file inputs, and there’s nothing that you will be able to do to work around that.

The reason is that if a website could do this, it would open up a vector that would allow any website to steal any file on your computer by guessing file paths–they could just have a script running in the background that tried to post interesting files back to the server.

The only workaround is to actually save the uploaded file on the server regardless of whether the form validates, and then when you render the form and errors back to the user, indicate that you have received a file and that they should only fill out that field to replace it.

0👍

I write some solution:

class CustomClearableFileInput(ClearableFileInput):

def render(self, name, value, attrs=None):
    if len(<YourModel>.objects.filter(id=self.form_instance_id))>0:
        file = <YourModel>.objects.get(id=self.form_instance_id).<yourField>
    else:
        file = ''
    substitutions = {
        'initial_text': self.initial_text,
        'input_text': self.input_text,
        'clear_template': '',
        'clear_checkbox_label': self.clear_checkbox_label,
    }
    template = '%(input)s'
    substitutions['input'] = super(ClearableFileInput, self).render(name, value, attrs)
    self.template_with_initial = ('<p class="file-upload">%s</p>'
                        % self.template_with_initial)
    self.template_with_clear = ('<span class="clearable-file-input">%s</span>'
                       % self.template_with_clear)

    if value and hasattr(value, "url"):
        template = self.template_with_initial
        substitutions['initial'] = format_html(self.url_markup_template,
                                               value.url,
                                               force_text(value))
        if not self.is_required:
            checkbox_name = self.clear_checkbox_name(name)
            checkbox_id = self.clear_checkbox_id(checkbox_name)
            substitutions['clear_checkbox_name'] = conditional_escape(checkbox_name)
            substitutions['clear_checkbox_id'] = conditional_escape(checkbox_id)
            substitutions['clear'] = CheckboxInput().render(checkbox_name, False, attrs={'id': checkbox_id})
            substitutions['clear_template'] = self.template_with_clear % substitutions
            url = '' if file == '' else file.url
    else:
        template = self.template_with_initial

        substitutions['initial'] = format_html(self.url_markup_template,
                                               url,
                                               force_text(file))
        if not self.is_required:
            checkbox_name = self.clear_checkbox_name(name)
            checkbox_id = self.clear_checkbox_id(checkbox_name)
            substitutions['clear_checkbox_name'] = conditional_escape(checkbox_name)
            substitutions['clear_checkbox_id'] = conditional_escape(checkbox_id)
            if fav == '':
                substitutions['clear'] = CheckboxInput().render(checkbox_name, False, attrs={'id': checkbox_id, 'disabled': 'disabled'})
            else:
                substitutions['clear'] = CheckboxInput().render(checkbox_name, False, attrs={'id': checkbox_id})
            substitutions['clear_template'] = self.template_with_clear % substitutions



    return mark_safe(template % substitutions)

And then in your form you must write:

class <YourModel>Form(ModelForm):
    class Meta:
        model = <YourModel>
        fields = '__all__'
        widgets= {'<YourField>': CustomClearableFileInput}

    def __init__(self, *args, **kwargs):
        super(OperatorSettingsForm, self).__init__(*args, **kwargs)
        self.fields['<YourField>'].widget.form_instance_id = self.instance.id

It works for me. I think you will have no problem too 🙂

Leave a comment