1
0
mirror of https://github.com/django/django.git synced 2025-07-05 10:19:20 +00:00

[gsoc2009-testing] Merging to latest trunk

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/test-improvements@10965 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Kevin Kubasik 2009-06-09 11:31:27 +00:00
parent 07558693b3
commit d55fcc9488
15 changed files with 348 additions and 44 deletions

View File

@ -8,5 +8,5 @@
{% endif %} {% endif %}
{{ cl.result_count }} {% ifequal cl.result_count 1 %}{{ cl.opts.verbose_name }}{% else %}{{ cl.opts.verbose_name_plural }}{% endifequal %} {{ cl.result_count }} {% ifequal cl.result_count 1 %}{{ cl.opts.verbose_name }}{% else %}{{ cl.opts.verbose_name_plural }}{% endifequal %}
{% if show_all_url %}&nbsp;&nbsp;<a href="{{ show_all_url }}" class="showall">{% trans 'Show all' %}</a>{% endif %} {% if show_all_url %}&nbsp;&nbsp;<a href="{{ show_all_url }}" class="showall">{% trans 'Show all' %}</a>{% endif %}
{% if cl.formset and cl.result_count %}<input type="submit" name="_save" class="default" value="Save"/>{% endif %} {% if cl.formset and cl.result_count %}<input type="submit" name="_save" class="default" value="{% trans 'Save' %}"/>{% endif %}
</p> </p>

View File

@ -35,7 +35,7 @@ class GeoWhereNode(WhereNode):
return super(WhereNode, self).add(data, connector) return super(WhereNode, self).add(data, connector)
obj, lookup_type, value = data obj, lookup_type, value = data
alias, col, field = obj.alias, obj.col, obj.field col, field = obj.col, obj.field
if not hasattr(field, "geom_type"): if not hasattr(field, "geom_type"):
# Not a geographic field, so call `WhereNode.add`. # Not a geographic field, so call `WhereNode.add`.
@ -76,7 +76,7 @@ class GeoWhereNode(WhereNode):
# the `get_geo_where_clause` to construct the appropriate # the `get_geo_where_clause` to construct the appropriate
# spatial SQL when `make_atom` is called. # spatial SQL when `make_atom` is called.
annotation = GeoAnnotation(field, value, where) annotation = GeoAnnotation(field, value, where)
return super(WhereNode, self).add(((alias, col, field.db_type()), lookup_type, annotation, params), connector) return super(WhereNode, self).add(((obj.alias, col, field.db_type()), lookup_type, annotation, params), connector)
def make_atom(self, child, qn): def make_atom(self, child, qn):
obj, lookup_type, value_annot, params = child obj, lookup_type, value_annot, params = child

View File

@ -32,3 +32,13 @@ class Parcel(models.Model):
border2 = models.PolygonField(srid=2276) border2 = models.PolygonField(srid=2276)
objects = models.GeoManager() objects = models.GeoManager()
def __unicode__(self): return self.name def __unicode__(self): return self.name
# These use the GeoManager but do not have any geographic fields.
class Author(models.Model):
name = models.CharField(max_length=100)
objects = models.GeoManager()
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, related_name='books')
objects = models.GeoManager()

View File

@ -1,10 +1,10 @@
import os, unittest import os, unittest
from django.contrib.gis.geos import * from django.contrib.gis.geos import *
from django.contrib.gis.db.backend import SpatialBackend from django.contrib.gis.db.backend import SpatialBackend
from django.contrib.gis.db.models import F, Extent, Union from django.contrib.gis.db.models import Count, Extent, F, Union
from django.contrib.gis.tests.utils import no_mysql, no_oracle, no_spatialite from django.contrib.gis.tests.utils import no_mysql, no_oracle, no_spatialite
from django.conf import settings from django.conf import settings
from models import City, Location, DirectoryEntry, Parcel from models import City, Location, DirectoryEntry, Parcel, Book, Author
cities = (('Aurora', 'TX', -97.516111, 33.058333), cities = (('Aurora', 'TX', -97.516111, 33.058333),
('Roswell', 'NM', -104.528056, 33.387222), ('Roswell', 'NM', -104.528056, 33.387222),
@ -196,8 +196,8 @@ class RelatedGeoModelTest(unittest.TestCase):
# ID values do not match their City ID values. # ID values do not match their City ID values.
loc1 = Location.objects.create(point='POINT (-95.363151 29.763374)') loc1 = Location.objects.create(point='POINT (-95.363151 29.763374)')
loc2 = Location.objects.create(point='POINT (-96.801611 32.782057)') loc2 = Location.objects.create(point='POINT (-96.801611 32.782057)')
dallas = City.objects.create(name='Dallas', location=loc2) dallas = City.objects.create(name='Dallas', state='TX', location=loc2)
houston = City.objects.create(name='Houston', location=loc1) houston = City.objects.create(name='Houston', state='TX', location=loc1)
# The expected ID values -- notice the last two location IDs # The expected ID values -- notice the last two location IDs
# are out of order. We want to make sure that the related # are out of order. We want to make sure that the related
@ -231,6 +231,32 @@ class RelatedGeoModelTest(unittest.TestCase):
q = pickle.loads(q_str) q = pickle.loads(q_str)
self.assertEqual(GeoQuery, q.__class__) self.assertEqual(GeoQuery, q.__class__)
def test12_count(self):
"Testing `Count` aggregate use with the `GeoManager`. See #11087."
# Creating a new City, 'Fort Worth', that uses the same location
# as Dallas.
dallas = City.objects.get(name='Dallas')
ftworth = City.objects.create(name='Fort Worth', state='TX', location=dallas.location)
# Count annotation should be 2 for the Dallas location now.
loc = Location.objects.annotate(num_cities=Count('city')).get(id=dallas.location.id)
self.assertEqual(2, loc.num_cities)
# Creating some data for the Book/Author non-geo models that
# use GeoManager. See #11087.
tp = Author.objects.create(name='Trevor Paglen')
Book.objects.create(title='Torture Taxi', author=tp)
Book.objects.create(title='I Could Tell You But Then You Would Have to be Destroyed by Me', author=tp)
Book.objects.create(title='Blank Spots on the Map', author=tp)
wp = Author.objects.create(name='William Patry')
Book.objects.create(title='Patry on Copyright', author=wp)
# Should only be one author (Trevor Paglen) returned by this query, and
# the annotation should have 3 for the number of books.
qs = Author.objects.annotate(num_books=Count('books')).filter(num_books__gt=1)
self.assertEqual(1, len(qs))
self.assertEqual(3, qs[0].num_books)
# TODO: Related tests for KML, GML, and distance lookups. # TODO: Related tests for KML, GML, and distance lookups.
def suite(): def suite():

View File

@ -411,29 +411,37 @@ class Model(object):
save.alters_data = True save.alters_data = True
def save_base(self, raw=False, cls=None, force_insert=False, def save_base(self, raw=False, cls=None, origin=None,
force_update=False): force_insert=False, force_update=False):
""" """
Does the heavy-lifting involved in saving. Subclasses shouldn't need to Does the heavy-lifting involved in saving. Subclasses shouldn't need to
override this method. It's separate from save() in order to hide the override this method. It's separate from save() in order to hide the
need for overrides of save() to pass around internal-only parameters need for overrides of save() to pass around internal-only parameters
('raw' and 'cls'). ('raw', 'cls', and 'origin').
""" """
assert not (force_insert and force_update) assert not (force_insert and force_update)
if not cls: if cls is None:
cls = self.__class__ cls = self.__class__
meta = self._meta meta = cls._meta
signal = True if not meta.proxy:
signals.pre_save.send(sender=self.__class__, instance=self, raw=raw) origin = cls
else: else:
meta = cls._meta meta = cls._meta
signal = False
if origin:
signals.pre_save.send(sender=origin, instance=self, raw=raw)
# If we are in a raw save, save the object exactly as presented. # If we are in a raw save, save the object exactly as presented.
# That means that we don't try to be smart about saving attributes # That means that we don't try to be smart about saving attributes
# that might have come from the parent class - we just save the # that might have come from the parent class - we just save the
# attributes we have been given to the class we have been given. # attributes we have been given to the class we have been given.
if not raw: # We also go through this process to defer the save of proxy objects
# to their actual underlying model.
if not raw or meta.proxy:
if meta.proxy:
org = cls
else:
org = None
for parent, field in meta.parents.items(): for parent, field in meta.parents.items():
# At this point, parent's primary key field may be unknown # At this point, parent's primary key field may be unknown
# (for example, from administration form which doesn't fill # (for example, from administration form which doesn't fill
@ -441,7 +449,8 @@ class Model(object):
if field and getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None: if field and getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None:
setattr(self, parent._meta.pk.attname, getattr(self, field.attname)) setattr(self, parent._meta.pk.attname, getattr(self, field.attname))
self.save_base(cls=parent) self.save_base(cls=parent, origin=org)
if field: if field:
setattr(self, field.attname, self._get_pk_val(parent._meta)) setattr(self, field.attname, self._get_pk_val(parent._meta))
if meta.proxy: if meta.proxy:
@ -492,8 +501,8 @@ class Model(object):
setattr(self, meta.pk.attname, result) setattr(self, meta.pk.attname, result)
transaction.commit_unless_managed() transaction.commit_unless_managed()
if signal: if origin:
signals.post_save.send(sender=self.__class__, instance=self, signals.post_save.send(sender=origin, instance=self,
created=(not record_exists), raw=raw) created=(not record_exists), raw=raw)
save_base.alters_data = True save_base.alters_data = True

View File

@ -132,7 +132,8 @@ class RelatedField(object):
v, field = getattr(v, v._meta.pk.name), v._meta.pk v, field = getattr(v, v._meta.pk.name), v._meta.pk
except AttributeError: except AttributeError:
pass pass
if field: if not field:
field = self.rel.get_related_field()
if lookup_type in ('range', 'in'): if lookup_type in ('range', 'in'):
v = [v] v = [v]
v = field.get_db_prep_lookup(lookup_type, v) v = field.get_db_prep_lookup(lookup_type, v)
@ -958,4 +959,3 @@ class ManyToManyField(RelatedField, Field):
# A ManyToManyField is not represented by a single column, # A ManyToManyField is not represented by a single column,
# so return None. # so return None.
return None return None

View File

@ -7,6 +7,8 @@ try:
except NameError: except NameError:
from sets import Set as set # Python 2.3 fallback from sets import Set as set # Python 2.3 fallback
from copy import deepcopy
from django.db import connection, transaction, IntegrityError from django.db import connection, transaction, IntegrityError
from django.db.models.aggregates import Aggregate from django.db.models.aggregates import Aggregate
from django.db.models.fields import DateField from django.db.models.fields import DateField
@ -40,6 +42,17 @@ class QuerySet(object):
# PYTHON MAGIC METHODS # # 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): def __getstate__(self):
""" """
Allows the QuerySet to be pickled. Allows the QuerySet to be pickled.
@ -190,7 +203,25 @@ class QuerySet(object):
index_start = len(extra_select) index_start = len(extra_select)
aggregate_start = index_start + len(self.model._meta.fields) 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 skip = None
if load_fields and not fill_cache: if load_fields and not fill_cache:
# Some fields have been deferred, so we have to initialise # Some fields have been deferred, so we have to initialise
@ -355,10 +386,11 @@ class QuerySet(object):
# Delete objects in chunks to prevent the list of related objects from # Delete objects in chunks to prevent the list of related objects from
# becoming too long. # becoming too long.
seen_objs = None
while 1: while 1:
# Collect all the objects to be deleted in this chunk, and all the # Collect all the objects to be deleted in this chunk, and all the
# objects that are related to the objects that are to be deleted. # objects that are related to the objects that are to be deleted.
seen_objs = CollectedObjects() seen_objs = CollectedObjects(seen_objs)
for object in del_query[:CHUNK_SIZE]: for object in del_query[:CHUNK_SIZE]:
object._collect_sub_objects(seen_objs) object._collect_sub_objects(seen_objs)

View File

@ -32,11 +32,21 @@ class CollectedObjects(object):
This is used for the database object deletion routines so that we can This is used for the database object deletion routines so that we can
calculate the 'leaf' objects which should be deleted first. calculate the 'leaf' objects which should be deleted first.
previously_seen is an optional argument. It must be a CollectedObjects
instance itself; any previously_seen collected object will be blocked from
being added to this instance.
""" """
def __init__(self): def __init__(self, previously_seen=None):
self.data = {} self.data = {}
self.children = {} self.children = {}
if previously_seen:
self.blocked = previously_seen.blocked
for cls, seen in previously_seen.data.items():
self.blocked.setdefault(cls, SortedDict()).update(seen)
else:
self.blocked = {}
def add(self, model, pk, obj, parent_model, nullable=False): def add(self, model, pk, obj, parent_model, nullable=False):
""" """
@ -53,6 +63,9 @@ class CollectedObjects(object):
Returns True if the item already existed in the structure and Returns True if the item already existed in the structure and
False otherwise. False otherwise.
""" """
if pk in self.blocked.get(model, {}):
return True
d = self.data.setdefault(model, SortedDict()) d = self.data.setdefault(model, SortedDict())
retval = pk in d retval = pk in d
d[pk] = obj d[pk] = obj

View File

@ -635,10 +635,10 @@ class BaseQuery(object):
# models. # models.
workset = {} workset = {}
for model, values in seen.iteritems(): 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: if field in values:
continue continue
add_to_dict(workset, f_model or model, field) add_to_dict(workset, model, field)
for model, values in must_include.iteritems(): for model, values in must_include.iteritems():
# If we haven't included a model in workset, we don't add the # If we haven't included a model in workset, we don't add the
# corresponding must_include fields for that model, since an # 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 # included any fields, we have to make sure it's mentioned
# so that only the "must include" fields are pulled in. # so that only the "must include" fields are pulled in.
seen[model] = values 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(): for model, values in seen.iteritems():
callback(target, model, values) callback(target, model, values)
@ -1619,10 +1625,14 @@ class BaseQuery(object):
entry.negate() entry.negate()
self.where.add(entry, AND) self.where.add(entry, AND)
break 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 # Leaky abstraction artifact: We have to specifically
# exclude the "foo__in=[]" case from this handling, because # exclude the "foo__in=[]" case from this handling, because
# it's short-circuited in the Where class. # 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 = self.where_class()
entry.add((Constraint(alias, col, None), 'isnull', True), AND) entry.add((Constraint(alias, col, None), 'isnull', True), AND)
entry.negate() entry.negate()

View File

@ -62,9 +62,7 @@ class BaseCoverageRunner(object):
packages, self.modules, self.excludes, self.errors = get_all_modules( packages, self.modules, self.excludes, self.errors = get_all_modules(
coverage_modules, getattr(settings, 'COVERAGE_MODULE_EXCLUDES', []), coverage_modules, getattr(settings, 'COVERAGE_MODULE_EXCLUDES', []),
getattr(settings, 'COVERAGE_PATH_EXCLUDES', [])) getattr(settings, 'COVERAGE_PATH_EXCLUDES', []))
#for mods in self.modules.keys():
# self.cov.analysis2(ModuleVars(mods, self.modules[mods]).source_file)
#coverage.analysis2(self.modules[mods])
self.cov.report(self.modules.values(), show_missing=1) self.cov.report(self.modules.values(), show_missing=1)
if self.excludes: if self.excludes:
@ -110,12 +108,10 @@ class ReportingCoverageRunner(BaseCoverageRunner):
with the results with the results
""" """
res = super(ReportingCoverageRunner, self).run_tests( *args, **kwargs) res = super(ReportingCoverageRunner, self).run_tests( *args, **kwargs)
#coverage._the_coverage.load() self.cov.html_report(self.modules.values(),
#covss = coverage.html.HtmlReporter(self.cov) directory=self.outdir,
self.cov.html_report(self.modules.values(), directory=self.outdir, ignore_errors=True, omit_prefixes='modeltests') ignore_errors=True,
#cov.report(self.modules.values(), self.outdir) omit_prefixes='modeltests')
#coverage._the_coverage.html_report(self.modules.values(), self.outdir)
print >>sys.stdout print >>sys.stdout
print >>sys.stdout, _("HTML reports were output to '%s'") %self.outdir print >>sys.stdout, _("HTML reports were output to '%s'") %self.outdir

View File

@ -9,6 +9,8 @@ this behavior by explicitly adding ``primary_key=True`` to a field.
from django.conf import settings from django.conf import settings
from django.db import models, transaction, IntegrityError from django.db import models, transaction, IntegrityError
from fields import MyAutoField
class Employee(models.Model): class Employee(models.Model):
employee_code = models.IntegerField(primary_key=True, db_column = 'code') employee_code = models.IntegerField(primary_key=True, db_column = 'code')
first_name = models.CharField(max_length=20) first_name = models.CharField(max_length=20)
@ -28,6 +30,16 @@ class Business(models.Model):
def __unicode__(self): def __unicode__(self):
return self.name return self.name
class Bar(models.Model):
id = MyAutoField(primary_key=True, db_index=True)
def __unicode__(self):
return repr(self.pk)
class Foo(models.Model):
bar = models.ForeignKey(Bar)
__test__ = {'API_TESTS':""" __test__ = {'API_TESTS':"""
>>> dan = Employee(employee_code=123, first_name='Dan', last_name='Jones') >>> dan = Employee(employee_code=123, first_name='Dan', last_name='Jones')
>>> dan.save() >>> dan.save()
@ -121,6 +133,21 @@ DoesNotExist: Employee matching query does not exist.
... print "Fail with %s" % type(e) ... print "Fail with %s" % type(e)
Pass Pass
# Regression for #10785 -- Custom fields can be used for primary keys.
>>> new_bar = Bar.objects.create()
>>> new_foo = Foo.objects.create(bar=new_bar)
>>> f = Foo.objects.get(bar=new_bar.pk)
>>> f == new_foo
True
>>> f.bar == new_bar
True
>>> f = Foo.objects.get(bar=new_bar)
>>> f == new_foo
True
>>> f.bar == new_bar
True
"""} """}
# SQLite lets objects be saved with an empty primary key, even though an # SQLite lets objects be saved with an empty primary key, even though an

View File

@ -17,6 +17,12 @@ class Primary(models.Model):
def __unicode__(self): def __unicode__(self):
return self.name return self.name
class Child(Primary):
pass
class BigChild(Primary):
other = models.CharField(max_length=50)
def count_delayed_fields(obj, debug=False): def count_delayed_fields(obj, debug=False):
""" """
Returns the number of delayed attributes on the given model instance. 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": """ __test__ = {"API_TEST": """
To all outward appearances, instances with deferred fields look the same as 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), number of deferred fields on returned instances (by poking at the internals),
as a way to observe what is going on. 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() >>> Primary.objects.all()
[<Primary: a new name>] [<Primary: a new name>]
# Regression for #10572 - A subclass with no extra fields can defer fields from the base class
>>> _ = Child.objects.create(name="c1", value="foo", related=s1)
# You can defer a field on a baseclass when the subclass has no fields
>>> obj = Child.objects.defer("value").get(name="c1")
>>> count_delayed_fields(obj)
1
>>> obj.name
u"c1"
>>> obj.value
u"foo"
>>> obj.name = "c2"
>>> obj.save()
# You can retrive a single column on a base class with no fields
>>> obj = Child.objects.only("name").get(name="c2")
>>> count_delayed_fields(obj)
3
>>> obj.name
u"c2"
>>> obj.value
u"foo"
>>> obj.name = "cc"
>>> obj.save()
>>> _ = BigChild.objects.create(name="b1", value="foo", related=s1, other="bar")
# You can defer a field on a baseclass
>>> obj = BigChild.objects.defer("value").get(name="b1")
>>> count_delayed_fields(obj)
1
>>> obj.name
u"b1"
>>> obj.value
u"foo"
>>> obj.other
u"bar"
>>> obj.name = "b2"
>>> obj.save()
# You can defer a field on a subclass
>>> obj = BigChild.objects.defer("other").get(name="b2")
>>> count_delayed_fields(obj)
1
>>> obj.name
u"b2"
>>> obj.value
u"foo"
>>> obj.other
u"bar"
>>> obj.name = "b3"
>>> obj.save()
# You can retrieve a single field on a baseclass
>>> obj = BigChild.objects.only("name").get(name="b3")
>>> count_delayed_fields(obj)
4
>>> obj.name
u"b3"
>>> obj.value
u"foo"
>>> obj.other
u"bar"
>>> obj.name = "b4"
>>> obj.save()
# You can retrieve a single field on a baseclass
>>> obj = BigChild.objects.only("other").get(name="b4")
>>> count_delayed_fields(obj)
4
>>> obj.name
u"b4"
>>> obj.value
u"foo"
>>> obj.other
u"bar"
>>> obj.name = "bb"
>>> obj.save()
# Finally, we need to flush the app cache for the defer module.
# Using only/defer creates some artifical entries in the app cache
# that messes up later tests. Purge all entries, just to be sure.
>>> from django.db.models.loading import cache
>>> cache.app_models['defer'] = {}
"""} """}

View File

@ -259,6 +259,40 @@ FieldError: Proxy model 'NoNewFields' contains model fields.
>>> OtherPerson._default_manager.all() >>> OtherPerson._default_manager.all()
[<OtherPerson: barney>, <OtherPerson: wilma>] [<OtherPerson: barney>, <OtherPerson: wilma>]
# Test save signals for proxy models
>>> from django.db.models import signals
>>> def make_handler(model, event):
... def _handler(*args, **kwargs):
... print u"%s %s save" % (model, event)
... return _handler
>>> h1 = make_handler('MyPerson', 'pre')
>>> h2 = make_handler('MyPerson', 'post')
>>> h3 = make_handler('Person', 'pre')
>>> h4 = make_handler('Person', 'post')
>>> signals.pre_save.connect(h1, sender=MyPerson)
>>> signals.post_save.connect(h2, sender=MyPerson)
>>> signals.pre_save.connect(h3, sender=Person)
>>> signals.post_save.connect(h4, sender=Person)
>>> dino = MyPerson.objects.create(name=u"dino")
MyPerson pre save
MyPerson post save
# Test save signals for proxy proxy models
>>> h5 = make_handler('MyPersonProxy', 'pre')
>>> h6 = make_handler('MyPersonProxy', 'post')
>>> signals.pre_save.connect(h5, sender=MyPersonProxy)
>>> signals.post_save.connect(h6, sender=MyPersonProxy)
>>> dino = MyPersonProxy.objects.create(name=u"pebbles")
MyPersonProxy pre save
MyPersonProxy post save
>>> signals.pre_save.disconnect(h1, sender=MyPerson)
>>> signals.post_save.disconnect(h2, sender=MyPerson)
>>> signals.pre_save.disconnect(h3, sender=Person)
>>> signals.post_save.disconnect(h4, sender=Person)
>>> signals.pre_save.disconnect(h5, sender=MyPersonProxy)
>>> signals.post_save.disconnect(h6, sender=MyPersonProxy)
# A proxy has the same content type as the model it is proxying for (at the # A proxy has the same content type as the model it is proxying for (at the
# storage level, it is meant to be essentially indistinguishable). # storage level, it is meant to be essentially indistinguishable).
>>> ctype = ContentType.objects.get_for_model >>> ctype = ContentType.objects.get_for_model
@ -266,7 +300,7 @@ FieldError: Proxy model 'NoNewFields' contains model fields.
True True
>>> MyPersonProxy.objects.all() >>> MyPersonProxy.objects.all()
[<MyPersonProxy: barney>, <MyPersonProxy: fred>] [<MyPersonProxy: barney>, <MyPersonProxy: dino>, <MyPersonProxy: fred>, <MyPersonProxy: pebbles>]
>>> u = User.objects.create(name='Bruce') >>> u = User.objects.create(name='Bruce')
>>> User.objects.all() >>> User.objects.all()
@ -327,4 +361,11 @@ True
# Select related + filter on a related proxy of proxy field # Select related + filter on a related proxy of proxy field
>>> ProxyImprovement.objects.select_related().get(associated_bug__summary__icontains='fix') >>> ProxyImprovement.objects.select_related().get(associated_bug__summary__icontains='fix')
<ProxyImprovement: ProxyImprovement:improve that> <ProxyImprovement: ProxyImprovement:improve that>
Proxy models can be loaded from fixtures (Regression for #11194)
>>> from django.core import management
>>> management.call_command('loaddata', 'mypeople.json', verbosity=0)
>>> MyPerson.objects.get(pk=100)
<MyPerson: Elvis Presley>
"""} """}

View File

@ -84,7 +84,8 @@ Some further checks for select_related() and inherited model behaviour
(regression for #10710). (regression for #10710).
>>> c1 = Child.objects.create(name="c1", value=42) >>> c1 = Child.objects.create(name="c1", value=42)
>>> obj = Leaf.objects.create(name="l1", child=c1) >>> c2 = Child.objects.create(name="c2", value=37)
>>> obj = Leaf.objects.create(name="l1", child=c1, second_child=c2)
>>> obj = Leaf.objects.only("name", "child").select_related()[0] >>> obj = Leaf.objects.only("name", "child").select_related()[0]
>>> obj.child.name >>> obj.child.name
@ -101,5 +102,24 @@ types as their non-deferred versions (bug #10738).
>>> c1 is c2 is c3 >>> c1 is c2 is c3
True True
# Regression for #10733 - only() can be used on a model with two foreign keys.
>>> results = Leaf.objects.all().only('name', 'child', 'second_child').select_related()
>>> results[0].child.name
u'c1'
>>> results[0].second_child.name
u'c2'
>>> results = Leaf.objects.all().only('name', 'child', 'second_child', 'child__name', 'second_child__name').select_related()
>>> results[0].child.name
u'c1'
>>> results[0].second_child.name
u'c2'
# Finally, we need to flush the app cache for the defer module.
# Using only/defer creates some artifical entries in the app cache
# that messes up later tests. Purge all entries, just to be sure.
>>> from django.db.models.loading import cache
>>> cache.app_models['defer_regress'] = {}
""" """
} }

View File

@ -1143,6 +1143,36 @@ True
>>> r.save() >>> r.save()
>>> Ranking.objects.all() >>> Ranking.objects.all()
[<Ranking: 3: a1>, <Ranking: 2: a2>, <Ranking: 1: a3>] [<Ranking: 3: a1>, <Ranking: 2: a2>, <Ranking: 1: a3>]
# Regression test for #10742:
# Queries used in an __in clause don't execute subqueries
>>> subq = Author.objects.filter(num__lt=3000)
>>> qs = Author.objects.filter(pk__in=subq)
>>> list(qs)
[<Author: a1>, <Author: a2>]
# The subquery result cache should not be populated
>>> subq._result_cache is None
True
>>> subq = Author.objects.filter(num__lt=3000)
>>> qs = Author.objects.exclude(pk__in=subq)
>>> list(qs)
[<Author: a3>, <Author: a4>]
# The subquery result cache should not be populated
>>> subq._result_cache is None
True
>>> subq = Author.objects.filter(num__lt=3000)
>>> list(Author.objects.filter(Q(pk__in=subq) & Q(name='a1')))
[<Author: a1>]
# The subquery result cache should not be populated
>>> subq._result_cache is None
True
"""} """}
# In Python 2.3 and the Python 2.6 beta releases, exceptions raised in __len__ # In Python 2.3 and the Python 2.6 beta releases, exceptions raised in __len__