summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Kaplan-Moss <jacob@jacobian.org>2009-01-16 21:48:37 +0000
committerJacob Kaplan-Moss <jacob@jacobian.org>2009-01-16 21:48:37 +0000
commit6332ad4804a040ec6a4869e69d484c7336a5cad1 (patch)
treea8ad9dd5970c01659000b028623a5f6b2a05dac0
parent79138a61065bf0c789d9f3f34c55de2e26fda422 (diff)
downloaddjango-6332ad4804a040ec6a4869e69d484c7336a5cad1.tar.gz
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
-rw-r--r--django/core/files/base.py2
-rw-r--r--django/db/models/fields/files.py33
-rw-r--r--tests/modeltests/files/models.py18
3 files changed, 46 insertions, 7 deletions
diff --git a/django/core/files/base.py b/django/core/files/base.py
index 69739d6488..1bf9330fed 100644
--- a/django/core/files/base.py
+++ b/django/core/files/base.py
@@ -32,6 +32,8 @@ class File(object):
return self.size
def _get_name(self):
+ if not hasattr(self, '_name'):
+ raise ValueError("This operation requires the file to have a name.")
return self._name
name = property(_get_name)
diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py
index 40b4e98026..ce76a14a7c 100644
--- a/django/db/models/fields/files.py
+++ b/django/db/models/fields/files.py
@@ -1,3 +1,4 @@
+import copy
import datetime
import os
@@ -21,6 +22,7 @@ class FieldFile(File):
self.storage = field.storage
self._name = name or u''
self._closed = False
+ self._committed = True
def __eq__(self, other):
# Older code may be expecting FileField values to be simple strings.
@@ -79,6 +81,7 @@ class FieldFile(File):
# Update the filesize cache
self._size = len(content)
+ self._committed = True
# Save the object because it has changed, unless save is False
if save:
@@ -100,6 +103,7 @@ class FieldFile(File):
# Delete the filesize cache
if hasattr(self, '_size'):
del self._size
+ self._committed = False
if save:
self.instance.save()
@@ -110,7 +114,7 @@ class FieldFile(File):
# 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
# be restored later, by FileDescriptor below.
- return {'_name': self.name, '_closed': False}
+ return {'_name': self.name, '_closed': False, '_committed': True}
class FileDescriptor(object):
def __init__(self, field):
@@ -120,10 +124,21 @@ class FileDescriptor(object):
if instance is None:
raise AttributeError, "%s can only be accessed from %s instances." % (self.field.name(self.owner.__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
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.
file.instance = instance
file.field = self.field
@@ -164,6 +179,14 @@ class FileField(Field):
return None
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):
super(FileField, self).contribute_to_class(cls, name)
setattr(cls, self.name, FileDescriptor(self))
@@ -190,10 +213,6 @@ class FileField(Field):
def generate_filename(self, instance, 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):
defaults = {'form_class': forms.FileField}
# If a file has been provided previously, then the form doesn't require
diff --git a/tests/modeltests/files/models.py b/tests/modeltests/files/models.py
index 3df3122cdb..741a28411b 100644
--- a/tests/modeltests/files/models.py
+++ b/tests/modeltests/files/models.py
@@ -9,6 +9,7 @@ import shutil
import tempfile
from django.db import models
from django.core.files.base import ContentFile
+from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.files.storage import FileSystemStorage
from django.core.cache import cache
@@ -54,6 +55,23 @@ ValueError: The 'normal' attribute has no file associated with it.
>>> obj1.normal.read()
'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.
>>> obj1.normal.open()