parts/django/tests/regressiontests/model_fields/imagefield.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     1 import os
       
     2 import shutil
       
     3 
       
     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
       
     8 
       
     9 from models import Image, Person, PersonWithHeight, PersonWithHeightAndWidth, \
       
    10         PersonDimensionsFirst, PersonTwoImages, TestImageFieldFile
       
    11 
       
    12 
       
    13 # If PIL available, do these tests.
       
    14 if Image:
       
    15 
       
    16     from models import temp_storage_dir
       
    17 
       
    18 
       
    19     class ImageFieldTestMixin(object):
       
    20         """
       
    21         Mixin class to provide common functionality to ImageField test classes.
       
    22         """
       
    23 
       
    24         # Person model to use for tests.
       
    25         PersonModel = PersonWithHeightAndWidth
       
    26         # File class to use for file instances.
       
    27         File = ImageFile
       
    28 
       
    29         def setUp(self):
       
    30             """
       
    31             Creates a pristine temp directory (or deletes and recreates if it
       
    32             already exists) that the model uses as its storage directory.
       
    33 
       
    34             Sets up two ImageFile instances for use in tests.
       
    35             """
       
    36             if os.path.exists(temp_storage_dir):
       
    37                 shutil.rmtree(temp_storage_dir)
       
    38             os.mkdir(temp_storage_dir)
       
    39 
       
    40             file_path1 = os.path.join(os.path.dirname(__file__), "4x8.png")
       
    41             self.file1 = self.File(open(file_path1, 'rb'))
       
    42 
       
    43             file_path2 = os.path.join(os.path.dirname(__file__), "8x4.png")
       
    44             self.file2 = self.File(open(file_path2, 'rb'))
       
    45 
       
    46         def tearDown(self):
       
    47             """
       
    48             Removes temp directory and all its contents.
       
    49             """
       
    50             shutil.rmtree(temp_storage_dir)
       
    51 
       
    52         def check_dimensions(self, instance, width, height,
       
    53                              field_name='mugshot'):
       
    54             """
       
    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.
       
    58 
       
    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
       
    62             accordingly.
       
    63 
       
    64             By default, we check the field named "mugshot", but this can be
       
    65             specified by passing the field_name parameter.
       
    66             """
       
    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')
       
    72             else:
       
    73                 self.assertEqual(field.width, width)
       
    74                 self.assertEqual(field.height, height)
       
    75 
       
    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)
       
    83 
       
    84 
       
    85     class ImageFieldTests(ImageFieldTestMixin, TestCase):
       
    86         """
       
    87         Tests for ImageField that don't need to be run with each of the
       
    88         different test model classes.
       
    89         """
       
    90 
       
    91         def test_equal_notequal_hash(self):
       
    92             """
       
    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).
       
    96             """
       
    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)
       
   104 
       
   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)
       
   109 
       
   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)
       
   114 
       
   115         def test_instantiate_missing(self):
       
   116             """
       
   117             If the underlying file is unavailable, still create instantiate the
       
   118             object without error.
       
   119             """
       
   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")
       
   126 
       
   127         def test_delete_when_missing(self):
       
   128             """
       
   129             Bug #8175: correctly delete an object where the file no longer
       
   130             exists on the file system.
       
   131             """
       
   132             p = self.PersonModel(name="Fred")
       
   133             p.mugshot.save("shot", self.file1)
       
   134             os.remove(p.mugshot.path)
       
   135             p.delete()
       
   136 
       
   137         def test_size_method(self):
       
   138             """
       
   139             Bug #8534: FileField.size should not leave the file open.
       
   140             """
       
   141             p = self.PersonModel(name="Joan")
       
   142             p.mugshot.save("shot", self.file1)
       
   143 
       
   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)
       
   148 
       
   149             # After asking for the size, the file should still be closed.
       
   150             _ = p.mugshot.size
       
   151             self.assertEqual(p.mugshot.closed, True)
       
   152 
       
   153         def test_pickle(self):
       
   154             """
       
   155             Tests that ImageField can be pickled, unpickled, and that the
       
   156             image of the unpickled version is the same as the original.
       
   157             """
       
   158             import pickle
       
   159 
       
   160             p = Person(name="Joe")
       
   161             p.mugshot.save("mug", self.file1)
       
   162             dump = pickle.dumps(p)
       
   163 
       
   164             p2 = Person(name="Bob")
       
   165             p2.mugshot = self.file1
       
   166 
       
   167             loaded_p = pickle.loads(dump)
       
   168             self.assertEqual(p.mugshot, loaded_p.mugshot)
       
   169 
       
   170 
       
   171     class ImageFieldTwoDimensionsTests(ImageFieldTestMixin, TestCase):
       
   172         """
       
   173         Tests behavior of an ImageField and its dimensions fields.
       
   174         """
       
   175 
       
   176         def test_constructor(self):
       
   177             """
       
   178             Tests assigning an image field through the model's constructor.
       
   179             """
       
   180             p = self.PersonModel(name='Joe', mugshot=self.file1)
       
   181             self.check_dimensions(p, 4, 8)
       
   182             p.save()
       
   183             self.check_dimensions(p, 4, 8)
       
   184 
       
   185         def test_image_after_constructor(self):
       
   186             """
       
   187             Tests behavior when image is not passed in constructor.
       
   188             """
       
   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)
       
   195 
       
   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)
       
   200 
       
   201         def test_create(self):
       
   202             """
       
   203             Tests assigning an image in Manager.create().
       
   204             """
       
   205             p = self.PersonModel.objects.create(name='Joe', mugshot=self.file1)
       
   206             self.check_dimensions(p, 4, 8)
       
   207 
       
   208         def test_default_value(self):
       
   209             """
       
   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).
       
   213             """
       
   214             p = self.PersonModel()
       
   215             self.assertEqual(isinstance(p.mugshot, TestImageFieldFile), True)
       
   216             self.assertEqual(bool(p.mugshot), False)
       
   217 
       
   218         def test_assignment_to_None(self):
       
   219             """
       
   220             Tests that assigning ImageField to None clears dimensions.
       
   221             """
       
   222             p = self.PersonModel(name='Joe', mugshot=self.file1)
       
   223             self.check_dimensions(p, 4, 8)
       
   224 
       
   225             # If image assigned to None, dimension fields should be cleared.
       
   226             p.mugshot = None
       
   227             self.check_dimensions(p, None, None)
       
   228 
       
   229             p.mugshot = self.file2
       
   230             self.check_dimensions(p, 8, 4)
       
   231 
       
   232         def test_field_save_and_delete_methods(self):
       
   233             """
       
   234             Tests assignment using the field's save method and deletion using
       
   235             the field's delete method.
       
   236             """
       
   237             p = self.PersonModel(name='Joe')
       
   238             p.mugshot.save("mug", self.file1)
       
   239             self.check_dimensions(p, 4, 8)
       
   240 
       
   241             # A new file should update dimensions.
       
   242             p.mugshot.save("mug", self.file2)
       
   243             self.check_dimensions(p, 8, 4)
       
   244 
       
   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)
       
   249 
       
   250         def test_dimensions(self):
       
   251             """
       
   252             Checks that dimensions are updated correctly in various situations.
       
   253             """
       
   254             p = self.PersonModel(name='Joe')
       
   255 
       
   256             # Dimensions should get set if file is saved.
       
   257             p.mugshot.save("mug", self.file1)
       
   258             self.check_dimensions(p, 4, 8)
       
   259 
       
   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
       
   264             # was opened.
       
   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
       
   268             # opened.
       
   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)
       
   275 
       
   276             # If we assign a new image to the instance, the dimensions should
       
   277             # update.
       
   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)
       
   282 
       
   283 
       
   284     class ImageFieldNoDimensionsTests(ImageFieldTwoDimensionsTests):
       
   285         """
       
   286         Tests behavior of an ImageField with no dimension fields.
       
   287         """
       
   288 
       
   289         PersonModel = Person
       
   290 
       
   291 
       
   292     class ImageFieldOneDimensionTests(ImageFieldTwoDimensionsTests):
       
   293         """
       
   294         Tests behavior of an ImageField with one dimensions field.
       
   295         """
       
   296 
       
   297         PersonModel = PersonWithHeight
       
   298 
       
   299 
       
   300     class ImageFieldDimensionsFirstTests(ImageFieldTwoDimensionsTests):
       
   301         """
       
   302         Tests behavior of an ImageField where the dimensions fields are
       
   303         defined before the ImageField.
       
   304         """
       
   305 
       
   306         PersonModel = PersonDimensionsFirst
       
   307 
       
   308 
       
   309     class ImageFieldUsingFileTests(ImageFieldTwoDimensionsTests):
       
   310         """
       
   311         Tests behavior of an ImageField when assigning it a File instance
       
   312         rather than an ImageFile instance.
       
   313         """
       
   314 
       
   315         PersonModel = PersonDimensionsFirst
       
   316         File = File
       
   317 
       
   318 
       
   319     class TwoImageFieldTests(ImageFieldTestMixin, TestCase):
       
   320         """
       
   321         Tests a model with two ImageFields.
       
   322         """
       
   323 
       
   324         PersonModel = PersonTwoImages
       
   325 
       
   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')
       
   330             p.save()
       
   331             self.check_dimensions(p, 4, 8, 'mugshot')
       
   332             self.check_dimensions(p, 8, 4, 'headshot')
       
   333 
       
   334         def test_create(self):
       
   335             p = self.PersonModel.objects.create(mugshot=self.file1,
       
   336                                                 headshot=self.file2)
       
   337             self.check_dimensions(p, 4, 8)
       
   338             self.check_dimensions(p, 8, 4, 'headshot')
       
   339 
       
   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')
       
   344 
       
   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')
       
   351 
       
   352             # Clear the ImageFields one at a time.
       
   353             p.mugshot = None
       
   354             self.check_dimensions(p, None, None, 'mugshot')
       
   355             self.check_dimensions(p, 8, 4, 'headshot')
       
   356             p.headshot = None
       
   357             self.check_dimensions(p, None, None, 'mugshot')
       
   358             self.check_dimensions(p, None, None, 'headshot')
       
   359 
       
   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')
       
   368 
       
   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')
       
   377 
       
   378         def test_dimensions(self):
       
   379             """
       
   380             Checks that dimensions are updated correctly in various situations.
       
   381             """
       
   382             p = self.PersonModel(name='Joe')
       
   383 
       
   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')
       
   389 
       
   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
       
   394             # was opened.
       
   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
       
   400             # have been opened.
       
   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)
       
   411 
       
   412             # If we assign a new image to the instance, the dimensions should
       
   413             # update.
       
   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)