Changeset 8244
- Timestamp:
- 08/08/08 15:59:02 (4 months ago)
- Files:
-
- django/trunk/django/conf/global_settings.py (modified) (1 diff)
- django/trunk/django/contrib/admin/widgets.py (modified) (1 diff)
- django/trunk/django/core/files/base.py (added)
- django/trunk/django/core/files/images.py (added)
- django/trunk/django/core/files/__init__.py (modified) (1 diff)
- django/trunk/django/core/files/storage.py (added)
- django/trunk/django/core/files/uploadedfile.py (modified) (5 diffs)
- django/trunk/django/db/models/base.py (modified) (3 diffs)
- django/trunk/django/db/models/fields/files.py (added)
- django/trunk/django/db/models/fields/__init__.py (modified) (3 diffs)
- django/trunk/django/db/models/__init__.py (modified) (1 diff)
- django/trunk/django/db/models/manipulators.py (modified) (1 diff)
- django/trunk/django/utils/images.py (modified) (1 diff)
- django/trunk/docs/custom_model_fields.txt (modified) (1 diff)
- django/trunk/docs/db-api.txt (modified) (1 diff)
- django/trunk/docs/files.txt (added)
- django/trunk/docs/model-api.txt (modified) (1 diff)
- django/trunk/docs/settings.txt (modified) (1 diff)
- django/trunk/docs/upload_handling.txt (modified) (2 diffs)
- django/trunk/tests/modeltests/files (added)
- django/trunk/tests/modeltests/files/__init__.py (added)
- django/trunk/tests/modeltests/files/models.py (added)
- django/trunk/tests/modeltests/model_forms/models.py (modified) (20 diffs)
- django/trunk/tests/regressiontests/admin_widgets/models.py (modified) (4 diffs)
- django/trunk/tests/regressiontests/bug639/models.py (modified) (1 diff)
- django/trunk/tests/regressiontests/bug639/tests.py (modified) (1 diff)
- django/trunk/tests/regressiontests/file_storage (added)
- django/trunk/tests/regressiontests/file_storage/__init__.py (added)
- django/trunk/tests/regressiontests/file_storage/models.py (added)
- django/trunk/tests/regressiontests/file_storage/test.png (added)
- django/trunk/tests/regressiontests/file_storage/tests.py (added)
- django/trunk/tests/regressiontests/file_uploads/models.py (modified) (1 diff)
- django/trunk/tests/regressiontests/file_uploads/tests.py (modified) (3 diffs)
- django/trunk/tests/regressiontests/serializers_regress/models.py (modified) (1 diff)
- django/trunk/tests/regressiontests/serializers_regress/tests.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/trunk/django/conf/global_settings.py
r8162 r8244 227 227 JING_PATH = "/usr/bin/jing" 228 228 229 # Default file storage mechanism that holds media. 230 DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage' 231 229 232 # Absolute path to the directory that holds media. 230 233 # Example: "/home/media/media.lawrence.com/" django/trunk/django/contrib/admin/widgets.py
r8240 r8244 86 86 output = [] 87 87 if value: 88 output.append('%s <a target="_blank" href="%s %s">%s</a> <br />%s ' % \89 (_('Currently:'), settings.MEDIA_URL, value, value, _('Change:')))88 output.append('%s <a target="_blank" href="%s">%s</a> <br />%s ' % \ 89 (_('Currently:'), value.url, value, _('Change:'))) 90 90 output.append(super(AdminFileWidget, self).render(name, value, attrs)) 91 91 return mark_safe(u''.join(output)) django/trunk/django/core/files/__init__.py
r7814 r8244 1 from django.core.files.base import File django/trunk/django/core/files/uploadedfile.py
r8217 r8244 11 11 12 12 from django.conf import settings 13 from django.core.files.base import File 13 14 14 15 from django.core.files import temp as tempfile … … 40 41 return property(getter, setter) 41 42 42 class UploadedFile( object):43 class UploadedFile(File): 43 44 """ 44 45 A abstract uploaded file (``TemporaryUploadedFile`` and … … 76 77 77 78 name = property(_get_name, _set_name) 78 79 def chunks(self, chunk_size=None):80 """81 Read the file and yield chucks of ``chunk_size`` bytes (defaults to82 ``UploadedFile.DEFAULT_CHUNK_SIZE``).83 """84 if not chunk_size:85 chunk_size = UploadedFile.DEFAULT_CHUNK_SIZE86 87 if hasattr(self, 'seek'):88 self.seek(0)89 # Assume the pointer is at zero...90 counter = self.size91 92 while counter > 0:93 yield self.read(chunk_size)94 counter -= chunk_size95 79 96 80 # Deprecated properties … … 109 93 data = property(_get_data) 110 94 111 def multiple_chunks(self, chunk_size=None):112 """113 Returns ``True`` if you can expect multiple chunks.114 115 NB: If a particular file representation is in memory, subclasses should116 always return ``False`` -- there's no good reason to read from memory in117 chunks.118 """119 if not chunk_size:120 chunk_size = UploadedFile.DEFAULT_CHUNK_SIZE121 return self.size > chunk_size122 123 95 # Abstract methods; subclasses *must* define read() and probably should 124 96 # define open/close. … … 131 103 def close(self): 132 104 pass 133 134 def xreadlines(self):135 return self136 137 def readlines(self):138 return list(self.xreadlines())139 140 def __iter__(self):141 # Iterate over this file-like object by newlines142 buffer_ = None143 for chunk in self.chunks():144 chunk_buffer = StringIO(chunk)145 146 for line in chunk_buffer:147 if buffer_:148 line = buffer_ + line149 buffer_ = None150 151 # If this is the end of a line, yield152 # otherwise, wait for the next round153 if line[-1] in ('\n', '\r'):154 yield line155 else:156 buffer_ = line157 158 if buffer_ is not None:159 yield buffer_160 105 161 106 # Backwards-compatible support for uploaded-files-as-dictionaries. django/trunk/django/db/models/base.py
r8223 r8244 4 4 import os 5 5 from itertools import izip 6 from warnings import warn 6 7 try: 7 8 set … … 13 14 from django.core import validators 14 15 from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError 15 from django.db.models.fields import AutoField , ImageField16 from django.db.models.fields import AutoField 16 17 from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField 17 18 from django.db.models.query import delete_objects, Q, CollectedObjects … … 464 465 465 466 def _get_FIELD_filename(self, field): 466 if getattr(self, field.attname): # Value is not blank. 467 return os.path.normpath(os.path.join(settings.MEDIA_ROOT, getattr(self, field.attname))) 468 return '' 467 warn("instance.get_%s_filename() is deprecated. Use instance.%s.path instead." % \ 468 (field.attname, field.attname), DeprecationWarning, stacklevel=3) 469 try: 470 return getattr(self, field.attname).path 471 except ValueError: 472 return '' 469 473 470 474 def _get_FIELD_url(self, field): 471 if getattr(self, field.attname): # Value is not blank. 472 import urlparse 473 return urlparse.urljoin(settings.MEDIA_URL, getattr(self, field.attname)).replace('\\', '/') 474 return '' 475 warn("instance.get_%s_url() is deprecated. Use instance.%s.url instead." % \ 476 (field.attname, field.attname), DeprecationWarning, stacklevel=3) 477 try: 478 return getattr(self, field.attname).url 479 except ValueError: 480 return '' 475 481 476 482 def _get_FIELD_size(self, field): 477 return os.path.getsize(self._get_FIELD_filename(field)) 478 479 def _save_FIELD_file(self, field, filename, raw_field, save=True): 480 # Create the upload directory if it doesn't already exist 481 directory = os.path.join(settings.MEDIA_ROOT, field.get_directory_name()) 482 if not os.path.exists(directory): 483 os.makedirs(directory) 484 elif not os.path.isdir(directory): 485 raise IOError('%s exists and is not a directory' % directory) 486 487 # Check for old-style usage (files-as-dictionaries). Warn here first 488 # since there are multiple locations where we need to support both new 489 # and old usage. 490 if isinstance(raw_field, dict): 491 import warnings 492 warnings.warn( 493 message = "Representing uploaded files as dictionaries is deprecated. Use django.core.files.uploadedfile.SimpleUploadedFile instead.", 494 category = DeprecationWarning, 495 stacklevel = 2 496 ) 497 from django.core.files.uploadedfile import SimpleUploadedFile 498 raw_field = SimpleUploadedFile.from_dict(raw_field) 499 500 elif isinstance(raw_field, basestring): 501 import warnings 502 warnings.warn( 503 message = "Representing uploaded files as strings is deprecated. Use django.core.files.uploadedfile.SimpleUploadedFile instead.", 504 category = DeprecationWarning, 505 stacklevel = 2 506 ) 507 from django.core.files.uploadedfile import SimpleUploadedFile 508 raw_field = SimpleUploadedFile(filename, raw_field) 509 510 if filename is None: 511 filename = raw_field.file_name 512 513 filename = field.get_filename(filename) 514 515 # If the filename already exists, keep adding an underscore to the name 516 # of the file until the filename doesn't exist. 517 while os.path.exists(os.path.join(settings.MEDIA_ROOT, filename)): 518 try: 519 dot_index = filename.rindex('.') 520 except ValueError: # filename has no dot. 521 filename += '_' 522 else: 523 filename = filename[:dot_index] + '_' + filename[dot_index:] 524 525 # Save the file name on the object and write the file to disk. 526 setattr(self, field.attname, filename) 527 full_filename = self._get_FIELD_filename(field) 528 if hasattr(raw_field, 'temporary_file_path'): 529 # This file has a file path that we can move. 530 file_move_safe(raw_field.temporary_file_path(), full_filename) 531 raw_field.close() 532 else: 533 # This is a normal uploadedfile that we can stream. 534 fp = open(full_filename, 'wb') 535 locks.lock(fp, locks.LOCK_EX) 536 for chunk in raw_field.chunks(): 537 fp.write(chunk) 538 locks.unlock(fp) 539 fp.close() 540 541 # Save the width and/or height, if applicable. 542 if isinstance(field, ImageField) and \ 543 (field.width_field or field.height_field): 544 from django.utils.images import get_image_dimensions 545 width, height = get_image_dimensions(full_filename) 546 if field.width_field: 547 setattr(self, field.width_field, width) 548 if field.height_field: 549 setattr(self, field.height_field, height) 550 551 # Save the object because it has changed, unless save is False. 552 if save: 553 self.save() 483 warn("instance.get_%s_size() is deprecated. Use instance.%s.size instead." % \ 484 (field.attname, field.attname), DeprecationWarning, stacklevel=3) 485 return getattr(self, field.attname).size 486 487 def _save_FIELD_file(self, field, filename, content, save=True): 488 warn("instance.save_%s_file() is deprecated. Use instance.%s.save() instead." % \ 489 (field.attname, field.attname), DeprecationWarning, stacklevel=3) 490 return getattr(self, field.attname).save(filename, content, save) 554 491 555 492 _save_FIELD_file.alters_data = True 556 493 557 494 def _get_FIELD_width(self, field): 558 return self._get_image_dimensions(field)[0] 495 warn("instance.get_%s_width() is deprecated. Use instance.%s.width instead." % \ 496 (field.attname, field.attname), DeprecationWarning, stacklevel=3) 497 return getattr(self, field.attname).width() 559 498 560 499 def _get_FIELD_height(self, field): 561 return self._get_image_dimensions(field)[1] 562 563 def _get_image_dimensions(self, field): 564 cachename = "__%s_dimensions_cache" % field.name 565 if not hasattr(self, cachename): 566 from django.utils.images import get_image_dimensions 567 filename = self._get_FIELD_filename(field) 568 setattr(self, cachename, get_image_dimensions(filename)) 569 return getattr(self, cachename) 500 warn("instance.get_%s_height() is deprecated. Use instance.%s.height instead." % \ 501 (field.attname, field.attname), DeprecationWarning, stacklevel=3) 502 return getattr(self, field.attname).height() 570 503 571 504 django/trunk/django/db/models/fields/__init__.py
r8229 r8244 11 11 from django.db.models import signals 12 12 from django.db.models.query_utils import QueryWrapper 13 from django.dispatch import dispatcher 13 14 from django.conf import settings 14 15 from django.core import validators … … 758 759 return super(EmailField, self).formfield(**defaults) 759 760 760 class FileField(Field):761 def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):762 self.upload_to = upload_to763 kwargs['max_length'] = kwargs.get('max_length', 100)764 Field.__init__(self, verbose_name, name, **kwargs)765 766 def get_internal_type(self):767 return "FileField"768 769 def get_db_prep_value(self, value):770 "Returns field's value prepared for saving into a database."771 # Need to convert UploadedFile objects provided via a form to unicode for database insertion772 if hasattr(value, 'name'):773 return value.name774 elif value is None:775 return None776 else:777 return unicode(value)778 779 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):780 field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)781 if not self.blank:782 if rel:783 # This validator makes sure FileFields work in a related context.784 class RequiredFileField(object):785 def __init__(self, other_field_names, other_file_field_name):786 self.other_field_names = other_field_names787 self.other_file_field_name = other_file_field_name788 self.always_test = True789 def __call__(self, field_data, all_data):790 if not all_data.get(self.other_file_field_name, False):791 c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, ugettext_lazy("This field is required."))792 c(field_data, all_data)793 # First, get the core fields, if any.794 core_field_names = []795 for f in opts.fields:796 if f.core and f != self:797 core_field_names.extend(f.get_manipulator_field_names(name_prefix))798 # Now, if there are any, add the validator to this FormField.799 if core_field_names:800 field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name))801 else:802 v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, ugettext_lazy("This field is required."))803 v.always_test = True804 field_list[0].validator_list.append(v)805 field_list[0].is_required = field_list[1].is_required = False806 807 # If the raw path is passed in, validate it's under the MEDIA_ROOT.808 def isWithinMediaRoot(field_data, all_data):809 f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data))810 if not f.startswith(os.path.abspath(os.path.normpath(settings.MEDIA_ROOT))):811 raise validators.ValidationError, _("Enter a valid filename.")812 field_list[1].validator_list.append(isWithinMediaRoot)813 return field_list814 815 def contribute_to_class(self, cls, name):816 super(FileField, self).contribute_to_class(cls, name)817 setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self))818 setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self))819 setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self))820 setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_field, save=True: instance._save_FIELD_file(self, filename, raw_field, save))821 signals.post_delete.connect(self.delete_file, sender=cls)822 823 def delete_file(self, instance, **kwargs):824 if getattr(instance, self.attname):825 file_name = getattr(instance, 'get_%s_filename' % self.name)()826 # If the file exists and no other object of this type references it,827 # delete it from the filesystem.828 if os.path.exists(file_name) and \829 not instance.__class__._default_manager.filter(**{'%s__exact' % self.name: getattr(instance, self.attname)}):830 os.remove(file_name)831 832 def get_manipulator_field_objs(self):833 return [oldforms.FileUploadField, oldforms.HiddenField]834 835 def get_manipulator_field_names(self, name_prefix):836 return [name_prefix + self.name + '_file', name_prefix + self.name]837 838 def save_file(self, new_data, new_object, original_object, change, rel, save=True):839 upload_field_name = self.get_manipulator_field_names('')[0]840 if new_data.get(upload_field_name, False):841 if rel:842 file = new_data[upload_field_name][0]843 else:844 file = new_data[upload_field_name]845 846 if not file:847 return848 849 # Backwards-compatible support for files-as-dictionaries.850 # We don't need to raise a warning because Model._save_FIELD_file will851 # do so for us.852 try:853 file_name = file.name854 except AttributeError:855 file_name = file['filename']856 857 func = getattr(new_object, 'save_%s_file' % self.name)858 func(file_name, file, save)859 860 def get_directory_name(self):861 return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to))))862 863 def get_filename(self, filename):864 from django.utils.text import get_valid_filename865 f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename)))866 return os.path.normpath(f)867 868 def save_form_data(self, instance, data):869 from django.core.files.uploadedfile import UploadedFile870 if data and isinstance(data, UploadedFile):871 getattr(instance, "save_%s_file" % self.name)(data.name, data, save=False)872 873 def formfield(self, **kwargs):874 defaults = {'form_class': forms.FileField}875 # If a file has been provided previously, then the form doesn't require876 # that a new file is provided this time.877 # The code to mark the form field as not required is used by878 # form_for_instance, but can probably be removed once form_for_instance879 # is gone. ModelForm uses a different method to check for an existing file.880 if 'initial' in kwargs:881 defaults['required'] = False882 defaults.update(kwargs)883 return super(FileField, self).formfield(**defaults)884 885 761 class FilePathField(Field): 886 762 def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs): … … 923 799 defaults.update(kwargs) 924 800 return super(FloatField, self).formfield(**defaults) 925 926 class ImageField(FileField):927 def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):928 self.width_field, self.height_field = width_field, height_field929 FileField.__init__(self, verbose_name, name, **kwargs)930 931 def get_manipulator_field_objs(self):932 return [oldforms.ImageUploadField, oldforms.HiddenField]933 934 def contribute_to_class(self, cls, name):935 super(ImageField, self).contribute_to_class(cls, name)936 # Add get_BLAH_width and get_BLAH_height methods, but only if the937 # image field doesn't have width and height cache fields.938 if not self.width_field:939 setattr(cls, 'get_%s_width' % self.name, curry(cls._get_FIELD_width, field=self))940 if not self.height_field:941 setattr(cls, 'get_%s_height' % self.name, curry(cls._get_FIELD_height, field=self))942 943 def save_file(self, new_data, new_object, original_object, change, rel, save=True):944 FileField.save_file(self, new_data, new_object, original_object, change, rel, save)945 # If the image has height and/or width field(s) and they haven't946 # changed, set the width and/or height field(s) back to their original947 # values.948 if change and (self.width_field or self.height_field) and save:949 if self.width_field:950 setattr(new_object, self.width_field, getattr(original_object, self.width_field))951 if self.height_field:952 setattr(new_object, self.height_field, getattr(original_object, self.height_field))953 new_object.save()954 955 def formfield(self, **kwargs):956 defaults = {'form_class': forms.ImageField}957 defaults.update(kwargs)958 return super(ImageField, self).formfield(**defaults)959 801 960 802 class IntegerField(Field): django/trunk/django/db/models/__init__.py
r8046 r8244 9 9 from django.db.models.fields import * 10 10 from django.db.models.fields.subclassing import SubfieldBase 11 from django.db.models.fields.files import FileField, ImageField 11 12 from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel, TABULAR, STACKED 12 13 from django.db.models import signals django/trunk/django/db/models/manipulators.py
r8223 r8244 2 2 from django import oldforms 3 3 from django.core import validators 4 from django.db.models.fields import FileField, AutoField 4 from django.db.models.fields import AutoField 5 from django.db.models.fields.files import FileField 5 6 from django.db.models import signals 6 7 from django.utils.functional import curry django/trunk/django/utils/images.py
r4265 r8244 1 """ 2 Utility functions for handling images. 1 import warnings 3 2 4 Requires PIL, as you might imagine. 5 """ 3 from django.core.files.images import get_image_dimensions 6 4 7 import ImageFile 8 9 def get_image_dimensions(path): 10 """Returns the (width, height) of an image at a given path.""" 11 p = ImageFile.Parser() 12 fp = open(path, 'rb') 13 while 1: 14 data = fp.read(1024) 15 if not data: 16 break 17 p.feed(data) 18 if p.image: 19 return p.image.size 20 break 21 fp.close() 22 return None 5 warnings.warn("django.utils.images has been moved to django.core.files.images.", DeprecationWarning) django/trunk/docs/custom_model_fields.txt
r8152 r8244 597 597 automatically converts to the string form of your Python object, you can 598 598 save yourself a lot of work. 599 600 Writing a ``FileField`` subclass 601 ================================= 602 603 In addition to the above methods, fields that deal with files have a few other 604 special requirements which must be taken into account. The majority of the 605 mechanics provided by ``FileField``, such as controlling database storage and 606 retrieval, can remain unchanged, leaving subclasses to deal with the challenge 607 of supporting a particular type of file. 608 609 Django provides a ``File`` class, which is used as a proxy to the file's 610 contents and operations. This can be subclassed to customzie hwo the file is 611 accessed, and what methods are available. It lives at 612 ``django.db.models.fields.files``, and its default behavior is explained in the 613 `file documentation`_. 614 615 Once a subclass of ``File`` is created, the new ``FileField`` subclass must be 616 told to use it. To do so, simply assign the new ``File`` subclass to the special 617 ``attr_class`` attribute of the ``FileField`` subclass. 618 619 .. _file documentation: ../files/ 620 621 A few suggestions 622 ------------------ 623 624 In addition to the above details, there are a few guidelines which can greatly 625 improve the efficiency and readability of the field's code. 626 627 1. The source for Django's own ``ImageField`` (in 628 ``django/db/models/fields/files.py``) is a great example of how to 629 subclass ``FileField`` to support a particular type of file, as it 630 incorporates all of the techniques described above. 631 632 2. Cache file attributes wherever possible. Since files may be stored in 633 remote storage systems, retrieving them may cost extra time, or even 634 money, that isn't always necessary. Once a file is retrieved to obtain 635 some data about its content, cache as much of that data as possible to 636 reduce the number of times the file must be retrieved on subsequent 637 calls for that information. django/trunk/docs/db-api.txt
r8232 r8244 2299 2299 ------------------ 2300 2300 2301 For every ``FileField``, the object will have a ``get_FOO_filename()`` method, 2302 where ``FOO`` is the name of the field. This returns the full filesystem path 2303 to the file, according to your ``MEDIA_ROOT`` setting. 2304 2305 .. note:: 2306 It is only valid to call this method **after** saving the model when the 2307 field has been set. Prior to saving, the value returned will not contain 2308 the upload directory (the `upload_to` parameter) in the path. 2309 2310 Note that ``ImageField`` is technically a subclass of ``FileField``, so every 2311 model with an ``ImageField`` will also get this method. 2301 **Deprecated in Django development version**; use ``object.FOO.name`` instead. 2302 See `managing files`_ for details. 2312 2303 2313 2304 get_FOO_url() 2314 2305 ------------- 2315 2306 2316 For every ``FileField``, the object will have a ``get_FOO_url()`` method, 2317 where ``FOO`` is the name of the field. This returns the full URL to the file, 2318 according to your ``MEDIA_URL`` setting. If the value is blank, this method 2319 returns an empty string. 2320 2321 .. note:: 2322 As with ``get_FOO_filename()``, it is only valid to call this method 2323 **after** saving the model, otherwise an incorrect result will be 2324 returned. 2307 **Deprecated in Django development version**; use ``object.FOO.url`` instead. 2308 See `managing files`_ for details. 2325 2309 2326 2310 get_FOO_size() 2327 2311 -------------- 2328 2312 2329 For every ``FileField``, the object will have a ``get_FOO_size()`` method, 2330 where ``FOO`` is the name of the field. This returns the size of the file, in 2331 bytes. (Behind the scenes, it uses ``os.path.getsize``.) 2313 **Deprecated in Django development version**; use ``object.FOO.size`` instead. 2314 See `managing files`_ for details. 2332 2315 2333 2316 save_FOO_file(filename, raw_contents) 2334 2317 ------------------------------------- 2335 2318 2336 For every ``FileField``, the object will have a ``save_FOO_file()`` method, 2337 where ``FOO`` is the name of the field. This saves the given file to the 2338 filesystem, using the given filename. If a file with the given filename already 2339 exists, Django adds an underscore to the end of the filename (but before the 2340 extension) until the filename is available. 2319 **Deprecated in Django development version**; use ``object.FOO.save()`` instead. 2320 See `managing files`_ for details. 2341 2321 2342 2322 get_FOO_height() and get_FOO_width() 2343 2323 ------------------------------------ 2344 2324 2345 For every ``ImageField``, the object will have ``get_FOO_height()`` and 2346 ``get_FOO_width()`` methods, where ``FOO`` is the name of the field. This 2347 returns the height (or width) of the image, as an integer, in pixels. 2325 **Deprecated in Django development version**; use ``object.FOO.width`` and 2326 ``object.FOO.height`` instead. See `managing files`_ for details. 2327 2328 .. _`managing files`: ../files/ 2348 2329 2349 2330 Shortcuts django/trunk/docs/model-api.txt
r8213 r8244 225 225 ~~~~~~~~~~~~~ 226 226 227 A file-upload field. Has one **required** argument: 227 A file-upload field. Has two special arguments, of which the first is 228 **required**: 228 229 229 230 ====================== =================================================== 230 231 Argument Description 231 232 ====================== =================================================== 232 ``upload_to`` A local filesystem path that will be appended to 233 your ``MEDIA_ROOT`` setting to determine the 234 output of the ``get_<fieldname>_url()`` helper 235 function. 233 ``upload_to`` Required. A filesystem-style path that will be 234 prepended to the filename before being committed to 235 the final storage destination. 236 237 **New in Django development version** 238 239 This may also be a callable, such as a function, 240 which will be called to obtain the upload path, 241 including the filename. See below for details. 242 243 ``storage`` **New in Django development version** 244 245 Optional. A storage object, which handles the 246 storage and retrieval of your files. See `managing 247 files`_ for details on how to provide this object. 236 248 ====================== =================================================== 237 249 238 This path may contain `strftime formatting`_, which will be replaced by the 239 date/time of the file upload (so that uploaded files don't fill up the given 240 directory). 250 .. _managing files: ../files/ 251 252 The ``upload_to`` path may contain `strftime formatting`_, which will be 253 replaced by the date/time of the file upload (so that uploaded files don't fill 254 up the given directory). 255 256 **New in Django development version** 257 258 If a callable is provided for the ``upload_to`` argument, that callable must be 259 able to accept two arguments, and return a Unix-style path (with forward 260 slashes) to be passed along to the storage system. The two arguments that will 261 be passed are: 262 263 ====================== =================================================== 264 Argument Description 265 ====================== =================================================== 266 ``instance`` An instance of the model where the ``FileField`` is 267 defined. More specifically, this is the particular 268 instance where the current file is being attached. 269 270 **Note**: In most cases, this object will not have 271 been saved to the database yet, so if it uses the 272 default ``AutoField``, *it might not yet have a 273 value for its primary key field*. 274 275 ``filename`` The filename that was originally given to the file. 276 This may or may not be taken into account when 277 determining the final destination path. 278 ====================== =================================================== 241 279 242 280 The admin represents this field as an ``<input type="file">`` (a file-upload 243 281 widget). 244 282 245 Using a ``FileField`` or an ``ImageField`` (see below) in a model takes a few246 s teps:283 Using a ``FileField`` or an ``ImageField`` (see below) in a model without a 284 specified storage system takes a few steps: 247 285 248 286 1. In your settings file, you'll need to define ``MEDIA_ROOT`` as the django/trunk/docs/settings.txt
r8147 r8244 426 426 isn't manually specified. Used with ``DEFAULT_CHARSET`` to construct the 427 427 ``Content-Type`` header. 428 429 DEFAULT_FILE_STORAGE 430 -------------------- 431 432 Default: ``'django.core.filestorage.filesystem.FileSystemStorage'`` 433 434 Default file storage class to be used for any file-related operations that don't 435 specify a particular storage system. See the `file documentation`_ for details. 436 437 .. _file documentation: ../files/ 428 438 429 439 DEFAULT_FROM_EMAIL django/trunk/docs/upload_handling.txt
r8092 r8244 156 156 ======================== 157 157 158 All ``UploadedFile`` objects define the following methods/attributes: 159 160 ``UploadedFile.read(self, num_bytes=None)`` 161 Returns a byte string of length ``num_bytes``, or the complete file if 162 ``num_bytes`` is ``None``. 163 164 ``UploadedFile.chunks(self, chunk_size=None)`` 165 A generator yielding small chunks from the file. If ``chunk_size`` isn't 166 given, chunks will be 64 KB. 167 168 ``UploadedFile.multiple_chunks(self, chunk_size=None)`` 169 Returns ``True`` if you can expect more than one chunk when calling 170 ``UploadedFile.chunks(self, chunk_size)``. 171 172 ``UploadedFile.size`` 173 The size, in bytes, of the uploaded file. 174 175 ``UploadedFile.name`` 176 The name of the uploaded file as provided by the user. 158 In addition to those inherited from `File`_, all ``UploadedFile`` objects define 159 the following methods/attributes: 177 160 178 161 ``UploadedFile.content_type`` … … 187 170 by the browser. Again, "trust but verify" is the best policy here. 188 171 189 ``UploadedFile.__iter__()``190 Iterates over the lines in the file.191 192 172 ``UploadedFile.temporary_file_path()`` 193 173 Only files uploaded onto disk will have this method; it returns the full 194 174 path to the temporary uploaded file. 195 175 176 .. _File: ../files/ 196 177 197 178 Upload Handlers django/trunk/tests/modeltests/model_forms/models.py
r8016 r8244 12 12 13 13 from django.db import models 14 from django.core.files.storage import FileSystemStorage 15 16 temp_storage = FileSystemStorage(tempfile.gettempdir()) 14 17 15 18 ARTICLE_STATUS = ( … … 61 64 class TextFile(models.Model): 62 65 description = models.CharField(max_length=20) 63 file = models.FileField( upload_to=tempfile.gettempdir())66 file = models.FileField(storage=temp_storage, upload_to='tests') 64 67 65 68 def __unicode__(self): … … 74 77 # If PIL is not available, this test is equivalent to TextFile above. 75 78 import Image, _imaging 76 image = models.ImageField( upload_to=tempfile.gettempdir())79 image = models.ImageField(storage=temp_storage, upload_to='tests') 77 80 except ImportError: 78 image = models.FileField( upload_to=tempfile.gettempdir())81 image = models.FileField(storage=temp_storage, upload_to='tests') 79 82 80 83 def __unicode__(self): … … 787 790 # FileField ################################################################### 788 791 792 # File forms. 793 789 794 >>> class TextFileForm(ModelForm): 790 795 ... class Meta: … … 809 814 >>> instance = f.save() 810 815 >>> instance.file 811 u'...test1.txt' 812 813 >>> os.unlink(instance.get_file_filename())816 <FieldFile: tests/test1.txt> 817 818 >>> instance.file.delete() 814 819 815 820 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test1.txt', 'hello world')}) … … 820 825 >>> instance = f.save() 821 826 >>> instance.file 822 u'...test1.txt' 827 <FieldFile: tests/test1.txt> 823 828 824 829 # Edit an instance that already has the file defined in the model. This will not … … 829 834 True 830 835 >>> f.cleaned_data['file'] 831 u'...test1.txt' 836 <FieldFile: tests/test1.txt> 832 837 >>> instance = f.save() 833 838 >>> instance.file 834 u'...test1.txt' 839 <FieldFile: tests/test1.txt> 835 840 836 841 # Delete the current file since this is not done by Django. 837 >>> os.unlink(instance.get_file_filename())842 >>> instance.file.delete() 838 843 839 844 # Override the file by uploading a new one. … … 844 849 >>> instance = f.save() 845 850 >>> instance.file 846 u'...test2.txt' 851 <FieldFile: tests/test2.txt>
