mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #30543 -- Fixed checks of ModelAdmin.list_display for fields accessible only via instance.
Co-Authored-By: Andrew Simons <andrewsimons@bubblegroup.com>
This commit is contained in:
		
				
					committed by
					
						 Mariusz Felisiak
						Mariusz Felisiak
					
				
			
			
				
	
			
			
			
						parent
						
							7991111af1
						
					
				
				
					commit
					ed668796f6
				
			| @@ -720,12 +720,24 @@ class ModelAdminChecks(BaseModelAdminChecks): | |||||||
|             return [] |             return [] | ||||||
|         elif hasattr(obj, item): |         elif hasattr(obj, item): | ||||||
|             return [] |             return [] | ||||||
|         elif hasattr(obj.model, item): |  | ||||||
|         try: |         try: | ||||||
|             field = obj.model._meta.get_field(item) |             field = obj.model._meta.get_field(item) | ||||||
|         except FieldDoesNotExist: |         except FieldDoesNotExist: | ||||||
|                 return [] |             try: | ||||||
|             else: |                 field = getattr(obj.model, item) | ||||||
|  |             except AttributeError: | ||||||
|  |                 return [ | ||||||
|  |                     checks.Error( | ||||||
|  |                         "The value of '%s' refers to '%s', which is not a " | ||||||
|  |                         "callable, an attribute of '%s', or an attribute or " | ||||||
|  |                         "method on '%s.%s'." % ( | ||||||
|  |                             label, item, obj.__class__.__name__, | ||||||
|  |                             obj.model._meta.app_label, obj.model._meta.object_name, | ||||||
|  |                         ), | ||||||
|  |                         obj=obj.__class__, | ||||||
|  |                         id='admin.E108', | ||||||
|  |                     ) | ||||||
|  |                 ] | ||||||
|         if isinstance(field, models.ManyToManyField): |         if isinstance(field, models.ManyToManyField): | ||||||
|             return [ |             return [ | ||||||
|                 checks.Error( |                 checks.Error( | ||||||
| @@ -735,18 +747,6 @@ class ModelAdminChecks(BaseModelAdminChecks): | |||||||
|                 ) |                 ) | ||||||
|             ] |             ] | ||||||
|         return [] |         return [] | ||||||
|         else: |  | ||||||
|             return [ |  | ||||||
|                 checks.Error( |  | ||||||
|                     "The value of '%s' refers to '%s', which is not a callable, " |  | ||||||
|                     "an attribute of '%s', or an attribute or method on '%s.%s'." % ( |  | ||||||
|                         label, item, obj.__class__.__name__, |  | ||||||
|                         obj.model._meta.app_label, obj.model._meta.object_name, |  | ||||||
|                     ), |  | ||||||
|                     obj=obj.__class__, |  | ||||||
|                     id='admin.E108', |  | ||||||
|                 ) |  | ||||||
|             ] |  | ||||||
|  |  | ||||||
|     def _check_list_display_links(self, obj): |     def _check_list_display_links(self, obj): | ||||||
|         """ Check that list_display_links is a unique subset of list_display. |         """ Check that list_display_links is a unique subset of list_display. | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ from django.contrib.admin import BooleanFieldListFilter, SimpleListFilter | |||||||
| from django.contrib.admin.options import VERTICAL, ModelAdmin, TabularInline | from django.contrib.admin.options import VERTICAL, ModelAdmin, TabularInline | ||||||
| from django.contrib.admin.sites import AdminSite | from django.contrib.admin.sites import AdminSite | ||||||
| from django.core.checks import Error | from django.core.checks import Error | ||||||
| from django.db.models import F | from django.db.models import F, Field, Model | ||||||
| from django.db.models.functions import Upper | from django.db.models.functions import Upper | ||||||
| from django.forms.models import BaseModelFormSet | from django.forms.models import BaseModelFormSet | ||||||
| from django.test import SimpleTestCase | from django.test import SimpleTestCase | ||||||
| @@ -509,6 +509,25 @@ class ListDisplayTests(CheckTestCase): | |||||||
|  |  | ||||||
|         self.assertIsValid(TestModelAdmin, ValidationTestModel) |         self.assertIsValid(TestModelAdmin, ValidationTestModel) | ||||||
|  |  | ||||||
|  |     def test_valid_field_accessible_via_instance(self): | ||||||
|  |         class PositionField(Field): | ||||||
|  |             """Custom field accessible only via instance.""" | ||||||
|  |             def contribute_to_class(self, cls, name): | ||||||
|  |                 super().contribute_to_class(cls, name) | ||||||
|  |                 setattr(cls, self.name, self) | ||||||
|  |  | ||||||
|  |             def __get__(self, instance, owner): | ||||||
|  |                 if instance is None: | ||||||
|  |                     raise AttributeError() | ||||||
|  |  | ||||||
|  |         class TestModel(Model): | ||||||
|  |             field = PositionField() | ||||||
|  |  | ||||||
|  |         class TestModelAdmin(ModelAdmin): | ||||||
|  |             list_display = ('field',) | ||||||
|  |  | ||||||
|  |         self.assertIsValid(TestModelAdmin, TestModel) | ||||||
|  |  | ||||||
|  |  | ||||||
| class ListDisplayLinksCheckTests(CheckTestCase): | class ListDisplayLinksCheckTests(CheckTestCase): | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user