diff options
| author | Florian Apolloner <florian@apolloner.eu> | 2013-02-26 09:53:47 +0100 |
|---|---|---|
| committer | Florian Apolloner <florian@apolloner.eu> | 2013-02-26 14:36:57 +0100 |
| commit | 89f40e36246100df6a11316c31a76712ebc6c501 (patch) | |
| tree | 6e65639683ddaf2027908d1ecb1739e0e2ff853b /tests/defer_regress | |
| parent | b3d2ccb5bfbaf6e7fe1f98843baaa48c35a70950 (diff) | |
Merged regressiontests and modeltests into the test root.
Diffstat (limited to 'tests/defer_regress')
| -rw-r--r-- | tests/defer_regress/__init__.py | 0 | ||||
| -rw-r--r-- | tests/defer_regress/models.py | 64 | ||||
| -rw-r--r-- | tests/defer_regress/tests.py | 248 |
3 files changed, 312 insertions, 0 deletions
diff --git a/tests/defer_regress/__init__.py b/tests/defer_regress/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/defer_regress/__init__.py diff --git a/tests/defer_regress/models.py b/tests/defer_regress/models.py new file mode 100644 index 0000000000..7528e3b2c9 --- /dev/null +++ b/tests/defer_regress/models.py @@ -0,0 +1,64 @@ +""" +Regression tests for defer() / only() behavior. +""" + +from django.db import models +from django.utils.encoding import python_2_unicode_compatible + + +@python_2_unicode_compatible +class Item(models.Model): + name = models.CharField(max_length=15) + text = models.TextField(default="xyzzy") + value = models.IntegerField() + other_value = models.IntegerField(default=0) + + def __str__(self): + return self.name + +class RelatedItem(models.Model): + item = models.ForeignKey(Item) + +class Child(models.Model): + name = models.CharField(max_length=10) + value = models.IntegerField() + +@python_2_unicode_compatible +class Leaf(models.Model): + name = models.CharField(max_length=10) + child = models.ForeignKey(Child) + second_child = models.ForeignKey(Child, related_name="other", null=True) + value = models.IntegerField(default=42) + + def __str__(self): + return self.name + +class ResolveThis(models.Model): + num = models.FloatField() + name = models.CharField(max_length=16) + +class Proxy(Item): + class Meta: + proxy = True + +@python_2_unicode_compatible +class SimpleItem(models.Model): + name = models.CharField(max_length=15) + value = models.IntegerField() + + def __str__(self): + return self.name + +class Feature(models.Model): + item = models.ForeignKey(SimpleItem) + +class SpecialFeature(models.Model): + feature = models.ForeignKey(Feature) + +class OneToOneItem(models.Model): + item = models.OneToOneField(Item, related_name="one_to_one_item") + name = models.CharField(max_length=15) + +class ItemAndSimpleItem(models.Model): + item = models.ForeignKey(Item) + simple = models.ForeignKey(SimpleItem) diff --git a/tests/defer_regress/tests.py b/tests/defer_regress/tests.py new file mode 100644 index 0000000000..d4d722035f --- /dev/null +++ b/tests/defer_regress/tests.py @@ -0,0 +1,248 @@ +from __future__ import absolute_import + +from operator import attrgetter + +from django.contrib.contenttypes.models import ContentType +from django.contrib.sessions.backends.db import SessionStore +from django.db.models import Count +from django.db.models.loading import cache +from django.test import TestCase + +from .models import (ResolveThis, Item, RelatedItem, Child, Leaf, Proxy, + SimpleItem, Feature, ItemAndSimpleItem, OneToOneItem, SpecialFeature) + + +class DeferRegressionTest(TestCase): + def test_basic(self): + # Deferred fields should really be deferred and not accidentally use + # the field's default value just because they aren't passed to __init__ + + Item.objects.create(name="first", value=42) + obj = Item.objects.only("name", "other_value").get(name="first") + # Accessing "name" doesn't trigger a new database query. Accessing + # "value" or "text" should. + with self.assertNumQueries(0): + self.assertEqual(obj.name, "first") + self.assertEqual(obj.other_value, 0) + + with self.assertNumQueries(1): + self.assertEqual(obj.value, 42) + + with self.assertNumQueries(1): + self.assertEqual(obj.text, "xyzzy") + + with self.assertNumQueries(0): + self.assertEqual(obj.text, "xyzzy") + + # Regression test for #10695. Make sure different instances don't + # inadvertently share data in the deferred descriptor objects. + i = Item.objects.create(name="no I'm first", value=37) + items = Item.objects.only("value").order_by("-value") + self.assertEqual(items[0].name, "first") + self.assertEqual(items[1].name, "no I'm first") + + RelatedItem.objects.create(item=i) + r = RelatedItem.objects.defer("item").get() + self.assertEqual(r.item_id, i.id) + self.assertEqual(r.item, i) + + # Some further checks for select_related() and inherited model + # behavior (regression for #10710). + c1 = Child.objects.create(name="c1", value=42) + c2 = Child.objects.create(name="c2", value=37) + Leaf.objects.create(name="l1", child=c1, second_child=c2) + + obj = Leaf.objects.only("name", "child").select_related()[0] + self.assertEqual(obj.child.name, "c1") + + self.assertQuerysetEqual( + Leaf.objects.select_related().only("child__name", "second_child__name"), [ + "l1", + ], + attrgetter("name") + ) + + # Models instances with deferred fields should still return the same + # content types as their non-deferred versions (bug #10738). + ctype = ContentType.objects.get_for_model + c1 = ctype(Item.objects.all()[0]) + c2 = ctype(Item.objects.defer("name")[0]) + c3 = ctype(Item.objects.only("name")[0]) + self.assertTrue(c1 is c2 is c3) + + # Regression for #10733 - only() can be used on a model with two + # foreign keys. + results = Leaf.objects.only("name", "child", "second_child").select_related() + self.assertEqual(results[0].child.name, "c1") + self.assertEqual(results[0].second_child.name, "c2") + + results = Leaf.objects.only("name", "child", "second_child", "child__name", "second_child__name").select_related() + self.assertEqual(results[0].child.name, "c1") + self.assertEqual(results[0].second_child.name, "c2") + + # Test for #12163 - Pickling error saving session with unsaved model + # instances. + SESSION_KEY = '2b1189a188b44ad18c35e1baac6ceead' + + item = Item() + item._deferred = False + s = SessionStore(SESSION_KEY) + s.clear() + s["item"] = item + s.save() + + s = SessionStore(SESSION_KEY) + s.modified = True + s.save() + + i2 = s["item"] + self.assertFalse(i2._deferred) + + # Regression for #11936 - loading.get_models should not return deferred + # models by default. + klasses = sorted( + cache.get_models(cache.get_app("defer_regress")), + key=lambda klass: klass.__name__ + ) + self.assertEqual( + klasses, [ + Child, + Feature, + Item, + ItemAndSimpleItem, + Leaf, + OneToOneItem, + Proxy, + RelatedItem, + ResolveThis, + SimpleItem, + SpecialFeature, + ] + ) + + klasses = sorted( + map( + attrgetter("__name__"), + cache.get_models( + cache.get_app("defer_regress"), include_deferred=True + ), + ) + ) + # FIXME: This is dependent on the order in which tests are run -- + # this test case has to be the first, otherwise a LOT more classes + # appear. + self.assertEqual( + klasses, [ + "Child", + "Child_Deferred_value", + "Feature", + "Item", + "ItemAndSimpleItem", + "Item_Deferred_name", + "Item_Deferred_name_other_value_text", + "Item_Deferred_name_other_value_value", + "Item_Deferred_other_value_text_value", + "Item_Deferred_text_value", + "Leaf", + "Leaf_Deferred_child_id_second_child_id_value", + "Leaf_Deferred_name_value", + "Leaf_Deferred_second_child_id_value", + "Leaf_Deferred_value", + "OneToOneItem", + "Proxy", + "RelatedItem", + "RelatedItem_Deferred_", + "RelatedItem_Deferred_item_id", + "ResolveThis", + "SimpleItem", + "SpecialFeature", + ] + ) + + # Regression for #16409 - make sure defer() and only() work with annotate() + self.assertIsInstance(list(SimpleItem.objects.annotate(Count('feature')).defer('name')), list) + self.assertIsInstance(list(SimpleItem.objects.annotate(Count('feature')).only('name')), list) + + def test_only_and_defer_usage_on_proxy_models(self): + # Regression for #15790 - only() broken for proxy models + proxy = Proxy.objects.create(name="proxy", value=42) + + msg = 'QuerySet.only() return bogus results with proxy models' + dp = Proxy.objects.only('other_value').get(pk=proxy.pk) + self.assertEqual(dp.name, proxy.name, msg=msg) + self.assertEqual(dp.value, proxy.value, msg=msg) + + # also test things with .defer() + msg = 'QuerySet.defer() return bogus results with proxy models' + dp = Proxy.objects.defer('name', 'text', 'value').get(pk=proxy.pk) + self.assertEqual(dp.name, proxy.name, msg=msg) + self.assertEqual(dp.value, proxy.value, msg=msg) + + def test_resolve_columns(self): + rt = ResolveThis.objects.create(num=5.0, name='Foobar') + qs = ResolveThis.objects.defer('num') + self.assertEqual(1, qs.count()) + self.assertEqual('Foobar', qs[0].name) + + def test_reverse_one_to_one_relations(self): + # Refs #14694. Test reverse relations which are known unique (reverse + # side has o2ofield or unique FK) - the o2o case + item = Item.objects.create(name="first", value=42) + o2o = OneToOneItem.objects.create(item=item, name="second") + self.assertEqual(len(Item.objects.defer('one_to_one_item__name')), 1) + self.assertEqual(len(Item.objects.select_related('one_to_one_item')), 1) + self.assertEqual(len(Item.objects.select_related( + 'one_to_one_item').defer('one_to_one_item__name')), 1) + self.assertEqual(len(Item.objects.select_related('one_to_one_item').defer('value')), 1) + # Make sure that `only()` doesn't break when we pass in a unique relation, + # rather than a field on the relation. + self.assertEqual(len(Item.objects.only('one_to_one_item')), 1) + with self.assertNumQueries(1): + i = Item.objects.select_related('one_to_one_item')[0] + self.assertEqual(i.one_to_one_item.pk, o2o.pk) + self.assertEqual(i.one_to_one_item.name, "second") + with self.assertNumQueries(1): + i = Item.objects.select_related('one_to_one_item').defer( + 'value', 'one_to_one_item__name')[0] + self.assertEqual(i.one_to_one_item.pk, o2o.pk) + self.assertEqual(i.name, "first") + with self.assertNumQueries(1): + self.assertEqual(i.one_to_one_item.name, "second") + with self.assertNumQueries(1): + self.assertEqual(i.value, 42) + + def test_defer_with_select_related(self): + item1 = Item.objects.create(name="first", value=47) + item2 = Item.objects.create(name="second", value=42) + simple = SimpleItem.objects.create(name="simple", value="23") + related = ItemAndSimpleItem.objects.create(item=item1, simple=simple) + + obj = ItemAndSimpleItem.objects.defer('item').select_related('simple').get() + self.assertEqual(obj.item, item1) + self.assertEqual(obj.item_id, item1.id) + + obj.item = item2 + obj.save() + + obj = ItemAndSimpleItem.objects.defer('item').select_related('simple').get() + self.assertEqual(obj.item, item2) + self.assertEqual(obj.item_id, item2.id) + + def test_only_with_select_related(self): + # Test for #17485. + item = SimpleItem.objects.create(name='first', value=47) + feature = Feature.objects.create(item=item) + SpecialFeature.objects.create(feature=feature) + + qs = Feature.objects.only('item__name').select_related('item') + self.assertEqual(len(qs), 1) + + qs = SpecialFeature.objects.only('feature__item__name').select_related('feature__item') + self.assertEqual(len(qs), 1) + + def test_deferred_class_factory(self): + from django.db.models.query_utils import deferred_class_factory + new_class = deferred_class_factory(Item, + ('this_is_some_very_long_attribute_name_so_modelname_truncation_is_triggered',)) + self.assertEqual(new_class.__name__, + 'Item_Deferred_this_is_some_very_long_attribute_nac34b1f495507dad6b02e2cb235c875e') |
