Skip to content

Commit 5286ed0

Browse files
committed
Merge pull request #168 from treyhunner/fix-missing-onetoone-in-admin
Fix one-to-one relations in admin and restoring to historical instances
2 parents 601592a + cd46edd commit 5286ed0

File tree

6 files changed

+48
-4
lines changed

6 files changed

+48
-4
lines changed

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Changes
44
tip (unreleased)
55
----------------
66
- Fix OneToOneField transformation for historical models (gh-166)
7+
- Disable cascading deletes from related models to historical models
8+
- Fix restoring historical instances with missing one-to-one relations (gh-162)
79

810
1.6.0 (2015-04-16)
911
------------------

simple_history/models.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ def copy_fields(self, model):
147147
db_index=True,
148148
serialize=True,
149149
unique=False,
150+
on_delete=models.DO_NOTHING,
150151
**field_arguments
151152
)
152153
field.name = old_field.name
@@ -173,7 +174,8 @@ def revert_url(self):
173174
[getattr(self, opts.pk.attname), self.history_id])
174175

175176
def get_instance(self):
176-
return model(**dict([(k, getattr(self, k)) for k in fields]))
177+
return model(**dict([(field.attname, getattr(self, field.attname))
178+
for field in fields.values()]))
177179

178180
return {
179181
'history_id': models.AutoField(primary_key=True),

simple_history/tests/admin.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from django.contrib import admin
44

55
from simple_history.admin import SimpleHistoryAdmin
6-
from .models import Poll, Choice, Person, Book, Document, Paper
6+
from .models import Poll, Choice, Person, Book, Document, Paper, Employee
77

88

99
class PersonAdmin(SimpleHistoryAdmin):
@@ -17,3 +17,4 @@ def has_change_permission(self, request, obj=None):
1717
admin.site.register(Book, SimpleHistoryAdmin)
1818
admin.site.register(Document, SimpleHistoryAdmin)
1919
admin.site.register(Paper, SimpleHistoryAdmin)
20+
admin.site.register(Employee, SimpleHistoryAdmin)

simple_history/tests/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,3 +245,8 @@ class UserAccessorDefault(models.Model):
245245

246246
class UserAccessorOverride(models.Model):
247247
pass
248+
249+
250+
class Employee(models.Model):
251+
manager = models.OneToOneField('Employee', null=True)
252+
history = HistoricalRecords()

simple_history/tests/tests/test_admin.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from django.conf import settings
1313
from simple_history.models import HistoricalRecords
1414

15-
from ..models import Book, Person, Poll, State
15+
from ..models import Book, Person, Poll, State, Employee
1616

1717

1818
today = datetime(2021, 1, 1, 10, 0)
@@ -234,3 +234,14 @@ def test_deleteting_user(self):
234234

235235
historical_poll = poll.history.all()[0]
236236
self.assertEqual(historical_poll.history_user, None)
237+
238+
def test_missing_one_to_one(self):
239+
"""A relation to a missing one-to-one model should still show history"""
240+
self.login()
241+
manager = Employee.objects.create()
242+
employee = Employee.objects.create(manager=manager)
243+
employee.manager = None
244+
employee.save()
245+
manager.delete()
246+
response = self.app.get(get_history_url(employee, 0))
247+
self.assertEqual(response.status_code, 200)

simple_history/tests/tests/test_models.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
AbstractBase, ConcreteAttr, ConcreteUtil, SelfFK, Temperature, WaterLevel,
1919
ExternalModel1, ExternalModel3, UnicodeVerboseName, HistoricalChoice,
2020
HistoricalState, HistoricalCustomFKError, Series, SeriesWork, PollInfo,
21-
UserAccessorDefault, UserAccessorOverride
21+
UserAccessorDefault, UserAccessorOverride, Employee
2222
)
2323
from ..external.models import ExternalModel2, ExternalModel4
2424

@@ -706,3 +706,26 @@ def test_accessor_default(self):
706706
def test_accessor_override(self):
707707
register(UserAccessorOverride, user_related_name='my_history_model_accessor')
708708
assert hasattr(User, 'my_history_model_accessor')
709+
710+
711+
class TestMissingOneToOne(TestCase):
712+
713+
def setUp(self):
714+
self.manager1 = Employee.objects.create()
715+
self.manager2 = Employee.objects.create()
716+
self.employee = Employee.objects.create(manager=self.manager1)
717+
self.employee.manager = self.manager2
718+
self.employee.save()
719+
self.manager1.delete()
720+
721+
def test_history_is_complete(self):
722+
historical_manager_ids = list(self.employee.history.order_by('pk')
723+
.values_list('manager_id', flat=True))
724+
self.assertEqual(historical_manager_ids, [1, 2])
725+
726+
def test_restore_employee(self):
727+
historical = self.employee.history.order_by('pk')[0]
728+
original = historical.instance
729+
self.assertEqual(original.manager_id, 1)
730+
with self.assertRaises(Employee.DoesNotExist):
731+
original.manager

0 commit comments

Comments
 (0)