Skip to content

Commit 2169175

Browse files
committed
Make historical records' m2m fields type-compatible with non-historical
Fixes #1186
1 parent 4b22c58 commit 2169175

File tree

3 files changed

+48
-15
lines changed

3 files changed

+48
-15
lines changed

simple_history/manager.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,35 @@ def __get__(self, instance, owner):
127127
return HistoryManager.from_queryset(HistoricalQuerySet)(self.model, instance)
128128

129129

130+
class HistoryManyToManyDescriptor:
131+
def __init__(self, model, rel):
132+
self.rel = rel
133+
self.model = model
134+
135+
def __get__(self, instance, owner):
136+
return HistoryManyRelatedManager.from_queryset(QuerySet)(
137+
self.model, self.rel, instance
138+
)
139+
140+
141+
class HistoryManyRelatedManager(models.Manager):
142+
def __init__(self, through, rel, instance=None):
143+
super().__init__()
144+
self.model = rel.model
145+
self.through = through
146+
self.instance = instance
147+
self._m2m_through_field_name = rel.field.m2m_reverse_field_name()
148+
149+
def get_queryset(self):
150+
qs = super().get_queryset()
151+
through_qs = HistoryManager.from_queryset(HistoricalQuerySet)(
152+
self.through, self.instance
153+
)
154+
return qs.filter(
155+
pk__in=through_qs.all().values_list(self._m2m_through_field_name, flat=True)
156+
)
157+
158+
130159
class HistoryManager(models.Manager):
131160
def __init__(self, model, instance=None):
132161
super().__init__()

simple_history/models.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@
3131
from simple_history import utils
3232

3333
from . import exceptions
34-
from .manager import SIMPLE_HISTORY_REVERSE_ATTR_NAME, HistoryDescriptor
34+
from .manager import (
35+
SIMPLE_HISTORY_REVERSE_ATTR_NAME,
36+
HistoryDescriptor,
37+
HistoryManyToManyDescriptor,
38+
)
3539
from .signals import (
3640
post_create_historical_m2m_records,
3741
post_create_historical_record,
@@ -227,7 +231,7 @@ def finalize(self, sender, **kwargs):
227231

228232
setattr(module, m2m_model.__name__, m2m_model)
229233

230-
m2m_descriptor = HistoryDescriptor(m2m_model)
234+
m2m_descriptor = HistoryManyToManyDescriptor(m2m_model, field.remote_field)
231235
setattr(history_model, field.name, m2m_descriptor)
232236

233237
def get_history_model_name(self, model):

simple_history/tests/tests/test_models.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1806,12 +1806,12 @@ def test_separation(self):
18061806
self.assertEqual(book.restaurants.all().count(), 0)
18071807
self.assertEqual(book.books.all().count(), 1)
18081808
self.assertEqual(book.places.all().count(), 1)
1809-
self.assertEqual(book.books.first().book, self.book)
1809+
self.assertEqual(book.books.first(), self.book)
18101810

18111811
self.assertEqual(place.restaurants.all().count(), 0)
18121812
self.assertEqual(place.books.all().count(), 0)
18131813
self.assertEqual(place.places.all().count(), 1)
1814-
self.assertEqual(place.places.first().place, self.place)
1814+
self.assertEqual(place.places.first(), self.place)
18151815

18161816
self.assertEqual(add.restaurants.all().count(), 0)
18171817
self.assertEqual(add.books.all().count(), 0)
@@ -1847,11 +1847,11 @@ def test_separation(self):
18471847

18481848
self.assertEqual(book.books.all().count(), 1)
18491849
self.assertEqual(book.places.all().count(), 1)
1850-
self.assertEqual(book.books.first().book, self.book)
1850+
self.assertEqual(book.books.first(), self.book)
18511851

18521852
self.assertEqual(place.books.all().count(), 0)
18531853
self.assertEqual(place.places.all().count(), 1)
1854-
self.assertEqual(place.places.first().place, self.place)
1854+
self.assertEqual(place.places.first(), self.place)
18551855

18561856
self.assertEqual(add.books.all().count(), 0)
18571857
self.assertEqual(add.places.all().count(), 0)
@@ -1860,11 +1860,11 @@ def test_separation(self):
18601860

18611861
self.assertEqual(restaurant.restaurants.all().count(), 1)
18621862
self.assertEqual(restaurant.places.all().count(), 1)
1863-
self.assertEqual(restaurant.restaurants.first().restaurant, self.restaurant)
1863+
self.assertEqual(restaurant.restaurants.first(), self.restaurant)
18641864

18651865
self.assertEqual(place.restaurants.all().count(), 0)
18661866
self.assertEqual(place.places.all().count(), 1)
1867-
self.assertEqual(place.places.first().place, self.place)
1867+
self.assertEqual(place.places.first(), self.place)
18681868

18691869
self.assertEqual(add.restaurants.all().count(), 0)
18701870
self.assertEqual(add.places.all().count(), 0)
@@ -1982,7 +1982,7 @@ def test_create(self):
19821982

19831983
# And the historical place is the correct one
19841984
historical_place = m2m_record.places.first()
1985-
self.assertEqual(historical_place.place, self.place)
1985+
self.assertEqual(historical_place, self.place)
19861986

19871987
def test_remove(self):
19881988
# Add and remove a many-to-many child
@@ -2002,7 +2002,7 @@ def test_remove(self):
20022002

20032003
# And the previous row still has the correct one
20042004
historical_place = previous_m2m_record.places.first()
2005-
self.assertEqual(historical_place.place, self.place)
2005+
self.assertEqual(historical_place, self.place)
20062006

20072007
def test_clear(self):
20082008
# Add some places
@@ -2054,7 +2054,7 @@ def test_delete_child(self):
20542054
# Place instance cannot be created...
20552055
historical_place = m2m_record.places.first()
20562056
with self.assertRaises(ObjectDoesNotExist):
2057-
historical_place.place.id
2057+
historical_place.id
20582058

20592059
# But the values persist
20602060
historical_place_values = m2m_record.places.all().values()[0]
@@ -2084,7 +2084,7 @@ def test_delete_parent(self):
20842084

20852085
# And it is the correct one
20862086
historical_place = prev_record.places.first()
2087-
self.assertEqual(historical_place.place, self.place)
2087+
self.assertEqual(historical_place, self.place)
20882088

20892089
def test_update_child(self):
20902090
self.poll.places.add(self.place)
@@ -2102,7 +2102,7 @@ def test_update_child(self):
21022102
m2m_record = self.poll.history.all()[0]
21032103
self.assertEqual(m2m_record.places.count(), 1)
21042104
historical_place = m2m_record.places.first()
2105-
self.assertEqual(historical_place.place.name, "Updated")
2105+
self.assertEqual(historical_place.name, "Updated")
21062106

21072107
def test_update_parent(self):
21082108
self.poll.places.add(self.place)
@@ -2120,7 +2120,7 @@ def test_update_parent(self):
21202120
m2m_record = self.poll.history.all()[0]
21212121
self.assertEqual(m2m_record.places.count(), 1)
21222122
historical_place = m2m_record.places.first()
2123-
self.assertEqual(historical_place.place, self.place)
2123+
self.assertEqual(historical_place, self.place)
21242124

21252125
def test_bulk_add_remove(self):
21262126
# Add some places
@@ -2152,7 +2152,7 @@ def test_bulk_add_remove(self):
21522152
self.assertEqual(m2m_record.places.all().count(), 1)
21532153

21542154
historical_place = m2m_record.places.first()
2155-
self.assertEqual(historical_place.place, self.place)
2155+
self.assertEqual(historical_place, self.place)
21562156

21572157
def test_m2m_relation(self):
21582158
# Ensure only the correct M2Ms are saved and returned for history objects

0 commit comments

Comments
 (0)