1# encoding: utf-8 2 3from __future__ import absolute_import, division, print_function, unicode_literals 4 5import datetime 6import time 7from threading import Thread 8 9from django.test import TestCase 10from django.utils.six.moves import queue 11from test_haystack.core.models import (AFifthMockModel, AThirdMockModel, ManyToManyLeftSideModel, 12 ManyToManyRightSideModel, MockModel) 13 14from haystack import connection_router, connections, indexes 15from haystack.exceptions import SearchFieldError 16from haystack.utils.loading import UnifiedIndex 17 18 19class BadSearchIndex1(indexes.SearchIndex, indexes.Indexable): 20 author = indexes.CharField(model_attr='author') 21 pub_date = indexes.DateTimeField(model_attr='pub_date') 22 23 def get_model(self): 24 return MockModel 25 26 27class BadSearchIndex2(indexes.SearchIndex, indexes.Indexable): 28 text = indexes.CharField(document=True, use_template=True) 29 content2 = indexes.CharField(document=True, use_template=True) 30 author = indexes.CharField(model_attr='author') 31 pub_date = indexes.DateTimeField(model_attr='pub_date') 32 33 def get_model(self): 34 return MockModel 35 36 37class GoodMockSearchIndex(indexes.SearchIndex, indexes.Indexable): 38 text = indexes.CharField(document=True, use_template=True) 39 author = indexes.CharField(model_attr='author') 40 pub_date = indexes.DateTimeField(model_attr='pub_date') 41 extra = indexes.CharField(indexed=False, use_template=True) 42 43 def get_model(self): 44 return MockModel 45 46 47# For testing inheritance... 48class AltGoodMockSearchIndex(GoodMockSearchIndex, indexes.Indexable): 49 additional = indexes.CharField(model_attr='author') 50 51 def get_model(self): 52 return MockModel 53 54 55class GoodCustomMockSearchIndex(indexes.SearchIndex, indexes.Indexable): 56 text = indexes.CharField(document=True, use_template=True) 57 author = indexes.CharField(model_attr='author', faceted=True) 58 pub_date = indexes.DateTimeField(model_attr='pub_date', faceted=True) 59 extra = indexes.CharField(indexed=False, use_template=True) 60 hello = indexes.CharField(model_attr='hello') 61 62 def prepare(self, obj): 63 super(GoodCustomMockSearchIndex, self).prepare(obj) 64 self.prepared_data['whee'] = 'Custom preparation.' 65 return self.prepared_data 66 67 def prepare_author(self, obj): 68 return "Hi, I'm %s" % self.prepared_data['author'] 69 70 def load_all_queryset(self): 71 return self.get_model()._default_manager.filter(id__gt=1) 72 73 def get_model(self): 74 return MockModel 75 76 def index_queryset(self, using=None): 77 return MockModel.objects.all() 78 79 def read_queryset(self, using=None): 80 return MockModel.objects.filter(author__in=['daniel1', 'daniel3']) 81 82 def build_queryset(self, start_date=None, end_date=None): 83 return MockModel.objects.filter(author__in=['daniel1', 'daniel3']) 84 85 86class GoodNullableMockSearchIndex(indexes.SearchIndex, indexes.Indexable): 87 text = indexes.CharField(document=True, use_template=True) 88 author = indexes.CharField(model_attr='author', null=True, faceted=True) 89 90 def get_model(self): 91 return MockModel 92 93 94class GoodOverriddenFieldNameMockSearchIndex(indexes.SearchIndex, indexes.Indexable): 95 text = indexes.CharField(document=True, use_template=True, index_fieldname='more_content') 96 author = indexes.CharField(model_attr='author', index_fieldname='name_s') 97 hello = indexes.CharField(model_attr='hello') 98 99 def get_model(self): 100 return MockModel 101 102 103class GoodFacetedMockSearchIndex(indexes.SearchIndex, indexes.Indexable): 104 text = indexes.CharField(document=True, use_template=True) 105 author = indexes.CharField(model_attr='author') 106 author_foo = indexes.FacetCharField(facet_for='author') 107 pub_date = indexes.DateTimeField(model_attr='pub_date') 108 pub_date_exact = indexes.FacetDateTimeField(facet_for='pub_date') 109 110 def get_model(self): 111 return MockModel 112 113 def prepare_author(self, obj): 114 return "Hi, I'm %s" % self.prepared_data['author'] 115 116 def prepare_pub_date_exact(self, obj): 117 return "2010-10-26T01:54:32" 118 119 120class MROFieldsSearchIndexA(indexes.SearchIndex, indexes.Indexable): 121 text = indexes.CharField(document=True, model_attr='test_a') 122 123 def get_model(self): 124 return MockModel 125 126 127class MROFieldsSearchIndexB(indexes.SearchIndex, indexes.Indexable): 128 text = indexes.CharField(document=True, model_attr='test_b') 129 130 def get_model(self): 131 return MockModel 132 133 134class MROFieldsSearchChild(MROFieldsSearchIndexA, MROFieldsSearchIndexB): 135 pass 136 137 138class ModelWithManyToManyFieldAndAttributeLookupSearchIndex(indexes.SearchIndex, indexes.Indexable): 139 text = indexes.CharField(document=True) 140 related_models = indexes.MultiValueField(model_attr='related_models__name') 141 142 def get_model(self): 143 return ManyToManyLeftSideModel 144 145 146class SearchIndexTestCase(TestCase): 147 fixtures = ['base_data'] 148 149 def setUp(self): 150 super(SearchIndexTestCase, self).setUp() 151 self.sb = connections['default'].get_backend() 152 self.mi = GoodMockSearchIndex() 153 self.cmi = GoodCustomMockSearchIndex() 154 self.cnmi = GoodNullableMockSearchIndex() 155 self.gfmsi = GoodFacetedMockSearchIndex() 156 157 # Fake the unified index. 158 self.old_unified_index = connections['default']._index 159 self.ui = UnifiedIndex() 160 self.ui.build(indexes=[self.mi]) 161 connections['default']._index = self.ui 162 163 self.sample_docs = { 164 u'core.mockmodel.1': { 165 'text': u'Indexed!\n1', 166 'django_id': u'1', 167 'django_ct': u'core.mockmodel', 168 'extra': u'Stored!\n1', 169 'author': u'daniel1', 170 'pub_date': datetime.datetime(2009, 3, 17, 6, 0), 171 'id': u'core.mockmodel.1' 172 }, 173 u'core.mockmodel.2': { 174 'text': u'Indexed!\n2', 175 'django_id': u'2', 176 'django_ct': u'core.mockmodel', 177 'extra': u'Stored!\n2', 178 'author': u'daniel2', 179 'pub_date': datetime.datetime(2009, 3, 17, 7, 0), 180 'id': u'core.mockmodel.2' 181 }, 182 u'core.mockmodel.3': { 183 'text': u'Indexed!\n3', 184 'django_id': u'3', 185 'django_ct': u'core.mockmodel', 186 'extra': u'Stored!\n3', 187 'author': u'daniel3', 188 'pub_date': datetime.datetime(2009, 3, 17, 8, 0), 189 'id': u'core.mockmodel.3' 190 } 191 } 192 193 def tearDown(self): 194 connections['default']._index = self.old_unified_index 195 super(SearchIndexTestCase, self).tearDown() 196 197 def test_no_contentfield_present(self): 198 self.assertRaises(SearchFieldError, BadSearchIndex1) 199 200 def test_too_many_contentfields_present(self): 201 self.assertRaises(SearchFieldError, BadSearchIndex2) 202 203 def test_contentfield_present(self): 204 try: 205 mi = GoodMockSearchIndex() 206 except: 207 self.fail() 208 209 def test_proper_fields(self): 210 self.assertEqual(len(self.mi.fields), 4) 211 self.assertTrue('text' in self.mi.fields) 212 self.assertTrue(isinstance(self.mi.fields['text'], indexes.CharField)) 213 self.assertTrue('author' in self.mi.fields) 214 self.assertTrue(isinstance(self.mi.fields['author'], indexes.CharField)) 215 self.assertTrue('pub_date' in self.mi.fields) 216 self.assertTrue(isinstance(self.mi.fields['pub_date'], indexes.DateTimeField)) 217 self.assertTrue('extra' in self.mi.fields) 218 self.assertTrue(isinstance(self.mi.fields['extra'], indexes.CharField)) 219 220 self.assertEqual(len(self.cmi.fields), 7) 221 self.assertTrue('text' in self.cmi.fields) 222 self.assertTrue(isinstance(self.cmi.fields['text'], indexes.CharField)) 223 self.assertTrue('author' in self.cmi.fields) 224 self.assertTrue(isinstance(self.cmi.fields['author'], indexes.CharField)) 225 self.assertTrue('author_exact' in self.cmi.fields) 226 self.assertTrue(isinstance(self.cmi.fields['author_exact'], indexes.FacetCharField)) 227 self.assertTrue('pub_date' in self.cmi.fields) 228 self.assertTrue(isinstance(self.cmi.fields['pub_date'], indexes.DateTimeField)) 229 self.assertTrue('pub_date_exact' in self.cmi.fields) 230 self.assertTrue(isinstance(self.cmi.fields['pub_date_exact'], indexes.FacetDateTimeField)) 231 self.assertTrue('extra' in self.cmi.fields) 232 self.assertTrue(isinstance(self.cmi.fields['extra'], indexes.CharField)) 233 self.assertTrue('hello' in self.cmi.fields) 234 self.assertTrue(isinstance(self.cmi.fields['extra'], indexes.CharField)) 235 236 def test_index_queryset(self): 237 self.assertEqual(len(self.cmi.index_queryset()), 3) 238 239 def test_read_queryset(self): 240 self.assertEqual(len(self.cmi.read_queryset()), 2) 241 242 def test_build_queryset(self): 243 # The custom SearchIndex.build_queryset returns the same records as 244 # the read_queryset 245 self.assertEqual(len(self.cmi.build_queryset()), 2) 246 247 # Store a reference to the original method 248 old_guf = self.mi.__class__.get_updated_field 249 250 self.mi.__class__.get_updated_field = lambda self: 'pub_date' 251 252 # With an updated field, we should get have filtered results 253 sd = datetime.datetime(2009, 3, 17, 7, 0) 254 self.assertEqual(len(self.mi.build_queryset(start_date=sd)), 2) 255 256 ed = datetime.datetime(2009, 3, 17, 7, 59) 257 self.assertEqual(len(self.mi.build_queryset(end_date=ed)), 2) 258 259 sd = datetime.datetime(2009, 3, 17, 6, 0) 260 ed = datetime.datetime(2009, 3, 17, 6, 59) 261 self.assertEqual(len(self.mi.build_queryset(start_date=sd, 262 end_date=ed)), 1) 263 264 # Remove the updated field for the next test 265 del self.mi.__class__.get_updated_field 266 267 # The default should return all 3 even if we specify a start date 268 # because there is no updated field specified 269 self.assertEqual(len(self.mi.build_queryset(start_date=sd)), 3) 270 271 # Restore the original attribute 272 self.mi.__class__.get_updated_field = old_guf 273 274 def test_prepare(self): 275 mock = MockModel() 276 mock.pk = 20 277 mock.author = 'daniel%s' % mock.id 278 mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0) 279 280 self.assertEqual(len(self.mi.prepare(mock)), 7) 281 self.assertEqual(sorted(self.mi.prepare(mock).keys()), ['author', 'django_ct', 'django_id', 'extra', 'id', 'pub_date', 'text']) 282 283 def test_custom_prepare(self): 284 mock = MockModel() 285 mock.pk = 20 286 mock.author = 'daniel%s' % mock.id 287 mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0) 288 289 self.assertEqual(len(self.cmi.prepare(mock)), 11) 290 self.assertEqual(sorted(self.cmi.prepare(mock).keys()), ['author', 'author_exact', 'django_ct', 'django_id', 'extra', 'hello', 'id', 'pub_date', 'pub_date_exact', 'text', 'whee']) 291 292 self.assertEqual(len(self.cmi.full_prepare(mock)), 11) 293 self.assertEqual(sorted(self.cmi.full_prepare(mock).keys()), ['author', 'author_exact', 'django_ct', 'django_id', 'extra', 'hello', 'id', 'pub_date', 'pub_date_exact', 'text', 'whee']) 294 295 def test_thread_safety(self): 296 # This is a regression. ``SearchIndex`` used to write to 297 # ``self.prepared_data``, which would leak between threads if things 298 # went too fast. 299 exceptions = [] 300 301 def threaded_prepare(index_queue, index, model): 302 try: 303 index.queue = index_queue 304 prepped = index.prepare(model) 305 except Exception as e: 306 exceptions.append(e) 307 raise 308 309 class ThreadedSearchIndex(GoodMockSearchIndex): 310 def prepare_author(self, obj): 311 if obj.pk == 20: 312 time.sleep(0.1) 313 else: 314 time.sleep(0.5) 315 316 index_queue.put(self.prepared_data['author']) 317 return self.prepared_data['author'] 318 319 tmi = ThreadedSearchIndex() 320 index_queue = queue.Queue() 321 mock_1 = MockModel() 322 mock_1.pk = 20 323 mock_1.author = 'foo' 324 mock_1.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0) 325 mock_2 = MockModel() 326 mock_2.pk = 21 327 mock_2.author = 'daniel%s' % mock_2.id 328 mock_2.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0) 329 330 th1 = Thread(target=threaded_prepare, args=(index_queue, tmi, mock_1)) 331 th2 = Thread(target=threaded_prepare, args=(index_queue, tmi, mock_2)) 332 333 th1.start() 334 th2.start() 335 th1.join() 336 th2.join() 337 338 mock_1_result = index_queue.get() 339 mock_2_result = index_queue.get() 340 self.assertEqual(mock_1_result, u'foo') 341 self.assertEqual(mock_2_result, u'daniel21') 342 343 def test_custom_prepare_author(self): 344 mock = MockModel() 345 mock.pk = 20 346 mock.author = 'daniel%s' % mock.id 347 mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0) 348 349 self.assertEqual(len(self.cmi.prepare(mock)), 11) 350 self.assertEqual(sorted(self.cmi.prepare(mock).keys()), ['author', 'author_exact', 'django_ct', 'django_id', 'extra', 'hello', 'id', 'pub_date', 'pub_date_exact', 'text', 'whee']) 351 352 self.assertEqual(len(self.cmi.full_prepare(mock)), 11) 353 self.assertEqual(sorted(self.cmi.full_prepare(mock).keys()), ['author', 'author_exact', 'django_ct', 'django_id', 'extra', 'hello', 'id', 'pub_date', 'pub_date_exact', 'text', 'whee']) 354 self.assertEqual(self.cmi.prepared_data['author'], "Hi, I'm daniel20") 355 self.assertEqual(self.cmi.prepared_data['author_exact'], "Hi, I'm daniel20") 356 357 def test_custom_model_attr(self): 358 mock = MockModel() 359 mock.pk = 20 360 mock.author = 'daniel%s' % mock.id 361 mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0) 362 363 self.assertEqual(len(self.cmi.prepare(mock)), 11) 364 self.assertEqual(sorted(self.cmi.prepare(mock).keys()), ['author', 'author_exact', 'django_ct', 'django_id', 'extra', 'hello', 'id', 'pub_date', 'pub_date_exact', 'text', 'whee']) 365 366 self.assertEqual(len(self.cmi.full_prepare(mock)), 11) 367 self.assertEqual(sorted(self.cmi.full_prepare(mock).keys()), ['author', 'author_exact', 'django_ct', 'django_id', 'extra', 'hello', 'id', 'pub_date', 'pub_date_exact', 'text', 'whee']) 368 self.assertEqual(self.cmi.prepared_data['hello'], u'World!') 369 370 def test_custom_index_fieldname(self): 371 mock = MockModel() 372 mock.pk = 20 373 mock.author = 'daniel%s' % mock.id 374 mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0) 375 376 cofnmi = GoodOverriddenFieldNameMockSearchIndex() 377 self.assertEqual(len(cofnmi.prepare(mock)), 6) 378 self.assertEqual(sorted(cofnmi.prepare(mock).keys()), ['django_ct', 'django_id', 'hello', 'id', 'more_content', 'name_s']) 379 self.assertEqual(cofnmi.prepared_data['name_s'], u'daniel20') 380 self.assertEqual(cofnmi.get_content_field(), 'more_content') 381 382 def test_get_content_field(self): 383 self.assertEqual(self.mi.get_content_field(), 'text') 384 385 def test_update(self): 386 self.sb.clear() 387 self.assertEqual(self.sb.search('*')['hits'], 0) 388 self.mi.update() 389 self.assertEqual(self.sb.search('*')['hits'], 3) 390 self.sb.clear() 391 392 def test_update_object(self): 393 self.sb.clear() 394 self.assertEqual(self.sb.search('*')['hits'], 0) 395 396 mock = MockModel() 397 mock.pk = 20 398 mock.author = 'daniel%s' % mock.id 399 mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0) 400 401 self.mi.update_object(mock) 402 self.assertEqual([(res.content_type(), res.pk) for res in self.sb.search('*')['results']], [(u'core.mockmodel', u'20')]) 403 self.sb.clear() 404 405 def test_remove_object(self): 406 self.mi.update() 407 self.assertEqual(self.sb.search('*')['hits'], 3) 408 409 mock = MockModel() 410 mock.pk = 20 411 mock.author = 'daniel%s' % mock.id 412 mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0) 413 414 self.mi.update_object(mock) 415 self.assertEqual(self.sb.search('*')['hits'], 4) 416 417 self.mi.remove_object(mock) 418 self.assertEqual([(res.content_type(), res.pk) for res in self.sb.search('*')['results']], [(u'core.mockmodel', u'1'), (u'core.mockmodel', u'2'), (u'core.mockmodel', u'3')]) 419 420 # Put it back so we can test passing kwargs. 421 mock = MockModel() 422 mock.pk = 20 423 mock.author = 'daniel%s' % mock.id 424 mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0) 425 426 self.mi.update_object(mock) 427 self.assertEqual(self.sb.search('*')['hits'], 4) 428 429 self.mi.remove_object(mock, commit=False) 430 self.assertEqual([(res.content_type(), res.pk) for res in self.sb.search('*')['results']], [(u'core.mockmodel', u'1'), (u'core.mockmodel', u'2'), (u'core.mockmodel', u'3'), (u'core.mockmodel', u'20')]) 431 432 self.sb.clear() 433 434 def test_clear(self): 435 self.mi.update() 436 self.assertGreater(self.sb.search('*')['hits'], 0) 437 438 self.mi.clear() 439 self.assertEqual(self.sb.search('*')['hits'], 0) 440 441 def test_reindex(self): 442 self.mi.reindex() 443 self.assertEqual([(res.content_type(), res.pk) for res in self.sb.search('*')['results']], [(u'core.mockmodel', u'1'), (u'core.mockmodel', u'2'), (u'core.mockmodel', u'3')]) 444 self.sb.clear() 445 446 def test_inheritance(self): 447 try: 448 agmi = AltGoodMockSearchIndex() 449 except: 450 self.fail() 451 452 self.assertEqual(len(agmi.fields), 5) 453 self.assertTrue('text' in agmi.fields) 454 self.assertTrue(isinstance(agmi.fields['text'], indexes.CharField)) 455 self.assertTrue('author' in agmi.fields) 456 self.assertTrue(isinstance(agmi.fields['author'], indexes.CharField)) 457 self.assertTrue('pub_date' in agmi.fields) 458 self.assertTrue(isinstance(agmi.fields['pub_date'], indexes.DateTimeField)) 459 self.assertTrue('extra' in agmi.fields) 460 self.assertTrue(isinstance(agmi.fields['extra'], indexes.CharField)) 461 self.assertTrue('additional' in agmi.fields) 462 self.assertTrue(isinstance(agmi.fields['additional'], indexes.CharField)) 463 464 def test_proper_field_resolution(self): 465 mrofsc = MROFieldsSearchChild() 466 mock = MockModel() 467 mock.pk = 20 468 mock.author = 'daniel%s' % mock.id 469 mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0) 470 mock.test_a = 'This is A' 471 mock.test_b = 'This is B' 472 473 self.assertEqual(len(mrofsc.fields), 1) 474 prepped_data = mrofsc.prepare(mock) 475 self.assertEqual(len(prepped_data), 4) 476 self.assertEqual(prepped_data['text'], 'This is A') 477 478 def test_load_all_queryset(self): 479 self.assertEqual([obj.id for obj in self.cmi.load_all_queryset()], [2, 3]) 480 481 def test_nullable(self): 482 mock = MockModel() 483 mock.pk = 20 484 mock.author = None 485 mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0) 486 487 prepared_data = self.cnmi.prepare(mock) 488 self.assertEqual(len(prepared_data), 6) 489 self.assertEqual(sorted(prepared_data.keys()), ['author', 'author_exact', 'django_ct', 'django_id', 'id', 'text']) 490 491 prepared_data = self.cnmi.full_prepare(mock) 492 self.assertEqual(len(prepared_data), 4) 493 self.assertEqual(sorted(prepared_data.keys()), ['django_ct', 'django_id', 'id', 'text']) 494 495 def test_custom_facet_fields(self): 496 mock = MockModel() 497 mock.pk = 20 498 mock.author = 'daniel' 499 mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0) 500 501 prepared_data = self.gfmsi.prepare(mock) 502 self.assertEqual(len(prepared_data), 8) 503 self.assertEqual(sorted(prepared_data.keys()), ['author', 'author_foo', 'django_ct', 'django_id', 'id', 'pub_date', 'pub_date_exact', 'text']) 504 505 prepared_data = self.gfmsi.full_prepare(mock) 506 self.assertEqual(len(prepared_data), 8) 507 self.assertEqual(sorted(prepared_data.keys()), ['author', 'author_foo', 'django_ct', 'django_id', 'id', 'pub_date', 'pub_date_exact', 'text']) 508 self.assertEqual(prepared_data['author_foo'], u"Hi, I'm daniel") 509 self.assertEqual(prepared_data['pub_date_exact'], '2010-10-26T01:54:32') 510 511 512class BasicModelSearchIndex(indexes.ModelSearchIndex, indexes.Indexable): 513 class Meta: 514 model = MockModel 515 516 517class FieldsModelSearchIndex(indexes.ModelSearchIndex, indexes.Indexable): 518 class Meta: 519 model = MockModel 520 fields = ['author', 'pub_date'] 521 522 523class ExcludesModelSearchIndex(indexes.ModelSearchIndex, indexes.Indexable): 524 class Meta: 525 model = MockModel 526 excludes = ['author', 'foo'] 527 528 529class FieldsWithOverrideModelSearchIndex(indexes.ModelSearchIndex, indexes.Indexable): 530 foo = indexes.IntegerField(model_attr='foo') 531 532 class Meta: 533 model = MockModel 534 fields = ['author', 'foo'] 535 536 def get_index_fieldname(self, f): 537 if f.name == 'author': 538 return 'author_bar' 539 else: 540 return f.name 541 542 543class YetAnotherBasicModelSearchIndex(indexes.ModelSearchIndex, indexes.Indexable): 544 text = indexes.CharField(document=True) 545 546 class Meta: 547 model = AThirdMockModel 548 549 550class GhettoAFifthMockModelSearchIndex(indexes.SearchIndex, indexes.Indexable): 551 text = indexes.CharField(document=True) 552 553 def get_model(self): 554 return AFifthMockModel 555 556 def index_queryset(self, using=None): 557 # Index everything, 558 return self.get_model().objects.complete_set() 559 560 def read_queryset(self, using=None): 561 return self.get_model().objects.all() 562 563 564class ReadQuerySetTestSearchIndex(indexes.SearchIndex, indexes.Indexable): 565 author = indexes.CharField(model_attr='author', document=True) 566 567 def get_model(self): 568 return AFifthMockModel 569 570 def read_queryset(self, using=None): 571 return self.get_model().objects.complete_set() 572 573 574class TextReadQuerySetTestSearchIndex(indexes.SearchIndex, indexes.Indexable): 575 text = indexes.CharField(model_attr='author', document=True) 576 577 def get_model(self): 578 return AFifthMockModel 579 580 def read_queryset(self, using=None): 581 return self.get_model().objects.complete_set() 582 583 584class ModelWithManyToManyFieldModelSearchIndex(indexes.ModelSearchIndex): 585 def get_model(self): 586 return ManyToManyLeftSideModel 587 588 589class ModelSearchIndexTestCase(TestCase): 590 def setUp(self): 591 super(ModelSearchIndexTestCase, self).setUp() 592 self.sb = connections['default'].get_backend() 593 self.bmsi = BasicModelSearchIndex() 594 self.fmsi = FieldsModelSearchIndex() 595 self.emsi = ExcludesModelSearchIndex() 596 self.fwomsi = FieldsWithOverrideModelSearchIndex() 597 self.yabmsi = YetAnotherBasicModelSearchIndex() 598 self.m2mmsi = ModelWithManyToManyFieldModelSearchIndex() 599 600 def test_basic(self): 601 self.assertEqual(len(self.bmsi.fields), 4) 602 self.assertTrue('foo' in self.bmsi.fields) 603 self.assertTrue(isinstance(self.bmsi.fields['foo'], indexes.CharField)) 604 self.assertEqual(self.bmsi.fields['foo'].null, False) 605 self.assertEqual(self.bmsi.fields['foo'].index_fieldname, 'foo') 606 self.assertTrue('author' in self.bmsi.fields) 607 self.assertTrue(isinstance(self.bmsi.fields['author'], indexes.CharField)) 608 self.assertEqual(self.bmsi.fields['author'].null, False) 609 self.assertTrue('pub_date' in self.bmsi.fields) 610 self.assertTrue(isinstance(self.bmsi.fields['pub_date'], indexes.DateTimeField)) 611 self.assertTrue(isinstance(self.bmsi.fields['pub_date'].default, datetime.datetime)) 612 self.assertTrue('text' in self.bmsi.fields) 613 self.assertTrue(isinstance(self.bmsi.fields['text'], indexes.CharField)) 614 self.assertEqual(self.bmsi.fields['text'].document, True) 615 self.assertEqual(self.bmsi.fields['text'].use_template, True) 616 617 def test_fields(self): 618 self.assertEqual(len(self.fmsi.fields), 3) 619 self.assertTrue('author' in self.fmsi.fields) 620 self.assertTrue(isinstance(self.fmsi.fields['author'], indexes.CharField)) 621 self.assertTrue('pub_date' in self.fmsi.fields) 622 self.assertTrue(isinstance(self.fmsi.fields['pub_date'], indexes.DateTimeField)) 623 self.assertTrue('text' in self.fmsi.fields) 624 self.assertTrue(isinstance(self.fmsi.fields['text'], indexes.CharField)) 625 626 def test_excludes(self): 627 self.assertEqual(len(self.emsi.fields), 2) 628 self.assertTrue('pub_date' in self.emsi.fields) 629 self.assertTrue(isinstance(self.emsi.fields['pub_date'], indexes.DateTimeField)) 630 self.assertTrue('text' in self.emsi.fields) 631 self.assertTrue(isinstance(self.emsi.fields['text'], indexes.CharField)) 632 self.assertNotIn('related_models', self.m2mmsi.fields) 633 634 def test_fields_with_override(self): 635 self.assertEqual(len(self.fwomsi.fields), 3) 636 self.assertTrue('author' in self.fwomsi.fields) 637 self.assertTrue(isinstance(self.fwomsi.fields['author'], indexes.CharField)) 638 self.assertTrue('foo' in self.fwomsi.fields) 639 self.assertTrue(isinstance(self.fwomsi.fields['foo'], indexes.IntegerField)) 640 self.assertTrue('text' in self.fwomsi.fields) 641 self.assertTrue(isinstance(self.fwomsi.fields['text'], indexes.CharField)) 642 643 def test_overriding_field_name_with_get_index_fieldname(self): 644 self.assertTrue(self.fwomsi.fields['foo'].index_fieldname, 'foo') 645 self.assertTrue(self.fwomsi.fields['author'].index_fieldname, 'author_bar') 646 647 def test_float_integer_fields(self): 648 self.assertEqual(len(self.yabmsi.fields), 5) 649 self.assertEqual(sorted(self.yabmsi.fields.keys()), ['author', 'average_delay', 'pub_date', 'text', 'view_count']) 650 self.assertTrue('author' in self.yabmsi.fields) 651 self.assertTrue(isinstance(self.yabmsi.fields['author'], indexes.CharField)) 652 self.assertEqual(self.yabmsi.fields['author'].null, False) 653 self.assertTrue('pub_date' in self.yabmsi.fields) 654 self.assertTrue(isinstance(self.yabmsi.fields['pub_date'], indexes.DateTimeField)) 655 self.assertTrue(isinstance(self.yabmsi.fields['pub_date'].default, datetime.datetime)) 656 self.assertTrue('text' in self.yabmsi.fields) 657 self.assertTrue(isinstance(self.yabmsi.fields['text'], indexes.CharField)) 658 self.assertEqual(self.yabmsi.fields['text'].document, True) 659 self.assertEqual(self.yabmsi.fields['text'].use_template, False) 660 self.assertTrue('view_count' in self.yabmsi.fields) 661 self.assertTrue(isinstance(self.yabmsi.fields['view_count'], indexes.IntegerField)) 662 self.assertEqual(self.yabmsi.fields['view_count'].null, False) 663 self.assertEqual(self.yabmsi.fields['view_count'].index_fieldname, 'view_count') 664 self.assertTrue('average_delay' in self.yabmsi.fields) 665 self.assertTrue(isinstance(self.yabmsi.fields['average_delay'], indexes.FloatField)) 666 self.assertEqual(self.yabmsi.fields['average_delay'].null, False) 667 self.assertEqual(self.yabmsi.fields['average_delay'].index_fieldname, 'average_delay') 668 669 670class ModelWithManyToManyFieldAndAttributeLookupSearchIndexTestCase(TestCase): 671 def test_full_prepare(self): 672 index = ModelWithManyToManyFieldAndAttributeLookupSearchIndex() 673 674 left_model = ManyToManyLeftSideModel.objects.create() 675 right_model_1 = ManyToManyRightSideModel.objects.create(name='Right side 1') 676 right_model_2 = ManyToManyRightSideModel.objects.create() 677 left_model.related_models.add(right_model_1) 678 left_model.related_models.add(right_model_2) 679 680 result = index.full_prepare(left_model) 681 682 self.assertDictEqual( 683 result, 684 { 685 'django_ct': 'core.manytomanyleftsidemodel', 686 'django_id': '1', 687 'text': None, 688 'id': 'core.manytomanyleftsidemodel.1', 689 'related_models': ['Right side 1', 'Default name'], 690 } 691 ) 692