diff --git a/django/core/management/validation.py b/django/core/management/validation.py
index 85897ee..9beb47e 100644
a
|
b
|
def get_validation_errors(outfile, app=None):
|
120 | 120 | if decimalp_ok and mdigits_ok: |
121 | 121 | if decimal_places > max_digits: |
122 | 122 | e.add(opts, invalid_values_msg % f.name) |
123 | | if isinstance(f, models.FileField) and not f.upload_to: |
124 | | e.add(opts, '"%s": FileFields require an "upload_to" attribute.' % f.name) |
125 | 123 | if isinstance(f, models.ImageField): |
126 | 124 | try: |
127 | 125 | from django.utils.image import Image |
diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt
index bcadf4d..2559aa7 100644
a
|
b
|
A :class:`CharField` that checks that the value is a valid email address.
|
542 | 542 | ``FileField`` |
543 | 543 | ------------- |
544 | 544 | |
545 | | .. class:: FileField(upload_to=None, [max_length=100, **options]) |
| 545 | .. class:: FileField([upload_to=None, max_length=100, **options]) |
546 | 546 | |
547 | 547 | A file-upload field. |
548 | 548 | |
… |
… |
Has one **required** argument:
|
586 | 586 | when determining the final destination path. |
587 | 587 | ====================== =============================================== |
588 | 588 | |
589 | | Also has one optional argument: |
| 589 | Also has two optional arguments: |
590 | 590 | |
591 | 591 | .. attribute:: FileField.storage |
592 | 592 | |
593 | 593 | Optional. A storage object, which handles the storage and retrieval of your |
594 | 594 | files. See :doc:`/topics/files` for details on how to provide this object. |
595 | 595 | |
| 596 | .. attribute:: FileField.upload_to |
| 597 | |
| 598 | Optional. Subdirectory of :setting:`MEDIA_ROOT` where it will receive the |
| 599 | uploaded files. |
| 600 | |
596 | 601 | The default form widget for this field is a :class:`~django.forms.FileInput`. |
597 | 602 | |
598 | 603 | Using a :class:`FileField` or an :class:`ImageField` (see below) in a model |
… |
… |
The default form widget for this field is a :class:`~django.forms.TextInput`.
|
807 | 812 | ``ImageField`` |
808 | 813 | -------------- |
809 | 814 | |
810 | | .. class:: ImageField(upload_to=None, [height_field=None, width_field=None, max_length=100, **options]) |
| 815 | .. class:: ImageField([upload_to=None, height_field=None, width_field=None, max_length=100, **options]) |
811 | 816 | |
812 | 817 | Inherits all attributes and methods from :class:`FileField`, but also |
813 | 818 | validates that the uploaded object is a valid image. |
diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt
index 0f1a5de..4756361 100644
a
|
b
|
File Uploads
|
246 | 246 | the file system permissions of directories created during file upload, like |
247 | 247 | :setting:`FILE_UPLOAD_PERMISSIONS` does for the files themselves. |
248 | 248 | |
| 249 | * The :attr:`FileField.upload_to |
| 250 | <django.db.models.fields.files.FileField>` is now optional. If it is omitted |
| 251 | or be given ``None`` or empty string, it means it does not use subdirectory |
| 252 | for storing the uploaded files. |
| 253 | |
249 | 254 | Forms |
250 | 255 | ^^^^^ |
251 | 256 | |
diff --git a/tests/files/models.py b/tests/files/models.py
index 4134472..eea3dda 100644
a
|
b
|
class Storage(models.Model):
|
28 | 28 | custom = models.FileField(storage=temp_storage, upload_to=custom_upload_to) |
29 | 29 | random = models.FileField(storage=temp_storage, upload_to=random_upload_to) |
30 | 30 | default = models.FileField(storage=temp_storage, upload_to='tests', default='tests/default.txt') |
| 31 | empty = models.FileField(storage=temp_storage) |
diff --git a/tests/files/tests.py b/tests/files/tests.py
index 36664fa..40aa9f6 100644
a
|
b
|
class FileStorageTests(TestCase):
|
106 | 106 | obj4.random.save("random_file", ContentFile("random content")) |
107 | 107 | self.assertTrue(obj4.random.name.endswith("/random_file")) |
108 | 108 | |
| 109 | # upload_to can be empty, meaning it does not use subdirectory. |
| 110 | obj5 = Storage() |
| 111 | obj5.empty.save('django_test.txt', ContentFile('more content')) |
| 112 | self.assertEqual(obj5.empty.name, "./django_test.txt") |
| 113 | self.assertEqual(obj5.empty.read(), b"more content") |
| 114 | |
109 | 115 | def test_file_object(self): |
110 | 116 | # Create sample file |
111 | 117 | temp_storage.save('tests/example.txt', ContentFile('some content')) |
diff --git a/tests/invalid_models/invalid_models/models.py b/tests/invalid_models/invalid_models/models.py
index 1113c0c..8e04a4d 100644
a
|
b
|
class FieldErrors(models.Model):
|
19 | 19 | decimalfield3 = models.DecimalField(max_digits="bad", decimal_places="bad") |
20 | 20 | decimalfield4 = models.DecimalField(max_digits=9, decimal_places=10) |
21 | 21 | decimalfield5 = models.DecimalField(max_digits=10, decimal_places=10) |
22 | | filefield = models.FileField() |
23 | 22 | choices = models.CharField(max_length=10, choices='bad') |
24 | 23 | choices2 = models.CharField(max_length=10, choices=[(1, 2, 3), (1, 2, 3)]) |
25 | 24 | index = models.CharField(max_length=10, db_index='bad') |
… |
… |
invalid_models.fielderrors: "decimalfield2": DecimalFields require a "max_digits
|
424 | 423 | invalid_models.fielderrors: "decimalfield3": DecimalFields require a "decimal_places" attribute that is a non-negative integer. |
425 | 424 | invalid_models.fielderrors: "decimalfield3": DecimalFields require a "max_digits" attribute that is a positive integer. |
426 | 425 | 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. |
427 | | invalid_models.fielderrors: "filefield": FileFields require an "upload_to" attribute. |
428 | 426 | invalid_models.fielderrors: "choices": "choices" should be iterable (e.g., a tuple or list). |
429 | 427 | invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-item iterables (e.g. list of 2 item tuples). |
430 | 428 | invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-item iterables (e.g. list of 2 item tuples). |