Repository: jazzband/django-simple-history

Issue:
history property not picked when model class is in a models directory
I have multiple model files under a models directory. Example:

```
    myapp/
            models/
                        __init__.py
                        model1.py
                        model2.py
                        model3.py
```

The model classes within model1.py, model2.py, etc are using an app_label and have a history property. Example:

```
    class Meta:
        app_label = 'myapp'

    history = HistoryRecords()
```

The history property is not getting picked up by south as a schema change (not sure about syncdb).

However, when I use a "traditional" app directory structure with a single models.py file, everything works. Example:

```
    myapp/
        models.py
```

Does this require an enhancement? Perhaps I am doing something wrong on my end. Thanks.



Discussion:
That sounds like a bug.

I'd recommend writing a test and submitting it, but the project lacks any tests at the moment... I'm hoping to change that soon.

Pull requests welcome.


Resolving patch:
diff --git a/runtests.py b/runtests.py
--- a/runtests.py
+++ b/runtests.py
@@ -22,7 +22,8 @@
             'django.contrib.sessions',
             'django.contrib.admin',
             'simple_history',
-            'simple_history.tests'
+            'simple_history.tests',
+            'simple_history.tests.external'
         ),
         DATABASES={
             'default': {
diff --git a/simple_history/models.py b/simple_history/models.py
--- a/simple_history/models.py
+++ b/simple_history/models.py
@@ -44,6 +44,14 @@ def create_history_model(self, model):
         """
         attrs = {'__module__': self.module}
 
+        app_module = models.get_app(model._meta.app_label)
+        if model.__module__ != self.module:
+            # registered under different app
+            attrs['__module__'] = self.module
+        elif app_module.__name__ != self.module:
+            # has meta options with app_label
+            attrs['__module__'] = app_module.__name__
+
         fields = self.copy_fields(model)
         attrs.update(fields)
         attrs.update(self.get_extra_fields(model, fields))


Test patch:
diff --git a/simple_history/tests/external/__init__.py b/simple_history/tests/external/__init__.py
new file mode 100644
diff --git a/simple_history/tests/external/models/__init__.py b/simple_history/tests/external/models/__init__.py
new file mode 100644
--- /dev/null
+++ b/simple_history/tests/external/models/__init__.py
@@ -0,0 +1,2 @@
+from .model2 import ExternalModel2
+from .model4 import ExternalModel4
\ No newline at end of file
diff --git a/simple_history/tests/external/models/model2.py b/simple_history/tests/external/models/model2.py
new file mode 100644
--- /dev/null
+++ b/simple_history/tests/external/models/model2.py
@@ -0,0 +1,9 @@
+
+from django.db import models
+from simple_history.models import HistoricalRecords
+
+class ExternalModel2(models.Model):
+    name = models.CharField(max_length=100)
+    history = HistoricalRecords()
+    class Meta:
+        app_label = 'external'
diff --git a/simple_history/tests/external/models/model4.py b/simple_history/tests/external/models/model4.py
new file mode 100644
--- /dev/null
+++ b/simple_history/tests/external/models/model4.py
@@ -0,0 +1,11 @@
+
+from django.db import models
+from simple_history import register
+
+class ExternalModel4(models.Model):
+    name = models.CharField(max_length=100)
+    class Meta:
+        app_label = 'external'
+
+register(ExternalModel4, app='simple_history.tests',
+    manager_name='histories')
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
@@ -58,3 +58,14 @@ def _history_user(self):
 register(User, app='simple_history.tests', manager_name='histories')
 
 
+class ExternalModel1(models.Model):
+    name = models.CharField(max_length=100)
+    history = HistoricalRecords()
+    class Meta:
+        app_label = 'external'
+
+class ExternalModel3(models.Model):
+    name = models.CharField(max_length=100)
+
+register(ExternalModel3, app='simple_history.tests.external',
+    manager_name='histories')
diff --git a/simple_history/tests/tests.py b/simple_history/tests/tests.py
--- a/simple_history/tests/tests.py
+++ b/simple_history/tests/tests.py
@@ -7,6 +7,8 @@
 from django.contrib.auth.models import User
 
 from .models import Poll, Choice, Restaurant, Person, FileModel, Document
+from .models import ExternalModel1, ExternalModel3
+from simple_history.tests.external.models import ExternalModel2, ExternalModel4
 
 today = datetime(2021, 1, 1, 10, 0)
 tomorrow = today + timedelta(days=1)
@@ -193,6 +195,32 @@ def test_register_separate_app(self):
         self.assertEqual(len(User.histories.all()), 1)
         self.assertEqual(len(user.histories.all()), 1)
 
+class AppLabelTest(TestCase):
+    def get_table_name(self, manager):
+        return manager.model._meta.db_table
+
+    def test_explicit_app_label(self):
+        self.assertEqual(self.get_table_name(ExternalModel1.objects),
+            'external_externalmodel1')
+        self.assertEqual(self.get_table_name(ExternalModel1.history),
+            'external_historicalexternalmodel1')
+
+    def test_default_app_label(self):
+        self.assertEqual(self.get_table_name(ExternalModel2.objects),
+            'external_externalmodel2')
+        self.assertEqual(self.get_table_name(ExternalModel2.history),
+            'external_historicalexternalmodel2')
+
+    def test_register_app_label(self):
+        self.assertEqual(self.get_table_name(ExternalModel3.objects),
+            'tests_externalmodel3')
+        self.assertEqual(self.get_table_name(ExternalModel3.histories),
+            'external_historicalexternalmodel3')
+        self.assertEqual(self.get_table_name(ExternalModel4.objects),
+            'external_externalmodel4')
+        self.assertEqual(self.get_table_name(ExternalModel4.histories),
+            'tests_historicalexternalmodel4')
+
 
 class HistoryManagerTest(TestCase):
     def test_most_recent(self):