Fixed #8918 -- Made FileField.upload_to optional.

Thanks leahculver for the suggestion and dc and vajrasky for work
on the patch.
This commit is contained in:
Tim Graham 2013-10-11 08:07:25 -04:00
parent b67ab75e82
commit 945e033a69
7 changed files with 25 additions and 17 deletions

View File

@ -120,8 +120,6 @@ def get_validation_errors(outfile, app=None):
if decimalp_ok and mdigits_ok: if decimalp_ok and mdigits_ok:
if decimal_places > max_digits: if decimal_places > max_digits:
e.add(opts, invalid_values_msg % f.name) e.add(opts, invalid_values_msg % f.name)
if isinstance(f, models.FileField) and not f.upload_to:
e.add(opts, '"%s": FileFields require an "upload_to" attribute.' % f.name)
if isinstance(f, models.ImageField): if isinstance(f, models.ImageField):
try: try:
from django.utils.image import Image from django.utils.image import Image

View File

@ -45,10 +45,9 @@ Using a :class:`~django.db.models.FileField` or an
account. account.
#. Add the :class:`~django.db.models.FileField` or #. Add the :class:`~django.db.models.FileField` or
:class:`~django.db.models.ImageField` to your model, making sure to :class:`~django.db.models.ImageField` to your model, defining the
define the :attr:`~django.db.models.FileField.upload_to` option to tell :attr:`~django.db.models.FileField.upload_to` option to specify a
Django to which subdirectory of :setting:`MEDIA_ROOT` it should upload subdirectory of :setting:`MEDIA_ROOT` to use for uploaded files.
files.
#. All that will be stored in your database is a path to the file #. All that will be stored in your database is a path to the file
(relative to :setting:`MEDIA_ROOT`). You'll most likely want to use the (relative to :setting:`MEDIA_ROOT`). You'll most likely want to use the

View File

@ -542,7 +542,7 @@ A :class:`CharField` that checks that the value is a valid email address.
``FileField`` ``FileField``
------------- -------------
.. class:: FileField(upload_to=None, [max_length=100, **options]) .. class:: FileField([upload_to=None, max_length=100, **options])
A file-upload field. A file-upload field.
@ -550,10 +550,14 @@ A file-upload field.
The ``primary_key`` and ``unique`` arguments are not supported, and will The ``primary_key`` and ``unique`` arguments are not supported, and will
raise a ``TypeError`` if used. raise a ``TypeError`` if used.
Has one **required** argument: Has two optional arguments:
.. attribute:: FileField.upload_to .. attribute:: FileField.upload_to
.. versionchanged:: 1.7
``upload_to`` was required in older versions of Django.
A local filesystem path that will be appended to your :setting:`MEDIA_ROOT` A local filesystem path that will be appended to your :setting:`MEDIA_ROOT`
setting to determine the value of the setting to determine the value of the
:attr:`~django.db.models.fields.files.FieldFile.url` attribute. :attr:`~django.db.models.fields.files.FieldFile.url` attribute.
@ -586,11 +590,9 @@ Has one **required** argument:
when determining the final destination path. when determining the final destination path.
====================== =============================================== ====================== ===============================================
Also has one optional argument:
.. attribute:: FileField.storage .. attribute:: FileField.storage
Optional. A storage object, which handles the storage and retrieval of your A storage object, which handles the storage and retrieval of your
files. See :doc:`/topics/files` for details on how to provide this object. files. See :doc:`/topics/files` for details on how to provide this object.
The default form widget for this field is a :class:`~django.forms.FileInput`. The default form widget for this field is a :class:`~django.forms.FileInput`.
@ -604,9 +606,9 @@ takes a few steps:
:setting:`MEDIA_URL` as the base public URL of that directory. Make sure :setting:`MEDIA_URL` as the base public URL of that directory. Make sure
that this directory is writable by the Web server's user account. that this directory is writable by the Web server's user account.
2. Add the :class:`FileField` or :class:`ImageField` to your model, making 2. Add the :class:`FileField` or :class:`ImageField` to your model, defining
sure to define the :attr:`~FileField.upload_to` option to tell Django the :attr:`~FileField.upload_to` option to specify a subdirectory of
to which subdirectory of :setting:`MEDIA_ROOT` it should upload files. :setting:`MEDIA_ROOT` to use for uploaded files.
3. All that will be stored in your database is a path to the file 3. All that will be stored in your database is a path to the file
(relative to :setting:`MEDIA_ROOT`). You'll most likely want to use the (relative to :setting:`MEDIA_ROOT`). You'll most likely want to use the
@ -807,7 +809,7 @@ The default form widget for this field is a :class:`~django.forms.TextInput`.
``ImageField`` ``ImageField``
-------------- --------------
.. class:: ImageField(upload_to=None, [height_field=None, width_field=None, max_length=100, **options]) .. class:: ImageField([upload_to=None, height_field=None, width_field=None, max_length=100, **options])
Inherits all attributes and methods from :class:`FileField`, but also Inherits all attributes and methods from :class:`FileField`, but also
validates that the uploaded object is a valid image. validates that the uploaded object is a valid image.

View File

@ -246,6 +246,10 @@ File Uploads
the file system permissions of directories created during file upload, like the file system permissions of directories created during file upload, like
:setting:`FILE_UPLOAD_PERMISSIONS` does for the files themselves. :setting:`FILE_UPLOAD_PERMISSIONS` does for the files themselves.
* The :attr:`FileField.upload_to <django.db.models.FileField.upload_to>`
attribute is now optional. If it is omitted or given ``None`` or an empty
string, a subdirectory won't be used for storing the uploaded files.
Forms Forms
^^^^^ ^^^^^

View File

@ -28,3 +28,4 @@ class Storage(models.Model):
custom = models.FileField(storage=temp_storage, upload_to=custom_upload_to) custom = models.FileField(storage=temp_storage, upload_to=custom_upload_to)
random = models.FileField(storage=temp_storage, upload_to=random_upload_to) random = models.FileField(storage=temp_storage, upload_to=random_upload_to)
default = models.FileField(storage=temp_storage, upload_to='tests', default='tests/default.txt') default = models.FileField(storage=temp_storage, upload_to='tests', default='tests/default.txt')
empty = models.FileField(storage=temp_storage)

View File

@ -106,6 +106,12 @@ class FileStorageTests(TestCase):
obj4.random.save("random_file", ContentFile("random content")) obj4.random.save("random_file", ContentFile("random content"))
self.assertTrue(obj4.random.name.endswith("/random_file")) self.assertTrue(obj4.random.name.endswith("/random_file"))
# upload_to can be empty, meaning it does not use subdirectory.
obj5 = Storage()
obj5.empty.save('django_test.txt', ContentFile('more content'))
self.assertEqual(obj5.empty.name, "./django_test.txt")
self.assertEqual(obj5.empty.read(), b"more content")
def test_file_object(self): def test_file_object(self):
# Create sample file # Create sample file
temp_storage.save('tests/example.txt', ContentFile('some content')) temp_storage.save('tests/example.txt', ContentFile('some content'))

View File

@ -19,7 +19,6 @@ class FieldErrors(models.Model):
decimalfield3 = models.DecimalField(max_digits="bad", decimal_places="bad") decimalfield3 = models.DecimalField(max_digits="bad", decimal_places="bad")
decimalfield4 = models.DecimalField(max_digits=9, decimal_places=10) decimalfield4 = models.DecimalField(max_digits=9, decimal_places=10)
decimalfield5 = models.DecimalField(max_digits=10, decimal_places=10) decimalfield5 = models.DecimalField(max_digits=10, decimal_places=10)
filefield = models.FileField()
choices = models.CharField(max_length=10, choices='bad') choices = models.CharField(max_length=10, choices='bad')
choices2 = models.CharField(max_length=10, choices=[(1, 2, 3), (1, 2, 3)]) choices2 = models.CharField(max_length=10, choices=[(1, 2, 3), (1, 2, 3)])
index = models.CharField(max_length=10, db_index='bad') index = models.CharField(max_length=10, db_index='bad')
@ -424,7 +423,6 @@ invalid_models.fielderrors: "decimalfield2": DecimalFields require a "max_digits
invalid_models.fielderrors: "decimalfield3": DecimalFields require a "decimal_places" attribute that is a non-negative integer. invalid_models.fielderrors: "decimalfield3": DecimalFields require a "decimal_places" attribute that is a non-negative integer.
invalid_models.fielderrors: "decimalfield3": DecimalFields require a "max_digits" attribute that is a positive integer. invalid_models.fielderrors: "decimalfield3": DecimalFields require a "max_digits" attribute that is a positive integer.
invalid_models.fielderrors: "decimalfield4": DecimalFields require a "max_digits" attribute value that is greater than or equal to the value of the "decimal_places" attribute. invalid_models.fielderrors: "decimalfield4": DecimalFields require a "max_digits" attribute value that is greater than or equal to the value of the "decimal_places" attribute.
invalid_models.fielderrors: "filefield": FileFields require an "upload_to" attribute.
invalid_models.fielderrors: "choices": "choices" should be iterable (e.g., a tuple or list). invalid_models.fielderrors: "choices": "choices" should be iterable (e.g., a tuple or list).
invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-item iterables (e.g. list of 2 item tuples). invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-item iterables (e.g. list of 2 item tuples).
invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-item iterables (e.g. list of 2 item tuples). invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-item iterables (e.g. list of 2 item tuples).