#30695: File storage API docs should be more clear about how a "path" may be
interpreted by storage backends
--------------------------------+--------------------------------------
     Reporter:  Tobias McNulty  |                    Owner:  nobody
         Type:  Uncategorized   |                   Status:  new
    Component:  Uncategorized   |                  Version:  2.2
     Severity:  Normal          |               Resolution:
     Keywords:                  |             Triage Stage:  Unreviewed
    Has patch:  0               |      Needs documentation:  0
  Needs tests:  0               |  Patch needs improvement:  0
Easy pickings:  0               |                    UI/UX:  0
--------------------------------+--------------------------------------
Description changed by Tobias McNulty:

Old description:

> This section of the docs
> (https://docs.djangoproject.com/en/2.2/topics/files/#storage-objects)
> suggests that one should use absolute paths when calling storage API
> methods, but executing this sample code on a fresh Django 2.2 project
> raises a `SuspiciousFileOperation` error:
>
> ```
> (filestorage-test) $ pip freeze
> Django==2.2.4
> pytz==2019.2
> sqlparse==0.3.0
> (filestorage-test) $ django-admin startproject filestorage_test
> (filestorage-test) $ cd filestorage_test/
> (filestorage-test) $ python manage.py shell
> Python 3.7.3 (default, Mar 27 2019, 09:23:15)
> [Clang 10.0.1 (clang-1001.0.46.3)] on darwin
> Type "help", "copyright", "credits" or "license" for more information.
> (InteractiveConsole)
> >>> from django.core.files.base import ContentFile
> >>> from django.core.files.storage import default_storage
> >>> default_storage.save('/path/to/file', ContentFile('new content'))
> Traceback (most recent call last):
>   File "<console>", line 1, in <module>
>   File "/Users/tobias/.virtualenvs/filestorage-test/lib/python3.7/site-
> packages/django/core/files/storage.py", line 51, in save
>     name = self.get_available_name(name, max_length=max_length)
>   File "/Users/tobias/.virtualenvs/filestorage-test/lib/python3.7/site-
> packages/django/core/files/storage.py", line 75, in get_available_name
>     while self.exists(name) or (max_length and len(name) > max_length):
>   File "/Users/tobias/.virtualenvs/filestorage-test/lib/python3.7/site-
> packages/django/core/files/storage.py", line 310, in exists
>     return os.path.exists(self.path(name))
>   File "/Users/tobias/.virtualenvs/filestorage-test/lib/python3.7/site-
> packages/django/core/files/storage.py", line 323, in path
>     return safe_join(self.location, name)
>   File "/Users/tobias/.virtualenvs/filestorage-test/lib/python3.7/site-
> packages/django/utils/_os.py", line 46, in safe_join
>     'component ({})'.format(final_path, base_path))
> django.core.exceptions.SuspiciousFileOperation: The joined path
> (/path/to/file) is located outside of the base path component
> (/Users/tobias/tmp/filestorage_test)
> ```
>
> I realize one could prepend the path to `MEDIA_ROOT` to work around that,
> but I'm not sure that's the right convention to establish when relative
> paths work just as well (not to mention that prepending `MEDIA_ROOT`
> would be the wrong approach for a remote storage).
>
> I think the convention is or should be to use relative paths when
> interacting with the Django file storage API. The other related section
> of the docs (https://docs.djangoproject.com/en/2.2/ref/files/storage
> /#the-storage-class) doesn't say either way whether the path should be
> relative or absolute.
>
> Creating this ticket to look through the file storage-related docs at
> some point to clarify what is a "path" when interacting with the file
> storage API (however we want to do that, if not how I've proposed here).
> At least, the sample code linked first above needs be updated so that
> someone can run it and see the results described in the docs.

New description:

 This section of the docs
 (https://docs.djangoproject.com/en/2.2/topics/files/#storage-objects)
 suggests that one should use absolute paths when calling storage API
 methods, but executing this sample code on a fresh Django 2.2 project
 raises a `SuspiciousFileOperation` error:

 {{{
 (filestorage-test) $ pip freeze
 Django==2.2.4
 pytz==2019.2
 sqlparse==0.3.0
 (filestorage-test) $ django-admin startproject filestorage_test
 (filestorage-test) $ cd filestorage_test/
 (filestorage-test) $ python manage.py shell
 Python 3.7.3 (default, Mar 27 2019, 09:23:15)
 [Clang 10.0.1 (clang-1001.0.46.3)] on darwin
 Type "help", "copyright", "credits" or "license" for more information.
 (InteractiveConsole)
 >>> from django.core.files.base import ContentFile
 >>> from django.core.files.storage import default_storage
 >>> default_storage.save('/path/to/file', ContentFile('new content'))
 Traceback (most recent call last):
   File "<console>", line 1, in <module>
   File "/Users/tobias/.virtualenvs/filestorage-test/lib/python3.7/site-
 packages/django/core/files/storage.py", line 51, in save
     name = self.get_available_name(name, max_length=max_length)
   File "/Users/tobias/.virtualenvs/filestorage-test/lib/python3.7/site-
 packages/django/core/files/storage.py", line 75, in get_available_name
     while self.exists(name) or (max_length and len(name) > max_length):
   File "/Users/tobias/.virtualenvs/filestorage-test/lib/python3.7/site-
 packages/django/core/files/storage.py", line 310, in exists
     return os.path.exists(self.path(name))
   File "/Users/tobias/.virtualenvs/filestorage-test/lib/python3.7/site-
 packages/django/core/files/storage.py", line 323, in path
     return safe_join(self.location, name)
   File "/Users/tobias/.virtualenvs/filestorage-test/lib/python3.7/site-
 packages/django/utils/_os.py", line 46, in safe_join
     'component ({})'.format(final_path, base_path))
 django.core.exceptions.SuspiciousFileOperation: The joined path
 (/path/to/file) is located outside of the base path component
 (/Users/tobias/tmp/filestorage_test)
 }}}

 I realize one could prepend the path to `MEDIA_ROOT` to work around that,
 but I'm not sure that's the right convention to establish when relative
 paths work just as well (not to mention that prepending `MEDIA_ROOT` would
 be the wrong approach for a remote storage).

 I think the convention is or should be to use relative paths when
 interacting with the Django file storage API. The other related section of
 the docs (https://docs.djangoproject.com/en/2.2/ref/files/storage/#the-
 storage-class) doesn't say either way whether the path should be relative
 or absolute.

 Creating this ticket to look through the file storage-related docs at some
 point to clarify what is a "path" when interacting with the file storage
 API (however we want to do that, if not how I've proposed here). At least,
 the sample code linked first above needs be updated so that someone can
 run it and see the results described in the docs.

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/30695#comment:1>
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 django-updates+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/071.756b4385ba3ce16cfe9dc11ce85a5c17%40djangoproject.com.

Reply via email to