/tests/modeltests/select_related/tests.py
Python | 168 lines | 152 code | 7 blank | 9 comment | 4 complexity | 68b7706010bc0f9e463ce9b0e62d1777 MD5 | raw file
Possible License(s): BSD-3-Clause
1from django.test import TestCase 2 3from models import Domain, Kingdom, Phylum, Klass, Order, Family, Genus, Species 4 5class SelectRelatedTests(TestCase): 6 7 def create_tree(self, stringtree): 8 """ 9 Helper to create a complete tree. 10 """ 11 names = stringtree.split() 12 models = [Domain, Kingdom, Phylum, Klass, Order, Family, Genus, Species] 13 assert len(names) == len(models), (names, models) 14 15 parent = None 16 for name, model in zip(names, models): 17 try: 18 obj = model.objects.get(name=name) 19 except model.DoesNotExist: 20 obj = model(name=name) 21 if parent: 22 setattr(obj, parent.__class__.__name__.lower(), parent) 23 obj.save() 24 parent = obj 25 26 def create_base_data(self): 27 self.create_tree("Eukaryota Animalia Anthropoda Insecta Diptera Drosophilidae Drosophila melanogaster") 28 self.create_tree("Eukaryota Animalia Chordata Mammalia Primates Hominidae Homo sapiens") 29 self.create_tree("Eukaryota Plantae Magnoliophyta Magnoliopsida Fabales Fabaceae Pisum sativum") 30 self.create_tree("Eukaryota Fungi Basidiomycota Homobasidiomycatae Agaricales Amanitacae Amanita muscaria") 31 32 def setUp(self): 33 # The test runner sets settings.DEBUG to False, but we want to gather 34 # queries so we'll set it to True here and reset it at the end of the 35 # test case. 36 self.create_base_data() 37 38 def test_access_fks_without_select_related(self): 39 """ 40 Normally, accessing FKs doesn't fill in related objects 41 """ 42 def test(): 43 fly = Species.objects.get(name="melanogaster") 44 domain = fly.genus.family.order.klass.phylum.kingdom.domain 45 self.assertEqual(domain.name, 'Eukaryota') 46 self.assertNumQueries(8, test) 47 48 def test_access_fks_with_select_related(self): 49 """ 50 A select_related() call will fill in those related objects without any 51 extra queries 52 """ 53 def test(): 54 person = Species.objects.select_related(depth=10).get(name="sapiens") 55 domain = person.genus.family.order.klass.phylum.kingdom.domain 56 self.assertEqual(domain.name, 'Eukaryota') 57 self.assertNumQueries(1, test) 58 59 def test_list_without_select_related(self): 60 """ 61 select_related() also of course applies to entire lists, not just 62 items. This test verifies the expected behavior without select_related. 63 """ 64 def test(): 65 world = Species.objects.all() 66 families = [o.genus.family.name for o in world] 67 self.assertEqual(sorted(families), [ 68 'Amanitacae', 69 'Drosophilidae', 70 'Fabaceae', 71 'Hominidae', 72 ]) 73 self.assertNumQueries(9, test) 74 75 def test_list_with_select_related(self): 76 """ 77 select_related() also of course applies to entire lists, not just 78 items. This test verifies the expected behavior with select_related. 79 """ 80 def test(): 81 world = Species.objects.all().select_related() 82 families = [o.genus.family.name for o in world] 83 self.assertEqual(sorted(families), [ 84 'Amanitacae', 85 'Drosophilidae', 86 'Fabaceae', 87 'Hominidae', 88 ]) 89 self.assertNumQueries(1, test) 90 91 def test_depth(self, depth=1, expected=7): 92 """ 93 The "depth" argument to select_related() will stop the descent at a 94 particular level. 95 """ 96 def test(): 97 pea = Species.objects.select_related(depth=depth).get(name="sativum") 98 self.assertEqual( 99 pea.genus.family.order.klass.phylum.kingdom.domain.name, 100 'Eukaryota' 101 ) 102 # Notice: one fewer queries than above because of depth=1 103 self.assertNumQueries(expected, test) 104 105 def test_larger_depth(self): 106 """ 107 The "depth" argument to select_related() will stop the descent at a 108 particular level. This tests a larger depth value. 109 """ 110 self.test_depth(depth=5, expected=3) 111 112 def test_list_with_depth(self): 113 """ 114 The "depth" argument to select_related() will stop the descent at a 115 particular level. This can be used on lists as well. 116 """ 117 def test(): 118 world = Species.objects.all().select_related(depth=2) 119 orders = [o.genus.family.order.name for o in world] 120 self.assertEqual(sorted(orders), 121 ['Agaricales', 'Diptera', 'Fabales', 'Primates']) 122 self.assertNumQueries(5, test) 123 124 def test_select_related_with_extra(self): 125 s = Species.objects.all().select_related(depth=1)\ 126 .extra(select={'a': 'select_related_species.id + 10'})[0] 127 self.assertEqual(s.id + 10, s.a) 128 129 def test_certain_fields(self): 130 """ 131 The optional fields passed to select_related() control which related 132 models we pull in. This allows for smaller queries and can act as an 133 alternative (or, in addition to) the depth parameter. 134 135 In this case, we explicitly say to select the 'genus' and 136 'genus.family' models, leading to the same number of queries as before. 137 """ 138 def test(): 139 world = Species.objects.select_related('genus__family') 140 families = [o.genus.family.name for o in world] 141 self.assertEqual(sorted(families), 142 ['Amanitacae', 'Drosophilidae', 'Fabaceae', 'Hominidae']) 143 self.assertNumQueries(1, test) 144 145 def test_more_certain_fields(self): 146 """ 147 In this case, we explicitly say to select the 'genus' and 148 'genus.family' models, leading to the same number of queries as before. 149 """ 150 def test(): 151 world = Species.objects.filter(genus__name='Amanita')\ 152 .select_related('genus__family') 153 orders = [o.genus.family.order.name for o in world] 154 self.assertEqual(orders, [u'Agaricales']) 155 self.assertNumQueries(2, test) 156 157 def test_field_traversal(self): 158 def test(): 159 s = Species.objects.all().select_related('genus__family__order' 160 ).order_by('id')[0:1].get().genus.family.order.name 161 self.assertEqual(s, u'Diptera') 162 self.assertNumQueries(1, test) 163 164 def test_depth_fields_fails(self): 165 self.assertRaises(TypeError, 166 Species.objects.select_related, 167 'genus__family__order', depth=4 168 )