Details

Include hidden fields

Make sure that hidden form fields are included:

{% for hidden in form.hidden_fields %}
    {{ hidden }}
{% endfor %}

NB: it's possible that the hidden fields are already included; for example if you use form.as_p. Do not include the hidden fields twice.

Also see the testproject in the repository.

Temp upload dir must exist

Make sure the FILE_FORM_UPLOAD_DIR directory exists.

temp_upload_dir = os.path.join(settings.MEDIA_ROOT,  settings.FILE_FORM_UPLOAD_DIR)

if not os.path.exists(temp_upload_dir):
  os.mkdir(temp_upload_dir)

Adding placeholder files

If you have used django-file-form to upload files, potentially have saved the files elsewhere, but would like to use django-file-form to edit (remove or replace) the original uploaded files and append new files, you can add information about the original uploaded files as placeholders to the UploadedFileField. More specifically, you can initialize your field with one or more PlaceholderUploadedFile as follows:

from django_file_form.models import PlaceholderUploadedFile

initial['my_field'] = [
  PlaceholderUploadedFile('testfile1.png')
]

You can also add options size and file_id to specify file size if the file does not exist locally, and an unique ID of the file, respectively.

initial['my_field'] = [
  PlaceholderUploadedFile('testfile1.png', size=12394, file_id=my_file.pk)
]

The placeholder file will be listed, and will either be kept intact, or be removed. When you save the form, you will have to handle the placeholders as follows:

for f in self.cleaned_data['my_field']:
    if f.is_placeholder:
        # do nothing, or something with f.name or f.file_id
        continue
    # handle newly uploaded files as usual

# remove existing files if the placeholders are deleted
# ...

Upload directly to AWS S3

django-file-form supports upload directly to AWS S3 compatible storages. The files will be uploaded by clients directly to S3 through AJAX operations and return to the backend as File objects with S3Boto3Storage.

Required packages: boto3 and django_storages.

To use this method, you will first need to make sure your S3 bucket is configured to allow upload to bucket directly, allow PUT method, and expose ETag header. You then need to define the following variables in settings

AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_STORAGE_BUCKET_NAME
AWS_S3_REGION_NAME (optional)
AWS_S3_SIGNATURE_VERSION (optional)
AWS_S3_ENDPOINT_URL (optional)

or through environment variables with the same names as described in django-storages documentation.

If you are using django-csp for setting the Content Security Policy, then the following CORS settings are also needed in settings

CSP_DEFAULT_SRC = ("'none'",)
CSP_STYLE_SRC = ("'self'")
CSP_SCRIPT_SRC = ("'self'",)
CSP_FONT_SRC = ("'self'")
CSP_IMG_SRC = ("'self'",)
CSP_CONNECT_SRC = ("'self'", AWS_S3_ENDPOINT_URL)

where AWS_S3_ENDPOINT_URL is the AWS endpoint defined above.

Finally, you will need to define

s3_upload_dir = "user_or_form_specific_id"

in the form class or passed parameter s3_upload_dir to the constructor of the form to inform the frontend to use the AJAX uploader for S3. The files will be uploaded to

${FILE_FORM_UPLOAD_DIR}/${s3_upload_dir}/

in the specified S3 bucket, where s3_upload_dir can be empty or a directory specific to user or form to avoid conflicts on the AWS side. If the object already exists in the S3 bucket, a random string will be added to filename.

After form submission, the files will be returned as customized S3Boto3StorageFile objects with S3Boto3Storage as its underlying storage. The objects will have attributes is_s3direct=True, is_placeholder=False, and original_name which is the name of the file that was uploaded that can be different from the basename of the object on S3 (f.name). Reading from these objects will download the files from S3.

Accept attribute

You can add an accept attribute to the file input using the accept parameter on UploadedFileField:

file = UploadedFileField(accept='image/*,.pdf')

Additional file metadata

If you want to add and maintain additional metadata such as short descriptions and categories of uploaded files, you can use the .metadata field of uploaded files. More specifically, the AJAX uploader has a hidden field called metadata (with form specific prefix) with its data being the JSON.dumped meta data of all files. The data should be in the format of

{
  'filename1': value1,
  'filename2': value2
}

To add metadata to uploaded files, you will need to listen to events triggered after the additional and removal of file list (see "Javascript events" for details) to add your own widgets and event handlers to update the data of this hidden field.

Upon form submission, django-file-form retrieves the data and assign them to returned file objects with matching filename (could be of placeholder and other types) as f.metadata.

Cache

Django-file-form uses the Django cache for storing data while uploading files.

  • Only meta data of the file is stored (including the upload progress), not contents of the file.
  • The default cache timeout is 24 hours. An upload will fail if it takes longer than that.
  • The timeout is configurable using the FILE_FORM_CACHE_TIMEOUT setting. See the 'Python settings' section.
  • The cache backed is configurable using FILE_FORM_CACHE.

Disabled property

Make the input disabled by setting the disabled attribute.

class ExampleForm(FileFormMixin, Form):
    input_file = UploadedFileField(disabled=True)

Also possibe: make the field disabled in the constructor.

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)

    self.fields["input_file"].disabled = True

Production

Local-memory caching

Don't use local-memory caching in your Django configuration if you use a multi process WSGI server. It will break file uploads, because file uploads use the Django cache and might hit different WSGI processes.

  • The default cache option in Django is local-memory caching.
  • Most WSGI servers are multi process.

Maximum size of uploaded chunk

Files are uploaded in chunks; the default size of a chunk is 2.5 MB. Your production setup must allow for uploads of this size.

  • It's possible that you use a reverse proxy server that has a maximum file upload size. E.g. Nginx has the client_max_body_size option.
  • Django has the DATA_UPLOAD_MAX_MEMORY_SIZE option, which also limits the upload size.
  • You can change the maximum chunk size with the chunkSize option in Javascript. See the 'javascript options' section in this documentation.