django-simple-history
django-simple-history copied to clipboard
Allow custom table name for M2M fields
Problem Statement In version 3.2 the ability to track changes in m2m fields was added. You can currently set a table name for the main table but not for any of the through tables that are created.
Describe the solution you'd like
I'd like to be able to set the database table name for each of the many to many fields. This could be done by passing a dict
into m2m_fields
when initializing the historical record.
@PiotrKurnik @lazarust Any progress regarding this feature? 😸
We would also be really interested if we could have historical support for "customized" m2m_fields
. For instance, currently, the following does not work 😢
class User(AbstractUser):
...
class Group(models.Model):
members = models.ManyToManyField(
User, through="Membership", through_fields=("group", "user")
)
history = HistoricalRecords(m2m_fields=[members])
class Membership(models.Model):
group = HistoricForeignKey(
Group,
related_name="memberships",
on_delete=models.CASCADE,
)
user = HistoricForeignKey(
User,
related_name="memberships",
on_delete=models.CASCADE,
)
Stacktrace
./manage.py makemigrations
Traceback (most recent call last):
File "/.venv/lib/python3.10/site-packages/django/db/models/utils.py", line 15, in make_model_tuple
app_label, model_name = model.split(".")
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/./manage.py", line 47, in <module>
main()
File "/./manage.py", line 43, in main
execute_from_command_line(sys.argv)
File "/.venv/lib/python3.10/site-packages/django/core/management/__init__.py", line 446, in execute_from_command_line
utility.execute()
File "/.venv/lib/python3.10/site-packages/django/core/management/__init__.py", line 386, in execute
settings.INSTALLED_APPS
File "/.venv/lib/python3.10/site-packages/django/conf/__init__.py", line 92, in __getattr__
self._setup(name)
File "/.venv/lib/python3.10/site-packages/django/conf/__init__.py", line 79, in _setup
self._wrapped = Settings(settings_module)
File "/.venv/lib/python3.10/site-packages/django/conf/__init__.py", line 190, in __init__
mod = importlib.import_module(self.SETTINGS_MODULE)
File "/usr/lib/python3.10/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
File "<frozen importlib._bootstrap>", line 992, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
File "<frozen importlib._bootstrap>", line 992, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 883, in exec_module
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
File "/project/__init__.py", line 3, in <module>
from project.celery import app as celery_app
File "/project/celery.py", line 21, in <module>
django.setup()
File "/.venv/lib/python3.10/site-packages/django/__init__.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "/.venv/lib/python3.10/site-packages/django/apps/registry.py", line 116, in populate
app_config.import_models()
File "/.venv/lib/python3.10/site-packages/django/apps/config.py", line 269, in import_models
self.models_module = import_module(models_module_name)
File "/usr/lib/python3.10/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "/project/models.py", line 40, in <module>
class Group(models.Model):
File "/.venv/lib/python3.10/site-packages/django/db/models/base.py", line 363, in __new__
new_class._prepare()
File "/.venv/lib/python3.10/site-packages/django/db/models/base.py", line 426, in _prepare
class_prepared.send(sender=cls)
File "/.venv/lib/python3.10/site-packages/django/dispatch/dispatcher.py", line 176, in send
return [
File "/.venv/lib/python3.10/site-packages/django/dispatch/dispatcher.py", line 177, in <listcomp>
(receiver, receiver(signal=self, sender=sender, **named))
File "/.venv/lib/python3.10/site-packages/simple_history/models.py", line 193, in finalize
m2m_changed.connect(
File "/.venv/lib/python3.10/site-packages/django/db/models/signals.py", line 27, in connect
self._lazy_method(
File "/.venv/lib/python3.10/site-packages/django/db/models/signals.py", line 22, in _lazy_method
apps.lazy_model_operation(partial_method, make_model_tuple(sender))
File "/.venv/lib/python3.10/site-packages/django/db/models/utils.py", line 22, in make_model_tuple
raise ValueError(
ValueError: Invalid model reference 'Membership'. String model references must be of the form 'app_label.ModelName'.
/cc @mlegner @bufferoverflow @max-wittig @fgreinacher
@lazarust When you're mentioning "You can currently set a table name for the main table", I'm assuming you're talking about the table_name
parameter..? In that case, what @antoineauger is talking about seems to be related to what's addressed with PR #1149, and not related to this issue.