diff --git a/django/contrib/admin/templates/admin/pagination.html b/django/contrib/admin/templates/admin/pagination.html index aaba97fdb7..358813290c 100644 --- a/django/contrib/admin/templates/admin/pagination.html +++ b/django/contrib/admin/templates/admin/pagination.html @@ -8,5 +8,5 @@ {% endif %} {{ cl.result_count }} {% ifequal cl.result_count 1 %}{{ cl.opts.verbose_name }}{% else %}{{ cl.opts.verbose_name_plural }}{% endifequal %} {% if show_all_url %} {% trans 'Show all' %}{% endif %} -{% if cl.formset and cl.result_count %}{% endif %} +{% if cl.formset and cl.result_count %}{% endif %}
diff --git a/django/db/models/query.py b/django/db/models/query.py index a4fd172ac2..89768ef023 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -7,6 +7,8 @@ try: except NameError: from sets import Set as set # Python 2.3 fallback +from copy import deepcopy + from django.db import connections, transaction, IntegrityError, DEFAULT_DB_ALIAS from django.db.models.aggregates import Aggregate from django.db.models.fields import DateField @@ -43,6 +45,17 @@ class QuerySet(object): # PYTHON MAGIC METHODS # ######################## + def __deepcopy__(self, memo): + """ + Deep copy of a QuerySet doesn't populate the cache + """ + obj_dict = deepcopy(self.__dict__, memo) + obj_dict['_iter'] = None + + obj = self.__class__() + obj.__dict__.update(obj_dict) + return obj + def __getstate__(self): """ Allows the QuerySet to be pickled. @@ -193,7 +206,25 @@ class QuerySet(object): index_start = len(extra_select) aggregate_start = index_start + len(self.model._meta.fields) - load_fields = only_load.get(self.model) + load_fields = [] + # If only/defer clauses have been specified, + # build the list of fields that are to be loaded. + if only_load: + for field, model in self.model._meta.get_fields_with_model(): + if model is None: + model = self.model + if field == self.model._meta.pk: + # Record the index of the primary key when it is found + pk_idx = len(load_fields) + try: + if field.name in only_load[model]: + # Add a field that has been explicitly included + load_fields.append(field.name) + except KeyError: + # Model wasn't explicitly listed in the only_load table + # Therefore, we need to load all fields from this model + load_fields.append(field.name) + skip = None if load_fields and not fill_cache: # Some fields have been deferred, so we have to initialise diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 02c3c732e5..2033bee371 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -635,10 +635,10 @@ class BaseQuery(object): # models. workset = {} for model, values in seen.iteritems(): - for field, f_model in model._meta.get_fields_with_model(): + for field in model._meta.local_fields: if field in values: continue - add_to_dict(workset, f_model or model, field) + add_to_dict(workset, model, field) for model, values in must_include.iteritems(): # If we haven't included a model in workset, we don't add the # corresponding must_include fields for that model, since an @@ -657,6 +657,12 @@ class BaseQuery(object): # included any fields, we have to make sure it's mentioned # so that only the "must include" fields are pulled in. seen[model] = values + # Now ensure that every model in the inheritance chain is mentioned + # in the parent list. Again, it must be mentioned to ensure that + # only "must include" fields are pulled in. + for model in orig_opts.get_parent_list(): + if model not in seen: + seen[model] = set() for model, values in seen.iteritems(): callback(target, model, values) @@ -1619,10 +1625,14 @@ class BaseQuery(object): entry.negate() self.where.add(entry, AND) break - elif not (lookup_type == 'in' and not value) and field.null: + elif not (lookup_type == 'in' + and not hasattr(value, 'as_sql') + and not hasattr(value, '_as_sql') + and not value) and field.null: # Leaky abstraction artifact: We have to specifically # exclude the "foo__in=[]" case from this handling, because # it's short-circuited in the Where class. + # We also need to handle the case where a subquery is provided entry = self.where_class() entry.add((Constraint(alias, col, None), 'isnull', True), AND) entry.negate() diff --git a/tests/modeltests/defer/models.py b/tests/modeltests/defer/models.py index ce65065d40..96eb427811 100644 --- a/tests/modeltests/defer/models.py +++ b/tests/modeltests/defer/models.py @@ -17,6 +17,12 @@ class Primary(models.Model): def __unicode__(self): return self.name +class Child(Primary): + pass + +class BigChild(Primary): + other = models.CharField(max_length=50) + def count_delayed_fields(obj, debug=False): """ Returns the number of delayed attributes on the given model instance. @@ -33,7 +39,7 @@ def count_delayed_fields(obj, debug=False): __test__ = {"API_TEST": """ To all outward appearances, instances with deferred fields look the same as -normal instances when we examine attribut values. Therefore we test for the +normal instances when we examine attribute values. Therefore we test for the number of deferred fields on returned instances (by poking at the internals), as a way to observe what is going on. @@ -98,5 +104,89 @@ Using defer() and only() with get() is also valid. >>> Primary.objects.all() [