diff options
| author | Marc Garcia <garcia.marc@gmail.com> | 2009-06-07 00:30:56 +0000 |
|---|---|---|
| committer | Marc Garcia <garcia.marc@gmail.com> | 2009-06-07 00:30:56 +0000 |
| commit | f973c57aaac3e918ac0234acbbbc86834ccfca86 (patch) | |
| tree | dcffd814d5881868ac72c01cabc220c4c4995ccb /tests | |
| parent | 3860c69bad92e4a514b1d10426445a00f3e9239a (diff) | |
Merged up to trunk r10934
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/i18n-improvements@10935 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/modeltests/defer/models.py | 92 | ||||
| -rw-r--r-- | tests/modeltests/model_forms/models.py | 21 | ||||
| -rw-r--r-- | tests/regressiontests/admin_scripts/tests.py | 2 | ||||
| -rw-r--r-- | tests/regressiontests/defer_regress/models.py | 22 | ||||
| -rw-r--r-- | tests/regressiontests/delete_regress/__init__.py | 1 | ||||
| -rw-r--r-- | tests/regressiontests/delete_regress/models.py | 61 | ||||
| -rw-r--r-- | tests/regressiontests/file_storage/models.py | 93 | ||||
| -rw-r--r-- | tests/regressiontests/file_storage/tests.py | 12 | ||||
| -rw-r--r-- | tests/regressiontests/model_fields/4x8.png | bin | 0 -> 87 bytes | |||
| -rw-r--r-- | tests/regressiontests/model_fields/8x4.png | bin | 0 -> 87 bytes | |||
| -rw-r--r-- | tests/regressiontests/model_fields/imagefield.py | 420 | ||||
| -rw-r--r-- | tests/regressiontests/model_fields/models.py | 107 | ||||
| -rw-r--r-- | tests/regressiontests/model_fields/tests.py | 16 | ||||
| -rw-r--r-- | tests/regressiontests/queries/models.py | 27 |
14 files changed, 758 insertions, 116 deletions
diff --git a/tests/modeltests/defer/models.py b/tests/modeltests/defer/models.py index ce65065d40..96eb427811 100644 --- a/tests/modeltests/defer/models.py +++ b/tests/modeltests/defer/models.py @@ -17,6 +17,12 @@ class Primary(models.Model): def __unicode__(self): return self.name +class Child(Primary): + pass + +class BigChild(Primary): + other = models.CharField(max_length=50) + def count_delayed_fields(obj, debug=False): """ Returns the number of delayed attributes on the given model instance. @@ -33,7 +39,7 @@ def count_delayed_fields(obj, debug=False): __test__ = {"API_TEST": """ To all outward appearances, instances with deferred fields look the same as -normal instances when we examine attribut values. Therefore we test for the +normal instances when we examine attribute values. Therefore we test for the number of deferred fields on returned instances (by poking at the internals), as a way to observe what is going on. @@ -98,5 +104,89 @@ Using defer() and only() with get() is also valid. >>> Primary.objects.all() [<Primary: a new name>] +# Regression for #10572 - A subclass with no extra fields can defer fields from the base class +>>> _ = Child.objects.create(name="c1", value="foo", related=s1) + +# You can defer a field on a baseclass when the subclass has no fields +>>> obj = Child.objects.defer("value").get(name="c1") +>>> count_delayed_fields(obj) +1 +>>> obj.name +u"c1" +>>> obj.value +u"foo" +>>> obj.name = "c2" +>>> obj.save() + +# You can retrive a single column on a base class with no fields +>>> obj = Child.objects.only("name").get(name="c2") +>>> count_delayed_fields(obj) +3 +>>> obj.name +u"c2" +>>> obj.value +u"foo" +>>> obj.name = "cc" +>>> obj.save() + +>>> _ = BigChild.objects.create(name="b1", value="foo", related=s1, other="bar") + +# You can defer a field on a baseclass +>>> obj = BigChild.objects.defer("value").get(name="b1") +>>> count_delayed_fields(obj) +1 +>>> obj.name +u"b1" +>>> obj.value +u"foo" +>>> obj.other +u"bar" +>>> obj.name = "b2" +>>> obj.save() + +# You can defer a field on a subclass +>>> obj = BigChild.objects.defer("other").get(name="b2") +>>> count_delayed_fields(obj) +1 +>>> obj.name +u"b2" +>>> obj.value +u"foo" +>>> obj.other +u"bar" +>>> obj.name = "b3" +>>> obj.save() + +# You can retrieve a single field on a baseclass +>>> obj = BigChild.objects.only("name").get(name="b3") +>>> count_delayed_fields(obj) +4 +>>> obj.name +u"b3" +>>> obj.value +u"foo" +>>> obj.other +u"bar" +>>> obj.name = "b4" +>>> obj.save() + +# You can retrieve a single field on a baseclass +>>> obj = BigChild.objects.only("other").get(name="b4") +>>> count_delayed_fields(obj) +4 +>>> obj.name +u"b4" +>>> obj.value +u"foo" +>>> obj.other +u"bar" +>>> obj.name = "bb" +>>> obj.save() + +# Finally, we need to flush the app cache for the defer module. +# Using only/defer creates some artifical entries in the app cache +# that messes up later tests. Purge all entries, just to be sure. +>>> from django.db.models.loading import cache +>>> cache.app_models['defer'] = {} """} diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index 95fda273f0..0fd24c18ad 100644 --- a/tests/modeltests/model_forms/models.py +++ b/tests/modeltests/model_forms/models.py @@ -1175,8 +1175,9 @@ True >>> instance.height 16 -# Delete the current file since this is not done by Django. ->>> instance.image.delete() +# Delete the current file since this is not done by Django, but don't save +# because the dimension fields are not null=True. +>>> instance.image.delete(save=False) >>> f = ImageFileForm(data={'description': u'An image'}, files={'image': SimpleUploadedFile('test.png', image_data)}) >>> f.is_valid() @@ -1207,9 +1208,9 @@ True >>> instance.width 16 -# Delete the current image since this is not done by Django. - ->>> instance.image.delete() +# Delete the current file since this is not done by Django, but don't save +# because the dimension fields are not null=True. +>>> instance.image.delete(save=False) # Override the file by uploading a new one. @@ -1224,8 +1225,9 @@ True >>> instance.width 48 -# Delete the current file since this is not done by Django. ->>> instance.image.delete() +# Delete the current file since this is not done by Django, but don't save +# because the dimension fields are not null=True. +>>> instance.image.delete(save=False) >>> instance.delete() >>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': SimpleUploadedFile('test2.png', image_data2)}) @@ -1239,8 +1241,9 @@ True >>> instance.width 48 -# Delete the current file since this is not done by Django. ->>> instance.image.delete() +# Delete the current file since this is not done by Django, but don't save +# because the dimension fields are not null=True. +>>> instance.image.delete(save=False) >>> instance.delete() # Test the non-required ImageField diff --git a/tests/regressiontests/admin_scripts/tests.py b/tests/regressiontests/admin_scripts/tests.py index 0e28a9749f..e67126e9a7 100644 --- a/tests/regressiontests/admin_scripts/tests.py +++ b/tests/regressiontests/admin_scripts/tests.py @@ -536,7 +536,7 @@ class DjangoAdminSettingsDirectory(AdminScriptTestCase): args = ['startapp','settings_test'] out, err = self.run_django_admin(args,'settings') self.assertNoOutput(err) - self.assertTrue(os.path.exists(os.path.join(test_dir, 'settings_test'))) + self.assert_(os.path.exists(os.path.join(test_dir, 'settings_test'))) shutil.rmtree(os.path.join(test_dir, 'settings_test')) def test_builtin_command(self): diff --git a/tests/regressiontests/defer_regress/models.py b/tests/regressiontests/defer_regress/models.py index 11ce1557fe..da9822ab88 100644 --- a/tests/regressiontests/defer_regress/models.py +++ b/tests/regressiontests/defer_regress/models.py @@ -84,7 +84,8 @@ Some further checks for select_related() and inherited model behaviour (regression for #10710). >>> c1 = Child.objects.create(name="c1", value=42) ->>> obj = Leaf.objects.create(name="l1", child=c1) +>>> c2 = Child.objects.create(name="c2", value=37) +>>> obj = Leaf.objects.create(name="l1", child=c1, second_child=c2) >>> obj = Leaf.objects.only("name", "child").select_related()[0] >>> obj.child.name @@ -101,5 +102,24 @@ types as their non-deferred versions (bug #10738). >>> c1 is c2 is c3 True +# Regression for #10733 - only() can be used on a model with two foreign keys. +>>> results = Leaf.objects.all().only('name', 'child', 'second_child').select_related() +>>> results[0].child.name +u'c1' +>>> results[0].second_child.name +u'c2' + +>>> results = Leaf.objects.all().only('name', 'child', 'second_child', 'child__name', 'second_child__name').select_related() +>>> results[0].child.name +u'c1' +>>> results[0].second_child.name +u'c2' + +# Finally, we need to flush the app cache for the defer module. +# Using only/defer creates some artifical entries in the app cache +# that messes up later tests. Purge all entries, just to be sure. +>>> from django.db.models.loading import cache +>>> cache.app_models['defer_regress'] = {} + """ } diff --git a/tests/regressiontests/delete_regress/__init__.py b/tests/regressiontests/delete_regress/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/tests/regressiontests/delete_regress/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/regressiontests/delete_regress/models.py b/tests/regressiontests/delete_regress/models.py new file mode 100644 index 0000000000..93cadc58fa --- /dev/null +++ b/tests/regressiontests/delete_regress/models.py @@ -0,0 +1,61 @@ +from django.conf import settings +from django.db import models, backend, connection, transaction +from django.db.models import sql, query +from django.test import TransactionTestCase + +class Book(models.Model): + pagecount = models.IntegerField() + +# Can't run this test under SQLite, because you can't +# get two connections to an in-memory database. +if settings.DATABASE_ENGINE != 'sqlite3': + class DeleteLockingTest(TransactionTestCase): + def setUp(self): + # Create a second connection to the database + self.conn2 = backend.DatabaseWrapper({ + 'DATABASE_HOST': settings.DATABASE_HOST, + 'DATABASE_NAME': settings.DATABASE_NAME, + 'DATABASE_OPTIONS': settings.DATABASE_OPTIONS, + 'DATABASE_PASSWORD': settings.DATABASE_PASSWORD, + 'DATABASE_PORT': settings.DATABASE_PORT, + 'DATABASE_USER': settings.DATABASE_USER, + 'TIME_ZONE': settings.TIME_ZONE, + }) + + # Put both DB connections into managed transaction mode + transaction.enter_transaction_management() + transaction.managed(True) + self.conn2._enter_transaction_management(True) + + def tearDown(self): + # Close down the second connection. + transaction.leave_transaction_management() + self.conn2.close() + + def test_concurrent_delete(self): + "Deletes on concurrent transactions don't collide and lock the database. Regression for #9479" + + # Create some dummy data + b1 = Book(id=1, pagecount=100) + b2 = Book(id=2, pagecount=200) + b3 = Book(id=3, pagecount=300) + b1.save() + b2.save() + b3.save() + + transaction.commit() + + self.assertEquals(3, Book.objects.count()) + + # Delete something using connection 2. + cursor2 = self.conn2.cursor() + cursor2.execute('DELETE from delete_regress_book WHERE id=1') + self.conn2._commit(); + + # Now perform a queryset delete that covers the object + # deleted in connection 2. This causes an infinite loop + # under MySQL InnoDB unless we keep track of already + # deleted objects. + Book.objects.filter(pagecount__lt=250).delete() + transaction.commit() + self.assertEquals(1, Book.objects.count()) diff --git a/tests/regressiontests/file_storage/models.py b/tests/regressiontests/file_storage/models.py index 32af5d7588..e69de29bb2 100644 --- a/tests/regressiontests/file_storage/models.py +++ b/tests/regressiontests/file_storage/models.py @@ -1,93 +0,0 @@ -import os -import tempfile -import shutil -from django.db import models -from django.core.files.storage import FileSystemStorage -from django.core.files.base import ContentFile - -# Test for correct behavior of width_field/height_field. -# Of course, we can't run this without PIL. - -try: - # Checking for the existence of Image is enough for CPython, but - # for PyPy, you need to check for the underlying modules - from PIL import Image, _imaging -except ImportError: - Image = None - -# If we have PIL, do these tests -if Image: - temp_storage_dir = tempfile.mkdtemp() - temp_storage = FileSystemStorage(temp_storage_dir) - - class Person(models.Model): - name = models.CharField(max_length=50) - mugshot = models.ImageField(storage=temp_storage, upload_to='tests', - height_field='mug_height', - width_field='mug_width') - mug_height = models.PositiveSmallIntegerField() - mug_width = models.PositiveSmallIntegerField() - - __test__ = {'API_TESTS': """ ->>> from django.core.files import File ->>> image_data = open(os.path.join(os.path.dirname(__file__), "test.png"), 'rb').read() ->>> p = Person(name="Joe") ->>> p.mugshot.save("mug", ContentFile(image_data)) ->>> p.mugshot.width -16 ->>> p.mugshot.height -16 ->>> p.mug_height -16 ->>> p.mug_width -16 - -# Bug #9786: Ensure '==' and '!=' work correctly. ->>> image_data = open(os.path.join(os.path.dirname(__file__), "test1.png"), 'rb').read() ->>> p1 = Person(name="Bob") ->>> p1.mugshot.save("mug", ContentFile(image_data)) ->>> p2 = Person.objects.get(name="Joe") ->>> p.mugshot == p2.mugshot -True ->>> p.mugshot != p2.mugshot -False ->>> p.mugshot != p1.mugshot -True - -Bug #9508: Similarly to the previous test, make sure hash() works as expected -(equal items must hash to the same value). ->>> hash(p.mugshot) == hash(p2.mugshot) -True - -# Bug #8175: correctly delete files that have been removed off the file system. ->>> import os ->>> p2 = Person(name="Fred") ->>> p2.mugshot.save("shot", ContentFile(image_data)) ->>> os.remove(p2.mugshot.path) ->>> p2.delete() - -# Bug #8534: FileField.size should not leave the file open. ->>> p3 = Person(name="Joan") ->>> p3.mugshot.save("shot", ContentFile(image_data)) - -# Get a "clean" model instance ->>> p3 = Person.objects.get(name="Joan") - -# It won't have an opened file. ->>> p3.mugshot.closed -True - -# After asking for the size, the file should still be closed. ->>> _ = p3.mugshot.size ->>> p3.mugshot.closed -True - -# Make sure that wrapping the file in a file still works ->>> p3.mugshot.file.open() ->>> p = Person.objects.create(name="Bob The Builder", mugshot=File(p3.mugshot.file)) ->>> p.save() ->>> p3.mugshot.file.close() - -# Delete all test files ->>> shutil.rmtree(temp_storage_dir) -"""} diff --git a/tests/regressiontests/file_storage/tests.py b/tests/regressiontests/file_storage/tests.py index 6e2b7be8e7..c228764e06 100644 --- a/tests/regressiontests/file_storage/tests.py +++ b/tests/regressiontests/file_storage/tests.py @@ -161,9 +161,9 @@ class FileStoragePathParsing(TestCase): self.storage.save('dotted.path/test', ContentFile("1")) self.storage.save('dotted.path/test', ContentFile("2")) - self.assertFalse(os.path.exists(os.path.join(self.storage_dir, 'dotted_.path'))) - self.assertTrue(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/test'))) - self.assertTrue(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/test_'))) + self.failIf(os.path.exists(os.path.join(self.storage_dir, 'dotted_.path'))) + self.assert_(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/test'))) + self.assert_(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/test_'))) def test_first_character_dot(self): """ @@ -173,13 +173,13 @@ class FileStoragePathParsing(TestCase): self.storage.save('dotted.path/.test', ContentFile("1")) self.storage.save('dotted.path/.test', ContentFile("2")) - self.assertTrue(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/.test'))) + self.assert_(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/.test'))) # Before 2.6, a leading dot was treated as an extension, and so # underscore gets added to beginning instead of end. if sys.version_info < (2, 6): - self.assertTrue(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/_.test'))) + self.assert_(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/_.test'))) else: - self.assertTrue(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/.test_'))) + self.assert_(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/.test_'))) if Image is not None: class DimensionClosingBug(TestCase): diff --git a/tests/regressiontests/model_fields/4x8.png b/tests/regressiontests/model_fields/4x8.png Binary files differnew file mode 100644 index 0000000000..ffce444d48 --- /dev/null +++ b/tests/regressiontests/model_fields/4x8.png diff --git a/tests/regressiontests/model_fields/8x4.png b/tests/regressiontests/model_fields/8x4.png Binary files differnew file mode 100644 index 0000000000..60e6d69ee1 --- /dev/null +++ b/tests/regressiontests/model_fields/8x4.png diff --git a/tests/regressiontests/model_fields/imagefield.py b/tests/regressiontests/model_fields/imagefield.py new file mode 100644 index 0000000000..dd79e7aefa --- /dev/null +++ b/tests/regressiontests/model_fields/imagefield.py @@ -0,0 +1,420 @@ +import os +import shutil + +from django.core.files import File +from django.core.files.base import ContentFile +from django.core.files.images import ImageFile +from django.test import TestCase + +from models import Image, Person, PersonWithHeight, PersonWithHeightAndWidth, \ + PersonDimensionsFirst, PersonTwoImages, TestImageFieldFile + + +# If PIL available, do these tests. +if Image: + + from models import temp_storage_dir + + + class ImageFieldTestMixin(object): + """ + Mixin class to provide common functionality to ImageField test classes. + """ + + # Person model to use for tests. + PersonModel = PersonWithHeightAndWidth + # File class to use for file instances. + File = ImageFile + + def setUp(self): + """ + Creates a pristine temp directory (or deletes and recreates if it + already exists) that the model uses as its storage directory. + + Sets up two ImageFile instances for use in tests. + """ + if os.path.exists(temp_storage_dir): + shutil.rmtree(temp_storage_dir) + os.mkdir(temp_storage_dir) + + file_path1 = os.path.join(os.path.dirname(__file__), "4x8.png") + self.file1 = self.File(open(file_path1, 'rb')) + + file_path2 = os.path.join(os.path.dirname(__file__), "8x4.png") + self.file2 = self.File(open(file_path2, 'rb')) + + def tearDown(self): + """ + Removes temp directory and all its contents. + """ + shutil.rmtree(temp_storage_dir) + + def check_dimensions(self, instance, width, height, + field_name='mugshot'): + """ + Asserts that the given width and height values match both the + field's height and width attributes and the height and width fields + (if defined) the image field is caching to. + + Note, this method will check for dimension fields named by adding + "_width" or "_height" to the name of the ImageField. So, the + models used in these tests must have their fields named + accordingly. + + By default, we check the field named "mugshot", but this can be + specified by passing the field_name parameter. + """ + field = getattr(instance, field_name) + # Check height/width attributes of field. + if width is None and height is None: + self.assertRaises(ValueError, getattr, field, 'width') + self.assertRaises(ValueError, getattr, field, 'height') + else: + self.assertEqual(field.width, width) + self.assertEqual(field.height, height) + + # Check height/width fields of model, if defined. + width_field_name = field_name + '_width' + if hasattr(instance, width_field_name): + self.assertEqual(getattr(instance, width_field_name), width) + height_field_name = field_name + '_height' + if hasattr(instance, height_field_name): + self.assertEqual(getattr(instance, height_field_name), height) + + + class ImageFieldTests(ImageFieldTestMixin, TestCase): + """ + Tests for ImageField that don't need to be run with each of the + different test model classes. + """ + + def test_equal_notequal_hash(self): + """ + Bug #9786: Ensure '==' and '!=' work correctly. + Bug #9508: make sure hash() works as expected (equal items must + hash to the same value). + """ + # Create two Persons with different mugshots. + p1 = self.PersonModel(name="Joe") + p1.mugshot.save("mug", self.file1) + p2 = self.PersonModel(name="Bob") + p2.mugshot.save("mug", self.file2) + self.assertEqual(p1.mugshot == p2.mugshot, False) + self.assertEqual(p1.mugshot != p2.mugshot, True) + + # Test again with an instance fetched from the db. + p1_db = self.PersonModel.objects.get(name="Joe") + self.assertEqual(p1_db.mugshot == p2.mugshot, False) + self.assertEqual(p1_db.mugshot != p2.mugshot, True) + + # Instance from db should match the local instance. + self.assertEqual(p1_db.mugshot == p1.mugshot, True) + self.assertEqual(hash(p1_db.mugshot), hash(p1.mugshot)) + self.assertEqual(p1_db.mugshot != p1.mugshot, False) + + def test_instantiate_missing(self): + """ + If the underlying file is unavailable, still create instantiate the + object without error. + """ + p = self.PersonModel(name="Joan") + p.mugshot.save("shot", self.file1) + p = self.PersonModel.objects.get(name="Joan") + path = p.mugshot.path + shutil.move(path, path + '.moved') + p2 = self.PersonModel.objects.get(name="Joan") + + def test_delete_when_missing(self): + """ + Bug #8175: correctly delete an object where the file no longer + exists on the file system. + """ + p = self.PersonModel(name="Fred") + p.mugshot.save("shot", self.file1) + os.remove(p.mugshot.path) + p.delete() + + def test_size_method(self): + """ + Bug #8534: FileField.size should not leave the file open. + """ + p = self.PersonModel(name="Joan") + p.mugshot.save("shot", self.file1) + + # Get a "clean" model instance + p = self.PersonModel.objects.get(name="Joan") + # It won't have an opened file. + self.assertEqual(p.mugshot.closed, True) + + # After asking for the size, the file should still be closed. + _ = p.mugshot.size + self.assertEqual(p.mugshot.closed, True) + + def test_pickle(self): + """ + Tests that ImageField can be pickled, unpickled, and that the + image of the unpickled version is the same as the original. + """ + import pickle + + p = Person(name="Joe") + p.mugshot.save("mug", self.file1) + dump = pickle.dumps(p) + + p2 = Person(name="Bob") + p2.mugshot = self.file1 + + loaded_p = pickle.loads(dump) + self.assertEqual(p.mugshot, loaded_p.mugshot) + + + class ImageFieldTwoDimensionsTests(ImageFieldTestMixin, TestCase): + """ + Tests behavior of an ImageField and its dimensions fields. + """ + + def test_constructor(self): + """ + Tests assigning an image field through the model's constructor. + """ + p = self.PersonModel(name='Joe', mugshot=self.file1) + self.check_dimensions(p, 4, 8) + p.save() + self.check_dimensions(p, 4, 8) + + def test_image_after_constructor(self): + """ + Tests behavior when image is not passed in constructor. + """ + p = self.PersonModel(name='Joe') + # TestImageField value will default to being an instance of its + # attr_class, a TestImageFieldFile, with name == None, which will + # cause it to evaluate as False. + self.assertEqual(isinstance(p.mugshot, TestImageFieldFile), True) + self.assertEqual(bool(p.mugshot), False) + + # Test setting a fresh created model instance. + p = self.PersonModel(name='Joe') + p.mugshot = self.file1 + self.check_dimensions(p, 4, 8) + + def test_create(self): + """ + Tests assigning an image in Manager.create(). + """ + p = self.PersonModel.objects.create(name='Joe', mugshot=self.file1) + self.check_dimensions(p, 4, 8) + + def test_default_value(self): + """ + Tests that the default value for an ImageField is an instance of + the field's attr_class (TestImageFieldFile in this case) with no + name (name set to None). + """ + p = self.PersonModel() + self.assertEqual(isinstance(p.mugshot, TestImageFieldFile), True) + self.assertEqual(bool(p.mugshot), False) + + def test_assignment_to_None(self): + """ + Tests that assigning ImageField to None clears dimensions. + """ + p = self.PersonModel(name='Joe', mugshot=self.file1) + self.check_dimensions(p, 4, 8) + + # If image assigned to None, dimension fields should be cleared. + p.mugshot = None + self.check_dimensions(p, None, None) + + p.mugshot = self.file2 + self.check_dimensions(p, 8, 4) + + def test_field_save_and_delete_methods(self): + """ + Tests assignment using the field's save method and deletion using + the field's delete method. + """ + p = self.PersonModel(name='Joe') + p.mugshot.save("mug", self.file1) + self.check_dimensions(p, 4, 8) + + # A new file should update dimensions. + p.mugshot.save("mug", self.file2) + self.check_dimensions(p, 8, 4) + + # Field and dimensions should be cleared after a delete. + p.mugshot.delete(save=False) + self.assertEqual(p.mugshot, None) + self.check_dimensions(p, None, None) + + def test_dimensions(self): + """ + Checks that dimensions are updated correctly in various situations. + """ + p = self.PersonModel(name='Joe') + + # Dimensions should get set if file is saved. + p.mugshot.save("mug", self.file1) + self.check_dimensions(p, 4, 8) + + # Test dimensions after fetching from database. + p = self.PersonModel.objects.get(name='Joe') + # Bug 11084: Dimensions should not get recalculated if file is + # coming from the database. We test this by checking if the file + # was opened. + self.assertEqual(p.mugshot.was_opened, False) + self.check_dimensions(p, 4, 8) + # After checking dimensions on the image field, the file will have + # opened. + self.assertEqual(p.mugshot.was_opened, True) + # Dimensions should now be cached, and if we reset was_opened and + # check dimensions again, the file should not have opened. + p.mugshot.was_opened = False + self.check_dimensions(p, 4, 8) + self.assertEqual(p.mugshot.was_opened, False) + + # If we assign a new image to the instance, the dimensions should + # update. + p.mugshot = self.file2 + self.check_dimensions(p, 8, 4) + # Dimensions were recalculated, and hence file should have opened. + self.assertEqual(p.mugshot.was_opened, True) + + + class ImageFieldNoDimensionsTests(ImageFieldTwoDimensionsTests): + """ + Tests behavior of an ImageField with no dimension fields. + """ + + PersonModel = Person + + + class ImageFieldOneDimensionTests(ImageFieldTwoDimensionsTests): + """ + Tests behavior of an ImageField with one dimensions field. + """ + + PersonModel = PersonWithHeight + + + class ImageFieldDimensionsFirstTests(ImageFieldTwoDimensionsTests): + """ + Tests behavior of an ImageField where the dimensions fields are + defined before the ImageField. + """ + + PersonModel = PersonDimensionsFirst + + + class ImageFieldUsingFileTests(ImageFieldTwoDimensionsTests): + """ + Tests behavior of an ImageField when assigning it a File instance + rather than an ImageFile instance. + """ + + PersonModel = PersonDimensionsFirst + File = File + + + class TwoImageFieldTests(ImageFieldTestMixin, TestCase): + """ + Tests a model with two ImageFields. + """ + + PersonModel = PersonTwoImages + + def test_constructor(self): + p = self.PersonModel(mugshot=self.file1, headshot=self.file2) + self.check_dimensions(p, 4, 8, 'mugshot') + self.check_dimensions(p, 8, 4, 'headshot') + p.save() + self.check_dimensions(p, 4, 8, 'mugshot') + self.check_dimensions(p, 8, 4, 'headshot') + + def test_create(self): + p = self.PersonModel.objects.create(mugshot=self.file1, + headshot=self.file2) + self.check_dimensions(p, 4, 8) + self.check_dimensions(p, 8, 4, 'headshot') + + def test_assignment(self): + p = self.PersonModel() + self.check_dimensions(p, None, None, 'mugshot') + self.check_dimensions(p, None, None, 'headshot') + + p.mugshot = self.file1 + self.check_dimensions(p, 4, 8, 'mugshot') + self.check_dimensions(p, None, None, 'headshot') + p.headshot = self.file2 + self.check_dimensions(p, 4, 8, 'mugshot') + self.check_dimensions(p, 8, 4, 'headshot') + + # Clear the ImageFields one at a time. + p.mugshot = None + self.check_dimensions(p, None, None, 'mugshot') + self.check_dimensions(p, 8, 4, 'headshot') + p.headshot = None + self.check_dimensions(p, None, None, 'mugshot') + self.check_dimensions(p, None, None, 'headshot') + + def test_field_save_and_delete_methods(self): + p = self.PersonModel(name='Joe') + p.mugshot.save("mug", self.file1) + self.check_dimensions(p, 4, 8, 'mugshot') + self.check_dimensions(p, None, None, 'headshot') + p.headshot.save("head", self.file2) + self.check_dimensions(p, 4, 8, 'mugshot') + self.check_dimensions(p, 8, 4, 'headshot') + + # We can use save=True when deleting the image field with null=True + # dimension fields and the other field has an image. + p.headshot.delete(save=True) + self.check_dimensions(p, 4, 8, 'mugshot') + self.check_dimensions(p, None, None, 'headshot') + p.mugshot.delete(save=False) + self.check_dimensions(p, None, None, 'mugshot') + self.check_dimensions(p, None, None, 'headshot') + + def test_dimensions(self): + """ + Checks that dimensions are updated correctly in various situations. + """ + p = self.PersonModel(name='Joe') + + # Dimensions should get set for the saved file. + p.mugshot.save("mug", self.file1) + p.headshot.save("head", self.file2) + self.check_dimensions(p, 4, 8, 'mugshot') + self.check_dimensions(p, 8, 4, 'headshot') + + # Test dimensions after fetching from database. + p = self.PersonModel.objects.get(name='Joe') + # Bug 11084: Dimensions should not get recalculated if file is + # coming from the database. We test this by checking if the file + # was opened. + self.assertEqual(p.mugshot.was_opened, False) + self.assertEqual(p.headshot.was_opened, False) + self.check_dimensions(p, 4, 8,'mugshot') + self.check_dimensions(p, 8, 4, 'headshot') + # After checking dimensions on the image fields, the files will + # have been opened. + self.assertEqual(p.mugshot.was_opened, True) + self.assertEqual(p.headshot.was_opened, True) + # Dimensions should now be cached, and if we reset was_opened and + # check dimensions again, the file should not have opened. + p.mugshot.was_opened = False + p.headshot.was_opened = False + self.check_dimensions(p, 4, 8,'mugshot') + self.check_dimensions(p, 8, 4, 'headshot') + self.assertEqual(p.mugshot.was_opened, False) + self.assertEqual(p.headshot.was_opened, False) + + # If we assign a new image to the instance, the dimensions should + # update. + p.mugshot = self.file2 + p.headshot = self.file1 + self.check_dimensions(p, 8, 4, 'mugshot') + self.check_dimensions(p, 4, 8, 'headshot') + # Dimensions were recalculated, and hence file should have opened. + self.assertEqual(p.mugshot.was_opened, True) + self.assertEqual(p.headshot.was_opened, True) diff --git a/tests/regressiontests/model_fields/models.py b/tests/regressiontests/model_fields/models.py index 43ef667d00..81e6c22f09 100644 --- a/tests/regressiontests/model_fields/models.py +++ b/tests/regressiontests/model_fields/models.py @@ -1,10 +1,23 @@ -from django.db import models +import os +import tempfile try: import decimal except ImportError: from django.utils import _decimal as decimal # Python 2.3 fallback +try: + # Checking for the existence of Image is enough for CPython, but for PyPy, + # you need to check for the underlying modules. + from PIL import Image, _imaging +except ImportError: + Image = None + +from django.core.files.storage import FileSystemStorage +from django.db import models +from django.db.models.fields.files import ImageFieldFile, ImageField + + class Foo(models.Model): a = models.CharField(max_length=10) d = models.DecimalField(max_digits=5, decimal_places=3) @@ -31,9 +44,97 @@ class Whiz(models.Model): (0,'Other'), ) c = models.IntegerField(choices=CHOICES, null=True) - + class BigD(models.Model): d = models.DecimalField(max_digits=38, decimal_places=30) class BigS(models.Model): - s = models.SlugField(max_length=255)
\ No newline at end of file + s = models.SlugField(max_length=255) + + +############################################################################### +# ImageField + +# If PIL available, do these tests. +if Image: + class TestImageFieldFile(ImageFieldFile): + """ + Custom Field File class that records whether or not the underlying file + was opened. + """ + def __init__(self, *args, **kwargs): + self.was_opened = False + super(TestImageFieldFile, self).__init__(*args,**kwargs) + def open(self): + self.was_opened = True + super(TestImageFieldFile, self).open() + + class TestImageField(ImageField): + attr_class = TestImageFieldFile + + # Set up a temp directory for file storage. + temp_storage_dir = tempfile.mkdtemp() + temp_storage = FileSystemStorage(temp_storage_dir) + temp_upload_to_dir = os.path.join(temp_storage.location, 'tests') + + class Person(models.Model): + """ + Model that defines an ImageField with no dimension fields. + """ + name = models.CharField(max_length=50) + mugshot = TestImageField(storage=temp_storage, upload_to='tests') + + class PersonWithHeight(models.Model): + """ + Model that defines an ImageField with only one dimension field. + """ + name = models.CharField(max_length=50) + mugshot = TestImageField(storage=temp_storage, upload_to='tests', + height_field='mugshot_height') + mugshot_height = models.PositiveSmallIntegerField() + + class PersonWithHeightAndWidth(models.Model): + """ + Model that defines height and width fields after the ImageField. + """ + name = models.CharField(max_length=50) + mugshot = TestImageField(storage=temp_storage, upload_to='tests', + height_field='mugshot_height', + width_field='mugshot_width') + mugshot_height = models.PositiveSmallIntegerField() + mugshot_width = models.PositiveSmallIntegerField() + + class PersonDimensionsFirst(models.Model): + """ + Model that defines height and width fields before the ImageField. + """ + name = models.CharField(max_length=50) + mugshot_height = models.PositiveSmallIntegerField() + mugshot_width = models.PositiveSmallIntegerField() + mugshot = TestImageField(storage=temp_storage, upload_to='tests', + height_field='mugshot_height', + width_field='mugshot_width') + + class PersonTwoImages(models.Model): + """ + Model that: + * Defines two ImageFields + * Defines the height/width fields before the ImageFields + * Has a nullalble ImageField + """ + name = models.CharField(max_length=50) + mugshot_height = models.PositiveSmallIntegerField() + mugshot_width = models.PositiveSmallIntegerField() + mugshot = TestImageField(storage=temp_storage, upload_to='tests', + height_field='mugshot_height', + width_field='mugshot_width') + headshot_height = models.PositiveSmallIntegerField( + blank=True, null=True) + headshot_width = models.PositiveSmallIntegerField( + blank=True, null=True) + headshot = TestImageField(blank=True, null=True, + storage=temp_storage, upload_to='tests', + height_field='headshot_height', + width_field='headshot_width') + +############################################################################### diff --git a/tests/regressiontests/model_fields/tests.py b/tests/regressiontests/model_fields/tests.py index 0815d9ef3c..7a6fee5a2a 100644 --- a/tests/regressiontests/model_fields/tests.py +++ b/tests/regressiontests/model_fields/tests.py @@ -6,13 +6,26 @@ from django import forms from django.db import models from django.core.exceptions import ValidationError -from models import Foo, Bar, Whiz, BigD, BigS +from models import Foo, Bar, Whiz, BigD, BigS, Image try: from decimal import Decimal except ImportError: from django.utils._decimal import Decimal + +# If PIL available, do these tests. +if Image: + from imagefield import \ + ImageFieldTests, \ + ImageFieldTwoDimensionsTests, \ + ImageFieldNoDimensionsTests, \ + ImageFieldOneDimensionTests, \ + ImageFieldDimensionsFirstTests, \ + ImageFieldUsingFileTests, \ + TwoImageFieldTests + + class DecimalFieldTests(django.test.TestCase): def test_to_python(self): f = models.DecimalField(max_digits=4, decimal_places=2) @@ -131,4 +144,3 @@ class SlugFieldTests(django.test.TestCase): bs = BigS.objects.create(s = 'slug'*50) bs = BigS.objects.get(pk=bs.pk) self.assertEqual(bs.s, 'slug'*50) - diff --git a/tests/regressiontests/queries/models.py b/tests/regressiontests/queries/models.py index b5fa377496..3649d27171 100644 --- a/tests/regressiontests/queries/models.py +++ b/tests/regressiontests/queries/models.py @@ -1143,6 +1143,33 @@ True >>> r.save() >>> Ranking.objects.all() [<Ranking: 3: a1>, <Ranking: 2: a2>, <Ranking: 1: a3>] + +# Regression test for #10742: +# Queries used in an __in clause don't execute subqueries + +>>> subq = Author.objects.filter(num__lt=3000) +>>> qs = Author.objects.filter(pk__in=subq) +>>> list(qs) +[<Author: a1>, <Author: a2>] +# The subquery result cache should not be populated +>>> subq._result_cache is None +True + +>>> subq = Author.objects.filter(num__lt=3000) +>>> qs = Author.objects.exclude(pk__in=subq) +>>> list(qs) +[<Author: a3>, <Author: a4>] +# The subquery result cache should not be populated +>>> subq._result_cache is None +True + +>>> subq = Author.objects.filter(num__lt=3000) +>>> list(Author.objects.filter(Q(pk__in=subq) & Q(name='a1'))) +[<Author: a1>] +# The subquery result cache should not be populated +>>> subq._result_cache is None +True + """} # In Python 2.3 and the Python 2.6 beta releases, exceptions raised in __len__ |
