From ecc55503f40e375b957aa9dfaf8db34e8f348f63 Mon Sep 17 00:00:00 2001 From: Karen Tracey Date: Mon, 16 Feb 2009 18:34:28 +0000 Subject: [PATCH] Fixed #10196: Restored setting of image file width and height fields lost in r9766, and added tests for this function. Thanks to vicvicvic for the ticket and patch and for Alex for reminding me not to break non-PIL-boxes with the new tests. git-svn-id: http://code.djangoproject.com/svn/django/trunk@9841 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- AUTHORS | 1 + django/db/models/fields/files.py | 2 +- tests/modeltests/model_forms/models.py | 127 ++++++++++++++++++++----- tests/modeltests/model_forms/test2.png | Bin 0 -> 2072 bytes 4 files changed, 103 insertions(+), 27 deletions(-) create mode 100644 tests/modeltests/model_forms/test2.png diff --git a/AUTHORS b/AUTHORS index 210f52c5a50..c89fd5ccacc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -35,6 +35,7 @@ answer newbie questions, and generally made Django that much better: Jeff Anderson Marian Andre Andreas + Victor Andrée andy@jadedplanet.net Fabrice Aneche ant9000@netwise.it diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py index 878d09c9760..f9bfc9d03fa 100644 --- a/django/db/models/fields/files.py +++ b/django/db/models/fields/files.py @@ -132,7 +132,7 @@ class FileDescriptor(object): # have the FieldFile interface added to them file_copy = copy.copy(file) file_copy.__class__ = type(file.__class__.__name__, - (file.__class__, FieldFile), {}) + (file.__class__, self.field.attr_class), {}) file_copy.instance = instance file_copy.field = self.field file_copy.storage = self.field.storage diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index 6ef44d909b6..e12363cf3c6 100644 --- a/tests/modeltests/model_forms/models.py +++ b/tests/modeltests/model_forms/models.py @@ -98,26 +98,47 @@ class TextFile(models.Model): def __unicode__(self): return self.description -class ImageFile(models.Model): - def custom_upload_path(self, filename): - path = self.path or 'tests' - return '%s/%s' % (path, filename) +try: + # If PIL is available, try testing ImageFields. + # Checking for the existence of Image is enough for CPython, but + # for PyPy, you need to check for the underlying modules + # If PIL is not available, ImageField tests are omitted. + from PIL import Image, _imaging + test_images = True - description = models.CharField(max_length=20) - try: - # If PIL is available, try testing PIL. - # Checking for the existence of Image is enough for CPython, but - # for PyPy, you need to check for the underlying modules - # If PIL is not available, this test is equivalent to TextFile above. - from PIL import Image, _imaging - image = models.ImageField(storage=temp_storage, upload_to=custom_upload_path) - except ImportError: - image = models.FileField(storage=temp_storage, upload_to=custom_upload_path) - path = models.CharField(max_length=16, blank=True, default='') + class ImageFile(models.Model): + def custom_upload_path(self, filename): + path = self.path or 'tests' + return '%s/%s' % (path, filename) + + description = models.CharField(max_length=20) + image = models.ImageField(storage=temp_storage, upload_to=custom_upload_path, + width_field='width', height_field='height') + width = models.IntegerField(editable=False) + height = models.IntegerField(editable=False) + path = models.CharField(max_length=16, blank=True, default='') - def __unicode__(self): - return self.description + def __unicode__(self): + return self.description + + class OptionalImageFile(models.Model): + def custom_upload_path(self, filename): + path = self.path or 'tests' + return '%s/%s' % (path, filename) + + description = models.CharField(max_length=20) + image = models.ImageField(storage=temp_storage, upload_to=custom_upload_path, + width_field='width', height_field='height', + blank=True, null=True) + width = models.IntegerField(editable=False, null=True) + height = models.IntegerField(editable=False, null=True) + path = models.CharField(max_length=16, blank=True, default='') + def __unicode__(self): + return self.description +except ImportError: + test_images = False + class CommaSeparatedInteger(models.Model): field = models.CommaSeparatedIntegerField(max_length=20) @@ -1056,7 +1077,10 @@ True # Delete the current file since this is not done by Django. >>> instance.file.delete() >>> instance.delete() +"""} +if test_images: + __test__['API_TESTS'] += """ # ImageField ################################################################### # ImageField and FileField are nearly identical, but they differ slighty when @@ -1068,6 +1092,7 @@ True ... model = ImageFile >>> image_data = open(os.path.join(os.path.dirname(__file__), "test.png"), 'rb').read() +>>> image_data2 = open(os.path.join(os.path.dirname(__file__), "test2.png"), 'rb').read() >>> f = ImageFileForm(data={'description': u'An image'}, files={'image': SimpleUploadedFile('test.png', image_data)}) >>> f.is_valid() @@ -1077,6 +1102,10 @@ True >>> instance = f.save() >>> instance.image <...FieldFile: tests/test.png> +>>> instance.width +16 +>>> instance.height +16 # Delete the current file since this is not done by Django. >>> instance.image.delete() @@ -1089,8 +1118,12 @@ True >>> instance = f.save() >>> instance.image <...FieldFile: tests/test.png> +>>> instance.width +16 +>>> instance.height +16 -# Edit an instance that already has the image defined in the model. This will not +# Edit an instance that already has the (required) image defined in the model. This will not # save the image again, but leave it exactly as it is. >>> f = ImageFileForm(data={'description': u'Look, it changed'}, instance=instance) @@ -1101,6 +1134,10 @@ True >>> instance = f.save() >>> instance.image <...FieldFile: tests/test.png> +>>> instance.height +16 +>>> instance.width +16 # Delete the current image since this is not done by Django. @@ -1108,23 +1145,31 @@ True # Override the file by uploading a new one. ->>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': SimpleUploadedFile('test2.png', image_data)}, instance=instance) +>>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': SimpleUploadedFile('test2.png', image_data2)}, instance=instance) >>> f.is_valid() True >>> instance = f.save() >>> instance.image <...FieldFile: tests/test2.png> +>>> instance.height +32 +>>> instance.width +48 # Delete the current file since this is not done by Django. >>> instance.image.delete() >>> instance.delete() ->>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': SimpleUploadedFile('test2.png', image_data)}) +>>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': SimpleUploadedFile('test2.png', image_data2)}) >>> f.is_valid() True >>> instance = f.save() >>> instance.image <...FieldFile: tests/test2.png> +>>> instance.height +32 +>>> instance.width +48 # Delete the current file since this is not done by Django. >>> instance.image.delete() @@ -1132,31 +1177,58 @@ True # Test the non-required ImageField ->>> f = ImageFileForm(data={'description': u'Test'}) ->>> f.fields['image'].required = False +>>> class OptionalImageFileForm(ModelForm): +... class Meta: +... model = OptionalImageFile + +>>> f = OptionalImageFileForm(data={'description': u'Test'}) >>> f.is_valid() True >>> instance = f.save() >>> instance.image <...FieldFile: None> +>>> instance.width +>>> instance.height ->>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': SimpleUploadedFile('test3.png', image_data)}, instance=instance) +>>> f = OptionalImageFileForm(data={'description': u'And a final one'}, files={'image': SimpleUploadedFile('test3.png', image_data)}, instance=instance) >>> f.is_valid() True >>> instance = f.save() >>> instance.image <...FieldFile: tests/test3.png> +>>> instance.width +16 +>>> instance.height +16 + +# Editing the instance without re-uploading the image should not affect the image or its width/height properties +>>> f = OptionalImageFileForm(data={'description': u'New Description'}, instance=instance) +>>> f.is_valid() +True +>>> instance = f.save() +>>> instance.description +u'New Description' +>>> instance.image +<...FieldFile: tests/test3.png> +>>> instance.width +16 +>>> instance.height +16 # Delete the current file since this is not done by Django. >>> instance.image.delete() >>> instance.delete() ->>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': SimpleUploadedFile('test3.png', image_data)}) +>>> f = OptionalImageFileForm(data={'description': u'And a final one'}, files={'image': SimpleUploadedFile('test4.png', image_data2)}) >>> f.is_valid() True >>> instance = f.save() >>> instance.image -<...FieldFile: tests/test3.png> +<...FieldFile: tests/test4.png> +>>> instance.width +48 +>>> instance.height +32 >>> instance.delete() # Test callable upload_to behavior that's dependent on the value of another field in the model @@ -1167,6 +1239,9 @@ True >>> instance.image <...FieldFile: foo/test4.png> >>> instance.delete() +""" + +__test__['API_TESTS'] += """ # Media on a ModelForm ######################################################## @@ -1355,4 +1430,4 @@ ValidationError: [u'Select a valid choice. z is not one of the available choices # Clean up >>> import shutil >>> shutil.rmtree(temp_storage_dir) -"""} +""" diff --git a/tests/modeltests/model_forms/test2.png b/tests/modeltests/model_forms/test2.png new file mode 100644 index 0000000000000000000000000000000000000000..10702f75a1b6b450a6a91a69f187b30e7f87f3aa GIT binary patch literal 2072 zcmV+z2Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXD- z78NbAhr!YS00)>!L_t(o!?jo2ZX3xFt?KR>&hSq0B9am%%91R5vre!G7T6#OkcTA5 zOOU_FzvMapkdFuuAjv~s76>*A?5?kpMO`RL;(f@ubywwKWSX*NS!?h1Bb!rQw{z+g z{^f6f1ps2Y81*~n$H(WV^UwzXB^4sVk9-UnA`(JW3*}aAePgw$ViN-Z5o@D~m;nHO zbU&63CeJYgk%_fd-yJ>!0A}{XU^1T)lN15~?wFqBPYhs)wAR`fVgg`9R6;7HlvKnN zYpsnYA_hi8DWsB0DJ29V3Iz0{pf|kq!vKH&=imNt!Q!o~E7hyqav z@c~Vsq-<6iyUnfbX1kax2qA(fJUlyoa`b%vtdnyyyX#wft({V?C{Tn^c-rsncVF(G z9!~ukGb<^XnV5-4S<3pDln{|NS_;`%S^f1-zxdtfzsb6pe+{ri{yJR6)3y$LhPtIDAiLVcJkob(|8F%vTYD=96@7Wg{BN=hXq z0NBdTx>?6^7FTJ<&1JGeNML5>C99}D7#m633q*keQ5d2~$F$TjhKw;90Adr*!?|X? zxQgOv=Ff--5b!mq5diMw5Hk}SGGv&DnTZimN-2fZ#%QA>EwwSswEUGNt(5I3sm}W6 z|N81b7sLKaKIx&TJLnyBJF{Rer2=L^Tmbe?4k4mMB?YKgJY`8`E6cW037#!w<bqbAB@O zXUrg_#Jf0@q)|y#$`#u6&E55_d-aW0ZLLwR7cx1=wxzr^9%G^?G^AHcwO>8@;*Y=h zeY08zfLO=>d-2Ub{`>F#crfuM%q)cfL{6T2+Ym7`vp{rhCzsBwoqz ztIblr=vb*F(9vudMxi#EnNxNu8&aobSCTOGpWYi0nYWZH3yxuR=d zk!%=8LvPgW_s#|v!|B)$gD8$-9V;ngtqsu&s*Ron-oP6cvbjPg2Z(OUSuNH4F!aMgx@nd%FrpAb3L)l!e|p(_(s_=6ty;5~Eo7YZ=IWZ8a&m5_ zS+4!0eSbI|g>hs_t6gtx)i)f=UYHUngp@)Ek&Ith_7xbeXevqSZA>i?K?p?Q`Cg}Y zEKnFCfgZ6TCY&GKrsP`cl`HxK6XFbLzw7?XC~Vzyvg*Dxh-Sgztg%W?nc{Ip$fZPwSVPlgi$p0z5aNJfGhb@xv-LTG7E>a)n{TYt zHfrm8t({NW_xD=cm0~4j*#O}Avy;oyZw?+mIeKwEx-ewqE6U|N>Y?RKh^v+0<}F1V zZHO#styh|lwjO@A^XOi)-6+>q3Z=!XLvQ@-_~7gPZx7BogUQesl2X30lz!}9wv0^> zfC$t1^yOK{knWAno0UebShg)Y*70~YIqV%jJ$mu{WWPTdMsbv6bh!wI=!e`~mrYwr zRg0BYwb>}IN+D;#eB@1r)6rl$9C_nN#{jU9a@S!0U}JZAx1Y?XT5ACK;8Z1YN{9M# z+>heud~}I`k&gW^2;(q}qu6K$UXTSdBchvf^XXhZomCGvcM~4F9QThex}HBvM(H~w z)%(u^o7$McbTpWb-Y)K