Skip to content

Commit

Permalink
Fixed #34345 -- Added system check for ManyToManyFields with intermed…
Browse files Browse the repository at this point in the history
…iate tables in ModelAdmin.filter_horizontal/vertical.
  • Loading branch information
hrushikeshrv authored and felixxm committed Jun 21, 2023
1 parent ddb6506 commit 1078657
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 4 deletions.
10 changes: 10 additions & 0 deletions django/contrib/admin/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,16 @@ def _check_filter_item(self, obj, field_name, label):
return must_be(
"a many-to-many field", option=label, obj=obj, id="admin.E020"
)
elif not field.remote_field.through._meta.auto_created:
return [
checks.Error(
f"The value of '{label}' cannot include the ManyToManyField "
f"'{field_name}', because that field manually specifies a "
f"relationship model.",
obj=obj.__class__,
id="admin.E013",
)
]
else:
return []

Expand Down
7 changes: 4 additions & 3 deletions docs/ref/checks.txt
Original file line number Diff line number Diff line change
Expand Up @@ -637,9 +637,10 @@ with the admin site:
* **admin.E011**: The value of ``fieldsets[n][1]`` must contain the key
``fields``.
* **admin.E012**: There are duplicate field(s) in ``fieldsets[n][1]``.
* **admin.E013**: The value of ``fields[n]/fieldsets[n][m]`` cannot include the
``ManyToManyField`` ``<field name>``, because that field manually specifies a
relationship model.
* **admin.E013**: The value of
``fields[n]/filter_horizontal[n]/filter_vertical[n]/fieldsets[n][m]`` cannot
include the ``ManyToManyField`` ``<field name>``, because that field manually
specifies a relationship model.
* **admin.E014**: The value of ``exclude`` must be a list or tuple.
* **admin.E015**: The value of ``exclude`` contains duplicate field(s).
* **admin.E016**: The value of ``form`` must inherit from ``BaseModelForm``.
Expand Down
43 changes: 42 additions & 1 deletion tests/modeladmin/test_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
from django.contrib.admin.options import VERTICAL, ModelAdmin, TabularInline
from django.contrib.admin.sites import AdminSite
from django.core.checks import Error
from django.db.models import CASCADE, F, Field, ForeignKey, Model
from django.db.models import CASCADE, F, Field, ForeignKey, ManyToManyField, Model
from django.db.models.functions import Upper
from django.forms.models import BaseModelFormSet
from django.test import SimpleTestCase
from django.test.utils import isolate_apps

from .models import Band, Song, User, ValidationTestInlineModel, ValidationTestModel

Expand Down Expand Up @@ -321,6 +322,26 @@ class TestModelAdmin(ModelAdmin):
"admin.E020",
)

@isolate_apps("modeladmin")
def test_invalid_m2m_field_with_through(self):
class Artist(Model):
bands = ManyToManyField("Band", through="BandArtist")

class BandArtist(Model):
artist = ForeignKey("Artist", on_delete=CASCADE)
band = ForeignKey("Band", on_delete=CASCADE)

class TestModelAdmin(ModelAdmin):
filter_vertical = ["bands"]

self.assertIsInvalid(
TestModelAdmin,
Artist,
"The value of 'filter_vertical[0]' cannot include the ManyToManyField "
"'bands', because that field manually specifies a relationship model.",
"admin.E013",
)

def test_valid_case(self):
class TestModelAdmin(ModelAdmin):
filter_vertical = ("users",)
Expand Down Expand Up @@ -363,6 +384,26 @@ class TestModelAdmin(ModelAdmin):
"admin.E020",
)

@isolate_apps("modeladmin")
def test_invalid_m2m_field_with_through(self):
class Artist(Model):
bands = ManyToManyField("Band", through="BandArtist")

class BandArtist(Model):
artist = ForeignKey("Artist", on_delete=CASCADE)
band = ForeignKey("Band", on_delete=CASCADE)

class TestModelAdmin(ModelAdmin):
filter_horizontal = ["bands"]

self.assertIsInvalid(
TestModelAdmin,
Artist,
"The value of 'filter_horizontal[0]' cannot include the ManyToManyField "
"'bands', because that field manually specifies a relationship model.",
"admin.E013",
)

def test_valid_case(self):
class TestModelAdmin(ModelAdmin):
filter_horizontal = ("users",)
Expand Down

0 comments on commit 1078657

Please sign in to comment.