4 from django
.core
.files
import File
5 from django
.core
.files
.base
import ContentFile
6 from django
.core
.files
.images
import ImageFile
7 from django
.test
import TestCase
9 from models
import Image
, Person
, PersonWithHeight
, PersonWithHeightAndWidth
, \
10 PersonDimensionsFirst
, PersonTwoImages
, TestImageFieldFile
13 # If PIL available, do these tests.
16 from models
import temp_storage_dir
19 class ImageFieldTestMixin(object):
21 Mixin class to provide common functionality to ImageField test classes.
24 # Person model to use for tests.
25 PersonModel
= PersonWithHeightAndWidth
26 # File class to use for file instances.
31 Creates a pristine temp directory (or deletes and recreates if it
32 already exists) that the model uses as its storage directory.
34 Sets up two ImageFile instances for use in tests.
36 if os
.path
.exists(temp_storage_dir
):
37 shutil
.rmtree(temp_storage_dir
)
38 os
.mkdir(temp_storage_dir
)
40 file_path1
= os
.path
.join(os
.path
.dirname(__file__
), "4x8.png")
41 self
.file1
= self
.File(open(file_path1
, 'rb'))
43 file_path2
= os
.path
.join(os
.path
.dirname(__file__
), "8x4.png")
44 self
.file2
= self
.File(open(file_path2
, 'rb'))
48 Removes temp directory and all its contents.
50 shutil
.rmtree(temp_storage_dir
)
52 def check_dimensions(self
, instance
, width
, height
,
53 field_name
='mugshot'):
55 Asserts that the given width and height values match both the
56 field's height and width attributes and the height and width fields
57 (if defined) the image field is caching to.
59 Note, this method will check for dimension fields named by adding
60 "_width" or "_height" to the name of the ImageField. So, the
61 models used in these tests must have their fields named
64 By default, we check the field named "mugshot", but this can be
65 specified by passing the field_name parameter.
67 field
= getattr(instance
, field_name
)
68 # Check height/width attributes of field.
69 if width
is None and height
is None:
70 self
.assertRaises(ValueError, getattr, field
, 'width')
71 self
.assertRaises(ValueError, getattr, field
, 'height')
73 self
.assertEqual(field
.width
, width
)
74 self
.assertEqual(field
.height
, height
)
76 # Check height/width fields of model, if defined.
77 width_field_name
= field_name
+ '_width'
78 if hasattr(instance
, width_field_name
):
79 self
.assertEqual(getattr(instance
, width_field_name
), width
)
80 height_field_name
= field_name
+ '_height'
81 if hasattr(instance
, height_field_name
):
82 self
.assertEqual(getattr(instance
, height_field_name
), height
)
85 class ImageFieldTests(ImageFieldTestMixin
, TestCase
):
87 Tests for ImageField that don't need to be run with each of the
88 different test model classes.
91 def test_equal_notequal_hash(self
):
93 Bug #9786: Ensure '==' and '!=' work correctly.
94 Bug #9508: make sure hash() works as expected (equal items must
95 hash to the same value).
97 # Create two Persons with different mugshots.
98 p1
= self
.PersonModel(name
="Joe")
99 p1
.mugshot
.save("mug", self
.file1
)
100 p2
= self
.PersonModel(name
="Bob")
101 p2
.mugshot
.save("mug", self
.file2
)
102 self
.assertEqual(p1
.mugshot
== p2
.mugshot
, False)
103 self
.assertEqual(p1
.mugshot
!= p2
.mugshot
, True)
105 # Test again with an instance fetched from the db.
106 p1_db
= self
.PersonModel
.objects
.get(name
="Joe")
107 self
.assertEqual(p1_db
.mugshot
== p2
.mugshot
, False)
108 self
.assertEqual(p1_db
.mugshot
!= p2
.mugshot
, True)
110 # Instance from db should match the local instance.
111 self
.assertEqual(p1_db
.mugshot
== p1
.mugshot
, True)
112 self
.assertEqual(hash(p1_db
.mugshot
), hash(p1
.mugshot
))
113 self
.assertEqual(p1_db
.mugshot
!= p1
.mugshot
, False)
115 def test_instantiate_missing(self
):
117 If the underlying file is unavailable, still create instantiate the
118 object without error.
120 p
= self
.PersonModel(name
="Joan")
121 p
.mugshot
.save("shot", self
.file1
)
122 p
= self
.PersonModel
.objects
.get(name
="Joan")
123 path
= p
.mugshot
.path
124 shutil
.move(path
, path
+ '.moved')
125 p2
= self
.PersonModel
.objects
.get(name
="Joan")
127 def test_delete_when_missing(self
):
129 Bug #8175: correctly delete an object where the file no longer
130 exists on the file system.
132 p
= self
.PersonModel(name
="Fred")
133 p
.mugshot
.save("shot", self
.file1
)
134 os
.remove(p
.mugshot
.path
)
137 def test_size_method(self
):
139 Bug #8534: FileField.size should not leave the file open.
141 p
= self
.PersonModel(name
="Joan")
142 p
.mugshot
.save("shot", self
.file1
)
144 # Get a "clean" model instance
145 p
= self
.PersonModel
.objects
.get(name
="Joan")
146 # It won't have an opened file.
147 self
.assertEqual(p
.mugshot
.closed
, True)
149 # After asking for the size, the file should still be closed.
151 self
.assertEqual(p
.mugshot
.closed
, True)
153 def test_pickle(self
):
155 Tests that ImageField can be pickled, unpickled, and that the
156 image of the unpickled version is the same as the original.
160 p
= Person(name
="Joe")
161 p
.mugshot
.save("mug", self
.file1
)
162 dump
= pickle
.dumps(p
)
164 p2
= Person(name
="Bob")
165 p2
.mugshot
= self
.file1
167 loaded_p
= pickle
.loads(dump
)
168 self
.assertEqual(p
.mugshot
, loaded_p
.mugshot
)
171 class ImageFieldTwoDimensionsTests(ImageFieldTestMixin
, TestCase
):
173 Tests behavior of an ImageField and its dimensions fields.
176 def test_constructor(self
):
178 Tests assigning an image field through the model's constructor.
180 p
= self
.PersonModel(name
='Joe', mugshot
=self
.file1
)
181 self
.check_dimensions(p
, 4, 8)
183 self
.check_dimensions(p
, 4, 8)
185 def test_image_after_constructor(self
):
187 Tests behavior when image is not passed in constructor.
189 p
= self
.PersonModel(name
='Joe')
190 # TestImageField value will default to being an instance of its
191 # attr_class, a TestImageFieldFile, with name == None, which will
192 # cause it to evaluate as False.
193 self
.assertEqual(isinstance(p
.mugshot
, TestImageFieldFile
), True)
194 self
.assertEqual(bool(p
.mugshot
), False)
196 # Test setting a fresh created model instance.
197 p
= self
.PersonModel(name
='Joe')
198 p
.mugshot
= self
.file1
199 self
.check_dimensions(p
, 4, 8)
201 def test_create(self
):
203 Tests assigning an image in Manager.create().
205 p
= self
.PersonModel
.objects
.create(name
='Joe', mugshot
=self
.file1
)
206 self
.check_dimensions(p
, 4, 8)
208 def test_default_value(self
):
210 Tests that the default value for an ImageField is an instance of
211 the field's attr_class (TestImageFieldFile in this case) with no
212 name (name set to None).
214 p
= self
.PersonModel()
215 self
.assertEqual(isinstance(p
.mugshot
, TestImageFieldFile
), True)
216 self
.assertEqual(bool(p
.mugshot
), False)
218 def test_assignment_to_None(self
):
220 Tests that assigning ImageField to None clears dimensions.
222 p
= self
.PersonModel(name
='Joe', mugshot
=self
.file1
)
223 self
.check_dimensions(p
, 4, 8)
225 # If image assigned to None, dimension fields should be cleared.
227 self
.check_dimensions(p
, None, None)
229 p
.mugshot
= self
.file2
230 self
.check_dimensions(p
, 8, 4)
232 def test_field_save_and_delete_methods(self
):
234 Tests assignment using the field's save method and deletion using
235 the field's delete method.
237 p
= self
.PersonModel(name
='Joe')
238 p
.mugshot
.save("mug", self
.file1
)
239 self
.check_dimensions(p
, 4, 8)
241 # A new file should update dimensions.
242 p
.mugshot
.save("mug", self
.file2
)
243 self
.check_dimensions(p
, 8, 4)
245 # Field and dimensions should be cleared after a delete.
246 p
.mugshot
.delete(save
=False)
247 self
.assertEqual(p
.mugshot
, None)
248 self
.check_dimensions(p
, None, None)
250 def test_dimensions(self
):
252 Checks that dimensions are updated correctly in various situations.
254 p
= self
.PersonModel(name
='Joe')
256 # Dimensions should get set if file is saved.
257 p
.mugshot
.save("mug", self
.file1
)
258 self
.check_dimensions(p
, 4, 8)
260 # Test dimensions after fetching from database.
261 p
= self
.PersonModel
.objects
.get(name
='Joe')
262 # Bug 11084: Dimensions should not get recalculated if file is
263 # coming from the database. We test this by checking if the file
265 self
.assertEqual(p
.mugshot
.was_opened
, False)
266 self
.check_dimensions(p
, 4, 8)
267 # After checking dimensions on the image field, the file will have
269 self
.assertEqual(p
.mugshot
.was_opened
, True)
270 # Dimensions should now be cached, and if we reset was_opened and
271 # check dimensions again, the file should not have opened.
272 p
.mugshot
.was_opened
= False
273 self
.check_dimensions(p
, 4, 8)
274 self
.assertEqual(p
.mugshot
.was_opened
, False)
276 # If we assign a new image to the instance, the dimensions should
278 p
.mugshot
= self
.file2
279 self
.check_dimensions(p
, 8, 4)
280 # Dimensions were recalculated, and hence file should have opened.
281 self
.assertEqual(p
.mugshot
.was_opened
, True)
284 class ImageFieldNoDimensionsTests(ImageFieldTwoDimensionsTests
):
286 Tests behavior of an ImageField with no dimension fields.
292 class ImageFieldOneDimensionTests(ImageFieldTwoDimensionsTests
):
294 Tests behavior of an ImageField with one dimensions field.
297 PersonModel
= PersonWithHeight
300 class ImageFieldDimensionsFirstTests(ImageFieldTwoDimensionsTests
):
302 Tests behavior of an ImageField where the dimensions fields are
303 defined before the ImageField.
306 PersonModel
= PersonDimensionsFirst
309 class ImageFieldUsingFileTests(ImageFieldTwoDimensionsTests
):
311 Tests behavior of an ImageField when assigning it a File instance
312 rather than an ImageFile instance.
315 PersonModel
= PersonDimensionsFirst
319 class TwoImageFieldTests(ImageFieldTestMixin
, TestCase
):
321 Tests a model with two ImageFields.
324 PersonModel
= PersonTwoImages
326 def test_constructor(self
):
327 p
= self
.PersonModel(mugshot
=self
.file1
, headshot
=self
.file2
)
328 self
.check_dimensions(p
, 4, 8, 'mugshot')
329 self
.check_dimensions(p
, 8, 4, 'headshot')
331 self
.check_dimensions(p
, 4, 8, 'mugshot')
332 self
.check_dimensions(p
, 8, 4, 'headshot')
334 def test_create(self
):
335 p
= self
.PersonModel
.objects
.create(mugshot
=self
.file1
,
337 self
.check_dimensions(p
, 4, 8)
338 self
.check_dimensions(p
, 8, 4, 'headshot')
340 def test_assignment(self
):
341 p
= self
.PersonModel()
342 self
.check_dimensions(p
, None, None, 'mugshot')
343 self
.check_dimensions(p
, None, None, 'headshot')
345 p
.mugshot
= self
.file1
346 self
.check_dimensions(p
, 4, 8, 'mugshot')
347 self
.check_dimensions(p
, None, None, 'headshot')
348 p
.headshot
= self
.file2
349 self
.check_dimensions(p
, 4, 8, 'mugshot')
350 self
.check_dimensions(p
, 8, 4, 'headshot')
352 # Clear the ImageFields one at a time.
354 self
.check_dimensions(p
, None, None, 'mugshot')
355 self
.check_dimensions(p
, 8, 4, 'headshot')
357 self
.check_dimensions(p
, None, None, 'mugshot')
358 self
.check_dimensions(p
, None, None, 'headshot')
360 def test_field_save_and_delete_methods(self
):
361 p
= self
.PersonModel(name
='Joe')
362 p
.mugshot
.save("mug", self
.file1
)
363 self
.check_dimensions(p
, 4, 8, 'mugshot')
364 self
.check_dimensions(p
, None, None, 'headshot')
365 p
.headshot
.save("head", self
.file2
)
366 self
.check_dimensions(p
, 4, 8, 'mugshot')
367 self
.check_dimensions(p
, 8, 4, 'headshot')
369 # We can use save=True when deleting the image field with null=True
370 # dimension fields and the other field has an image.
371 p
.headshot
.delete(save
=True)
372 self
.check_dimensions(p
, 4, 8, 'mugshot')
373 self
.check_dimensions(p
, None, None, 'headshot')
374 p
.mugshot
.delete(save
=False)
375 self
.check_dimensions(p
, None, None, 'mugshot')
376 self
.check_dimensions(p
, None, None, 'headshot')
378 def test_dimensions(self
):
380 Checks that dimensions are updated correctly in various situations.
382 p
= self
.PersonModel(name
='Joe')
384 # Dimensions should get set for the saved file.
385 p
.mugshot
.save("mug", self
.file1
)
386 p
.headshot
.save("head", self
.file2
)
387 self
.check_dimensions(p
, 4, 8, 'mugshot')
388 self
.check_dimensions(p
, 8, 4, 'headshot')
390 # Test dimensions after fetching from database.
391 p
= self
.PersonModel
.objects
.get(name
='Joe')
392 # Bug 11084: Dimensions should not get recalculated if file is
393 # coming from the database. We test this by checking if the file
395 self
.assertEqual(p
.mugshot
.was_opened
, False)
396 self
.assertEqual(p
.headshot
.was_opened
, False)
397 self
.check_dimensions(p
, 4, 8,'mugshot')
398 self
.check_dimensions(p
, 8, 4, 'headshot')
399 # After checking dimensions on the image fields, the files will
401 self
.assertEqual(p
.mugshot
.was_opened
, True)
402 self
.assertEqual(p
.headshot
.was_opened
, True)
403 # Dimensions should now be cached, and if we reset was_opened and
404 # check dimensions again, the file should not have opened.
405 p
.mugshot
.was_opened
= False
406 p
.headshot
.was_opened
= False
407 self
.check_dimensions(p
, 4, 8,'mugshot')
408 self
.check_dimensions(p
, 8, 4, 'headshot')
409 self
.assertEqual(p
.mugshot
.was_opened
, False)
410 self
.assertEqual(p
.headshot
.was_opened
, False)
412 # If we assign a new image to the instance, the dimensions should
414 p
.mugshot
= self
.file2
415 p
.headshot
= self
.file1
416 self
.check_dimensions(p
, 8, 4, 'mugshot')
417 self
.check_dimensions(p
, 4, 8, 'headshot')
418 # Dimensions were recalculated, and hence file should have opened.
419 self
.assertEqual(p
.mugshot
.was_opened
, True)
420 self
.assertEqual(p
.headshot
.was_opened
, True)