Commit 914c72be authored by Cristiano's avatar Cristiano Committed by Tim Graham
Browse files

Fixed #26058 -- Delegated os.path bits of FileField's filename generation to the Storage.

parent 8dcf352c
Loading
Loading
Loading
Loading
+12 −5
Original line number Diff line number Diff line
@@ -51,10 +51,7 @@ class Storage(object):
            content = File(content, name)

        name = self.get_available_name(name, max_length=max_length)
        name = self._save(name, content)

        # Store filenames with forward slashes, even on Windows
        return force_text(name.replace('\\', '/'))
        return self._save(name, content)

    # These methods are part of the public API, with default implementations.

@@ -96,6 +93,15 @@ class Storage(object):
                name = os.path.join(dir_name, "%s_%s%s" % (file_root, get_random_string(7), file_ext))
        return name

    def generate_filename(self, filename):
        """
        Validate the filename by calling get_valid_name() and return a filename
        to be passed to the save() method.
        """
        # `filename` may include a path as returned by FileField.upload_to.
        dirname, filename = os.path.split(filename)
        return os.path.normpath(os.path.join(dirname, self.get_valid_name(filename)))

    def path(self, name):
        """
        Returns a local filesystem path where the file can be retrieved using
@@ -367,7 +373,8 @@ class FileSystemStorage(Storage):
        if self.file_permissions_mode is not None:
            os.chmod(full_path, self.file_permissions_mode)

        return name
        # Store filenames with forward slashes, even on Windows.
        return force_text(name.replace('\\', '/'))

    def delete(self, name):
        assert name, "The name argument is not allowed to be empty."
+24 −7
Original line number Diff line number Diff line
import datetime
import os
import posixpath
import warnings

from django import forms
from django.core import checks
@@ -9,6 +11,7 @@ from django.core.files.storage import default_storage
from django.db.models import signals
from django.db.models.fields import Field
from django.utils import six
from django.utils.deprecation import RemovedInDjango20Warning
from django.utils.encoding import force_str, force_text
from django.utils.translation import ugettext_lazy as _

@@ -294,20 +297,34 @@ class FileField(Field):
        setattr(cls, self.name, self.descriptor_class(self))

    def get_directory_name(self):
        warnings.warn(
            'FileField now delegates file name and folder processing to the '
            'storage. get_directory_name() will be removed in Django 2.0.',
            RemovedInDjango20Warning, stacklevel=2
        )
        return os.path.normpath(force_text(datetime.datetime.now().strftime(force_str(self.upload_to))))

    def get_filename(self, filename):
        warnings.warn(
            'FileField now delegates file name and folder processing to the '
            'storage. get_filename() will be removed in Django 2.0.',
            RemovedInDjango20Warning, stacklevel=2
        )
        return os.path.normpath(self.storage.get_valid_name(os.path.basename(filename)))

    def generate_filename(self, instance, filename):
        # If upload_to is a callable, make sure that the path it returns is
        # passed through get_valid_name() of the underlying storage.
        """
        Apply (if callable) or prepend (if a string) upload_to to the filename,
        then delegate further processing of the name to the storage backend.
        Until the storage layer, all file paths are expected to be Unix style
        (with forward slashes).
        """
        if callable(self.upload_to):
            directory_name, filename = os.path.split(self.upload_to(instance, filename))
            filename = self.storage.get_valid_name(filename)
            return os.path.normpath(os.path.join(directory_name, filename))

        return os.path.join(self.get_directory_name(), self.get_filename(filename))
            filename = self.upload_to(instance, filename)
        else:
            dirname = force_text(datetime.datetime.now().strftime(force_str(self.upload_to)))
            filename = posixpath.join(dirname, filename)
        return self.storage.generate_filename(filename)

    def save_form_data(self, instance, data):
        # Important: None means "no change", other false value means "clear"
+3 −0
Original line number Diff line number Diff line
@@ -165,6 +165,9 @@ details on these changes.

* Support for ``Widget._format_value()`` will be removed.

* ``FileField`` methods ``get_directory_name()`` and ``get_filename()`` will be
  removed.

.. _deprecation-removed-in-1.10:

1.10
+15 −0
Original line number Diff line number Diff line
@@ -164,6 +164,21 @@ The ``Storage`` class
        Returns a filename based on the ``name`` parameter that's suitable
        for use on the target storage system.

    .. method:: generate_filename(filename)

        .. versionadded:: 1.10

        Validates the ``filename`` by calling :attr:`get_valid_name()` and
        returns a filename to be passed to the :meth:`save` method.

        The ``filename`` argument may include a path as returned by
        :attr:`FileField.upload_to <django.db.models.FileField.upload_to>`.
        In that case, the path won't be passed to :attr:`get_valid_name()` but
        will be prepended back to the resulting name.

        The default implementation uses :mod:`os.path` operations. Override
        this method if that's not appropriate for your storage.

    .. method:: listdir(path)

        Lists the contents of the specified path, returning a 2-tuple of lists;
+24 −0
Original line number Diff line number Diff line
@@ -251,6 +251,10 @@ File Storage
  timezone-aware ``datetime`` if :setting:`USE_TZ` is ``True`` and a naive
  ``datetime`` in the local timezone otherwise.

* The new :meth:`Storage.generate_filename()
  <django.core.files.storage.Storage.generate_filename>` method makes it easier
  to implement custom storages that don't use the ``os.path`` calls previously
  in :class:`~django.db.models.FileField`.

File Uploads
~~~~~~~~~~~~
@@ -789,6 +793,21 @@ Miscellaneous
* The ``Model._deferred`` attribute is removed as dynamic model classes when
  using ``QuerySet.defer()`` and ``only()`` is removed.

* :meth:`Storage.save() <django.core.files.storage.Storage.save>` no longer
  replaces ``'\'`` with ``'/'``. This behavior is moved to
  :class:`~django.core.files.storage.FileSystemStorage` since this is a storage
  specific implementation detail. Any Windows user with a custom storage
  implementation that relies on this behavior will need to implement it in the
  custom storage's ``save()`` method.

* Private :class:`~django.db.models.FileField` methods ``get_directory_name()``
  and ``get_filename()`` are no longer called (and are now deprecated) which is
  a backwards incompatible change for users overriding those methods on custom
  fields. To adapt such code, override ``FileField.generate_filename()`` or
  :meth:`Storage.generate_filename()
  <django.core.files.storage.Storage.generate_filename>` instead. It
  might be possible to use :attr:`~django.db.models.FileField.upload_to` also.

.. _deprecated-features-1.10:

Features deprecated in 1.10
@@ -998,6 +1017,11 @@ Miscellaneous
  :meth:`~django.forms.Widget.format_value`. The old name will work
  through a deprecation period.

* Private ``FileField`` methods ``get_directory_name()`` and ``get_filename()``
  are deprecated in favor of performing this work in
  :meth:`Storage.generate_filename()
  <django.core.files.storage.Storage.generate_filename>`).

.. _removed-features-1.10:

Features removed in 1.10
Loading