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:
Jacob Kaplan-Moss 2009-01-16 21:48:37 +00:00
parent 79138a6106
commit 6332ad4804
3 changed files with 46 additions and 7 deletions

View File

@ -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)

View File

@ -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

View File

@ -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()