summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHrushikesh <hrushikeshrv@gmail.com>2023-06-16 19:06:24 +0530
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-06-21 07:48:09 +0200
commit107865780aa44914e21d27fdf4ca269bc61c7f01 (patch)
treebb8163452630f2cbd4322400aaa8cdab4a6d57f2
parentddb6506618ea52c6b20e97eefad03ed847a1e3de (diff)
Fixed #34345 -- Added system check for ManyToManyFields with intermediate tables in ModelAdmin.filter_horizontal/vertical.
-rw-r--r--django/contrib/admin/checks.py10
-rw-r--r--docs/ref/checks.txt7
-rw-r--r--tests/modeladmin/test_checks.py43
3 files changed, 56 insertions, 4 deletions
diff --git a/django/contrib/admin/checks.py b/django/contrib/admin/checks.py
index 2c90dedc31..45d895dff0 100644
--- a/django/contrib/admin/checks.py
+++ b/django/contrib/admin/checks.py
@@ -533,6 +533,16 @@ class BaseModelAdminChecks:
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 []
diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt
index 50f7c1b166..de7b0dc382 100644
--- a/docs/ref/checks.txt
+++ b/docs/ref/checks.txt
@@ -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``.
diff --git a/tests/modeladmin/test_checks.py b/tests/modeladmin/test_checks.py
index 69aa242a64..2ed27f8a3d 100644
--- a/tests/modeladmin/test_checks.py
+++ b/tests/modeladmin/test_checks.py
@@ -4,10 +4,11 @@ from django.contrib.admin import BooleanFieldListFilter, SimpleListFilter
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
@@ -321,6 +322,26 @@ class FilterVerticalCheckTests(CheckTestCase):
"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",)
@@ -363,6 +384,26 @@ class FilterHorizontalCheckTests(CheckTestCase):
"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",)