Ticket #28490: modeladmin_none_descriptor_error.diff

File modeladmin_none_descriptor_error.diff, 5.3 KB (added by Hunter Richards, 7 years ago)
  • django/contrib/admin/checks.py

    diff --git a/django/contrib/admin/checks.py b/django/contrib/admin/checks.py
    index 830a190..6c4beda 100644
    a b class ModelAdminChecks(BaseModelAdminChecks):  
    587587        elif hasattr(obj, item):
    588588            return []
    589589        elif hasattr(model, item):
    590             # getattr(model, item) could be an X_RelatedObjectsDescriptor
     590            # Because hasattr(model, item) succeeded, we know that
     591            # getattr(model, item) will succeed, and can thus be displayed in
     592            # the listview, since the former is implemented by calling the
     593            # latter and testing for errors.  In this branch, we therefore only
     594            # need to test for ManyToManyFields.
    591595            try:
    592596                field = model._meta.get_field(item)
    593597            except FieldDoesNotExist:
    594                 try:
    595                     field = getattr(model, item)
    596                 except AttributeError:
    597                     field = None
    598 
    599             if field is None:
    600                 return [
    601                     checks.Error(
    602                         "The value of '%s' refers to '%s', which is not a "
    603                         "callable, an attribute of '%s', or an attribute or method on '%s.%s'." % (
    604                             label, item, obj.__class__.__name__, model._meta.app_label, model._meta.object_name
    605                         ),
    606                         obj=obj.__class__,
    607                         id='admin.E108',
    608                     )
    609                 ]
    610             elif isinstance(field, models.ManyToManyField):
    611                 return [
    612                     checks.Error(
    613                         "The value of '%s' must not be a ManyToManyField." % label,
    614                         obj=obj.__class__,
    615                         id='admin.E109',
    616                     )
    617                 ]
    618             else:
    619598                return []
    620         else:
    621             try:
    622                 model._meta.get_field(item)
    623             except FieldDoesNotExist:
    624                 return [
    625                     # This is a deliberate repeat of E108; there's more than one path
    626                     # required to test this condition.
    627                     checks.Error(
    628                         "The value of '%s' refers to '%s', which is not a callable, "
    629                         "an attribute of '%s', or an attribute or method on '%s.%s'." % (
    630                             label, item, obj.__class__.__name__, model._meta.app_label, model._meta.object_name
    631                         ),
    632                         obj=obj.__class__,
    633                         id='admin.E108',
    634                     )
    635                 ]
    636599            else:
    637                 return []
     600                if isinstance(field, models.ManyToManyField):
     601                    return [
     602                        checks.Error(
     603                            "The value of '%s' must not be a ManyToManyField." % label,
     604                            obj=obj.__class__,
     605                            id='admin.E109',
     606                        )
     607                    ]
     608                else:
     609                    return []
     610        else:
     611            return [
     612                checks.Error(
     613                    "The value of '%s' refers to '%s', which is not a callable, "
     614                    "an attribute of '%s', or an attribute or method on '%s.%s'." % (
     615                        label, item, obj.__class__.__name__, model._meta.app_label, model._meta.object_name
     616                    ),
     617                    obj=obj.__class__,
     618                    id='admin.E108',
     619                )
     620            ]
    638621
    639622    def _check_list_display_links(self, obj):
    640623        """ Check that list_display_links is a unique subset of list_display.
  • tests/modeladmin/models.py

    diff --git a/tests/modeladmin/models.py b/tests/modeladmin/models.py
    index 861a2db..845b119 100644
    a b class Concert(models.Model):  
    2525    ), blank=True)
    2626
    2727
     28class DescriptorThatReturnsNone(object):
     29    def __get__(self, obj, objtype):
     30        # In general, Descriptors should return `self` when `obj is None`, but
     31        # let's test the case where they don't.
     32        return None
     33
     34    def __set__(self, obj, value):
     35        pass
     36
     37
    2838class ValidationTestModel(models.Model):
    2939    name = models.CharField(max_length=100)
    3040    slug = models.SlugField()
    class ValidationTestModel(models.Model):  
    3646    best_friend = models.OneToOneField(User, models.CASCADE, related_name='best_friend')
    3747    # This field is intentionally 2 characters long (#16080).
    3848    no = models.IntegerField(verbose_name="Number", blank=True, null=True)
     49    none_descriptor = DescriptorThatReturnsNone()
    3950
    4051    def decade_published_in(self):
    4152        return self.pub_date.strftime('%Y')[:3] + "0's"
  • tests/modeladmin/test_checks.py

    diff --git a/tests/modeladmin/test_checks.py b/tests/modeladmin/test_checks.py
    index acca6b1..5d3528a 100644
    a b class ListDisplayTests(CheckTestCase):  
    479479        class TestModelAdmin(ModelAdmin):
    480480            def a_method(self, obj):
    481481                pass
    482             list_display = ('name', 'decade_published_in', 'a_method', a_callable)
     482            list_display = ('name', 'decade_published_in', 'a_method', a_callable, 'none_descriptor')
    483483
    484484        self.assertIsValid(TestModelAdmin, ValidationTestModel)
    485485
Back to Top