#27150: Document that a name should be provided when wrapping file-like objects
that don't have one with File
-------------------------------------+-------------------------------------
Reporter: Sergei Zh | Owner: nobody
Type: | Status: new
Cleanup/optimization |
Component: Documentation | Version: 1.10
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by blighj):
* stage: Accepted => Unreviewed
Comment:
A 10 year old ticket that never really reached consensus may not be a good
ticket to pick up and introduce new wording to the docs.
Reading between the lines it feels like this is one where someone needs to
do a bit of work investigating deeper. Defining a base File class and then
giving it a `__bool__` method that relies on a name seems like a break in
the abstraction.
I amended the `__bool__` call to log its usage and ran the test suite, and
then did some searching to see what other cases I could see. I think this
is pretty comprehensive. I've given the intent of the calling code, why
does it want to know if the file is true, and what type of file is it
working with currently.
|| '''Location''' || '''Calls''' || '''Intent''' || '''Instance(s)''' ||
||
[https://github.com/django/django/blob/main/django/db/models/fields/files.py#L336
fields/files.py:336] `pre_save` || 847 || Skip committing the file if the
field is empty (name is the DB value; empty name means nothing to save) ||
FieldFile ||
||
[https://github.com/django/django/blob/main/django/db/models/fields/files.py#L42
fields/files.py:42] `_require_file` || 85 || Guard: raise `ValueError` if
no file is associated with this field before attempting file operations ||
FieldFile ||
||
[https://github.com/django/django/blob/main/django/http/multipartparser.py#L374
http/multipartparser.py:374] `handle_file_complete` || 59 || Check whether
an upload handler returned a completed file object (vs `None` meaning not
yet done) || InMemoryUploadedFile, TemporaryUploadedFile ||
|| [https://github.com/django/django/blob/main/django/forms/fields.py#L696
forms/fields.py:696] `FileField.clean` || 37 || Fall back to the initial
value if no new file was uploaded || FieldFile, SimpleUploadedFile ||
||
[https://github.com/django/django/blob/main/django/forms/widgets.py#L552
forms/widgets.py:552] `ClearableFileInput.is_initial` || 24 || Determine
whether the current value is an existing stored file (to decide whether to
render the "currently" link) || FieldFile, SimpleUploadedFile ||
||
[https://github.com/django/django/blob/main/django/db/models/fields/files.py#L368
fields/files.py:368] `save_form_data` || 11 || Use empty string if no file
was provided (`data or ""`) to avoid storing `False` in the DB column ||
FieldFile, SimpleUploadedFile ||
||
[https://github.com/django/django/blob/main/django/db/models/fields/files.py#L109
fields/files.py:109] `delete` || 9 || Early return if no file is
associated with the field — nothing to delete || FieldFile ||
||
[https://github.com/django/django/blob/main/django/forms/widgets.py#L521
forms/widgets.py:521] `FileInput.use_required_attribute` || 5 || Suppress
the HTML `required` attribute if a file is already set as the initial
value || FieldFile ||
||
[https://github.com/django/django/blob/main/django/forms/widgets.py#L585
forms/widgets.py:585] `ClearableFileInput.value_from_datadict` || 2 ||
Detect contradiction: user uploaded a new file AND checked the clear
checkbox simultaneously || SimpleUploadedFile ||
||
[https://github.com/django/django/blob/main/django/contrib/admin/utils.py#L463
contrib/admin/utils.py:463] `display_for_field` || 2 || Only render a
hyperlink to the file if a file is actually set on the field || FieldFile
||
||
[https://github.com/django/django/blob/main/django/db/models/fields/files.py#L500
fields/files.py:500] `update_dimension_fields` || 0 || Skip reading image
dimensions if no file is associated with the ImageField || FieldFile ||
||
[https://github.com/django/django/blob/main/django/db/models/fields/files.py#L518
fields/files.py:518] `update_dimension_fields` || 0 || Read width/height
from the image if a file is set; clear dimension fields to `None`
otherwise || FieldFile ||
||
[https://github.com/django/django/blob/main/django/core/files/base.py#L23
core/files/base.py:23] `File.__repr__` || 0 || Presentational: display the
file's name in repr output, or `"None"` if no name is set ||
In reality the cases are checking either a FieldFile or a subclass of
UploadedFile, in those cases it makes sense to check the name because if
you have no name, then the row in the db is empty or the uploadedfile
didn't work. We want to keep that behaviour.
But the default File looks like a thin wrapper around a python File, and
the docs support this
https://docs.djangoproject.com/en/6.0/ref/files/file/#django.core.files.File
and have examples of constructing a file without a name, which makes sense
for a thin wrapper, but results in a False file
https://docs.djangoproject.com/en/6.0/topics/files/#the-file-object and
you get bugs like #26495
So what I think we should do is have `__bool__` on File return True, which
would make sense for a wrapper class, files are kind of truthy by default
in python, it's what ContentFile does already. But then add `__bool__`
methods for FieldFile and UploadedFile that rely on the name. This
preserves the behaviour that most of the code is working with and makes
sense for those classes, no name = not a file = no need to check the
underlying file object.
That leaves that `__repr__` method, which should probably be updated to
`return "<%s: %s>" % (self.__class__.__name__, self.name or "None")`
The docs don't really then need updating at all, you could make a case for
documenting the `__bool__` behaviour of FieldFile and UploadedFile but I
think you'd be fine without it.
I think we should close and pause the current PR and instead move this
ticket back to unreviewed, so a member of the triage team can have a think
about it.
----
'''*''' The intent here is genuinely `is not None`, the upload handler API
[https://docs.djangoproject.com/en/6.0/ref/files/uploads/#django.core.files.uploadhandler.FileUploadHandler.file_complete
documents that handlers return None to signal "not done yet"]. This case
works incidentally, and would continue to work with the `__bool__`
changes. Maybe that line could be if `file_obj is not None:` as a
separate/additional cleanup.
--
Ticket URL: <https://code.djangoproject.com/ticket/27150#comment:11>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/django-updates/0107019d1fcbea3d-3810585c-f692-4838-b173-de2d7249d7fe-000000%40eu-central-1.amazonses.com.