django-s3-file-field 2.0 rebuilds every layer of the library. The form widget is a new <s3-file-input> HTML custom element, with upload progress, cancellation, drag and drop, and Django admin support. Uploaded files can now be validated by their content and limited in size, and the upload protocol is hardened with signed tokens. Both client libraries upload in parallel, retry transient failures, and fail with a single displayable error.
New features
- Rewrite the form widget as a
<s3-file-input>custom element- Shows upload progress, with a cancel button; accepts a dropped file; explains a failed upload
- Works in the Django admin, including dynamically added inline forms
- Survives a form redisplay after a validation error elsewhere on the form
- Follows the page's DaisyUI or Vuetify theme, or may be themed with CSS properties
- Has a documented API, for direct use from a frontend framework: a
value,input/change/errorevents, and, within a<form>, native constraint validation (a missing required file or an in-progress upload blocks submission) - Works from a frontend on a different origin, authenticating with a token or, on the same site, a session
- Support validation of uploaded files
- Model field
validatorsrun in both aModelFormand aModelSerializer - Validators,
clean_<field>, andvalidate_<field>receive aFilewhose content can be read from the storage backend, so it can be inspected (e.g. for its actual type)
- Model field
- Add a configurable maximum upload size: the
max_sizeoption ofS3FileField, or theS3_FILE_FIELD_MAX_SIZEsetting- Enforced by the server, and pre-checked by the widget
- Harden security and input validation
- All upload state is carried in signed, expiring tokens
- An upload is bound to the field it was initiated for
- All endpoint input is strictly validated
- Both clients upload a file's parts in parallel, retry transient failures, and recover from a lost response to the completion request
- Don't wrap an upload in further retry logic: an
S3FileFieldErrormeans the clients' own retries are exhausted, and an outer retry would restart the whole upload
- Don't wrap an upload in further retry logic: an
- Both clients report a failed upload as a single
S3FileFieldError, with a message suitable for display - Both clients are fully typed
- The JavaScript client supports canceling an upload, via an
AbortSignal, and no longer depends on Axios - Add support for Python 3.14 and 3.15, and Django 6.0 and 6.1
- Explain rejected uploads in the server's error responses (e.g. a file over the maximum size)
Breaking changes
Requirements
- The upload protocol changed, so all clients must be upgraded along with the server
- Require Python 3.12 (dropping 3.10 and 3.11) and Django 5.2 (dropping 4.2 through 5.1)
- Require Django REST framework 3.16
- The
minioextra requiresdjango-minio-storage>=0.6.0andminio>=7.2.18 S3Storagemust sign with SigV4; a system check (s3_file_field.E002) now enforces this# settings.py AWS_S3_SIGNATURE_VERSION = "s3v4"
Django
S3FormFileFieldtakesmodel_field(theS3FileFieldinstance) instead ofmodel_field_idS3FormFileField(model_field=Resource._meta.get_field("blob"))
S3FileSerializerFieldon a plainSerializerrequiresmodel_field; aModelSerializerresolves it automaticallyS3FileSerializerField(model_field=Resource._meta.get_field("blob"))
S3FileSerializerFieldvalidates to aFile-like value, rather than the stored object's name as astr- A
validate_<field>method, or other code readingvalidated_data, must use.namefor the string; assigning the value to a model instance works as before
- A
- The form widget no longer accepts a directly-submitted file when its JavaScript is not running
- Ensure
{{ form.media }}is rendered with every form containing the field
- Ensure
- The form widget requires an evergreen browser, with ES2024 and
ElementInternalssupport S3PlaceholderFilemoved froms3_file_field.widgetstos3_file_field.files, and its constructor changed- Code which unwrapped it by hand is no longer necessary, as validated values are now
File-like
- Code which unwrapped it by hand is no longer necessary, as validated values are now
pytest fixtures
- The
s3ff_field_valuefixture is removed; uses3ff_field_value_factory s3ff_field_value_factoryrequires the target fields3ff_field_value_factory(file_object, Resource._meta.get_field("blob"))
Python client
S3FileFieldClientno longer accepts arequests.Session; passheadersfor the API's requests insteadS3FileFieldClientmust now be used as a context manager, which releases its connections- Failures raise
S3FileFieldErrorrather thanrequestsexceptions; the underlying exception is its__cause__
JavaScript client
- Direct use of the client is discouraged, in favor of the
<s3-file-input>element; the client remains for a file which doesn't come from a file input apiConfigis nowfetchoptions (aRequestInit) rather than an Axios config- Pass
{ headers: ... }instead of an Axios instance'sdefaults
- Pass
- Failures throw
S3FileFieldErrorrather than Axios errors; the underlying error is itscause S3FileFieldProgressStatemembers are renamed and renumbered:Initiating,Uploading,Completing,Finalizing,Done- Upload progress is reported as each part completes, rather than continuously
- Emit target updated from ES2020 to ES2024
Bug fixes
- Fix validation of disabled form fields, which failed with "Not a valid signed S3 upload"
- Fix model field
validatorscrashing in aModelSerializer - Fix the JavaScript client treating an error in the body of a successful S3 completion response as success