diff options
| author | Hrushikesh <hrushikeshrv@gmail.com> | 2023-06-16 19:06:24 +0530 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2023-06-21 07:48:09 +0200 |
| commit | 107865780aa44914e21d27fdf4ca269bc61c7f01 (patch) | |
| tree | bb8163452630f2cbd4322400aaa8cdab4a6d57f2 | |
| parent | ddb6506618ea52c6b20e97eefad03ed847a1e3de (diff) | |
Fixed #34345 -- Added system check for ManyToManyFields with intermediate tables in ModelAdmin.filter_horizontal/vertical.
| -rw-r--r-- | django/contrib/admin/checks.py | 10 | ||||
| -rw-r--r-- | docs/ref/checks.txt | 7 | ||||
| -rw-r--r-- | tests/modeladmin/test_checks.py | 43 |
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",) |
