/tests/modeltests/many_to_one/tests.py
Python | 372 lines | 299 code | 21 blank | 52 comment | 0 complexity | 269b4818f6aa11c1c68afe91619a7fbe MD5 | raw file
1from datetime import datetime 2from django.test import TestCase 3from django.core.exceptions import FieldError 4from models import Article, Reporter 5 6class ManyToOneTests(TestCase): 7 8 def setUp(self): 9 # Create a few Reporters. 10 self.r = Reporter(first_name='John', last_name='Smith', email='john@example.com') 11 self.r.save() 12 self.r2 = Reporter(first_name='Paul', last_name='Jones', email='paul@example.com') 13 self.r2.save() 14 # Create an Article. 15 self.a = Article(id=None, headline="This is a test", 16 pub_date=datetime(2005, 7, 27), reporter=self.r) 17 self.a.save() 18 19 def test_get(self): 20 # Article objects have access to their related Reporter objects. 21 r = self.a.reporter 22 self.assertEqual(r.id, self.r.id) 23 # These are strings instead of unicode strings because that's what was used in 24 # the creation of this reporter (and we haven't refreshed the data from the 25 # database, which always returns unicode strings). 26 self.assertEqual((r.first_name, self.r.last_name), ('John', 'Smith')) 27 28 def test_create(self): 29 # You can also instantiate an Article by passing the Reporter's ID 30 # instead of a Reporter object. 31 a3 = Article(id=None, headline="Third article", 32 pub_date=datetime(2005, 7, 27), reporter_id=self.r.id) 33 a3.save() 34 self.assertEqual(a3.reporter.id, self.r.id) 35 36 # Similarly, the reporter ID can be a string. 37 a4 = Article(id=None, headline="Fourth article", 38 pub_date=datetime(2005, 7, 27), reporter_id=str(self.r.id)) 39 a4.save() 40 self.assertEqual(repr(a4.reporter), "<Reporter: John Smith>") 41 42 def test_add(self): 43 # Create an Article via the Reporter object. 44 new_article = self.r.article_set.create(headline="John's second story", 45 pub_date=datetime(2005, 7, 29)) 46 self.assertEqual(repr(new_article), "<Article: John's second story>") 47 self.assertEqual(new_article.reporter.id, self.r.id) 48 49 # Create a new article, and add it to the article set. 50 new_article2 = Article(headline="Paul's story", pub_date=datetime(2006, 1, 17)) 51 self.r.article_set.add(new_article2) 52 self.assertEqual(new_article2.reporter.id, self.r.id) 53 self.assertQuerysetEqual(self.r.article_set.all(), 54 [ 55 "<Article: John's second story>", 56 "<Article: Paul's story>", 57 "<Article: This is a test>", 58 ]) 59 60 # Add the same article to a different article set - check that it moves. 61 self.r2.article_set.add(new_article2) 62 self.assertEqual(new_article2.reporter.id, self.r2.id) 63 self.assertQuerysetEqual(self.r2.article_set.all(), ["<Article: Paul's story>"]) 64 65 # Adding an object of the wrong type raises TypeError. 66 self.assertRaises(TypeError, self.r.article_set.add, self.r2) 67 self.assertQuerysetEqual(self.r.article_set.all(), 68 [ 69 "<Article: John's second story>", 70 "<Article: This is a test>", 71 ]) 72 73 def test_assign(self): 74 new_article = self.r.article_set.create(headline="John's second story", 75 pub_date=datetime(2005, 7, 29)) 76 new_article2 = self.r2.article_set.create(headline="Paul's story", 77 pub_date=datetime(2006, 1, 17)) 78 # Assign the article to the reporter directly using the descriptor. 79 new_article2.reporter = self.r 80 new_article2.save() 81 self.assertEqual(repr(new_article2.reporter), "<Reporter: John Smith>") 82 self.assertEqual(new_article2.reporter.id, self.r.id) 83 self.assertQuerysetEqual(self.r.article_set.all(), [ 84 "<Article: John's second story>", 85 "<Article: Paul's story>", 86 "<Article: This is a test>", 87 ]) 88 self.assertQuerysetEqual(self.r2.article_set.all(), []) 89 # Set the article back again using set descriptor. 90 self.r2.article_set = [new_article, new_article2] 91 self.assertQuerysetEqual(self.r.article_set.all(), ["<Article: This is a test>"]) 92 self.assertQuerysetEqual(self.r2.article_set.all(), 93 [ 94 "<Article: John's second story>", 95 "<Article: Paul's story>", 96 ]) 97 98 # Funny case - assignment notation can only go so far; because the 99 # ForeignKey cannot be null, existing members of the set must remain. 100 self.r.article_set = [new_article] 101 self.assertQuerysetEqual(self.r.article_set.all(), 102 [ 103 "<Article: John's second story>", 104 "<Article: This is a test>", 105 ]) 106 self.assertQuerysetEqual(self.r2.article_set.all(), ["<Article: Paul's story>"]) 107 # Reporter cannot be null - there should not be a clear or remove method 108 self.assertFalse(hasattr(self.r2.article_set, 'remove')) 109 self.assertFalse(hasattr(self.r2.article_set, 'clear')) 110 111 def test_selects(self): 112 new_article = self.r.article_set.create(headline="John's second story", 113 pub_date=datetime(2005, 7, 29)) 114 new_article2 = self.r2.article_set.create(headline="Paul's story", 115 pub_date=datetime(2006, 1, 17)) 116 # Reporter objects have access to their related Article objects. 117 self.assertQuerysetEqual(self.r.article_set.all(), [ 118 "<Article: John's second story>", 119 "<Article: This is a test>", 120 ]) 121 self.assertQuerysetEqual(self.r.article_set.filter(headline__startswith='This'), 122 ["<Article: This is a test>"]) 123 self.assertEqual(self.r.article_set.count(), 2) 124 self.assertEqual(self.r2.article_set.count(), 1) 125 # Get articles by id 126 self.assertQuerysetEqual(Article.objects.filter(id__exact=self.a.id), 127 ["<Article: This is a test>"]) 128 self.assertQuerysetEqual(Article.objects.filter(pk=self.a.id), 129 ["<Article: This is a test>"]) 130 # Query on an article property 131 self.assertQuerysetEqual(Article.objects.filter(headline__startswith='This'), 132 ["<Article: This is a test>"]) 133 # The API automatically follows relationships as far as you need. 134 # Use double underscores to separate relationships. 135 # This works as many levels deep as you want. There's no limit. 136 # Find all Articles for any Reporter whose first name is "John". 137 self.assertQuerysetEqual(Article.objects.filter(reporter__first_name__exact='John'), 138 [ 139 "<Article: John's second story>", 140 "<Article: This is a test>", 141 ]) 142 # Check that implied __exact also works 143 self.assertQuerysetEqual(Article.objects.filter(reporter__first_name='John'), 144 [ 145 "<Article: John's second story>", 146 "<Article: This is a test>", 147 ]) 148 # Query twice over the related field. 149 self.assertQuerysetEqual( 150 Article.objects.filter(reporter__first_name__exact='John', 151 reporter__last_name__exact='Smith'), 152 [ 153 "<Article: John's second story>", 154 "<Article: This is a test>", 155 ]) 156 # The underlying query only makes one join when a related table is referenced twice. 157 queryset = Article.objects.filter(reporter__first_name__exact='John', 158 reporter__last_name__exact='Smith') 159 self.assertNumQueries(1, list, queryset) 160 self.assertEqual(queryset.query.get_compiler(queryset.db).as_sql()[0].count('INNER JOIN'), 1) 161 162 # The automatically joined table has a predictable name. 163 self.assertQuerysetEqual( 164 Article.objects.filter(reporter__first_name__exact='John').extra( 165 where=["many_to_one_reporter.last_name='Smith'"]), 166 [ 167 "<Article: John's second story>", 168 "<Article: This is a test>", 169 ]) 170 # ... and should work fine with the unicode that comes out of forms.Form.cleaned_data 171 self.assertQuerysetEqual( 172 Article.objects.filter(reporter__first_name__exact='John' 173 ).extra(where=["many_to_one_reporter.last_name='%s'" % u'Smith']), 174 [ 175 "<Article: John's second story>", 176 "<Article: This is a test>", 177 ]) 178 # Find all Articles for a Reporter. 179 # Use direct ID check, pk check, and object comparison 180 self.assertQuerysetEqual( 181 Article.objects.filter(reporter__id__exact=self.r.id), 182 [ 183 "<Article: John's second story>", 184 "<Article: This is a test>", 185 ]) 186 self.assertQuerysetEqual( 187 Article.objects.filter(reporter__pk=self.r.id), 188 [ 189 "<Article: John's second story>", 190 "<Article: This is a test>", 191 ]) 192 self.assertQuerysetEqual( 193 Article.objects.filter(reporter=self.r.id), 194 [ 195 "<Article: John's second story>", 196 "<Article: This is a test>", 197 ]) 198 self.assertQuerysetEqual( 199 Article.objects.filter(reporter=self.r), 200 [ 201 "<Article: John's second story>", 202 "<Article: This is a test>", 203 ]) 204 self.assertQuerysetEqual( 205 Article.objects.filter(reporter__in=[self.r.id,self.r2.id]).distinct(), 206 [ 207 "<Article: John's second story>", 208 "<Article: Paul's story>", 209 "<Article: This is a test>", 210 ]) 211 self.assertQuerysetEqual( 212 Article.objects.filter(reporter__in=[self.r,self.r2]).distinct(), 213 [ 214 "<Article: John's second story>", 215 "<Article: Paul's story>", 216 "<Article: This is a test>", 217 ]) 218 # You can also use a queryset instead of a literal list of instances. 219 # The queryset must be reduced to a list of values using values(), 220 # then converted into a query 221 self.assertQuerysetEqual( 222 Article.objects.filter( 223 reporter__in=Reporter.objects.filter(first_name='John').values('pk').query 224 ).distinct(), 225 [ 226 "<Article: John's second story>", 227 "<Article: This is a test>", 228 ]) 229 # You need two underscores between "reporter" and "id" -- not one. 230 self.assertRaises(FieldError, Article.objects.filter, reporter_id__exact=self.r.id) 231 # You need to specify a comparison clause 232 self.assertRaises(FieldError, Article.objects.filter, reporter_id=self.r.id) 233 234 def test_reverse_selects(self): 235 a3 = Article.objects.create(id=None, headline="Third article", 236 pub_date=datetime(2005, 7, 27), reporter_id=self.r.id) 237 a4 = Article.objects.create(id=None, headline="Fourth article", 238 pub_date=datetime(2005, 7, 27), reporter_id=str(self.r.id)) 239 # Reporters can be queried 240 self.assertQuerysetEqual(Reporter.objects.filter(id__exact=self.r.id), 241 ["<Reporter: John Smith>"]) 242 self.assertQuerysetEqual(Reporter.objects.filter(pk=self.r.id), 243 ["<Reporter: John Smith>"]) 244 self.assertQuerysetEqual(Reporter.objects.filter(first_name__startswith='John'), 245 ["<Reporter: John Smith>"]) 246 # Reporters can query in opposite direction of ForeignKey definition 247 self.assertQuerysetEqual(Reporter.objects.filter(article__id__exact=self.a.id), 248 ["<Reporter: John Smith>"]) 249 self.assertQuerysetEqual(Reporter.objects.filter(article__pk=self.a.id), 250 ["<Reporter: John Smith>"]) 251 self.assertQuerysetEqual(Reporter.objects.filter(article=self.a.id), 252 ["<Reporter: John Smith>"]) 253 self.assertQuerysetEqual(Reporter.objects.filter(article=self.a), 254 ["<Reporter: John Smith>"]) 255 self.assertQuerysetEqual( 256 Reporter.objects.filter(article__in=[self.a.id,a3.id]).distinct(), 257 ["<Reporter: John Smith>"]) 258 self.assertQuerysetEqual( 259 Reporter.objects.filter(article__in=[self.a.id,a3]).distinct(), 260 ["<Reporter: John Smith>"]) 261 self.assertQuerysetEqual( 262 Reporter.objects.filter(article__in=[self.a,a3]).distinct(), 263 ["<Reporter: John Smith>"]) 264 self.assertQuerysetEqual( 265 Reporter.objects.filter(article__headline__startswith='T'), 266 ["<Reporter: John Smith>", "<Reporter: John Smith>"]) 267 self.assertQuerysetEqual( 268 Reporter.objects.filter(article__headline__startswith='T').distinct(), 269 ["<Reporter: John Smith>"]) 270 271 # Counting in the opposite direction works in conjunction with distinct() 272 self.assertEqual( 273 Reporter.objects.filter(article__headline__startswith='T').count(), 2) 274 self.assertEqual( 275 Reporter.objects.filter(article__headline__startswith='T').distinct().count(), 1) 276 277 # Queries can go round in circles. 278 self.assertQuerysetEqual( 279 Reporter.objects.filter(article__reporter__first_name__startswith='John'), 280 [ 281 "<Reporter: John Smith>", 282 "<Reporter: John Smith>", 283 "<Reporter: John Smith>", 284 ]) 285 self.assertQuerysetEqual( 286 Reporter.objects.filter(article__reporter__first_name__startswith='John').distinct(), 287 ["<Reporter: John Smith>"]) 288 self.assertQuerysetEqual( 289 Reporter.objects.filter(article__reporter__exact=self.r).distinct(), 290 ["<Reporter: John Smith>"]) 291 292 # Check that implied __exact also works. 293 self.assertQuerysetEqual( 294 Reporter.objects.filter(article__reporter=self.r).distinct(), 295 ["<Reporter: John Smith>"]) 296 297 # It's possible to use values() calls across many-to-one relations. 298 # (Note, too, that we clear the ordering here so as not to drag the 299 # 'headline' field into the columns being used to determine uniqueness) 300 d = {'reporter__first_name': u'John', 'reporter__last_name': u'Smith'} 301 self.assertEqual([d], 302 list(Article.objects.filter(reporter=self.r).distinct().order_by() 303 .values('reporter__first_name', 'reporter__last_name'))) 304 305 def test_select_related(self): 306 # Check that Article.objects.select_related().dates() works properly when 307 # there are multiple Articles with the same date but different foreign-key 308 # objects (Reporters). 309 r1 = Reporter.objects.create(first_name='Mike', last_name='Royko', email='royko@suntimes.com') 310 r2 = Reporter.objects.create(first_name='John', last_name='Kass', email='jkass@tribune.com') 311 a1 = Article.objects.create(headline='First', pub_date=datetime(1980, 4, 23), reporter=r1) 312 a2 = Article.objects.create(headline='Second', pub_date=datetime(1980, 4, 23), reporter=r2) 313 self.assertEqual(list(Article.objects.select_related().dates('pub_date', 'day')), 314 [ 315 datetime(1980, 4, 23, 0, 0), 316 datetime(2005, 7, 27, 0, 0), 317 ]) 318 self.assertEqual(list(Article.objects.select_related().dates('pub_date', 'month')), 319 [ 320 datetime(1980, 4, 1, 0, 0), 321 datetime(2005, 7, 1, 0, 0), 322 ]) 323 self.assertEqual(list(Article.objects.select_related().dates('pub_date', 'year')), 324 [ 325 datetime(1980, 1, 1, 0, 0), 326 datetime(2005, 1, 1, 0, 0), 327 ]) 328 329 def test_delete(self): 330 new_article = self.r.article_set.create(headline="John's second story", 331 pub_date=datetime(2005, 7, 29)) 332 new_article2 = self.r2.article_set.create(headline="Paul's story", 333 pub_date=datetime(2006, 1, 17)) 334 a3 = Article.objects.create(id=None, headline="Third article", 335 pub_date=datetime(2005, 7, 27), reporter_id=self.r.id) 336 a4 = Article.objects.create(id=None, headline="Fourth article", 337 pub_date=datetime(2005, 7, 27), reporter_id=str(self.r.id)) 338 # If you delete a reporter, his articles will be deleted. 339 self.assertQuerysetEqual(Article.objects.all(), 340 [ 341 "<Article: Fourth article>", 342 "<Article: John's second story>", 343 "<Article: Paul's story>", 344 "<Article: Third article>", 345 "<Article: This is a test>", 346 ]) 347 self.assertQuerysetEqual(Reporter.objects.order_by('first_name'), 348 [ 349 "<Reporter: John Smith>", 350 "<Reporter: Paul Jones>", 351 ]) 352 self.r2.delete() 353 self.assertQuerysetEqual(Article.objects.all(), 354 [ 355 "<Article: Fourth article>", 356 "<Article: John's second story>", 357 "<Article: Third article>", 358 "<Article: This is a test>", 359 ]) 360 self.assertQuerysetEqual(Reporter.objects.order_by('first_name'), 361 ["<Reporter: John Smith>"]) 362 # You can delete using a JOIN in the query. 363 Reporter.objects.filter(article__headline__startswith='This').delete() 364 self.assertQuerysetEqual(Reporter.objects.all(), []) 365 self.assertQuerysetEqual(Article.objects.all(), []) 366 367 def test_regression_12876(self): 368 # Regression for #12876 -- Model methods that include queries that 369 # recursive don't cause recursion depth problems under deepcopy. 370 self.r.cached_query = Article.objects.filter(reporter=self.r) 371 from copy import deepcopy 372 self.assertEqual(repr(deepcopy(self.r)), "<Reporter: John Smith>")