Fixed #10044: You can now assign directly to file fields (`instance.filefield = somefile`). Thanks, Marty Alchin.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@9766 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
79138a6106
commit
6332ad4804
|
@ -32,6 +32,8 @@ class File(object):
|
||||||
return self.size
|
return self.size
|
||||||
|
|
||||||
def _get_name(self):
|
def _get_name(self):
|
||||||
|
if not hasattr(self, '_name'):
|
||||||
|
raise ValueError("This operation requires the file to have a name.")
|
||||||
return self._name
|
return self._name
|
||||||
name = property(_get_name)
|
name = property(_get_name)
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
@ -21,6 +22,7 @@ class FieldFile(File):
|
||||||
self.storage = field.storage
|
self.storage = field.storage
|
||||||
self._name = name or u''
|
self._name = name or u''
|
||||||
self._closed = False
|
self._closed = False
|
||||||
|
self._committed = True
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
# Older code may be expecting FileField values to be simple strings.
|
# Older code may be expecting FileField values to be simple strings.
|
||||||
|
@ -79,6 +81,7 @@ class FieldFile(File):
|
||||||
|
|
||||||
# Update the filesize cache
|
# Update the filesize cache
|
||||||
self._size = len(content)
|
self._size = len(content)
|
||||||
|
self._committed = True
|
||||||
|
|
||||||
# Save the object because it has changed, unless save is False
|
# Save the object because it has changed, unless save is False
|
||||||
if save:
|
if save:
|
||||||
|
@ -100,6 +103,7 @@ class FieldFile(File):
|
||||||
# Delete the filesize cache
|
# Delete the filesize cache
|
||||||
if hasattr(self, '_size'):
|
if hasattr(self, '_size'):
|
||||||
del self._size
|
del self._size
|
||||||
|
self._committed = False
|
||||||
|
|
||||||
if save:
|
if save:
|
||||||
self.instance.save()
|
self.instance.save()
|
||||||
|
@ -110,7 +114,7 @@ class FieldFile(File):
|
||||||
# it's attached to in order to work properly, but the only necessary
|
# it's attached to in order to work properly, but the only necessary
|
||||||
# data to be pickled is the file's name itself. Everything else will
|
# data to be pickled is the file's name itself. Everything else will
|
||||||
# be restored later, by FileDescriptor below.
|
# be restored later, by FileDescriptor below.
|
||||||
return {'_name': self.name, '_closed': False}
|
return {'_name': self.name, '_closed': False, '_committed': True}
|
||||||
|
|
||||||
class FileDescriptor(object):
|
class FileDescriptor(object):
|
||||||
def __init__(self, field):
|
def __init__(self, field):
|
||||||
|
@ -120,10 +124,21 @@ class FileDescriptor(object):
|
||||||
if instance is None:
|
if instance is None:
|
||||||
raise AttributeError, "%s can only be accessed from %s instances." % (self.field.name(self.owner.__name__))
|
raise AttributeError, "%s can only be accessed from %s instances." % (self.field.name(self.owner.__name__))
|
||||||
file = instance.__dict__[self.field.name]
|
file = instance.__dict__[self.field.name]
|
||||||
if not isinstance(file, FieldFile):
|
if isinstance(file, basestring) or file is None:
|
||||||
# Create a new instance of FieldFile, based on a given file name
|
# Create a new instance of FieldFile, based on a given file name
|
||||||
instance.__dict__[self.field.name] = self.field.attr_class(instance, self.field, file)
|
instance.__dict__[self.field.name] = self.field.attr_class(instance, self.field, file)
|
||||||
elif not hasattr(file, 'field'):
|
elif isinstance(file, File) and not isinstance(file, FieldFile):
|
||||||
|
# Other types of files may be assigned as well, but they need to
|
||||||
|
# have the FieldFile interface added to them
|
||||||
|
file_copy = copy.copy(file)
|
||||||
|
file_copy.__class__ = type(file.__class__.__name__,
|
||||||
|
(file.__class__, FieldFile), {})
|
||||||
|
file_copy.instance = instance
|
||||||
|
file_copy.field = self.field
|
||||||
|
file_copy.storage = self.field.storage
|
||||||
|
file_copy._committed = False
|
||||||
|
instance.__dict__[self.field.name] = file_copy
|
||||||
|
elif isinstance(file, FieldFile) and not hasattr(file, 'field'):
|
||||||
# The FieldFile was pickled, so some attributes need to be reset.
|
# The FieldFile was pickled, so some attributes need to be reset.
|
||||||
file.instance = instance
|
file.instance = instance
|
||||||
file.field = self.field
|
file.field = self.field
|
||||||
|
@ -164,6 +179,14 @@ class FileField(Field):
|
||||||
return None
|
return None
|
||||||
return unicode(value)
|
return unicode(value)
|
||||||
|
|
||||||
|
def pre_save(self, model_instance, add):
|
||||||
|
"Returns field's value just before saving."
|
||||||
|
file = super(FileField, self).pre_save(model_instance, add)
|
||||||
|
if file and not file._committed:
|
||||||
|
# Commit the file to storage prior to saving the model
|
||||||
|
file.save(file.name, file, save=False)
|
||||||
|
return file
|
||||||
|
|
||||||
def contribute_to_class(self, cls, name):
|
def contribute_to_class(self, cls, name):
|
||||||
super(FileField, self).contribute_to_class(cls, name)
|
super(FileField, self).contribute_to_class(cls, name)
|
||||||
setattr(cls, self.name, FileDescriptor(self))
|
setattr(cls, self.name, FileDescriptor(self))
|
||||||
|
@ -190,10 +213,6 @@ class FileField(Field):
|
||||||
def generate_filename(self, instance, filename):
|
def generate_filename(self, instance, filename):
|
||||||
return os.path.join(self.get_directory_name(), self.get_filename(filename))
|
return os.path.join(self.get_directory_name(), self.get_filename(filename))
|
||||||
|
|
||||||
def save_form_data(self, instance, data):
|
|
||||||
if data and isinstance(data, UploadedFile):
|
|
||||||
getattr(instance, self.name).save(data.name, data, save=False)
|
|
||||||
|
|
||||||
def formfield(self, **kwargs):
|
def formfield(self, **kwargs):
|
||||||
defaults = {'form_class': forms.FileField}
|
defaults = {'form_class': forms.FileField}
|
||||||
# If a file has been provided previously, then the form doesn't require
|
# If a file has been provided previously, then the form doesn't require
|
||||||
|
|
|
@ -9,6 +9,7 @@ import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||||
from django.core.files.storage import FileSystemStorage
|
from django.core.files.storage import FileSystemStorage
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
|
||||||
|
@ -54,6 +55,23 @@ ValueError: The 'normal' attribute has no file associated with it.
|
||||||
>>> obj1.normal.read()
|
>>> obj1.normal.read()
|
||||||
'content'
|
'content'
|
||||||
|
|
||||||
|
# File objects can be assigned to FileField attributes, but shouldn't get
|
||||||
|
# committed until the model it's attached to is saved.
|
||||||
|
|
||||||
|
>>> obj1.normal = SimpleUploadedFile('assignment.txt', 'content')
|
||||||
|
>>> dirs, files = temp_storage.listdir('tests')
|
||||||
|
>>> dirs
|
||||||
|
[]
|
||||||
|
>>> files.sort()
|
||||||
|
>>> files
|
||||||
|
[u'default.txt', u'django_test.txt']
|
||||||
|
|
||||||
|
>>> obj1.save()
|
||||||
|
>>> dirs, files = temp_storage.listdir('tests')
|
||||||
|
>>> files.sort()
|
||||||
|
>>> files
|
||||||
|
[u'assignment.txt', u'default.txt', u'django_test.txt']
|
||||||
|
|
||||||
# Files can be read in a little at a time, if necessary.
|
# Files can be read in a little at a time, if necessary.
|
||||||
|
|
||||||
>>> obj1.normal.open()
|
>>> obj1.normal.open()
|
||||||
|
|
Loading…
Reference in New Issue