Skip to content

Revert fails when UUIDField is used as primary key in the model #617

Open
@serranomorante

Description

@serranomorante

Hi, thanks for this wonderful library.
I have a problem.
Whenever I try to revert one of my models in a programatic way to a past version of it, I get Key (id)=(da7304bb-64c9-4f6b-91d5-195bec1389a1) already exists. This is not happening when the model uses the default AutoField from django as its primary key and this is also not happening when I revert it from the admin interface (whether with AutoField or UUIDField as primary keys).

To Reproduce
Steps to reproduce the behavior:

  1. Just create two models. Both of them need to have the history field. But just one of them is going to have its primary key overrided with a UUIDField.
from simple_history.models import HistoricalRecords

from django.db import models

import uuid


class ModelOne(models.Model):
    """
    This model can be reverted programatically.
    """

    name = models.CharField(max_length=255)
    history = HistoricalRecords()


class ModelTwo(models.Model):
    """
    This model cannot be reverted programatically.
    """

    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False
    )
    name = models.CharField(max_length=255)
    history = HistoricalRecords()
  1. Run makemigrations, then migrate.
  2. Go to a fresh command line interface. Create new records and try to revert to a previous one.
>>> from testing_simple.models import ModelTwo
>>> mt = ModelTwo.objects.create(name="name 1")
>>> mt.name = "name 2"
>>> mt.save()
>>> mt.history.all()
<QuerySet [<HistoricalModelTwo: ModelTwo object (921d5275-9ded-4295-9716-f1cc0f6b72ab) as of 2019-12-28 06:20:12.254575+00:00>, <HistoricalModelTwo: ModelTwo object (921d5275-9ded-4295-9716-f1cc0f6b72ab) as of 2019-12-28 06:20:03.498981+00:00>]>
>>> mt_earliest = mt.history.earliest()
>>> mt_earliest.instance.save()
Traceback (most recent call last):
  File "/home/disonante/pjs/tests/simple_history_test/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
  File "/home/disonante/pjs/tests/simple_history_test/venv/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 396, in execute
    return Database.Cursor.execute(self, query, params)
sqlite3.IntegrityError: UNIQUE constraint failed: testing_simple_modeltwo.id

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/disonante/pjs/tests/simple_history_test/venv/lib/python3.6/site-packages/django/db/models/base.py", line 746, in save
    force_update=force_update, update_fields=update_fields)
  File "/home/disonante/pjs/tests/simple_history_test/venv/lib/python3.6/site-packages/django/db/models/base.py", line 784, in save_base
    force_update, using, update_fields,
  File "/home/disonante/pjs/tests/simple_history_test/venv/lib/python3.6/site-packages/django/db/models/base.py", line 886, in _save_table
    results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
  File "/home/disonante/pjs/tests/simple_history_test/venv/lib/python3.6/site-packages/django/db/models/base.py", line 925, in _do_insert
    using=using, raw=raw,
  File "/home/disonante/pjs/tests/simple_history_test/venv/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/disonante/pjs/tests/simple_history_test/venv/lib/python3.6/site-packages/django/db/models/query.py", line 1204, in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
  File "/home/disonante/pjs/tests/simple_history_test/venv/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1377, in execute_sql
    cursor.execute(sql, params)
  File "/home/disonante/pjs/tests/simple_history_test/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 100, in execute
    return super().execute(sql, params)
  File "/home/disonante/pjs/tests/simple_history_test/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 68, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/home/disonante/pjs/tests/simple_history_test/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/home/disonante/pjs/tests/simple_history_test/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
  File "/home/disonante/pjs/tests/simple_history_test/venv/lib/python3.6/site-packages/django/db/utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/home/disonante/pjs/tests/simple_history_test/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
  File "/home/disonante/pjs/tests/simple_history_test/venv/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 396, in execute
    return Database.Cursor.execute(self, query, params)
django.db.utils.IntegrityError: UNIQUE constraint failed: testing_simple_modeltwo.id

Environment

  • OS: Ubuntu 18.04
  • Database: tested with both, PostgreSQL latest and SQLlite.
  • asgiref==3.2.3
  • Django==3.0.1
  • django-simple-history==2.8.0
  • pytz==2019.3
  • six==1.13.0
  • sqlparse==0.3.0

Additional context
The code above was tested in an isolated virtual environment, with a brand new
django project and django-simple-history installation.

Things I have tried:

  1. Exclude the id from the HistoricalRecords with the excluded_fields option.
  2. Set the UUIDField as the primary key to the historical model too (with history_id_field).

Non of them have worked.

I would appreciate some workaround that I can follow for the moment.
Thank you.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions