|
14 | 14 | from django.contrib.auth.models import User
|
15 | 15 | from django.db import models
|
16 | 16 | from django.db.models.loading import get_model
|
| 17 | +from django.db.models.fields.proxy import OrderWrt |
17 | 18 | from django.test import TestCase
|
18 | 19 | from django.core.files.base import ContentFile
|
19 | 20 |
|
|
24 | 25 | Person, FileModel, Document, Book, HistoricalPoll, Library, State,
|
25 | 26 | AbstractBase, ConcreteAttr, ConcreteUtil, SelfFK, Temperature, WaterLevel,
|
26 | 27 | ExternalModel1, ExternalModel3, UnicodeVerboseName, HistoricalChoice,
|
27 |
| - HistoricalState, HistoricalCustomFKError |
| 28 | + HistoricalState, HistoricalCustomFKError, Series, SeriesWork |
28 | 29 | )
|
29 | 30 | from ..external.models import ExternalModel2, ExternalModel4
|
30 | 31 |
|
@@ -519,3 +520,122 @@ def test_non_relational(self):
|
519 | 520 | with self.settings(DATABASES={'default': {
|
520 | 521 | 'ENGINE': 'django_mongodb_engine'}}):
|
521 | 522 | assert convert_auto_field(self.field) == models.TextField
|
| 523 | + |
| 524 | + |
| 525 | +class TestOrderWrtField(TestCase): |
| 526 | + """Check behaviour of _order field added by Meta.order_with_respect_to. |
| 527 | +
|
| 528 | + The Meta.order_with_respect_to option adds an OrderWrt field named |
| 529 | + "_order", where OrderWrt is a proxy class for an IntegerField that sets |
| 530 | + some default options. |
| 531 | +
|
| 532 | + The simple_history strategy is: |
| 533 | + - Convert to a plain IntegerField in the historical record |
| 534 | + - When restoring a historical instance, add the old value. This may |
| 535 | + result in duplicate ordering values and non-deterministic ordering. |
| 536 | + """ |
| 537 | + |
| 538 | + def setUp(self): |
| 539 | + """Create works in published order.""" |
| 540 | + s = self.series = Series.objects.create( |
| 541 | + name="The Chronicles of Narnia", author="C.S. Lewis") |
| 542 | + self.w_lion = s.works.create( |
| 543 | + title="The Lion, the Witch and the Wardrobe") |
| 544 | + self.w_caspian = s.works.create(title="Prince Caspian") |
| 545 | + self.w_voyage = s.works.create(title="The Voyage of the Dawn Treader") |
| 546 | + self.w_chair = s.works.create(title="The Silver Chair") |
| 547 | + self.w_horse = s.works.create(title="The Horse and His Boy") |
| 548 | + self.w_nephew = s.works.create(title="The Magician's Nephew") |
| 549 | + self.w_battle = s.works.create(title="The Last Battle") |
| 550 | + |
| 551 | + def test_order(self): |
| 552 | + """Confirm that works are ordered by creation.""" |
| 553 | + order = self.series.get_serieswork_order() |
| 554 | + expected = [ |
| 555 | + self.w_lion.pk, |
| 556 | + self.w_caspian.pk, |
| 557 | + self.w_voyage.pk, |
| 558 | + self.w_chair.pk, |
| 559 | + self.w_horse.pk, |
| 560 | + self.w_nephew.pk, |
| 561 | + self.w_battle.pk] |
| 562 | + self.assertEqual(order, expected) |
| 563 | + self.assertEqual(0, self.w_lion._order) |
| 564 | + self.assertEqual(1, self.w_caspian._order) |
| 565 | + self.assertEqual(2, self.w_voyage._order) |
| 566 | + self.assertEqual(3, self.w_chair._order) |
| 567 | + self.assertEqual(4, self.w_horse._order) |
| 568 | + self.assertEqual(5, self.w_nephew._order) |
| 569 | + self.assertEqual(6, self.w_battle._order) |
| 570 | + |
| 571 | + def test_order_field_in_historical_model(self): |
| 572 | + work_order_field = self.w_lion._meta.get_field_by_name('_order')[0] |
| 573 | + self.assertEqual(type(work_order_field), OrderWrt) |
| 574 | + |
| 575 | + history = self.w_lion.history.all()[0] |
| 576 | + history_order_field = history._meta.get_field_by_name('_order')[0] |
| 577 | + self.assertEqual(type(history_order_field), models.IntegerField) |
| 578 | + |
| 579 | + def test_history_object_has_order(self): |
| 580 | + history = self.w_lion.history.all()[0] |
| 581 | + self.assertEqual(self.w_lion._order, history.history_object._order) |
| 582 | + |
| 583 | + def test_restore_object_with_changed_order(self): |
| 584 | + # Change a title |
| 585 | + self.w_caspian.title = 'Prince Caspian: The Return to Narnia' |
| 586 | + self.w_caspian.save() |
| 587 | + self.assertEqual(2, len(self.w_caspian.history.all())) |
| 588 | + self.assertEqual(1, self.w_caspian._order) |
| 589 | + |
| 590 | + # Switch to internal chronological order |
| 591 | + chronological = [ |
| 592 | + self.w_nephew.pk, |
| 593 | + self.w_lion.pk, |
| 594 | + self.w_horse.pk, |
| 595 | + self.w_caspian.pk, |
| 596 | + self.w_voyage.pk, |
| 597 | + self.w_chair.pk, |
| 598 | + self.w_battle.pk] |
| 599 | + self.series.set_serieswork_order(chronological) |
| 600 | + self.assertEqual(self.series.get_serieswork_order(), chronological) |
| 601 | + |
| 602 | + # This uses an update, not a save, so no new history is created |
| 603 | + w_caspian = SeriesWork.objects.get(id=self.w_caspian.id) |
| 604 | + self.assertEqual(2, len(w_caspian.history.all())) |
| 605 | + self.assertEqual(1, w_caspian.history.all()[0]._order) |
| 606 | + self.assertEqual(1, w_caspian.history.all()[1]._order) |
| 607 | + self.assertEqual(3, w_caspian._order) |
| 608 | + |
| 609 | + # Revert to first title, old order |
| 610 | + old = w_caspian.history.all()[1].history_object |
| 611 | + old.save() |
| 612 | + w_caspian = SeriesWork.objects.get(id=self.w_caspian.id) |
| 613 | + self.assertEqual(3, len(w_caspian.history.all())) |
| 614 | + self.assertEqual(1, w_caspian.history.all()[0]._order) |
| 615 | + self.assertEqual(1, w_caspian.history.all()[1]._order) |
| 616 | + self.assertEqual(1, w_caspian.history.all()[2]._order) |
| 617 | + self.assertEqual(1, w_caspian._order) # The order changed |
| 618 | + w_lion = SeriesWork.objects.get(id=self.w_lion.id) |
| 619 | + self.assertEqual(1, w_lion._order) # and is identical to another order |
| 620 | + |
| 621 | + # New order is non-deterministic around identical IDs |
| 622 | + series = Series.objects.get(id=self.series.id) |
| 623 | + order = series.get_serieswork_order() |
| 624 | + self.assertEqual(order[0], self.w_nephew.pk) |
| 625 | + self.assertTrue(order[1] in (self.w_lion.pk, self.w_caspian.pk)) |
| 626 | + self.assertTrue(order[2] in (self.w_lion.pk, self.w_caspian.pk)) |
| 627 | + self.assertEqual(order[3], self.w_horse.pk) |
| 628 | + self.assertEqual(order[4], self.w_voyage.pk) |
| 629 | + self.assertEqual(order[5], self.w_chair.pk) |
| 630 | + self.assertEqual(order[6], self.w_battle.pk) |
| 631 | + |
| 632 | + @skipUnless(django.get_version() >= "1.7", "Requires 1.7 migrations") |
| 633 | + def test_migrations_include_order(self): |
| 634 | + from django.db.migrations import state |
| 635 | + model_state = state.ModelState.from_model(SeriesWork.history.model) |
| 636 | + found = False |
| 637 | + for name, field in model_state.fields: |
| 638 | + if name == '_order': |
| 639 | + found = True |
| 640 | + self.assertEqual(type(field), models.IntegerField) |
| 641 | + assert found, '_order not in fields ' + repr(model_state.fields) |
0 commit comments