From 014373b459e0925ad757a56d9b4f049bc4f776fc Mon Sep 17 00:00:00 2001 From: Malcolm Tredinnick Date: Wed, 27 Feb 2008 00:44:44 +0000 Subject: [PATCH] queryset-refactor: Fixed #6664. Calling list() no longer swallows field errors. This is slightly backwards incompatible with previous behaviour if you were doing Tricky Stuff(tm) -- the exception type has changed if you try to create a bad queryset. git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7163 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/core/exceptions.py | 5 +++++ django/db/models/base.py | 4 ++-- django/db/models/sql/query.py | 15 ++++++++------- tests/modeltests/custom_columns/models.py | 2 +- tests/modeltests/field_subclassing/models.py | 5 +++-- tests/modeltests/lookup/models.py | 4 ++-- tests/modeltests/many_to_one/models.py | 4 ++-- tests/modeltests/model_inheritance/models.py | 2 +- tests/modeltests/reverse_lookup/models.py | 2 +- tests/regressiontests/null_queries/models.py | 2 +- tests/regressiontests/queries/models.py | 2 +- 11 files changed, 27 insertions(+), 20 deletions(-) diff --git a/django/core/exceptions.py b/django/core/exceptions.py index d9fc326cf2..e5df8caca8 100644 --- a/django/core/exceptions.py +++ b/django/core/exceptions.py @@ -27,3 +27,8 @@ class MiddlewareNotUsed(Exception): class ImproperlyConfigured(Exception): "Django is somehow improperly configured" pass + +class FieldError(Exception): + """Some kind of problem with a model field.""" + pass + diff --git a/django/db/models/base.py b/django/db/models/base.py index dc2b4ca0fe..67d1ec974c 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -6,7 +6,7 @@ from itertools import izip import django.db.models.manipulators import django.db.models.manager from django.core import validators -from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned +from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField from django.db.models.query import delete_objects, Q @@ -101,7 +101,7 @@ class ModelBase(type): names = [f.name for f in new_class._meta.local_fields + new_class._meta.many_to_many] for field in base._meta.local_fields: if field.name in names: - raise TypeError('Local field %r in class %r clashes with field of similar name from abstract base class %r' + raise FieldError('Local field %r in class %r clashes with field of similar name from abstract base class %r' % (field.name, name, base.__name__)) new_class.add_to_class(field.name, field) diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index c9a0fc7f1f..0abbbbfe79 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -19,6 +19,7 @@ from django.db.models.sql.where import WhereNode, EverythingNode, AND, OR from django.db.models.sql.datastructures import Count, Date from django.db.models.fields import FieldDoesNotExist, Field, related from django.contrib.contenttypes import generic +from django.core.exceptions import FieldError from datastructures import EmptyResultSet try: @@ -548,7 +549,7 @@ class Query(object): already_seen = {} join_tuple = tuple([tuple(j) for j in joins]) if join_tuple in already_seen: - raise TypeError('Infinite loop caused by ordering.') + raise FieldError('Infinite loop caused by ordering.') already_seen[join_tuple] = True results = [] @@ -735,7 +736,7 @@ class Query(object): arg, value = filter_expr parts = arg.split(LOOKUP_SEP) if not parts: - raise TypeError("Cannot parse keyword query %r" % arg) + raise FieldError("Cannot parse keyword query %r" % arg) # Work out the lookup type and remove it from 'parts', if necessary. if len(parts) == 1 or parts[-1] not in self.query_terms: @@ -867,7 +868,7 @@ class Query(object): field, model, direct, m2m = opts.get_field_by_name(name) except FieldDoesNotExist: names = opts.get_all_field_names() - raise TypeError("Cannot resolve keyword %r into field. " + raise FieldError("Cannot resolve keyword %r into field. " "Choices are: %s" % (name, ", ".join(names))) if model: # The field lives on a base class of the current model. @@ -972,7 +973,7 @@ class Query(object): joins.append([alias]) if pos != len(names) - 1: - raise TypeError("Join on field %r not permitted." % name) + raise FieldError("Join on field %r not permitted." % name) return field, target, opts, joins @@ -1033,7 +1034,7 @@ class Query(object): if not ORDER_PATTERN.match(item): errors.append(item) if errors: - raise TypeError('Invalid order_by arguments: %s' % errors) + raise FieldError('Invalid order_by arguments: %s' % errors) if ordering: self.order_by.extend(ordering) else: @@ -1228,7 +1229,7 @@ class UpdateQuery(Query): for alias in alias_list: if self.alias_map[alias][ALIAS_REFCOUNT]: if table: - raise TypeError('Updates can only access a single database table at a time.') + raise FieldError('Updates can only access a single database table at a time.') table = alias else: table = self.tables[0] @@ -1271,7 +1272,7 @@ class UpdateQuery(Query): field, model, direct, m2m = self.model._meta.get_field_by_name(name) if not direct or m2m: # Can only update non-relation fields and foreign keys. - raise TypeError('Cannot update model field %r (only non-relations and foreign keys permitted).' % field) + raise fieldError('Cannot update model field %r (only non-relations and foreign keys permitted).' % field) if field.rel and isinstance(val, Model): val = val.pk self.values.append((field.column, val)) diff --git a/tests/modeltests/custom_columns/models.py b/tests/modeltests/custom_columns/models.py index 302a9aee27..af405b7bf3 100644 --- a/tests/modeltests/custom_columns/models.py +++ b/tests/modeltests/custom_columns/models.py @@ -71,7 +71,7 @@ __test__ = {'API_TESTS':""" >>> Author.objects.filter(firstname__exact='John') Traceback (most recent call last): ... -TypeError: Cannot resolve keyword 'firstname' into field. Choices are: article, first_name, id, last_name +FieldError: Cannot resolve keyword 'firstname' into field. Choices are: article, first_name, id, last_name >>> a = Author.objects.get(last_name__exact='Smith') >>> a.first_name diff --git a/tests/modeltests/field_subclassing/models.py b/tests/modeltests/field_subclassing/models.py index 97804f5cd5..baf07a072f 100644 --- a/tests/modeltests/field_subclassing/models.py +++ b/tests/modeltests/field_subclassing/models.py @@ -5,6 +5,7 @@ Tests for field subclassing. from django.db import models from django.utils.encoding import force_unicode from django.core import serializers +from django.core.exceptions import FieldError class Small(object): """ @@ -50,7 +51,7 @@ class SmallField(models.Field): return [force_unicode(v) for v in value] if lookup_type == 'isnull': return [] - raise TypeError('Invalid lookup type: %r' % lookup_type) + raise FieldError('Invalid lookup type: %r' % lookup_type) def flatten_data(self, follow, obj=None): return {self.attname: force_unicode(self._get_val_from_obj(obj))} @@ -94,7 +95,7 @@ True >>> MyModel.objects.filter(data__lt=s) Traceback (most recent call last): ... -TypeError: Invalid lookup type: 'lt' +FieldError: Invalid lookup type: 'lt' # Serialization works, too. >>> stream = serializers.serialize("json", MyModel.objects.all()) diff --git a/tests/modeltests/lookup/models.py b/tests/modeltests/lookup/models.py index d21ffba19d..a87ce523a2 100644 --- a/tests/modeltests/lookup/models.py +++ b/tests/modeltests/lookup/models.py @@ -270,12 +270,12 @@ DoesNotExist: Article matching query does not exist. >>> Article.objects.filter(pub_date_year='2005').count() Traceback (most recent call last): ... -TypeError: Cannot resolve keyword 'pub_date_year' into field. Choices are: headline, id, pub_date +FieldError: Cannot resolve keyword 'pub_date_year' into field. Choices are: headline, id, pub_date >>> Article.objects.filter(headline__starts='Article') Traceback (most recent call last): ... -TypeError: Join on field 'headline' not permitted. +FieldError: Join on field 'headline' not permitted. # Create some articles with a bit more interesting headlines for testing field lookups: >>> now = datetime.now() diff --git a/tests/modeltests/many_to_one/models.py b/tests/modeltests/many_to_one/models.py index 2231572b45..c55b0e3e7c 100644 --- a/tests/modeltests/many_to_one/models.py +++ b/tests/modeltests/many_to_one/models.py @@ -179,13 +179,13 @@ False >>> Article.objects.filter(reporter_id__exact=1) Traceback (most recent call last): ... -TypeError: Cannot resolve keyword 'reporter_id' into field. Choices are: headline, id, pub_date, reporter +FieldError: Cannot resolve keyword 'reporter_id' into field. Choices are: headline, id, pub_date, reporter # You need to specify a comparison clause >>> Article.objects.filter(reporter_id=1) Traceback (most recent call last): ... -TypeError: Cannot resolve keyword 'reporter_id' into field. Choices are: headline, id, pub_date, reporter +FieldError: Cannot resolve keyword 'reporter_id' into field. Choices are: headline, id, pub_date, reporter # You can also instantiate an Article by passing # the Reporter's ID instead of a Reporter object. diff --git a/tests/modeltests/model_inheritance/models.py b/tests/modeltests/model_inheritance/models.py index 95ff4b55f2..fb4aad4016 100644 --- a/tests/modeltests/model_inheritance/models.py +++ b/tests/modeltests/model_inheritance/models.py @@ -148,7 +148,7 @@ Test constructor for Restaurant. >>> Restaurant.objects.filter(supplier__name='foo') Traceback (most recent call last): ... -TypeError: Cannot resolve keyword 'supplier' into field. Choices are: address, id, italianrestaurant, lot, name, place_ptr, provider, rating, serves_hot_dogs, serves_pizza +FieldError: Cannot resolve keyword 'supplier' into field. Choices are: address, id, italianrestaurant, lot, name, place_ptr, provider, rating, serves_hot_dogs, serves_pizza # Parent fields can be used directly in filters on the child model. >>> Restaurant.objects.filter(name='Demon Dogs') diff --git a/tests/modeltests/reverse_lookup/models.py b/tests/modeltests/reverse_lookup/models.py index 5d722e54bf..ef385b4b18 100644 --- a/tests/modeltests/reverse_lookup/models.py +++ b/tests/modeltests/reverse_lookup/models.py @@ -55,5 +55,5 @@ __test__ = {'API_TESTS':""" >>> Poll.objects.get(choice__name__exact="This is the answer") Traceback (most recent call last): ... -TypeError: Cannot resolve keyword 'choice' into field. Choices are: creator, id, poll_choice, question, related_choice +FieldError: Cannot resolve keyword 'choice' into field. Choices are: creator, id, poll_choice, question, related_choice """} diff --git a/tests/regressiontests/null_queries/models.py b/tests/regressiontests/null_queries/models.py index d6993b8be5..df04f14eba 100644 --- a/tests/regressiontests/null_queries/models.py +++ b/tests/regressiontests/null_queries/models.py @@ -37,7 +37,7 @@ Excluding the previous result returns everything. >>> Choice.objects.filter(foo__exact=None) Traceback (most recent call last): ... -TypeError: Cannot resolve keyword 'foo' into field. Choices are: choice, id, poll +FieldError: Cannot resolve keyword 'foo' into field. Choices are: choice, id, poll # Can't use None on anything other than __exact >>> Choice.objects.filter(id__gt=None) diff --git a/tests/regressiontests/queries/models.py b/tests/regressiontests/queries/models.py index 3908d51f66..c95026a54e 100644 --- a/tests/regressiontests/queries/models.py +++ b/tests/regressiontests/queries/models.py @@ -394,7 +394,7 @@ Bug #2076 >>> LoopX.objects.all() Traceback (most recent call last): ... -TypeError: Infinite loop caused by ordering. +FieldError: Infinite loop caused by ordering. # If the remote model does not have a default ordering, we order by its 'id' # field.