Skip to content

Cannot resolve a custom through model passed as app_label.ModelName for m2m relations #1266

Open
@antoineauger

Description

@antoineauger

PS: this is a corner case of #1093. I made some experiments in #1149 before creating this issue because I don't have the required knowledge to fix it.

Describe the bug

django-simple-history cannot resolve a custom through model passed as app_label.ModelName for m2m relations.

To Reproduce

When trying to pass the Membership model as a tests.Membership string, this produces an error when testing inside the project repository with python3 runtests.py:

class Membership(models.Model):
    group = models.ForeignKey("Group", on_delete=models.CASCADE)
    person = models.ForeignKey("Person", on_delete=models.CASCADE)
    inviter = models.ForeignKey(
        "Person",
        on_delete=models.CASCADE,
        related_name="membership_invites",
    )
    invite_reason = models.CharField(max_length=64)


class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(
        "Person",
        through="tests.Membership",
        through_fields=("group", "person"),
    )

    history = HistoricalRecords(m2m_fields=[members])

Test output:

python3 runtests.py                                                             
Traceback (most recent call last):
  File "/home/antoine/_Github/django-simple-history/runtests.py", line 193, in <module>
    main()
  File "/home/antoine/_Github/django-simple-history/runtests.py", line 180, in main
    django.setup()
  File "/home/antoine/.local/lib/python3.10/site-packages/django/__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "/home/antoine/.local/lib/python3.10/site-packages/django/apps/registry.py", line 116, in populate
    app_config.import_models()
  File "/home/antoine/.local/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 "<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 "/home/antoine/_Github/django-simple-history/simple_history/tests/models.py", line 159, in <module>
    class Group(models.Model):
  File "/home/antoine/.local/lib/python3.10/site-packages/django/db/models/base.py", line 365, in __new__
    new_class._prepare()
  File "/home/antoine/.local/lib/python3.10/site-packages/django/db/models/base.py", line 428, in _prepare
    class_prepared.send(sender=cls)
  File "/home/antoine/.local/lib/python3.10/site-packages/django/dispatch/dispatcher.py", line 176, in send
    return [
  File "/home/antoine/.local/lib/python3.10/site-packages/django/dispatch/dispatcher.py", line 177, in <listcomp>
    (receiver, receiver(signal=self, sender=sender, **named))
  File "/home/antoine/_Github/django-simple-history/simple_history/models.py", line 223, in finalize
    m2m_model = self.create_history_m2m_model(
  File "/home/antoine/_Github/django-simple-history/simple_history/models.py", line 258, in create_history_m2m_model
    fields = self.copy_fields(through_model)
  File "/home/antoine/_Github/django-simple-history/simple_history/models.py", line 330, in copy_fields
    for field in self.fields_included(model):
  File "/home/antoine/_Github/django-simple-history/simple_history/models.py", line 313, in fields_included
    for field in model._meta.fields:
AttributeError: 'str' object has no attribute '_meta'

Expected behavior

It correctly works when passing the Membership model as a Python class:

class Membership(models.Model):
    group = models.ForeignKey("Group", on_delete=models.CASCADE)
    person = models.ForeignKey("Person", on_delete=models.CASCADE)
    inviter = models.ForeignKey(
        "Person",
        on_delete=models.CASCADE,
        related_name="membership_invites",
    )
    invite_reason = models.CharField(max_length=64)


class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(
        "Person",
        through=Membership,
        through_fields=("group", "person"),
    )

    history = HistoricalRecords(m2m_fields=[members])

Environment (please complete the following information):

  • OS: Ubuntu 22.04.3 LTS
  • Django Simple History Version: 3.4.0
  • Django Version: 4.2.5

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions