github kitware-resonant/django-s3-file-field v2.0.0

latest release: v2.0.1
2 hours ago

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 / error events, 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 validators run in both a ModelForm and a ModelSerializer
    • Validators, clean_<field>, and validate_<field> receive a File whose content can be read from the storage backend, so it can be inspected (e.g. for its actual type)
  • Add a configurable maximum upload size: the max_size option of S3FileField, or the S3_FILE_FIELD_MAX_SIZE setting
    • 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 S3FileFieldError means the clients' own retries are exhausted, and an outer retry would restart the whole upload
  • 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 minio extra requires django-minio-storage>=0.6.0 and minio>=7.2.18
  • S3Storage must sign with SigV4; a system check (s3_file_field.E002) now enforces this
    # settings.py
    AWS_S3_SIGNATURE_VERSION = "s3v4"

Django

  • S3FormFileField takes model_field (the S3FileField instance) instead of model_field_id
    • S3FormFileField(model_field=Resource._meta.get_field("blob"))
  • S3FileSerializerField on a plain Serializer requires model_field; a ModelSerializer resolves it automatically
    • S3FileSerializerField(model_field=Resource._meta.get_field("blob"))
  • S3FileSerializerField validates to a File-like value, rather than the stored object's name as a str
    • A validate_<field> method, or other code reading validated_data, must use .name for the string; assigning the value to a model instance works as before
  • 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
  • The form widget requires an evergreen browser, with ES2024 and ElementInternals support
  • S3PlaceholderFile moved from s3_file_field.widgets to s3_file_field.files, and its constructor changed
    • Code which unwrapped it by hand is no longer necessary, as validated values are now File-like

pytest fixtures

  • The s3ff_field_value fixture is removed; use s3ff_field_value_factory
  • s3ff_field_value_factory requires the target field
    • s3ff_field_value_factory(file_object, Resource._meta.get_field("blob"))

Python client

  • S3FileFieldClient no longer accepts a requests.Session; pass headers for the API's requests instead
  • S3FileFieldClient must now be used as a context manager, which releases its connections
  • Failures raise S3FileFieldError rather than requests exceptions; 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
  • apiConfig is now fetch options (a RequestInit) rather than an Axios config
    • Pass { headers: ... } instead of an Axios instance's defaults
  • Failures throw S3FileFieldError rather than Axios errors; the underlying error is its cause
  • S3FileFieldProgressState members 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 validators crashing in a ModelSerializer
  • Fix the JavaScript client treating an error in the body of a successful S3 completion response as success

Don't miss a new django-s3-file-field release

NewReleases is sending notifications on new releases.