|
110 | 110 | Street,
|
111 | 111 | Temperature,
|
112 | 112 | TestHistoricParticipanToHistoricOrganization,
|
| 113 | + TestHistoricParticipanToHistoricOrganizationOneToOne, |
113 | 114 | TestHistoricParticipantToOrganization,
|
| 115 | + TestHistoricParticipantToOrganizationOneToOne, |
114 | 116 | TestOrganization,
|
115 | 117 | TestOrganizationWithHistory,
|
116 | 118 | TestParticipantToHistoricOrganization,
|
| 119 | + TestParticipantToHistoricOrganizationOneToOne, |
117 | 120 | UnicodeVerboseName,
|
118 | 121 | UnicodeVerboseNamePlural,
|
119 | 122 | UserTextFieldChangeReasonModel,
|
@@ -2841,3 +2844,132 @@ def test_historic_to_historic(self):
|
2841 | 2844 | )[0]
|
2842 | 2845 | pt1i = pt1h.instance
|
2843 | 2846 | self.assertEqual(pt1i.organization.name, "original")
|
| 2847 | + |
| 2848 | + |
| 2849 | +class HistoricOneToOneFieldTest(TestCase): |
| 2850 | + """ |
| 2851 | + Tests chasing OneToOne foreign keys across time points naturally with |
| 2852 | + HistoricForeignKey. |
| 2853 | + """ |
| 2854 | + |
| 2855 | + def test_non_historic_to_historic(self): |
| 2856 | + """ |
| 2857 | + Non-historic table with one to one relationship to historic table. |
| 2858 | +
|
| 2859 | + In this case it should simply behave like OneToOneField because |
| 2860 | + the origin model (this one) cannot be historic, so OneToOneField |
| 2861 | + lookups are always "current". |
| 2862 | + """ |
| 2863 | + org = TestOrganizationWithHistory.objects.create(name="original") |
| 2864 | + part = TestParticipantToHistoricOrganizationOneToOne.objects.create( |
| 2865 | + name="part", organization=org |
| 2866 | + ) |
| 2867 | + before_mod = timezone.now() |
| 2868 | + self.assertEqual(part.organization.id, org.id) |
| 2869 | + self.assertEqual(org.participant, part) |
| 2870 | + |
| 2871 | + historg = TestOrganizationWithHistory.history.as_of(before_mod).get( |
| 2872 | + name="original" |
| 2873 | + ) |
| 2874 | + self.assertEqual(historg.participant, part) |
| 2875 | + |
| 2876 | + self.assertEqual(org.history.count(), 1) |
| 2877 | + org.name = "modified" |
| 2878 | + org.save() |
| 2879 | + self.assertEqual(org.history.count(), 2) |
| 2880 | + |
| 2881 | + # drop internal caches, re-select |
| 2882 | + part = TestParticipantToHistoricOrganizationOneToOne.objects.get(name="part") |
| 2883 | + self.assertEqual(part.organization.name, "modified") |
| 2884 | + |
| 2885 | + def test_historic_to_non_historic(self): |
| 2886 | + """ |
| 2887 | + Historic table OneToOneField to non-historic table. |
| 2888 | +
|
| 2889 | + In this case it should simply behave like OneToOneField because |
| 2890 | + the origin model (this one) can be historic but the target model |
| 2891 | + is not, so foreign key lookups are always "current". |
| 2892 | + """ |
| 2893 | + org = TestOrganization.objects.create(name="org") |
| 2894 | + part = TestHistoricParticipantToOrganizationOneToOne.objects.create( |
| 2895 | + name="original", organization=org |
| 2896 | + ) |
| 2897 | + self.assertEqual(part.organization.id, org.id) |
| 2898 | + self.assertEqual(org.participant, part) |
| 2899 | + |
| 2900 | + histpart = TestHistoricParticipantToOrganizationOneToOne.objects.get( |
| 2901 | + name="original" |
| 2902 | + ) |
| 2903 | + self.assertEqual(histpart.organization.id, org.id) |
| 2904 | + |
| 2905 | + def test_historic_to_historic(self): |
| 2906 | + """ |
| 2907 | + Historic table with one to one relationship to historic table. |
| 2908 | +
|
| 2909 | + In this case as_of queries on the origin model (this one) |
| 2910 | + or on the target model (the other one) will traverse the |
| 2911 | + foreign key relationship honoring the timepoint of the |
| 2912 | + original query. This only happens when both tables involved |
| 2913 | + are historic. |
| 2914 | +
|
| 2915 | + At t1 we have one org, one participant. |
| 2916 | + At t2 we have one org, one participant, however the org's name has changed. |
| 2917 | + """ |
| 2918 | + org = TestOrganizationWithHistory.objects.create(name="original") |
| 2919 | + |
| 2920 | + p1 = TestHistoricParticipanToHistoricOrganizationOneToOne.objects.create( |
| 2921 | + name="p1", organization=org |
| 2922 | + ) |
| 2923 | + t1 = timezone.now() |
| 2924 | + org.name = "modified" |
| 2925 | + org.save() |
| 2926 | + p1.name = "p1_modified" |
| 2927 | + p1.save() |
| 2928 | + t2 = timezone.now() |
| 2929 | + |
| 2930 | + # forward relationships - see how natural chasing timepoint relations is |
| 2931 | + p1t1 = TestHistoricParticipanToHistoricOrganizationOneToOne.history.as_of( |
| 2932 | + t1 |
| 2933 | + ).get(name="p1") |
| 2934 | + self.assertEqual(p1t1.organization, org) |
| 2935 | + self.assertEqual(p1t1.organization.name, "original") |
| 2936 | + p1t2 = TestHistoricParticipanToHistoricOrganizationOneToOne.history.as_of( |
| 2937 | + t2 |
| 2938 | + ).get(name="p1_modified") |
| 2939 | + self.assertEqual(p1t2.organization, org) |
| 2940 | + self.assertEqual(p1t2.organization.name, "modified") |
| 2941 | + |
| 2942 | + # reverse relationships |
| 2943 | + # at t1 |
| 2944 | + ot1 = TestOrganizationWithHistory.history.as_of(t1).all()[0] |
| 2945 | + self.assertEqual(ot1.historic_participant.name, "p1") |
| 2946 | + |
| 2947 | + # at t2 |
| 2948 | + ot2 = TestOrganizationWithHistory.history.as_of(t2).all()[0] |
| 2949 | + self.assertEqual(ot2.historic_participant.name, "p1_modified") |
| 2950 | + |
| 2951 | + # current |
| 2952 | + self.assertEqual(org.historic_participant.name, "p1_modified") |
| 2953 | + |
| 2954 | + self.assertTrue(is_historic(ot1)) |
| 2955 | + self.assertFalse(is_historic(org)) |
| 2956 | + |
| 2957 | + self.assertIsInstance( |
| 2958 | + to_historic(ot1), TestOrganizationWithHistory.history.model |
| 2959 | + ) |
| 2960 | + self.assertIsNone(to_historic(org)) |
| 2961 | + |
| 2962 | + # test querying directly from the history table and converting |
| 2963 | + # to an instance, it should chase the foreign key properly |
| 2964 | + # in this case if _as_of is not present we use the history_date |
| 2965 | + # https://github.com/jazzband/django-simple-history/issues/983 |
| 2966 | + pt1h = TestHistoricParticipanToHistoricOrganizationOneToOne.history.all()[0] |
| 2967 | + pt1i = pt1h.instance |
| 2968 | + self.assertEqual(pt1i.organization.name, "modified") |
| 2969 | + pt1h = ( |
| 2970 | + TestHistoricParticipanToHistoricOrganizationOneToOne.history.all().order_by( |
| 2971 | + "history_date" |
| 2972 | + )[0] |
| 2973 | + ) |
| 2974 | + pt1i = pt1h.instance |
| 2975 | + self.assertEqual(pt1i.organization.name, "original") |
0 commit comments