Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to load an initial value for a ClearableFileInput / FileField (before form is bound)

How do I set an initial value for a FileField that will display in a ClearableFileInput widget when creating a new object (unbound form)?

I have tried the following but widget.value does not return a FeildFile instance if it is the first time the user is creating the object:

models.py

class MyModel(models.Model):
    myfile=models.FileField()

forms.py

class MyForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['myfile'].initial= 'myfile.pdf'

    class Meta:
        model = Issuer
        fields = ['myfile']

This results in :

enter image description here

Similarly, setting a default value in the modelfield does not work:

class MyModel(models.Model):
    myfile=models.FileField(default='myfile.pdf')

The widget initial value is still None, but if it is left empty and saved the file object myfile.pdf will be created. The settings.MEDIA_URL and urls.py is definitely correct and the file is on the system because it is loaded after form save.

What I am missing is showing it as an initial value before a form is saved and an object created.

This answer suggests you can't provide initial data but you can provide an initial value with a url attribute to fake the appearance of a file. It's not clear how you would do this though.

Trying to create an initial file object in the form also returns widget.value = None

class MyForm(forms.ModelForm):

    f_path = os.path.join(settings.BASE_DIR + settings.MEDIA_URL, 'myfile.pdf')

    f = open(f_path)
    myfile = File(f)

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['privacy_policy'].initial = self.myfile
like image 818
alias51 Avatar asked Nov 07 '22 07:11

alias51


2 Answers

Your last block of code is quite close.

You are actually telling the user through the ClearableFileInput widget that if the user does not input anything in the file input, the initial value will be your pdf file.

But, this is only for display, you have to actually put the logic when you save the instance! (FileField won't do it for you) So the right thing is to override the save() method of your form, and in the case of a creation, put the actual file as the value. Like this:

class MyForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        path = os.path.join(settings.MEDIA_ROOT, 'myfile.pdf')

        self.default_file = open(path)

        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['privacy_policy'].initial = File(self.default_file)
        self.fields['privacy_policy'].initial.url = default_storage.url('myfile.pdf')

    def save(self, commit=True):
        # Actually put the default file as a value if no input for creation
        if not self.instance.pk and not self.cleaned_data.get('privacy_policy'):
            self.instance.privacy_policy = self.default_file
            self.instance.privacy_policy._committed = True
        super().save(commit)
like image 95
SebCorbin Avatar answered Nov 14 '22 12:11

SebCorbin


You can't. The only way to set the value of a file input is by the user to select it. This is done for security reasons. Otherwise, you would be able to create a Javascript that automatically uploads a specific file from the client's computer. it has nothing to with Django (model, form) its a browser feature, we only do the things with Django form if it is possible in Html and javascript.

your form:

class MyForm(forms.ModelForm):

f_path = os.path.join(settings.BASE_DIR + settings.MEDIA_URL, 'myfile.pdf')

f = open(f_path)
myfile = File(f)

def __init__(self, *args, **kwargs):
    super(MyForm, self).__init__(*args, **kwargs)
    self.fields['privacy_policy'].value = self.myfile

the default ClearableFileField template is

{% if widget.is_initial %}{{ widget.initial_text }}: <a href="{{ widget.value.url }}">{{ widget.value }}</a>{% if not widget.required %}
<input type="checkbox" name="{{ widget.checkbox_name }}" id="{{ widget.checkbox_id }}">
<label for="{{ widget.checkbox_id }}">{{ widget.clear_checkbox_label }}</label>{% endif %}<br>
{{ widget.input_text }}:{% endif %}
<input type="{{ widget.type }}" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>

The widget is_initial should be true to see the link.

class ClearableFileInput(FileInput):
    ...

    def is_initial(self, value):
        """
        Return whether value is considered to be initial value.
        """
        return bool(value and getattr(value, 'url', False))

     ....
like image 40
Naqib Hakimi Avatar answered Nov 14 '22 11:11

Naqib Hakimi