Ticket #5361: filestorage.3.diff
File filestorage.3.diff, 19.6 KB (added by , 17 years ago) |
---|
-
django/db/models/base.py
18 18 import types 19 19 import sys 20 20 import os 21 from warnings import warn 21 22 22 23 class ModelBase(type): 23 24 "Metaclass for all models" … … 359 360 return getattr(self, cachename) 360 361 361 362 def _get_FIELD_filename(self, field): 362 if getattr(self, field.attname): # value is not blank 363 return os.path.join(settings.MEDIA_ROOT, getattr(self, field.attname)) 364 return '' 363 warn("Use instance.%s.open() if you need access to the file." % field.attname, DeprecationWarning) 364 return field.backend._get_absolute_path(self.__dict__[field.attname]) 365 365 366 366 def _get_FIELD_url(self, field): 367 if getattr(self, field.attname): # value is not blank 368 import urlparse 369 return urlparse.urljoin(settings.MEDIA_URL, getattr(self, field.attname)).replace('\\', '/') 370 return '' 367 warn("Use instance.%s.get_absolute_url()." % field.attname, DeprecationWarning) 368 return getattr(self, field.attname).get_absolute_url() 371 369 372 370 def _get_FIELD_size(self, field): 373 return os.path.getsize(self._get_FIELD_filename(field)) 371 warn("Use instance.%s.get_filesize()." % field.attname, DeprecationWarning) 372 return getattr(self, field.attname).get_filesize() 374 373 375 374 def _save_FIELD_file(self, field, filename, raw_contents, save=True): 376 directory = field.get_directory_name() 377 try: # Create the date-based directory if it doesn't exist. 378 os.makedirs(os.path.join(settings.MEDIA_ROOT, directory)) 379 except OSError: # Directory probably already exists. 380 pass 381 filename = field.get_filename(filename) 375 warn("Use instance.%s.save_file()." % field.attname, DeprecationWarning) 376 return getattr(self, field.attname).save_file(filename, raw_contents, save) 382 377 383 # If the filename already exists, keep adding an underscore to the name of384 # the file until the filename doesn't exist.385 while os.path.exists(os.path.join(settings.MEDIA_ROOT, filename)):386 try:387 dot_index = filename.rindex('.')388 except ValueError: # filename has no dot389 filename += '_'390 else:391 filename = filename[:dot_index] + '_' + filename[dot_index:]392 393 # Write the file to disk.394 setattr(self, field.attname, filename)395 396 full_filename = self._get_FIELD_filename(field)397 fp = open(full_filename, 'wb')398 fp.write(raw_contents)399 fp.close()400 401 # Save the width and/or height, if applicable.402 if isinstance(field, ImageField) and (field.width_field or field.height_field):403 from django.utils.images import get_image_dimensions404 width, height = get_image_dimensions(full_filename)405 if field.width_field:406 setattr(self, field.width_field, width)407 if field.height_field:408 setattr(self, field.height_field, height)409 410 # Save the object because it has changed unless save is False411 if save:412 self.save()413 414 _save_FIELD_file.alters_data = True415 416 378 def _get_FIELD_width(self, field): 417 return self._get_image_dimensions(field)[0] 379 warn("Use instance.%s.get_width()." % field.attname, DeprecationWarning) 380 return getattr(self, field.attname).get_width() 418 381 419 382 def _get_FIELD_height(self, field): 420 return self._get_image_dimensions(field)[1] 383 warn("Use instance.%s.get_height()." % field.attname, DeprecationWarning) 384 return getattr(self, field.attname).get_height() 421 385 422 def _get_image_dimensions(self, field):423 cachename = "__%s_dimensions_cache" % field.name424 if not hasattr(self, cachename):425 from django.utils.images import get_image_dimensions426 filename = self._get_FIELD_filename(field)427 setattr(self, cachename, get_image_dimensions(filename))428 return getattr(self, cachename)429 430 386 ############################################ 431 387 # HELPER FUNCTIONS (CURRIED MODEL METHODS) # 432 388 ############################################ -
django/db/models/fields/__init__.py
1 1 2 import datetime 2 3 import os 3 4 import time … … 704 705 defaults.update(kwargs) 705 706 return super(EmailField, self).formfield(**defaults) 706 707 708 class File(object): 709 def __init__(self, obj, field, filename): 710 self.obj = obj 711 self.field = field 712 self.backend = field.backend 713 self.filename = filename 714 715 def __str__(self): 716 return self.backend.get_filename(self.filename) 717 718 def get_absolute_url(self): 719 return self.backend.get_absolute_url(self.filename) 720 721 def get_filesize(self): 722 if not hasattr(self, '_filesize'): 723 self._filesize = self.backend.get_filesize(self.filename) 724 return self._filesize 725 726 def open(self, mode='rb'): 727 return self.backend.open(self.filename, mode) 728 729 def save_file(self, filename, raw_contents, save=True): 730 self.filename = self.backend.save_file(filename, raw_contents) 731 732 # Update the filesize cache 733 self._filesize = len(raw_contents) 734 735 # Save the object because it has changed, unless save is False 736 if save: 737 self.obj.save() 738 739 class FileProxy(object): 740 def __init__(self, field): 741 self.field = field 742 self.cache_name = self.field.get_cache_name() 743 744 def __get__(self, instance=None, owner=None): 745 if instance is None: 746 raise AttributeError, "%s can only be accessed from %s instances." % (self.field.attname, self.owner.__name__) 747 return getattr(instance, self.cache_name) 748 749 def __set__(self, instance, value): 750 if hasattr(instance, self.cache_name): 751 raise AttributeError, "%s can not be set in this manner." % self.field.attname 752 instance.__dict__[self.field.attname] = value 753 attr = self.field.attr_class(instance, self.field, value) 754 setattr(instance, self.cache_name, attr) 755 707 756 class FileField(Field): 708 def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs): 709 self.upload_to = upload_to 757 attr_class = File 758 759 def __init__(self, verbose_name=None, name=None, upload_to='', backend=None, **kwargs): 760 if backend is None: 761 from django.core.urlresolvers import get_callable 762 backend = get_callable(settings.DEFAULT_FILESTORAGE_BACKEND)(location=upload_to) 763 self.backend = self.upload_to = backend 710 764 Field.__init__(self, verbose_name, name, **kwargs) 711 765 712 766 def get_db_prep_save(self, value): … … 714 768 # Need to convert UploadedFile objects provided via a form to unicode for database insertion 715 769 if value is None: 716 770 return None 717 return unicode(value )771 return unicode(value.filename) 718 772 719 773 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): 720 774 field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow) … … 754 808 755 809 def contribute_to_class(self, cls, name): 756 810 super(FileField, self).contribute_to_class(cls, name) 811 setattr(cls, self.attname, FileProxy(self)) 757 812 setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self)) 758 813 setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self)) 759 814 setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self)) 760 815 setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_contents, save=True: instance._save_FIELD_file(self, filename, raw_contents, save)) 761 816 dispatcher.connect(self.delete_file, signal=signals.post_delete, sender=cls) 762 817 763 def delete_file(self, instance): 764 if getattr(instance, self.attname): 765 file_name = getattr(instance, 'get_%s_filename' % self.name)() 766 # If the file exists and no other object of this type references it, 767 # delete it from the filesystem. 768 if os.path.exists(file_name) and \ 769 not instance.__class__._default_manager.filter(**{'%s__exact' % self.name: getattr(instance, self.attname)}): 770 os.remove(file_name) 818 def delete_file(self, instance, sender): 819 filename = getattr(instance, self.attname).filename 820 # If no other object of this type references the file, 821 # delete it from the backend. 822 if not sender._default_manager.filter(**{self.name: filename}): 823 self.backend.delete_file(filename) 771 824 772 825 def get_manipulator_field_objs(self): 773 826 return [oldforms.FileUploadField, oldforms.HiddenField] … … 778 831 def save_file(self, new_data, new_object, original_object, change, rel, save=True): 779 832 upload_field_name = self.get_manipulator_field_names('')[0] 780 833 if new_data.get(upload_field_name, False): 781 func = getattr(new_object, 'save_%s_file' % self.name)782 834 if rel: 783 f unc(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"], save)835 field = new_data[upload_field_name][0] 784 836 else: 785 func(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"], save) 837 field = new_data[upload_field_name] 838 getattr(new_object, self.attname).save_file(field["filename"], field["content"], save) 786 839 787 def get_directory_name(self):788 return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to))))789 790 def get_filename(self, filename):791 from django.utils.text import get_valid_filename792 f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename)))793 return os.path.normpath(f)794 795 840 def save_form_data(self, instance, data): 796 841 if data: 797 getattr(instance, "save_%s_file" % self.name)(data.filename, data.content, save=False)842 getattr(instance, self.attname).save_file(data.filename, data.content) 798 843 799 844 def formfield(self, **kwargs): 800 845 defaults = {'form_class': forms.FileField} … … 824 869 defaults.update(kwargs) 825 870 return super(FloatField, self).formfield(**defaults) 826 871 872 class ImageFile(File): 873 def get_width(self): 874 return self._get_image_dimensions()[0] 875 876 def get_height(self): 877 return self._get_image_dimensions()[1] 878 879 def _get_image_dimensions(self): 880 if not hasattr(self, '_dimensions_cache'): 881 from django.utils.images import get_image_dimensions 882 self._dimensions_cache = get_image_dimensions(self.open()) 883 return self._dimensions_cache 884 885 def save_file(self, filename, raw_contents): 886 super(ImageFile, self).save_file(filename, raw_contents) 887 888 # Update the cache for image dimensions 889 from django.utils.images import get_image_dimensions 890 from cStringIO import StringIO 891 self._dimensions_cache = get_image_dimensions(StringIO(raw_contents)) 892 827 893 class ImageField(FileField): 894 attr_class = ImageFile 895 828 896 def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs): 829 897 self.width_field, self.height_field = width_field, height_field 830 898 FileField.__init__(self, verbose_name, name, **kwargs) … … 842 910 setattr(cls, 'get_%s_height' % self.name, curry(cls._get_FIELD_height, field=self)) 843 911 844 912 def save_file(self, new_data, new_object, original_object, change, rel, save=True): 845 FileField.save_file(self, new_data, new_object, original_object, change, rel, save)846 913 # If the image has height and/or width field(s) and they haven't 847 914 # changed, set the width and/or height field(s) back to their original 848 915 # values. 849 if change and (self.width_field or self.height_field) and save: 850 if self.width_field: 851 setattr(new_object, self.width_field, getattr(original_object, self.width_field)) 852 if self.height_field: 853 setattr(new_object, self.height_field, getattr(original_object, self.height_field)) 854 new_object.save() 916 if self.width_field or self.height_field: 917 if original_object and not change: 918 if self.width_field: 919 setattr(new_object, self.width_field, getattr(original_object, self.width_field)) 920 if self.height_field: 921 setattr(new_object, self.height_field, getattr(original_object, self.height_field)) 922 else: 923 from cStringIO import StringIO 924 from django.utils.images import get_image_dimensions 855 925 926 upload_field_name = self.get_manipulator_field_names('')[0] 927 if rel: 928 field = new_data[upload_field_name][0] 929 else: 930 field = new_data[upload_field_name] 931 932 # Get the width and height from the raw content to avoid extra 933 # unnecessary trips to the file backend. 934 width, height = get_image_dimensions(StringIO(field["content"])) 935 936 if self.width_field: 937 setattr(new_object, self.width_field, width) 938 if self.height_field: 939 setattr(new_object, self.height_field, height) 940 FileField.save_file(self, new_data, new_object, original_object, change, rel, save) 941 856 942 def formfield(self, **kwargs): 857 943 defaults = {'form_class': forms.ImageField} 858 944 defaults.update(kwargs) -
django/conf/global_settings.py
250 250 from django import get_version 251 251 URL_VALIDATOR_USER_AGENT = "Django/%s (http://www.djangoproject.com)" % get_version() 252 252 253 # Default backend to use for saving FileFields 254 DEFAULT_FILESTORAGE_BACKEND = 'django.core.filestorage.filesystem.FileSystemBackend' 255 256 253 257 ############## 254 258 # MIDDLEWARE # 255 259 ############## -
django/utils/images.py
6 6 7 7 import ImageFile 8 8 9 def get_image_dimensions( path):10 """Returns the (width, height) of an image at a givenpath."""9 def get_image_dimensions(file_or_path): 10 """Returns the (width, height) of an image, given an open file or a path.""" 11 11 p = ImageFile.Parser() 12 fp = open(path, 'rb') 12 if hasattr(file_or_path, 'read'): 13 fp = file_or_path 14 else: 15 fp = open(file_or_path, 'rb') 13 16 while 1: 14 17 data = fp.read(1024) 15 18 if not data: … … 19 22 return p.image.size 20 23 break 21 24 fp.close() 22 return None 25 return None 26 No newline at end of file -
docs/db-api.txt
1851 1851 get_FOO_filename() 1852 1852 ------------------ 1853 1853 1854 **Deprecated in Django development version. See `managing files` for the new, 1855 preferred method for dealing with files.** 1856 1854 1857 For every ``FileField``, the object will have a ``get_FOO_filename()`` method, 1855 1858 where ``FOO`` is the name of the field. This returns the full filesystem path 1856 1859 to the file, according to your ``MEDIA_ROOT`` setting. … … 1861 1864 get_FOO_url() 1862 1865 ------------- 1863 1866 1867 **Deprecated in Django development version. See `managing files` for the new, 1868 preferred method for dealing with files.** 1869 1864 1870 For every ``FileField``, the object will have a ``get_FOO_url()`` method, 1865 1871 where ``FOO`` is the name of the field. This returns the full URL to the file, 1866 1872 according to your ``MEDIA_URL`` setting. If the value is blank, this method … … 1869 1875 get_FOO_size() 1870 1876 -------------- 1871 1877 1878 **Deprecated in Django development version. See `managing files` for the new, 1879 preferred method for dealing with files.** 1880 1872 1881 For every ``FileField``, the object will have a ``get_FOO_size()`` method, 1873 1882 where ``FOO`` is the name of the field. This returns the size of the file, in 1874 1883 bytes. (Behind the scenes, it uses ``os.path.getsize``.) … … 1876 1885 save_FOO_file(filename, raw_contents) 1877 1886 ------------------------------------- 1878 1887 1888 **Deprecated in Django development version. See `managing files` for the new, 1889 preferred method for dealing with files.** 1890 1879 1891 For every ``FileField``, the object will have a ``save_FOO_file()`` method, 1880 1892 where ``FOO`` is the name of the field. This saves the given file to the 1881 1893 filesystem, using the given filename. If a file with the given filename already … … 1885 1897 get_FOO_height() and get_FOO_width() 1886 1898 ------------------------------------ 1887 1899 1900 **Deprecated in Django development version. See `managing files` for the new, 1901 preferred method for dealing with files.** 1902 1888 1903 For every ``ImageField``, the object will have ``get_FOO_height()`` and 1889 1904 ``get_FOO_width()`` methods, where ``FOO`` is the name of the field. This 1890 1905 returns the height (or width) of the image, as an integer, in pixels. -
docs/model-api.txt
229 229 ``FileField`` 230 230 ~~~~~~~~~~~~~ 231 231 232 A file-upload field. Has one **required** argument:232 A file-upload field. **Requires** exactly one of the following two arguments: 233 233 234 234 ====================== =================================================== 235 235 Argument Description 236 236 ====================== =================================================== 237 237 ``upload_to`` A local filesystem path that will be appended to 238 238 your ``MEDIA_ROOT`` setting to determine the 239 output of the ``get_<fieldname>_url()`` helper 240 function. 239 final storage destination. If this argument is 240 supplied, the storage backend will default to 241 ``FileSystemBackend``. 242 ``backend`` **New in Django development version** 243 244 A storage backend object, which handles the storage 245 and retrieval of your files. See `managing files`_ 246 for details on how to provide this object. 241 247 ====================== =================================================== 242 248 243 This path may contain `strftime formatting`_, which will be replaced by the 244 date/time of the file upload (so that uploaded files don't fill up the given 245 directory). 249 .. _managing files: ../files/ 246 250 251 The ``upload_to`` path may contain `strftime formatting`_, which will be 252 replaced by the date/time of the file upload (so that uploaded files don't fill 253 up the given directory). 254 247 255 The admin represents this field as an ``<input type="file">`` (a file-upload 248 256 widget). 249 257 250 Using a ``FileField`` or an ``ImageField`` (see below) in a model takes a few251 s teps:258 Using a ``FileField`` or an ``ImageField`` (see below) in a model without a 259 specified backend takes a few steps: 252 260 253 261 1. In your settings file, you'll need to define ``MEDIA_ROOT`` as the 254 262 full path to a directory where you'd like Django to store uploaded