mirror of https://github.com/django/django.git
Objects with FileFields no longer get save() called multiple times from the AutomaticManipulator! This fixes #639, #572, and likely others I don't know of.
This may be slightly backwards-incompatible: if you've been relying on the multiple-save behavior (why?), then you'll no longer see that happen. git-svn-id: http://code.djangoproject.com/svn/django/trunk@4609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
f313e07b6e
commit
a30e3fca48
|
@ -356,7 +356,7 @@ class Model(object):
|
||||||
def _get_FIELD_size(self, field):
|
def _get_FIELD_size(self, field):
|
||||||
return os.path.getsize(self._get_FIELD_filename(field))
|
return os.path.getsize(self._get_FIELD_filename(field))
|
||||||
|
|
||||||
def _save_FIELD_file(self, field, filename, raw_contents):
|
def _save_FIELD_file(self, field, filename, raw_contents, save=True):
|
||||||
directory = field.get_directory_name()
|
directory = field.get_directory_name()
|
||||||
try: # Create the date-based directory if it doesn't exist.
|
try: # Create the date-based directory if it doesn't exist.
|
||||||
os.makedirs(os.path.join(settings.MEDIA_ROOT, directory))
|
os.makedirs(os.path.join(settings.MEDIA_ROOT, directory))
|
||||||
|
@ -391,8 +391,9 @@ class Model(object):
|
||||||
if field.height_field:
|
if field.height_field:
|
||||||
setattr(self, field.height_field, height)
|
setattr(self, field.height_field, height)
|
||||||
|
|
||||||
# Save the object, because it has changed.
|
# Save the object because it has changed unless save is False
|
||||||
self.save()
|
if save:
|
||||||
|
self.save()
|
||||||
|
|
||||||
_save_FIELD_file.alters_data = True
|
_save_FIELD_file.alters_data = True
|
||||||
|
|
||||||
|
|
|
@ -635,7 +635,7 @@ class FileField(Field):
|
||||||
setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self))
|
setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self))
|
||||||
setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self))
|
setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self))
|
||||||
setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self))
|
setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self))
|
||||||
setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_contents: instance._save_FIELD_file(self, filename, raw_contents))
|
setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_contents, save=True: instance._save_FIELD_file(self, filename, raw_contents, save))
|
||||||
dispatcher.connect(self.delete_file, signal=signals.post_delete, sender=cls)
|
dispatcher.connect(self.delete_file, signal=signals.post_delete, sender=cls)
|
||||||
|
|
||||||
def delete_file(self, instance):
|
def delete_file(self, instance):
|
||||||
|
@ -653,14 +653,14 @@ class FileField(Field):
|
||||||
def get_manipulator_field_names(self, name_prefix):
|
def get_manipulator_field_names(self, name_prefix):
|
||||||
return [name_prefix + self.name + '_file', name_prefix + self.name]
|
return [name_prefix + self.name + '_file', name_prefix + self.name]
|
||||||
|
|
||||||
def save_file(self, new_data, new_object, original_object, change, rel):
|
def save_file(self, new_data, new_object, original_object, change, rel, save=True):
|
||||||
upload_field_name = self.get_manipulator_field_names('')[0]
|
upload_field_name = self.get_manipulator_field_names('')[0]
|
||||||
if new_data.get(upload_field_name, False):
|
if new_data.get(upload_field_name, False):
|
||||||
func = getattr(new_object, 'save_%s_file' % self.name)
|
func = getattr(new_object, 'save_%s_file' % self.name)
|
||||||
if rel:
|
if rel:
|
||||||
func(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"])
|
func(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"], save)
|
||||||
else:
|
else:
|
||||||
func(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"])
|
func(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"], save)
|
||||||
|
|
||||||
def get_directory_name(self):
|
def get_directory_name(self):
|
||||||
return os.path.normpath(datetime.datetime.now().strftime(self.upload_to))
|
return os.path.normpath(datetime.datetime.now().strftime(self.upload_to))
|
||||||
|
@ -704,12 +704,12 @@ class ImageField(FileField):
|
||||||
if not self.height_field:
|
if not self.height_field:
|
||||||
setattr(cls, 'get_%s_height' % self.name, curry(cls._get_FIELD_height, field=self))
|
setattr(cls, 'get_%s_height' % self.name, curry(cls._get_FIELD_height, field=self))
|
||||||
|
|
||||||
def save_file(self, new_data, new_object, original_object, change, rel):
|
def save_file(self, new_data, new_object, original_object, change, rel, save=True):
|
||||||
FileField.save_file(self, new_data, new_object, original_object, change, rel)
|
FileField.save_file(self, new_data, new_object, original_object, change, rel, save)
|
||||||
# If the image has height and/or width field(s) and they haven't
|
# If the image has height and/or width field(s) and they haven't
|
||||||
# changed, set the width and/or height field(s) back to their original
|
# changed, set the width and/or height field(s) back to their original
|
||||||
# values.
|
# values.
|
||||||
if change and (self.width_field or self.height_field):
|
if change and (self.width_field or self.height_field) and save:
|
||||||
if self.width_field:
|
if self.width_field:
|
||||||
setattr(new_object, self.width_field, getattr(original_object, self.width_field))
|
setattr(new_object, self.width_field, getattr(original_object, self.width_field))
|
||||||
if self.height_field:
|
if self.height_field:
|
||||||
|
|
|
@ -96,14 +96,16 @@ class AutomaticManipulator(oldforms.Manipulator):
|
||||||
if self.change:
|
if self.change:
|
||||||
params[self.opts.pk.attname] = self.obj_key
|
params[self.opts.pk.attname] = self.obj_key
|
||||||
|
|
||||||
# First, save the basic object itself.
|
# First, create the basic object itself.
|
||||||
new_object = self.model(**params)
|
new_object = self.model(**params)
|
||||||
new_object.save()
|
|
||||||
|
|
||||||
# Now that the object's been saved, save any uploaded files.
|
# Now that the object's been created, save any uploaded files.
|
||||||
for f in self.opts.fields:
|
for f in self.opts.fields:
|
||||||
if isinstance(f, FileField):
|
if isinstance(f, FileField):
|
||||||
f.save_file(new_data, new_object, self.change and self.original_object or None, self.change, rel=False)
|
f.save_file(new_data, new_object, self.change and self.original_object or None, self.change, rel=False, save=False)
|
||||||
|
|
||||||
|
# Now save the object
|
||||||
|
new_object.save()
|
||||||
|
|
||||||
# Calculate which primary fields have changed.
|
# Calculate which primary fields have changed.
|
||||||
if self.change:
|
if self.change:
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import tempfile
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Photo(models.Model):
|
||||||
|
title = models.CharField(maxlength=30)
|
||||||
|
image = models.ImageField(upload_to=tempfile.gettempdir())
|
||||||
|
|
||||||
|
# Support code for the tests; this keeps track of how many times save() gets
|
||||||
|
# called on each instance.
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(Photo, self).__init__(*args, **kwargs)
|
||||||
|
self._savecount = 0
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
super(Photo, self).save()
|
||||||
|
self._savecount +=1
|
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
|
@ -0,0 +1,35 @@
|
||||||
|
"""
|
||||||
|
Tests for file field behavior, and specifically #639, in which Model.save() gets
|
||||||
|
called *again* for each FileField. This test will fail if calling an
|
||||||
|
auto-manipulator's save() method causes Model.save() to be called more than once.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
from regressiontests.bug639.models import Photo
|
||||||
|
from django.http import QueryDict
|
||||||
|
from django.utils.datastructures import MultiValueDict
|
||||||
|
|
||||||
|
class Bug639Test(unittest.TestCase):
|
||||||
|
|
||||||
|
def testBug639(self):
|
||||||
|
"""
|
||||||
|
Simulate a file upload and check how many times Model.save() gets called.
|
||||||
|
"""
|
||||||
|
# Grab an image for testing
|
||||||
|
img = open(os.path.join(os.path.dirname(__file__), "test.jpg"), "rb").read()
|
||||||
|
|
||||||
|
# Fake a request query dict with the file
|
||||||
|
qd = QueryDict("title=Testing&image=", mutable=True)
|
||||||
|
qd["image_file"] = {
|
||||||
|
"filename" : "test.jpg",
|
||||||
|
"content-type" : "image/jpeg",
|
||||||
|
"content" : img
|
||||||
|
}
|
||||||
|
|
||||||
|
manip = Photo.AddManipulator()
|
||||||
|
manip.do_html2python(qd)
|
||||||
|
p = manip.save(qd)
|
||||||
|
|
||||||
|
# Check the savecount stored on the object (see the model)
|
||||||
|
self.assertEqual(p._savecount, 1)
|
Loading…
Reference in New Issue