Skip to content

Commit 1a3a90a

Browse files
committed
Fixed a bug where simple-history can't handle foreign keys pointing to
one to one fields.
1 parent 3d629b1 commit 1a3a90a

File tree

6 files changed

+57
-4
lines changed

6 files changed

+57
-4
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ MANIFEST
99
.tox/
1010
htmlcov/
1111
test_files/
12+
/.ve
13+
/.project
14+
/.pydevproject

AUTHORS.rst

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Authors
44
- Aleksey Kladov
55
- Corey Bertram
66
- Damien Nozay
7+
- Daniel Levy
78
- George Vilches
89
- Joao Pedro Francese
910
- Jonathan Sanchez

CHANGES.rst

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Changes
33

44
tip (unreleased)
55
----------------
6+
- Fixed error that occurs when models have a foreign key pointing to a one to one field.
67

78
1.3.0 (2013-05-17)
89
------------------

simple_history/models.py

+20-3
Original file line numberDiff line numberDiff line change
@@ -179,13 +179,25 @@ class CustomForeignKey(parent_type):
179179

180180
def get_attname(self):
181181
return self.name
182-
183-
def do_related_class(self, other, cls):
182+
183+
def get_field(self, other, cls):
184184
# this hooks into contribute_to_class() and this is
185185
# called specifically after the class_prepared signal
186186
to_field = copy.copy(self.rel.to._meta.pk)
187187
field = self
188-
if isinstance(to_field, models.AutoField):
188+
if isinstance(to_field, models.OneToOneField):
189+
#HACK This creates a new custom foreign key based on to_field,
190+
# and calls itself with that, effectively making the calls
191+
# recursive
192+
temp_field = self.__class__(to_field.rel.to._meta.object_name)
193+
for key, val in to_field.__dict__.items():
194+
if (isinstance(key, basestring)
195+
and not key.startswith('_')):
196+
setattr(temp_field, key, val)
197+
field = self.__class__.get_field(
198+
temp_field, other, to_field.rel.to)
199+
200+
elif isinstance(to_field, models.AutoField):
189201
field.__class__ = models.IntegerField
190202
else:
191203
field.__class__ = to_field.__class__
@@ -216,6 +228,11 @@ def do_related_class(self, other, cls):
216228
and not key.startswith(excluded_prefixes)
217229
and not key in excluded_attributes):
218230
setattr(field, key, val)
231+
return field
232+
233+
def do_related_class(self, other, cls):
234+
field = self.get_field(other, cls)
235+
219236
transform_field(field)
220237
field.rel = None
221238

simple_history/tests/models.py

+10
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,12 @@ class Document(models.Model):
6161
def _history_user(self):
6262
return self.changed_by
6363

64+
class Profile(User):
65+
date_of_birth = models.DateField()
6466

67+
class AdminProfile(models.Model):
68+
profile = models.ForeignKey(Profile)
69+
6570
class State(models.Model):
6671
library = models.ForeignKey('Library', null=True)
6772
history = HistoricalRecords()
@@ -71,6 +76,11 @@ class Book(models.Model):
7176
isbn = models.CharField(max_length=15, primary_key=True)
7277
history = HistoricalRecords()
7378

79+
class HardbackBook(Book):
80+
price = models.FloatField()
81+
82+
class Bookcase(models.Model):
83+
books = models.ForeignKey(HardbackBook)
7484

7585
class Library(models.Model):
7686
book = models.ForeignKey(Book, null=True)

simple_history/tests/tests.py

+22-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
from django_webtest import WebTest
77
from django.core.files.base import ContentFile
88
from django.core.urlresolvers import reverse
9+
from simple_history.tests.models import Profile, AdminProfile, Bookcase
10+
from django.db import models
11+
from simple_history.models import HistoricalRecords
912
try:
1013
from django.contrib.auth import get_user_model
1114
User = get_user_model()
@@ -219,7 +222,6 @@ def test_raw_save(self):
219222
'history_type': "~",
220223
})
221224

222-
223225
class RegisterTest(TestCase):
224226
def test_register_no_args(self):
225227
self.assertEqual(len(Choice.history.all()), 0)
@@ -243,6 +245,25 @@ def test_reregister(self):
243245
self.assertTrue(hasattr(User, 'histories'))
244246
self.assertFalse(hasattr(User, 'again'))
245247

248+
class CreateHistoryModelTests(TestCase):
249+
250+
def test_create_history_model_with_one_to_one_field_to_integer_field(self):
251+
records = HistoricalRecords()
252+
records.module = AdminProfile.__module__
253+
try:
254+
records.create_history_model(AdminProfile)
255+
except:
256+
self.fail("SimpleHistory should handle foreign keys to one to one"
257+
"fields to integer fields without throwing an exception.")
258+
259+
def test_create_history_model_with_one_to_one_field_to_char_field(self):
260+
records = HistoricalRecords()
261+
records.module = Bookcase.__module__
262+
try:
263+
records.create_history_model(Bookcase)
264+
except:
265+
self.fail("SimpleHistory should handle foreign keys to one to one"
266+
"fields to char fields without throwing an exception.")
246267

247268
class AppLabelTest(TestCase):
248269
def get_table_name(self, manager):

0 commit comments

Comments
 (0)