Fixed #29135 -- Prevented get_object/list_or_404() from hiding AttributeError raised by QuerySet filtering.

This commit is contained in:
dizballanze 2018-02-17 11:27:46 +03:00 committed by Tim Graham
parent 9b1125bfc7
commit eb002e7892
3 changed files with 21 additions and 6 deletions

View File

@ -83,14 +83,14 @@ def get_object_or_404(klass, *args, **kwargs):
one object is found. one object is found.
""" """
queryset = _get_queryset(klass) queryset = _get_queryset(klass)
try: if not hasattr(queryset, 'get'):
return queryset.get(*args, **kwargs)
except AttributeError:
klass__name = klass.__name__ if isinstance(klass, type) else klass.__class__.__name__ klass__name = klass.__name__ if isinstance(klass, type) else klass.__class__.__name__
raise ValueError( raise ValueError(
"First argument to get_object_or_404() must be a Model, Manager, " "First argument to get_object_or_404() must be a Model, Manager, "
"or QuerySet, not '%s'." % klass__name "or QuerySet, not '%s'." % klass__name
) )
try:
return queryset.get(*args, **kwargs)
except queryset.model.DoesNotExist: except queryset.model.DoesNotExist:
raise Http404('No %s matches the given query.' % queryset.model._meta.object_name) raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
@ -104,14 +104,13 @@ def get_list_or_404(klass, *args, **kwargs):
arguments and keyword arguments are used in the filter() query. arguments and keyword arguments are used in the filter() query.
""" """
queryset = _get_queryset(klass) queryset = _get_queryset(klass)
try: if not hasattr(queryset, 'filter'):
obj_list = list(queryset.filter(*args, **kwargs))
except AttributeError:
klass__name = klass.__name__ if isinstance(klass, type) else klass.__class__.__name__ klass__name = klass.__name__ if isinstance(klass, type) else klass.__class__.__name__
raise ValueError( raise ValueError(
"First argument to get_list_or_404() must be a Model, Manager, or " "First argument to get_list_or_404() must be a Model, Manager, or "
"QuerySet, not '%s'." % klass__name "QuerySet, not '%s'." % klass__name
) )
obj_list = list(queryset.filter(*args, **kwargs))
if not obj_list: if not obj_list:
raise Http404('No %s matches the given query.' % queryset.model._meta.object_name) raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
return obj_list return obj_list

View File

@ -25,11 +25,17 @@ class ArticleManager(models.Manager):
return super().get_queryset().filter(authors__name__icontains='sir') return super().get_queryset().filter(authors__name__icontains='sir')
class AttributeErrorManager(models.Manager):
def get_queryset(self):
raise AttributeError('AttributeErrorManager')
class Article(models.Model): class Article(models.Model):
authors = models.ManyToManyField(Author) authors = models.ManyToManyField(Author)
title = models.CharField(max_length=50) title = models.CharField(max_length=50)
objects = models.Manager() objects = models.Manager()
by_a_sir = ArticleManager() by_a_sir = ArticleManager()
attribute_error_objects = AttributeErrorManager()
def __str__(self): def __str__(self):
return self.title return self.title

View File

@ -94,3 +94,13 @@ class GetObjectOr404Tests(TestCase):
msg = "First argument to get_list_or_404() must be a Model, Manager, or QuerySet, not 'list'." msg = "First argument to get_list_or_404() must be a Model, Manager, or QuerySet, not 'list'."
with self.assertRaisesMessage(ValueError, msg): with self.assertRaisesMessage(ValueError, msg):
get_list_or_404([Article], title__icontains="Run") get_list_or_404([Article], title__icontains="Run")
def test_get_object_or_404_queryset_attribute_error(self):
"""AttributeError raised by QuerySet.get() isn't hidden."""
with self.assertRaisesMessage(AttributeError, 'AttributeErrorManager'):
get_object_or_404(Article.attribute_error_objects, id=42)
def test_get_list_or_404_queryset_attribute_error(self):
"""AttributeError raised by QuerySet.filter() isn't hidden."""
with self.assertRaisesMessage(AttributeError, 'AttributeErrorManager'):
get_list_or_404(Article.attribute_error_objects, title__icontains='Run')