Repository: jazzband/django-simple-history

Issue:
test_sameinstant is not idempotent
**Describe the bug**

Two records with the same history_date cannot be retrieved idempotently with `latest()`.  The `get_latest_by` meta property uses `history_date` which means if multiple records have the same timestamp, the resulting record returned by the database is not predictable (it would depend on the behavior of the database).  If history_date is not indexed the result may be different than if it is indexed, for example (this was observed in #729).  The meta property should include sorting by history_id.

**To Reproduce**

The unit test `test_sameinstant` tests for this.  When indexing was added in #729, the test started to fail until the meta was fixed.

**Expected behavior**

If two records have the same history_date then use a secondary sort to guarantee the response order that gets limited to one record inside django.

**Environment (please complete the following information):**
 - Django Simple History Version: 3.0.0
 - Django Version: 3.2.6
 - Database Version: sqlite (whatever the tests use) but applies to all


Discussion:

Resolving patch:
diff --git a/simple_history/models.py b/simple_history/models.py
--- a/simple_history/models.py
+++ b/simple_history/models.py
@@ -458,7 +458,7 @@ def get_meta_options(self, model):
         """
         meta_fields = {
             "ordering": ("-history_date", "-history_id"),
-            "get_latest_by": "history_date",
+            "get_latest_by": ("history_date", "history_id"),
         }
         if self.user_set_verbose_name:
             name = self.user_set_verbose_name


Test patch:
diff --git a/simple_history/registry_tests/migration_test_app/migrations/0003_alter_historicalmodelwithcustomattrforeignkey_options_and_more.py b/simple_history/registry_tests/migration_test_app/migrations/0003_alter_historicalmodelwithcustomattrforeignkey_options_and_more.py
new file mode 100644
--- /dev/null
+++ b/simple_history/registry_tests/migration_test_app/migrations/0003_alter_historicalmodelwithcustomattrforeignkey_options_and_more.py
@@ -0,0 +1,32 @@
+# Generated by Django 4.0.dev20210802171549 on 2021-08-11 11:05
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        (
+            "migration_test_app",
+            "0002_historicalmodelwithcustomattrforeignkey_modelwithcustomattrforeignkey",
+        ),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name="historicalmodelwithcustomattrforeignkey",
+            options={
+                "get_latest_by": ("history_date", "history_id"),
+                "ordering": ("-history_date", "-history_id"),
+                "verbose_name": "historical model with custom attr foreign key",
+            },
+        ),
+        migrations.AlterModelOptions(
+            name="historicalyar",
+            options={
+                "get_latest_by": ("history_date", "history_id"),
+                "ordering": ("-history_date", "-history_id"),
+                "verbose_name": "historical yar",
+            },
+        ),
+    ]
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
@@ -1219,7 +1219,7 @@ def test_sameinstant(self):
         self.write_history(
             [{"pk": 1, "history_date": yesterday}, {"pk": 2, "history_date": yesterday}]
         )
-        assert HistoricalPoll.objects.latest().pk == 1
+        assert HistoricalPoll.objects.latest().pk == 2
 
 
 class TestMissingOneToOne(TestCase):