Opened 6 months ago

Closed 6 months ago

#36239 closed Bug (fixed)

ManyToManyField check error with invalid "to" when passing through/through_fields

Reported by: Jordan Hyatt Owned by: JaeHyuckSa
Component: Core (System checks) Version: 5.1
Severity: Normal Keywords: check
Cc: Jordan Hyatt Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: yes UI/UX: no

Description

Bug was discussed in the forums herehttps://forum.djangoproject.com/t/manytomanyfield-error-message-with-incorrect-to-argument/39394.

Basically, if an invalid "to" argument is passed to ManyToManyField while also passing the through/through_fields arguments the check framework crashes giving unhelpful error message/traceback.

Minimal reproduceable example:

class Foo(Model):
    pass

class Bar(Model):
    foos = ManyToManyField(
        to = "Fo", # NOTE incorrect "to" argument
        through = 'FooBar',
        through_fields = ('bar', 'foo')
    )

class FooBar(Model):
    foo = ForeignKey('Foo', on_delete=CASCADE)
    bar = ForeignKey('Bar', on_delete=CASCADE)

Traceback:

File "django/core/checks/registry.py", line 88, in run_checks
    new_errors = check(app_configs=app_configs, databases=databases)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "django/core/checks/model_checks.py", line 36, in check_all_models
    errors.extend(model.check(**kwargs))
                  ^^^^^^^^^^^^^^^^^^^^^
  File "django/db/models/base.py", line 1691, in check
    *cls._check_fields(**kwargs),
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "django/db/models/base.py", line 1828, in _check_fields
    errors.extend(field.check(from_model=cls, **kwargs))
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "django/db/models/fields/related.py", line 1400, in check
    *self._check_relationship_model(**kwargs),
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "django/db/models/fields/related.py", line 1676, in _check_relationship_model
    related_model._meta.object_name,
    ^^^^^^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute '_meta'

Proposed Patch:

diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 6a9cb12a90..dd4c09a4e3 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -1707,13 +1707,18 @@ def _check_relationship_model(self, from_model=None, **kwargs):
                             and getattr(field.remote_field, "model", None)
                             == related_model
                         ):
+                            related_object_name = (
+                                related_model
+                                if isinstance(related_model, str)
+                                else related_model._meta.object_name
+                            )
                             errors.append(
                                 checks.Error(
                                     "'%s.%s' is not a foreign key to '%s'."
                                     % (
                                         through._meta.object_name,
                                         field_name,
-                                        related_model._meta.object_name,
+                                        related_object_name,
                                     ),
                                     hint=hint,
                                     obj=self,

Change History (17)

comment:1 by Amaan-ali03, 6 months ago

Hey Django Team,
I’d love to work on this issue!
Requesting to assign this ticket to @Amaan-ali03 to implement the proposed fix for the ManyToManyField error with an invalid "to" argument.

Proposed Approach:

Identify the Issue: The bug occurs when an invalid "to" argument is passed to ManyToManyField along with through and through_fields, causing an AttributeError due to improper handling of the related_model variable.
Proposed Solution: Update the _check_relationship_model method in django/db/models/fields/related.py to include a type check for related_model. Instead of directly accessing related_model._meta, the code should verify if related_model is a string and handle it accordingly to avoid the crash.

comment:2 by Usman Asif, 6 months ago

Owner: set to Usman Asif
Status: newassigned

comment:3 by Sarah Boyce, 6 months ago

Triage Stage: UnreviewedAccepted

comment:4 by Sarah Boyce, 6 months ago

Needs tests: set
Patch needs improvement: set

comment:5 by Usman Asif, 6 months ago

Owner: Usman Asif removed
Status: assignednew

comment:6 by Samruddhi Dharankar, 6 months ago

Owner: set to Samruddhi Dharankar
Status: newassigned

comment:7 by Samruddhi Dharankar, 6 months ago

Owner: Samruddhi Dharankar removed
Status: assignednew

comment:8 by Ahmed Nassar, 6 months ago

Hi Django Team,
I noticed that PR #19248 is currently open for this issue, but some unresolved test failures remain.
Is the current contributor still working on it, or would it be okay for me to assist in fixing the remaining issues?
Thanks!

comment:9 by JaeHyuckSa, 6 months ago

Owner: set to JaeHyuckSa
Status: newassigned

comment:10 by Tanishq, 6 months ago

Hey Django Team,

I’d love to jump on ticket #36239 (ManyToManyField crash with invalid to). Can you assign it to @Amaan-ali03? I’ve dug into the issue and want to implement the fix.

Issue:
An invalid to (e.g., "Fo") with through/through_fields crashes with AttributeError: 'str' has no '_meta' because related_model isn’t handled properly.

Proposed Fix:
Update _check_relationship_model in django/db/models/fields/related.py with a type check:

related_object_name = (
    related_model if isinstance(related_model, str) else related_model._meta.object_name
)
errors.append(
    checks.Error(
        f"'{through._meta.object_name}.{field_name}' is not a foreign key to '{related_object_name}'.",
        obj=self,
    )
)

comment:11 by Tanishq, 6 months ago

Needs tests: unset
Patch needs improvement: unset

comment:12 by Tanishq, 6 months ago

Fixes #36239: Prevents AttributeError: 'str' object has no attribute '_meta' in ManyToManyField when to is invalid with through/through_fields.

  • Issue: The validation framework assumed related_model was always resolved, causing a crash when it remained a string.
  • Fix: If related_model is a string, we handle it gracefully in _check_relationship_model.
  • Test: Added test in tests/model_fields/tests.py to confirm expected error messages (E300, E339).
  • Impact: Prevents crashes and ensures Django correctly raises validation errors instead.

https://github.com/django/django/pull/19301

comment:13 by Simon Charette, 6 months ago

We appreciate your interest Tanishq but there's no need to step in here.

JaeHyuckSa's PR is already passing the full suite with any failures and is only awaiting a final review.

in reply to:  13 comment:14 by Tanishq, 6 months ago

Replying to Simon Charette:

We appreciate your interest Tanishq but there's no need to step in here.

JaeHyuckSa's PR is already passing the full suite with any failures and is only awaiting a final review.

okay, thanks for informing me, I will continue contributing in other issues, if that's fine?

comment:15 by Simon Charette, 6 months ago

sure, try to find tickets that don't already have any active owner and ready for review PRs.

comment:16 by Sarah Boyce, 6 months ago

Triage Stage: AcceptedReady for checkin

comment:17 by Sarah Boyce <42296566+sarahboyce@…>, 6 months ago

Resolution: fixed
Status: assignedclosed

In c1a4fcc:

Fixed #36239 -- Fixed a crash in ManyToManyField.through_fields check when to model is invalid.

Signed-off-by: saJaeHyukc <wogur981208@…>

Note: See TracTickets for help on using tickets.
Back to Top