Repository: jazzband/django-simple-history

Issue:
simple-history 2.6.0 generates a migration while 2.5.1 doesn't
**Describe the bug**
Kiwi TCMS uses simple-history as dependency. Until recently we had version 2.5.1 and we've upgraded to 2.6.0. The problem is that with 2.5.1 ./manage.py migrate/makemigrations doesn't detect any new changes but with 2.6.0 it does.

Here's how the initial migration looks like:
```
from django.db import migrations, models

        migrations.AddField(
            model_name='testplan',
            name='parent',
            field=models.ForeignKey(blank=True, null=True, on_delete=models.deletion.CASCADE,
                                    related_name='child_set', to='testplans.TestPlan'),
        ),

        migrations.CreateModel(
            name='HistoricalTestPlan',
            fields=[
                ('parent', models.ForeignKey(blank=True, db_constraint=False, null=True,
                                             on_delete=models.deletion.DO_NOTHING, related_name='+',
                                             to='testplans.TestPlan')),
            ]
        )
```

Here's what `makemigrations` produces with 2.6.0:

```
import django.db.models.deletion

        migrations.AlterField(
            model_name='historicaltestplan',
            name='parent',
            field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='testplans.HistoricalTestPlan'),
        ),
```

The only difference I see is how we reference the `on_delete` constraint.

**To Reproduce**
Steps to reproduce the behavior:
1. git clone https://github.com/kiwitcms/Kiwi/
2. pip install -r requirements/devel.txt
3. ./manage.py migrate
4. ./manage.py makemigrations  <--- produces a new migration

Repeat the above with 2.5.1 instead and the last step tells you there are no changes detected

**Environment (please complete the following information):**
 - OS: Linux, RHEL 7
 - Django Simple History Version: 2.5.1 and 2.6
 - Django Version: 2.1.5
 - Database Version: SQLite



Discussion:
Hey @atodorov thanks for this. There seems to be a difference in the `to` field. My guess is that the issue stems from #462 and I'm looking into it now. Will let you know when I have an update
Found the issue @atodorov ! It's this line here https://github.com/treyhunner/django-simple-history/pull/462/files#diff-db0d12c32869d48141bfca2c94659322L203. In adding the deconstruct method to that code, we accidentally got rid of that line. DSH still supports foreign keys to the same object, but we accidentally got rid of support for using `'self'` as the ForeignKey (as you do here https://github.com/kiwitcms/Kiwi/blob/master/tcms/testplans/models.py#L49). I'll get a fix together for this, but if you change `'self'` to `'TestPlan'` in line 49, that will also fix the issue on your end. Let me know if that helps!

Resolving patch:
diff --git a/simple_history/models.py b/simple_history/models.py
--- a/simple_history/models.py
+++ b/simple_history/models.py
@@ -213,6 +213,13 @@ def copy_fields(self, model):
                 else:
                     FieldType = type(old_field)
 
+                # If field_args['to'] is 'self' then we have a case where the object
+                # has a foreign key to itself. If we pass the historical record's
+                # field to = 'self', the foreign key will point to an historical
+                # record rather than the base record. We can use old_field.model here.
+                if field_args.get("to", None) == "self":
+                    field_args["to"] = old_field.model
+
                 # Override certain arguments passed when creating the field
                 # so that they work for the historical field.
                 field_args.update(


Test patch:
diff --git a/simple_history/tests/models.py b/simple_history/tests/models.py
--- a/simple_history/tests/models.py
+++ b/simple_history/tests/models.py
@@ -553,3 +553,13 @@ class CustomNameModel(models.Model):
 class CustomManagerNameModel(models.Model):
     name = models.CharField(max_length=15)
     log = HistoricalRecords()
+
+
+class ForeignKeyToSelfModel(models.Model):
+    fk_to_self = models.ForeignKey(
+        "ForeignKeyToSelfModel", null=True, related_name="+", on_delete=models.CASCADE
+    )
+    fk_to_self_using_str = models.ForeignKey(
+        "self", null=True, related_name="+", on_delete=models.CASCADE
+    )
+    history = HistoricalRecords()
diff --git a/simple_history/tests/tests/test_models.py b/simple_history/tests/tests/test_models.py
--- a/simple_history/tests/tests/test_models.py
+++ b/simple_history/tests/tests/test_models.py
@@ -1,6 +1,7 @@
 from __future__ import unicode_literals
 
 import unittest
+
 import uuid
 import warnings
 from datetime import datetime, timedelta
@@ -43,6 +44,7 @@
     ExternalModel1,
     ExternalModel3,
     FileModel,
+    ForeignKeyToSelfModel,
     HistoricalChoice,
     HistoricalCustomFKError,
     HistoricalPoll,
@@ -583,7 +585,6 @@ def setUp(self):
         self.poll.save()
 
     def test_get_prev_record(self):
-
         self.poll.question = "ask questions?"
         self.poll.save()
         self.poll.question = "eh?"
@@ -1203,7 +1204,6 @@ def test_signal_is_able_to_retrieve_request_from_thread(self):
 
 class WarningOnAbstractModelWithInheritFalseTest(TestCase):
     def test_warning_on_abstract_model_with_inherit_false(self):
-
         with warnings.catch_warnings(record=True) as w:
 
             class AbstractModelWithInheritFalse(models.Model):
@@ -1224,7 +1224,6 @@ class Meta:
 
 
 class MultiDBWithUsingTest(TestCase):
-
     """Asserts historical manager respects `using()` and the `using`
     keyword argument in `save()`.
     """
@@ -1309,3 +1308,19 @@ def test_multidb_with_using_keyword_in_save_and_delete(self):
                 .order_by("history_date")
             ],
         )
+
+
+class ForeignKeyToSelfTest(TestCase):
+    def setUp(self):
+        self.model = ForeignKeyToSelfModel
+        self.history_model = self.model.history.model
+
+    def test_foreign_key_to_self_using_model_str(self):
+        self.assertEqual(
+            self.model, self.history_model.fk_to_self.field.remote_field.model
+        )
+
+    def test_foreign_key_to_self_using_self_str(self):
+        self.assertEqual(
+            self.model, self.history_model.fk_to_self_using_str.field.remote_field.model
+        )