Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Added isolated model_fields test (copy from original django test suite)

  • Loading branch information...
commit c941e4a3f2d6102a696dd88690130c4c7081a63b 1 parent 27e6c28
Maximiliano Robaina authored
BIN  tests/test_main/model_fields/4x8.png
BIN  tests/test_main/model_fields/8x4.png
0  tests/test_main/model_fields/__init__.py
No changes.
433 tests/test_main/model_fields/imagefield.py
... ... @@ -0,0 +1,433 @@
  1 +from __future__ import absolute_import
  2 +
  3 +import os
  4 +import shutil
  5 +
  6 +from django.core.files import File
  7 +from django.core.files.images import ImageFile
  8 +from django.test import TestCase
  9 +from django.utils.unittest import skipIf
  10 +
  11 +from .models import Image
  12 +
  13 +if Image:
  14 + from .models import (Person, PersonWithHeight, PersonWithHeightAndWidth,
  15 + PersonDimensionsFirst, PersonTwoImages, TestImageFieldFile)
  16 + from .models import temp_storage_dir
  17 +else:
  18 + # PIL not available, create dummy classes (tests will be skipped anyway)
  19 + class Person():
  20 + pass
  21 + PersonWithHeight = PersonWithHeightAndWidth = PersonDimensionsFirst = Person
  22 + PersonTwoImages = Person
  23 +
  24 +
  25 +class ImageFieldTestMixin(object):
  26 + """
  27 + Mixin class to provide common functionality to ImageField test classes.
  28 + """
  29 +
  30 + # Person model to use for tests.
  31 + PersonModel = PersonWithHeightAndWidth
  32 + # File class to use for file instances.
  33 + File = ImageFile
  34 +
  35 + def setUp(self):
  36 + """
  37 + Creates a pristine temp directory (or deletes and recreates if it
  38 + already exists) that the model uses as its storage directory.
  39 +
  40 + Sets up two ImageFile instances for use in tests.
  41 + """
  42 + if os.path.exists(temp_storage_dir):
  43 + shutil.rmtree(temp_storage_dir)
  44 + os.mkdir(temp_storage_dir)
  45 +
  46 + file_path1 = os.path.join(os.path.dirname(__file__), "4x8.png")
  47 + self.file1 = self.File(open(file_path1, 'rb'))
  48 +
  49 + file_path2 = os.path.join(os.path.dirname(__file__), "8x4.png")
  50 + self.file2 = self.File(open(file_path2, 'rb'))
  51 +
  52 + def tearDown(self):
  53 + """
  54 + Removes temp directory and all its contents.
  55 + """
  56 + shutil.rmtree(temp_storage_dir)
  57 +
  58 + def check_dimensions(self, instance, width, height,
  59 + field_name='mugshot'):
  60 + """
  61 + Asserts that the given width and height values match both the
  62 + field's height and width attributes and the height and width fields
  63 + (if defined) the image field is caching to.
  64 +
  65 + Note, this method will check for dimension fields named by adding
  66 + "_width" or "_height" to the name of the ImageField. So, the
  67 + models used in these tests must have their fields named
  68 + accordingly.
  69 +
  70 + By default, we check the field named "mugshot", but this can be
  71 + specified by passing the field_name parameter.
  72 + """
  73 + field = getattr(instance, field_name)
  74 + # Check height/width attributes of field.
  75 + if width is None and height is None:
  76 + self.assertRaises(ValueError, getattr, field, 'width')
  77 + self.assertRaises(ValueError, getattr, field, 'height')
  78 + else:
  79 + self.assertEqual(field.width, width)
  80 + self.assertEqual(field.height, height)
  81 +
  82 + # Check height/width fields of model, if defined.
  83 + width_field_name = field_name + '_width'
  84 + if hasattr(instance, width_field_name):
  85 + self.assertEqual(getattr(instance, width_field_name), width)
  86 + height_field_name = field_name + '_height'
  87 + if hasattr(instance, height_field_name):
  88 + self.assertEqual(getattr(instance, height_field_name), height)
  89 +
  90 +
  91 +@skipIf(Image is None, "PIL is required to test ImageField")
  92 +class ImageFieldTests(ImageFieldTestMixin, TestCase):
  93 + """
  94 + Tests for ImageField that don't need to be run with each of the
  95 + different test model classes.
  96 + """
  97 +
  98 + def test_equal_notequal_hash(self):
  99 + """
  100 + Bug #9786: Ensure '==' and '!=' work correctly.
  101 + Bug #9508: make sure hash() works as expected (equal items must
  102 + hash to the same value).
  103 + """
  104 + # Create two Persons with different mugshots.
  105 + p1 = self.PersonModel(name="Joe")
  106 + p1.mugshot.save("mug", self.file1)
  107 + p2 = self.PersonModel(name="Bob")
  108 + p2.mugshot.save("mug", self.file2)
  109 + self.assertEqual(p1.mugshot == p2.mugshot, False)
  110 + self.assertEqual(p1.mugshot != p2.mugshot, True)
  111 +
  112 + # Test again with an instance fetched from the db.
  113 + p1_db = self.PersonModel.objects.get(name="Joe")
  114 + self.assertEqual(p1_db.mugshot == p2.mugshot, False)
  115 + self.assertEqual(p1_db.mugshot != p2.mugshot, True)
  116 +
  117 + # Instance from db should match the local instance.
  118 + self.assertEqual(p1_db.mugshot == p1.mugshot, True)
  119 + self.assertEqual(hash(p1_db.mugshot), hash(p1.mugshot))
  120 + self.assertEqual(p1_db.mugshot != p1.mugshot, False)
  121 +
  122 + def test_instantiate_missing(self):
  123 + """
  124 + If the underlying file is unavailable, still create instantiate the
  125 + object without error.
  126 + """
  127 + p = self.PersonModel(name="Joan")
  128 + p.mugshot.save("shot", self.file1)
  129 + p = self.PersonModel.objects.get(name="Joan")
  130 + path = p.mugshot.path
  131 + shutil.move(path, path + '.moved')
  132 + p2 = self.PersonModel.objects.get(name="Joan")
  133 +
  134 + def test_delete_when_missing(self):
  135 + """
  136 + Bug #8175: correctly delete an object where the file no longer
  137 + exists on the file system.
  138 + """
  139 + p = self.PersonModel(name="Fred")
  140 + p.mugshot.save("shot", self.file1)
  141 + os.remove(p.mugshot.path)
  142 + p.delete()
  143 +
  144 + def test_size_method(self):
  145 + """
  146 + Bug #8534: FileField.size should not leave the file open.
  147 + """
  148 + p = self.PersonModel(name="Joan")
  149 + p.mugshot.save("shot", self.file1)
  150 +
  151 + # Get a "clean" model instance
  152 + p = self.PersonModel.objects.get(name="Joan")
  153 + # It won't have an opened file.
  154 + self.assertEqual(p.mugshot.closed, True)
  155 +
  156 + # After asking for the size, the file should still be closed.
  157 + _ = p.mugshot.size
  158 + self.assertEqual(p.mugshot.closed, True)
  159 +
  160 + def test_pickle(self):
  161 + """
  162 + Tests that ImageField can be pickled, unpickled, and that the
  163 + image of the unpickled version is the same as the original.
  164 + """
  165 + import pickle
  166 +
  167 + p = Person(name="Joe")
  168 + p.mugshot.save("mug", self.file1)
  169 + dump = pickle.dumps(p)
  170 +
  171 + p2 = Person(name="Bob")
  172 + p2.mugshot = self.file1
  173 +
  174 + loaded_p = pickle.loads(dump)
  175 + self.assertEqual(p.mugshot, loaded_p.mugshot)
  176 +
  177 +
  178 +@skipIf(Image is None, "PIL is required to test ImageField")
  179 +class ImageFieldTwoDimensionsTests(ImageFieldTestMixin, TestCase):
  180 + """
  181 + Tests behavior of an ImageField and its dimensions fields.
  182 + """
  183 +
  184 + def test_constructor(self):
  185 + """
  186 + Tests assigning an image field through the model's constructor.
  187 + """
  188 + p = self.PersonModel(name='Joe', mugshot=self.file1)
  189 + self.check_dimensions(p, 4, 8)
  190 + p.save()
  191 + self.check_dimensions(p, 4, 8)
  192 +
  193 + def test_image_after_constructor(self):
  194 + """
  195 + Tests behavior when image is not passed in constructor.
  196 + """
  197 + p = self.PersonModel(name='Joe')
  198 + # TestImageField value will default to being an instance of its
  199 + # attr_class, a TestImageFieldFile, with name == None, which will
  200 + # cause it to evaluate as False.
  201 + self.assertEqual(isinstance(p.mugshot, TestImageFieldFile), True)
  202 + self.assertEqual(bool(p.mugshot), False)
  203 +
  204 + # Test setting a fresh created model instance.
  205 + p = self.PersonModel(name='Joe')
  206 + p.mugshot = self.file1
  207 + self.check_dimensions(p, 4, 8)
  208 +
  209 + def test_create(self):
  210 + """
  211 + Tests assigning an image in Manager.create().
  212 + """
  213 + p = self.PersonModel.objects.create(name='Joe', mugshot=self.file1)
  214 + self.check_dimensions(p, 4, 8)
  215 +
  216 + def test_default_value(self):
  217 + """
  218 + Tests that the default value for an ImageField is an instance of
  219 + the field's attr_class (TestImageFieldFile in this case) with no
  220 + name (name set to None).
  221 + """
  222 + p = self.PersonModel()
  223 + self.assertEqual(isinstance(p.mugshot, TestImageFieldFile), True)
  224 + self.assertEqual(bool(p.mugshot), False)
  225 +
  226 + def test_assignment_to_None(self):
  227 + """
  228 + Tests that assigning ImageField to None clears dimensions.
  229 + """
  230 + p = self.PersonModel(name='Joe', mugshot=self.file1)
  231 + self.check_dimensions(p, 4, 8)
  232 +
  233 + # If image assigned to None, dimension fields should be cleared.
  234 + p.mugshot = None
  235 + self.check_dimensions(p, None, None)
  236 +
  237 + p.mugshot = self.file2
  238 + self.check_dimensions(p, 8, 4)
  239 +
  240 + def test_field_save_and_delete_methods(self):
  241 + """
  242 + Tests assignment using the field's save method and deletion using
  243 + the field's delete method.
  244 + """
  245 + p = self.PersonModel(name='Joe')
  246 + p.mugshot.save("mug", self.file1)
  247 + self.check_dimensions(p, 4, 8)
  248 +
  249 + # A new file should update dimensions.
  250 + p.mugshot.save("mug", self.file2)
  251 + self.check_dimensions(p, 8, 4)
  252 +
  253 + # Field and dimensions should be cleared after a delete.
  254 + p.mugshot.delete(save=False)
  255 + self.assertEqual(p.mugshot, None)
  256 + self.check_dimensions(p, None, None)
  257 +
  258 + def test_dimensions(self):
  259 + """
  260 + Checks that dimensions are updated correctly in various situations.
  261 + """
  262 + p = self.PersonModel(name='Joe')
  263 +
  264 + # Dimensions should get set if file is saved.
  265 + p.mugshot.save("mug", self.file1)
  266 + self.check_dimensions(p, 4, 8)
  267 +
  268 + # Test dimensions after fetching from database.
  269 + p = self.PersonModel.objects.get(name='Joe')
  270 + # Bug 11084: Dimensions should not get recalculated if file is
  271 + # coming from the database. We test this by checking if the file
  272 + # was opened.
  273 + self.assertEqual(p.mugshot.was_opened, False)
  274 + self.check_dimensions(p, 4, 8)
  275 + # After checking dimensions on the image field, the file will have
  276 + # opened.
  277 + self.assertEqual(p.mugshot.was_opened, True)
  278 + # Dimensions should now be cached, and if we reset was_opened and
  279 + # check dimensions again, the file should not have opened.
  280 + p.mugshot.was_opened = False
  281 + self.check_dimensions(p, 4, 8)
  282 + self.assertEqual(p.mugshot.was_opened, False)
  283 +
  284 + # If we assign a new image to the instance, the dimensions should
  285 + # update.
  286 + p.mugshot = self.file2
  287 + self.check_dimensions(p, 8, 4)
  288 + # Dimensions were recalculated, and hence file should have opened.
  289 + self.assertEqual(p.mugshot.was_opened, True)
  290 +
  291 +
  292 +@skipIf(Image is None, "PIL is required to test ImageField")
  293 +class ImageFieldNoDimensionsTests(ImageFieldTwoDimensionsTests):
  294 + """
  295 + Tests behavior of an ImageField with no dimension fields.
  296 + """
  297 +
  298 + PersonModel = Person
  299 +
  300 +
  301 +@skipIf(Image is None, "PIL is required to test ImageField")
  302 +class ImageFieldOneDimensionTests(ImageFieldTwoDimensionsTests):
  303 + """
  304 + Tests behavior of an ImageField with one dimensions field.
  305 + """
  306 +
  307 + PersonModel = PersonWithHeight
  308 +
  309 +
  310 +@skipIf(Image is None, "PIL is required to test ImageField")
  311 +class ImageFieldDimensionsFirstTests(ImageFieldTwoDimensionsTests):
  312 + """
  313 + Tests behavior of an ImageField where the dimensions fields are
  314 + defined before the ImageField.
  315 + """
  316 +
  317 + PersonModel = PersonDimensionsFirst
  318 +
  319 +
  320 +@skipIf(Image is None, "PIL is required to test ImageField")
  321 +class ImageFieldUsingFileTests(ImageFieldTwoDimensionsTests):
  322 + """
  323 + Tests behavior of an ImageField when assigning it a File instance
  324 + rather than an ImageFile instance.
  325 + """
  326 +
  327 + PersonModel = PersonDimensionsFirst
  328 + File = File
  329 +
  330 +
  331 +@skipIf(Image is None, "PIL is required to test ImageField")
  332 +class TwoImageFieldTests(ImageFieldTestMixin, TestCase):
  333 + """
  334 + Tests a model with two ImageFields.
  335 + """
  336 +
  337 + PersonModel = PersonTwoImages
  338 +
  339 + def test_constructor(self):
  340 + p = self.PersonModel(mugshot=self.file1, headshot=self.file2)
  341 + self.check_dimensions(p, 4, 8, 'mugshot')
  342 + self.check_dimensions(p, 8, 4, 'headshot')
  343 + p.save()
  344 + self.check_dimensions(p, 4, 8, 'mugshot')
  345 + self.check_dimensions(p, 8, 4, 'headshot')
  346 +
  347 + def test_create(self):
  348 + p = self.PersonModel.objects.create(mugshot=self.file1,
  349 + headshot=self.file2)
  350 + self.check_dimensions(p, 4, 8)
  351 + self.check_dimensions(p, 8, 4, 'headshot')
  352 +
  353 + def test_assignment(self):
  354 + p = self.PersonModel()
  355 + self.check_dimensions(p, None, None, 'mugshot')
  356 + self.check_dimensions(p, None, None, 'headshot')
  357 +
  358 + p.mugshot = self.file1
  359 + self.check_dimensions(p, 4, 8, 'mugshot')
  360 + self.check_dimensions(p, None, None, 'headshot')
  361 + p.headshot = self.file2
  362 + self.check_dimensions(p, 4, 8, 'mugshot')
  363 + self.check_dimensions(p, 8, 4, 'headshot')
  364 +
  365 + # Clear the ImageFields one at a time.
  366 + p.mugshot = None
  367 + self.check_dimensions(p, None, None, 'mugshot')
  368 + self.check_dimensions(p, 8, 4, 'headshot')
  369 + p.headshot = None
  370 + self.check_dimensions(p, None, None, 'mugshot')
  371 + self.check_dimensions(p, None, None, 'headshot')
  372 +
  373 + def test_field_save_and_delete_methods(self):
  374 + p = self.PersonModel(name='Joe')
  375 + p.mugshot.save("mug", self.file1)
  376 + self.check_dimensions(p, 4, 8, 'mugshot')
  377 + self.check_dimensions(p, None, None, 'headshot')
  378 + p.headshot.save("head", self.file2)
  379 + self.check_dimensions(p, 4, 8, 'mugshot')
  380 + self.check_dimensions(p, 8, 4, 'headshot')
  381 +
  382 + # We can use save=True when deleting the image field with null=True
  383 + # dimension fields and the other field has an image.
  384 + p.headshot.delete(save=True)
  385 + self.check_dimensions(p, 4, 8, 'mugshot')
  386 + self.check_dimensions(p, None, None, 'headshot')
  387 + p.mugshot.delete(save=False)
  388 + self.check_dimensions(p, None, None, 'mugshot')
  389 + self.check_dimensions(p, None, None, 'headshot')
  390 +
  391 + def test_dimensions(self):
  392 + """
  393 + Checks that dimensions are updated correctly in various situations.
  394 + """
  395 + p = self.PersonModel(name='Joe')
  396 +
  397 + # Dimensions should get set for the saved file.
  398 + p.mugshot.save("mug", self.file1)
  399 + p.headshot.save("head", self.file2)
  400 + self.check_dimensions(p, 4, 8, 'mugshot')
  401 + self.check_dimensions(p, 8, 4, 'headshot')
  402 +
  403 + # Test dimensions after fetching from database.
  404 + p = self.PersonModel.objects.get(name='Joe')
  405 + # Bug 11084: Dimensions should not get recalculated if file is
  406 + # coming from the database. We test this by checking if the file
  407 + # was opened.
  408 + self.assertEqual(p.mugshot.was_opened, False)
  409 + self.assertEqual(p.headshot.was_opened, False)
  410 + self.check_dimensions(p, 4, 8,'mugshot')
  411 + self.check_dimensions(p, 8, 4, 'headshot')
  412 + # After checking dimensions on the image fields, the files will
  413 + # have been opened.
  414 + self.assertEqual(p.mugshot.was_opened, True)
  415 + self.assertEqual(p.headshot.was_opened, True)
  416 + # Dimensions should now be cached, and if we reset was_opened and
  417 + # check dimensions again, the file should not have opened.
  418 + p.mugshot.was_opened = False
  419 + p.headshot.was_opened = False
  420 + self.check_dimensions(p, 4, 8,'mugshot')
  421 + self.check_dimensions(p, 8, 4, 'headshot')
  422 + self.assertEqual(p.mugshot.was_opened, False)
  423 + self.assertEqual(p.headshot.was_opened, False)
  424 +
  425 + # If we assign a new image to the instance, the dimensions should
  426 + # update.
  427 + p.mugshot = self.file2
  428 + p.headshot = self.file1
  429 + self.check_dimensions(p, 8, 4, 'mugshot')
  430 + self.check_dimensions(p, 4, 8, 'headshot')
  431 + # Dimensions were recalculated, and hence file should have opened.
  432 + self.assertEqual(p.mugshot.was_opened, True)
  433 + self.assertEqual(p.headshot.was_opened, True)
196 tests/test_main/model_fields/models.py
... ... @@ -0,0 +1,196 @@
  1 +import os
  2 +import tempfile
  3 +
  4 +# Try to import PIL in either of the two ways it can end up installed.
  5 +# Checking for the existence of Image is enough for CPython, but for PyPy,
  6 +# you need to check for the underlying modules.
  7 +
  8 +try:
  9 + from PIL import Image, _imaging
  10 +except ImportError:
  11 + try:
  12 + import Image, _imaging
  13 + except ImportError:
  14 + Image = None
  15 +
  16 +from django.core.files.storage import FileSystemStorage
  17 +from django.db import models
  18 +from django.db.models.fields.files import ImageFieldFile, ImageField
  19 +
  20 +
  21 +class Foo(models.Model):
  22 + a = models.CharField(max_length=10)
  23 + d = models.DecimalField(max_digits=5, decimal_places=3)
  24 +
  25 +def get_foo():
  26 + return Foo.objects.get(id=1)
  27 +
  28 +class Bar(models.Model):
  29 + b = models.CharField(max_length=10)
  30 + a = models.ForeignKey(Foo, default=get_foo)
  31 +
  32 +class Whiz(models.Model):
  33 + CHOICES = (
  34 + ('Group 1', (
  35 + (1,'First'),
  36 + (2,'Second'),
  37 + )
  38 + ),
  39 + ('Group 2', (
  40 + (3,'Third'),
  41 + (4,'Fourth'),
  42 + )
  43 + ),
  44 + (0,'Other'),
  45 + )
  46 + c = models.IntegerField(choices=CHOICES, null=True)
  47 +
  48 +class BigD(models.Model):
  49 + #d = models.DecimalField(max_digits=38, decimal_places=30)
  50 + d = models.DecimalField(max_digits=18, decimal_places=10)
  51 +
  52 +class BigS(models.Model):
  53 + s = models.SlugField(max_length=255)
  54 +
  55 +class BigInt(models.Model):
  56 + value = models.BigIntegerField()
  57 + null_value = models.BigIntegerField(null = True, blank = True)
  58 +
  59 +class Post(models.Model):
  60 + title = models.CharField(max_length=100)
  61 + body = models.TextField()
  62 +
  63 +class NullBooleanModel(models.Model):
  64 + nbfield = models.NullBooleanField()
  65 +
  66 +class BooleanModel(models.Model):
  67 + bfield = models.BooleanField()
  68 + string = models.CharField(max_length=10, default='abc')
  69 +
  70 +class RenamedField(models.Model):
  71 + modelname = models.IntegerField(name="fieldname", choices=((1,'One'),))
  72 +
  73 +class VerboseNameField(models.Model):
  74 + id = models.AutoField("verbose pk", primary_key=True)
  75 + field1 = models.BigIntegerField("verbose field1")
  76 + field2 = models.BooleanField("verbose field2")
  77 + field3 = models.CharField("verbose field3", max_length=10)
  78 + field4 = models.CommaSeparatedIntegerField("verbose field4", max_length=99)
  79 + field5 = models.DateField("verbose field5")
  80 + field6 = models.DateTimeField("verbose field6")
  81 + field7 = models.DecimalField("verbose field7", max_digits=6, decimal_places=1)
  82 + field8 = models.EmailField("verbose field8")
  83 + field9 = models.FileField("verbose field9", upload_to="unused")
  84 + field10 = models.FilePathField("verbose field10")
  85 + field11 = models.FloatField("verbose field11")
  86 + # Don't want to depend on PIL in this test
  87 + #field_image = models.ImageField("verbose field")
  88 + field12 = models.IntegerField("verbose field12")
  89 + field13 = models.IPAddressField("verbose field13")
  90 + field14 = models.GenericIPAddressField("verbose field14", protocol="ipv4")
  91 + field15 = models.NullBooleanField("verbose field15")
  92 + field16 = models.PositiveIntegerField("verbose field16")
  93 + field17 = models.PositiveSmallIntegerField("verbose field17")
  94 + field18 = models.SlugField("verbose field18")
  95 + field19 = models.SmallIntegerField("verbose field19")
  96 + field20 = models.TextField("verbose field20")
  97 + field21 = models.TimeField("verbose field21")
  98 + field22 = models.URLField("verbose field22")
  99 +
  100 +# This model isn't used in any test, just here to ensure it validates successfully.
  101 +# See ticket #16570.
  102 +class DecimalLessThanOne(models.Model):
  103 + d = models.DecimalField(max_digits=3, decimal_places=3)
  104 +
  105 +###############################################################################
  106 +# FileField
  107 +
  108 +class Document(models.Model):
  109 + myfile = models.FileField(upload_to='unused')
  110 +
  111 +###############################################################################
  112 +# ImageField
  113 +
  114 +# If PIL available, do these tests.
  115 +if Image:
  116 + class TestImageFieldFile(ImageFieldFile):
  117 + """
  118 + Custom Field File class that records whether or not the underlying file
  119 + was opened.
  120 + """
  121 + def __init__(self, *args, **kwargs):
  122 + self.was_opened = False
  123 + super(TestImageFieldFile, self).__init__(*args,**kwargs)
  124 + def open(self):
  125 + self.was_opened = True
  126 + super(TestImageFieldFile, self).open()
  127 +
  128 + class TestImageField(ImageField):
  129 + attr_class = TestImageFieldFile
  130 +
  131 + # Set up a temp directory for file storage.
  132 + temp_storage_dir = tempfile.mkdtemp()
  133 + temp_storage = FileSystemStorage(temp_storage_dir)
  134 + temp_upload_to_dir = os.path.join(temp_storage.location, 'tests')
  135 +
  136 + class Person(models.Model):
  137 + """
  138 + Model that defines an ImageField with no dimension fields.
  139 + """
  140 + name = models.CharField(max_length=50)
  141 + mugshot = TestImageField(storage=temp_storage, upload_to='tests')
  142 +
  143 + class PersonWithHeight(models.Model):
  144 + """
  145 + Model that defines an ImageField with only one dimension field.
  146 + """
  147 + name = models.CharField(max_length=50)
  148 + mugshot = TestImageField(storage=temp_storage, upload_to='tests',
  149 + height_field='mugshot_height')
  150 + mugshot_height = models.PositiveSmallIntegerField()
  151 +
  152 + class PersonWithHeightAndWidth(models.Model):
  153 + """
  154 + Model that defines height and width fields after the ImageField.
  155 + """
  156 + name = models.CharField(max_length=50)
  157 + mugshot = TestImageField(storage=temp_storage, upload_to='tests',
  158 + height_field='mugshot_height',
  159 + width_field='mugshot_width')
  160 + mugshot_height = models.PositiveSmallIntegerField()
  161 + mugshot_width = models.PositiveSmallIntegerField()
  162 +
  163 + class PersonDimensionsFirst(models.Model):
  164 + """
  165 + Model that defines height and width fields before the ImageField.
  166 + """
  167 + name = models.CharField(max_length=50)
  168 + mugshot_height = models.PositiveSmallIntegerField()
  169 + mugshot_width = models.PositiveSmallIntegerField()
  170 + mugshot = TestImageField(storage=temp_storage, upload_to='tests',
  171 + height_field='mugshot_height',
  172 + width_field='mugshot_width')
  173 +
  174 + class PersonTwoImages(models.Model):
  175 + """
  176 + Model that:
  177 + * Defines two ImageFields
  178 + * Defines the height/width fields before the ImageFields
  179 + * Has a nullalble ImageField
  180 + """
  181 + name = models.CharField(max_length=50)
  182 + mugshot_height = models.PositiveSmallIntegerField()
  183 + mugshot_width = models.PositiveSmallIntegerField()
  184 + mugshot = TestImageField(storage=temp_storage, upload_to='tests',
  185 + height_field='mugshot_height',
  186 + width_field='mugshot_width')
  187 + headshot_height = models.PositiveSmallIntegerField(
  188 + blank=True, null=True)
  189 + headshot_width = models.PositiveSmallIntegerField(
  190 + blank=True, null=True)
  191 + headshot = TestImageField(blank=True, null=True,
  192 + storage=temp_storage, upload_to='tests',
  193 + height_field='headshot_height',
  194 + width_field='headshot_width')
  195 +
  196 +###############################################################################
378 tests/test_main/model_fields/tests.py
... ... @@ -0,0 +1,378 @@
  1 +from __future__ import absolute_import, unicode_literals
  2 +
  3 +import datetime
  4 +from decimal import Decimal
  5 +
  6 +from django import test
  7 +from django import forms
  8 +from django.core.exceptions import ValidationError
  9 +from django.db import models
  10 +from django.db.models.fields.files import FieldFile
  11 +from django.utils import six
  12 +from django.utils import unittest
  13 +
  14 +from .models import (Foo, Bar, Whiz, BigD, BigS, Image, BigInt, Post,
  15 + NullBooleanModel, BooleanModel, Document, RenamedField, VerboseNameField)
  16 +
  17 +from .imagefield import (ImageFieldTests, ImageFieldTwoDimensionsTests,
  18 + TwoImageFieldTests, ImageFieldNoDimensionsTests,
  19 + ImageFieldOneDimensionTests, ImageFieldDimensionsFirstTests,
  20 + ImageFieldUsingFileTests)
  21 +
  22 +
  23 +class BasicFieldTests(test.TestCase):
  24 + def test_show_hidden_initial(self):
  25 + """
  26 + Regression test for #12913. Make sure fields with choices respect
  27 + show_hidden_initial as a kwarg to models.Field.formfield()
  28 + """
  29 + choices = [(0, 0), (1, 1)]
  30 + model_field = models.Field(choices=choices)
  31 + form_field = model_field.formfield(show_hidden_initial=True)
  32 + self.assertTrue(form_field.show_hidden_initial)
  33 +
  34 + form_field = model_field.formfield(show_hidden_initial=False)
  35 + self.assertFalse(form_field.show_hidden_initial)
  36 +
  37 + def test_nullbooleanfield_blank(self):
  38 + """
  39 + Regression test for #13071: NullBooleanField should not throw
  40 + a validation error when given a value of None.
  41 +
  42 + """
  43 + nullboolean = NullBooleanModel(nbfield=None)
  44 + try:
  45 + nullboolean.full_clean()
  46 + except ValidationError as e:
  47 + self.fail("NullBooleanField failed validation with value of None: %s" % e.messages)
  48 +
  49 + def test_field_repr(self):
  50 + """
  51 + Regression test for #5931: __repr__ of a field also displays its name
  52 + """
  53 + f = Foo._meta.get_field('a')
  54 + self.assertEqual(repr(f), '<django.db.models.fields.CharField: a>')
  55 + f = models.fields.CharField()
  56 + self.assertEqual(repr(f), '<django.db.models.fields.CharField>')
  57 +
  58 + def test_field_name(self):
  59 + """
  60 + Regression test for #14695: explicitly defined field name overwritten
  61 + by model's attribute name.
  62 + """
  63 + instance = RenamedField()
  64 + self.assertTrue(hasattr(instance, 'get_fieldname_display'))
  65 + self.assertFalse(hasattr(instance, 'get_modelname_display'))
  66 +
  67 + def test_field_verbose_name(self):
  68 + m = VerboseNameField
  69 + for i in range(1, 23):
  70 + self.assertEqual(m._meta.get_field('field%d' % i).verbose_name,
  71 + 'verbose field%d' % i)
  72 +
  73 + self.assertEqual(m._meta.get_field('id').verbose_name, 'verbose pk')
  74 +
  75 +class DecimalFieldTests(test.TestCase):
  76 + def test_to_python(self):
  77 + f = models.DecimalField(max_digits=4, decimal_places=2)
  78 + self.assertEqual(f.to_python(3), Decimal("3"))
  79 + self.assertEqual(f.to_python("3.14"), Decimal("3.14"))
  80 + self.assertRaises(ValidationError, f.to_python, "abc")
  81 +
  82 + def test_default(self):
  83 + f = models.DecimalField(default=Decimal("0.00"))
  84 + self.assertEqual(f.get_default(), Decimal("0.00"))
  85 +
  86 + def test_format(self):
  87 + f = models.DecimalField(max_digits=5, decimal_places=1)
  88 + self.assertEqual(f._format(f.to_python(2)), '2.0')
  89 + self.assertEqual(f._format(f.to_python('2.6')), '2.6')
  90 + self.assertEqual(f._format(None), None)
  91 +
  92 + def test_get_db_prep_lookup(self):
  93 + from django.db import connection
  94 + f = models.DecimalField(max_digits=5, decimal_places=1)
  95 + self.assertEqual(f.get_db_prep_lookup('exact', None, connection=connection), [None])
  96 +
  97 + def test_filter_with_strings(self):
  98 + """
  99 + We should be able to filter decimal fields using strings (#8023)
  100 + """
  101 + Foo.objects.create(id=1, a='abc', d=Decimal("12.34"))
  102 + self.assertEqual(list(Foo.objects.filter(d='1.23')), [])
  103 +
  104 + def test_save_without_float_conversion(self):
  105 + """
  106 + Ensure decimals don't go through a corrupting float conversion during
  107 + save (#5079).
  108 + """
  109 + bd = BigD(d="12.9")
  110 + bd.save()
  111 + bd = BigD.objects.get(pk=bd.pk)
  112 + self.assertEqual(bd.d, Decimal("12.9"))
  113 +
  114 + def test_lookup_really_big_value(self):
  115 + """
  116 + Ensure that really big values can be used in a filter statement, even
  117 + with older Python versions.
  118 + """
  119 + # This should not crash. That counts as a win for our purposes.
  120 + Foo.objects.filter(d__gte=100000000000)
  121 +
  122 +class ForeignKeyTests(test.TestCase):
  123 + def test_callable_default(self):
  124 + """Test the use of a lazy callable for ForeignKey.default"""
  125 + a = Foo.objects.create(id=1, a='abc', d=Decimal("12.34"))
  126 + b = Bar.objects.create(b="bcd")
  127 + self.assertEqual(b.a, a)
  128 +
  129 +class DateTimeFieldTests(unittest.TestCase):
  130 + def test_datetimefield_to_python_usecs(self):
  131 + """DateTimeField.to_python should support usecs"""
  132 + f = models.DateTimeField()
  133 + self.assertEqual(f.to_python('2001-01-02 03:04:05.000006'),
  134 + datetime.datetime(2001, 1, 2, 3, 4, 5, 6))
  135 + self.assertEqual(f.to_python('2001-01-02 03:04:05.999999'),
  136 + datetime.datetime(2001, 1, 2, 3, 4, 5, 999999))
  137 +
  138 + def test_timefield_to_python_usecs(self):
  139 + """TimeField.to_python should support usecs"""
  140 + f = models.TimeField()
  141 + self.assertEqual(f.to_python('01:02:03.000004'),
  142 + datetime.time(1, 2, 3, 4))
  143 + self.assertEqual(f.to_python('01:02:03.999999'),
  144 + datetime.time(1, 2, 3, 999999))
  145 +
  146 +class BooleanFieldTests(unittest.TestCase):
  147 + def _test_get_db_prep_lookup(self, f):
  148 + from django.db import connection
  149 + self.assertEqual(f.get_db_prep_lookup('exact', True, connection=connection), [True])
  150 + self.assertEqual(f.get_db_prep_lookup('exact', '1', connection=connection), [True])
  151 + self.assertEqual(f.get_db_prep_lookup('exact', 1, connection=connection), [True])
  152 + self.assertEqual(f.get_db_prep_lookup('exact', False, connection=connection), [False])
  153 + self.assertEqual(f.get_db_prep_lookup('exact', '0', connection=connection), [False])
  154 + self.assertEqual(f.get_db_prep_lookup('exact', 0, connection=connection), [False])
  155 + self.assertEqual(f.get_db_prep_lookup('exact', None, connection=connection), [None])
  156 +
  157 + def _test_to_python(self, f):
  158 + self.assertTrue(f.to_python(1) is True)
  159 + self.assertTrue(f.to_python(0) is False)
  160 +
  161 + def test_booleanfield_get_db_prep_lookup(self):
  162 + self._test_get_db_prep_lookup(models.BooleanField())
  163 +
  164 + def test_nullbooleanfield_get_db_prep_lookup(self):
  165 + self._test_get_db_prep_lookup(models.NullBooleanField())
  166 +
  167 + def test_booleanfield_to_python(self):
  168 + self._test_to_python(models.BooleanField())
  169 +
  170 + def test_nullbooleanfield_to_python(self):
  171 + self._test_to_python(models.NullBooleanField())
  172 +
  173 + def test_booleanfield_choices_blank(self):
  174 + """
  175 + Test that BooleanField with choices and defaults doesn't generate a
  176 + formfield with the blank option (#9640, #10549).
  177 + """
  178 + choices = [(1, 'Si'), (2, 'No')]
  179 + f = models.BooleanField(choices=choices, default=1, null=True)
  180 + self.assertEqual(f.formfield().choices, [('', '---------')] + choices)
  181 +
  182 + f = models.BooleanField(choices=choices, default=1, null=False)
  183 + self.assertEqual(f.formfield().choices, choices)
  184 +
  185 + def test_return_type(self):
  186 + b = BooleanModel()
  187 + b.bfield = True
  188 + b.save()
  189 + b2 = BooleanModel.objects.get(pk=b.pk)
  190 + self.assertTrue(isinstance(b2.bfield, bool))
  191 + self.assertEqual(b2.bfield, True)
  192 +
  193 + b3 = BooleanModel()
  194 + b3.bfield = False
  195 + b3.save()
  196 + b4 = BooleanModel.objects.get(pk=b3.pk)
  197 + self.assertTrue(isinstance(b4.bfield, bool))
  198 + self.assertEqual(b4.bfield, False)
  199 +
  200 + b = NullBooleanModel()
  201 + b.nbfield = True
  202 + b.save()
  203 + b2 = NullBooleanModel.objects.get(pk=b.pk)
  204 + self.assertTrue(isinstance(b2.nbfield, bool))
  205 + self.assertEqual(b2.nbfield, True)
  206 +
  207 + b3 = NullBooleanModel()
  208 + b3.nbfield = False
  209 + b3.save()
  210 + b4 = NullBooleanModel.objects.get(pk=b3.pk)
  211 + self.assertTrue(isinstance(b4.nbfield, bool))
  212 + self.assertEqual(b4.nbfield, False)
  213 +
  214 + # http://code.djangoproject.com/ticket/13293
  215 + # Verify that when an extra clause exists, the boolean
  216 + # conversions are applied with an offset
  217 + b5 = BooleanModel.objects.all().extra(
  218 + select={'string_col': 'string'})[0]
  219 + self.assertFalse(isinstance(b5.pk, bool))
  220 +
  221 +class ChoicesTests(test.TestCase):
  222 + def test_choices_and_field_display(self):
  223 + """
  224 + Check that get_choices and get_flatchoices interact with
  225 + get_FIELD_display to return the expected values (#7913).
  226 + """
  227 + self.assertEqual(Whiz(c=1).get_c_display(), 'First') # A nested value
  228 + self.assertEqual(Whiz(c=0).get_c_display(), 'Other') # A top level value
  229 + self.assertEqual(Whiz(c=9).get_c_display(), 9) # Invalid value
  230 + self.assertEqual(Whiz(c=None).get_c_display(), None) # Blank value
  231 + self.assertEqual(Whiz(c='').get_c_display(), '') # Empty value
  232 +
  233 +class SlugFieldTests(test.TestCase):
  234 + def test_slugfield_max_length(self):
  235 + """
  236 + Make sure SlugField honors max_length (#9706)
  237 + """
  238 + bs = BigS.objects.create(s = 'slug'*50)
  239 + bs = BigS.objects.get(pk=bs.pk)
  240 + self.assertEqual(bs.s, 'slug'*50)
  241 +
  242 +
  243 +class ValidationTest(test.TestCase):
  244 + def test_charfield_raises_error_on_empty_string(self):
  245 + f = models.CharField()
  246 + self.assertRaises(ValidationError, f.clean, "", None)
  247 +
  248 + def test_charfield_cleans_empty_string_when_blank_true(self):
  249 + f = models.CharField(blank=True)
  250 + self.assertEqual('', f.clean('', None))
  251 +
  252 + def test_integerfield_cleans_valid_string(self):
  253 + f = models.IntegerField()
  254 + self.assertEqual(2, f.clean('2', None))
  255 +
  256 + def test_integerfield_raises_error_on_invalid_intput(self):
  257 + f = models.IntegerField()
  258 + self.assertRaises(ValidationError, f.clean, "a", None)
  259 +
  260 + def test_charfield_with_choices_cleans_valid_choice(self):
  261 + f = models.CharField(max_length=1, choices=[('a','A'), ('b','B')])
  262 + self.assertEqual('a', f.clean('a', None))
  263 +
  264 + def test_charfield_with_choices_raises_error_on_invalid_choice(self):
  265 + f = models.CharField(choices=[('a','A'), ('b','B')])
  266 + self.assertRaises(ValidationError, f.clean, "not a", None)
  267 +
  268 + def test_choices_validation_supports_named_groups(self):
  269 + f = models.IntegerField(choices=(('group',((10,'A'),(20,'B'))),(30,'C')))
  270 + self.assertEqual(10, f.clean(10, None))
  271 +
  272 + def test_nullable_integerfield_raises_error_with_blank_false(self):
  273 + f = models.IntegerField(null=True, blank=False)
  274 + self.assertRaises(ValidationError, f.clean, None, None)
  275 +
  276 + def test_nullable_integerfield_cleans_none_on_null_and_blank_true(self):
  277 + f = models.IntegerField(null=True, blank=True)
  278 + self.assertEqual(None, f.clean(None, None))
  279 +
  280 + def test_integerfield_raises_error_on_empty_input(self):
  281 + f = models.IntegerField(null=False)
  282 + self.assertRaises(ValidationError, f.clean, None, None)
  283 + self.assertRaises(ValidationError, f.clean, '', None)
  284 +
  285 + def test_integerfield_validates_zero_against_choices(self):
  286 + f = models.IntegerField(choices=((1, 1),))
  287 + self.assertRaises(ValidationError, f.clean, '0', None)
  288 +
  289 + def test_charfield_raises_error_on_empty_input(self):
  290 + f = models.CharField(null=False)
  291 + self.assertRaises(ValidationError, f.clean, None, None)
  292 +
  293 + def test_datefield_cleans_date(self):
  294 + f = models.DateField()
  295 + self.assertEqual(datetime.date(2008, 10, 10), f.clean('2008-10-10', None))
  296 +
  297 + def test_boolean_field_doesnt_accept_empty_input(self):
  298 + f = models.BooleanField()
  299 + self.assertRaises(ValidationError, f.clean, None, None)
  300 +
  301 +
  302 +class BigIntegerFieldTests(test.TestCase):
  303 + def test_limits(self):
  304 + # Ensure that values that are right at the limits can be saved
  305 + # and then retrieved without corruption.
  306 + maxval = 9223372036854775807
  307 + minval = -maxval - 1
  308 + BigInt.objects.create(value=maxval)
  309 + qs = BigInt.objects.filter(value__gte=maxval)
  310 + self.assertEqual(qs.count(), 1)
  311 + self.assertEqual(qs[0].value, maxval)
  312 + BigInt.objects.create(value=minval)
  313 + qs = BigInt.objects.filter(value__lte=minval)
  314 + self.assertEqual(qs.count(), 1)
  315 + self.assertEqual(qs[0].value, minval)
  316 +
  317 + def test_types(self):
  318 + b = BigInt(value = 0)
  319 + self.assertTrue(isinstance(b.value, six.integer_types))
  320 + b.save()
  321 + self.assertTrue(isinstance(b.value, six.integer_types))
  322 + b = BigInt.objects.all()[0]
  323 + self.assertTrue(isinstance(b.value, six.integer_types))
  324 +
  325 + def test_coercing(self):
  326 + BigInt.objects.create(value ='10')
  327 + b = BigInt.objects.get(value = '10')
  328 + self.assertEqual(b.value, 10)
  329 +
  330 +class TypeCoercionTests(test.TestCase):
  331 + """
  332 + Test that database lookups can accept the wrong types and convert
  333 + them with no error: especially on Postgres 8.3+ which does not do
  334 + automatic casting at the DB level. See #10015.
  335 +
  336 + """
  337 + def test_lookup_integer_in_charfield(self):
  338 + self.assertEqual(Post.objects.filter(title=9).count(), 0)
  339 +
  340 + def test_lookup_integer_in_textfield(self):
  341 + self.assertEqual(Post.objects.filter(body=24).count(), 0)
  342 +
  343 +class FileFieldTests(unittest.TestCase):
  344 + def test_clearable(self):
  345 + """
  346 + Test that FileField.save_form_data will clear its instance attribute
  347 + value if passed False.
  348 +
  349 + """
  350 + d = Document(myfile='something.txt')
  351 + self.assertEqual(d.myfile, 'something.txt')
  352 + field = d._meta.get_field('myfile')
  353 + field.save_form_data(d, False)
  354 + self.assertEqual(d.myfile, '')
  355 +
  356 + def test_unchanged(self):
  357 + """
  358 + Test that FileField.save_form_data considers None to mean "no change"
  359 + rather than "clear".
  360 +
  361 + """
  362 + d = Document(myfile='something.txt')
  363 + self.assertEqual(d.myfile, 'something.txt')
  364 + field = d._meta.get_field('myfile')
  365 + field.save_form_data(d, None)
  366 + self.assertEqual(d.myfile, 'something.txt')
  367 +
  368 + def test_changed(self):
  369 + """
  370 + Test that FileField.save_form_data, if passed a truthy value, updates
  371 + its instance attribute.
  372 +
  373 + """
  374 + d = Document(myfile='something.txt')
  375 + self.assertEqual(d.myfile, 'something.txt')
  376 + field = d._meta.get_field('myfile')
  377 + field.save_form_data(d, 'else.txt')
  378 + self.assertEqual(d.myfile, 'else.txt')
1  tests/test_main/test_main/settings.py
@@ -135,6 +135,7 @@
135 135 # Uncomment the next line to enable admin documentation:
136 136 # 'django.contrib.admindocs',
137 137 'test_base',
  138 + 'model_fields',
138 139 )
139 140
140 141 # A sample logging configuration. The only tangible logging

0 comments on commit c941e4a

Please sign in to comment.
Something went wrong with that request. Please try again.