summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorLoic Bistuer <loic.bistuer@gmail.com>2014-09-23 23:29:17 +0700
committerLoic Bistuer <loic.bistuer@gmail.com>2014-09-24 02:03:12 +0700
commit1fd6e13bf2db025dc93482184439cc1a900db276 (patch)
tree06bfcb423a572a081df7b9f67bff6411f5cc598b /tests
parent2a1bdf5ced3f22c2ff687afab45d811cfa555e74 (diff)
Consolidated some many_to_one tests.
Diffstat (limited to 'tests')
-rw-r--r--tests/many_to_one/models.py68
-rw-r--r--tests/many_to_one/tests.py168
-rw-r--r--tests/many_to_one_null/models.py8
-rw-r--r--tests/many_to_one_null/tests.py9
-rw-r--r--tests/many_to_one_regress/__init__.py0
-rw-r--r--tests/many_to_one_regress/models.py69
-rw-r--r--tests/many_to_one_regress/tests.py137
-rw-r--r--tests/reverse_single_related/__init__.py0
-rw-r--r--tests/reverse_single_related/models.py15
-rw-r--r--tests/reverse_single_related/tests.py46
10 files changed, 244 insertions, 276 deletions
diff --git a/tests/many_to_one/models.py b/tests/many_to_one/models.py
index 8c50ffe53b..0b45ff0ef3 100644
--- a/tests/many_to_one/models.py
+++ b/tests/many_to_one/models.py
@@ -30,3 +30,71 @@ class Article(models.Model):
class Meta:
ordering = ('headline',)
+
+
+# If ticket #1578 ever slips back in, these models will not be able to be
+# created (the field names being lower-cased versions of their opposite
+# classes is important here).
+class First(models.Model):
+ second = models.IntegerField()
+
+
+class Second(models.Model):
+ first = models.ForeignKey(First, related_name='the_first')
+
+
+# Protect against repetition of #1839, #2415 and #2536.
+class Third(models.Model):
+ name = models.CharField(max_length=20)
+ third = models.ForeignKey('self', null=True, related_name='child_set')
+
+
+class Parent(models.Model):
+ name = models.CharField(max_length=20, unique=True)
+ bestchild = models.ForeignKey('Child', null=True, related_name='favored_by')
+
+
+class Child(models.Model):
+ name = models.CharField(max_length=20)
+ parent = models.ForeignKey(Parent)
+
+
+class ToFieldChild(models.Model):
+ parent = models.ForeignKey(Parent, to_field='name')
+
+
+# Multiple paths to the same model (#7110, #7125)
+@python_2_unicode_compatible
+class Category(models.Model):
+ name = models.CharField(max_length=20)
+
+ def __str__(self):
+ return self.name
+
+
+class Record(models.Model):
+ category = models.ForeignKey(Category)
+
+
+@python_2_unicode_compatible
+class Relation(models.Model):
+ left = models.ForeignKey(Record, related_name='left_set')
+ right = models.ForeignKey(Record, related_name='right_set')
+
+ def __str__(self):
+ return "%s - %s" % (self.left.category.name, self.right.category.name)
+
+
+# Test related objects visibility.
+class SchoolManager(models.Manager):
+ def get_queryset(self):
+ return super(SchoolManager, self).get_queryset().filter(is_public=True)
+
+
+class School(models.Model):
+ is_public = models.BooleanField(default=False)
+ objects = SchoolManager()
+
+
+class Student(models.Model):
+ school = models.ForeignKey(School)
diff --git a/tests/many_to_one/tests.py b/tests/many_to_one/tests.py
index c02c247b29..f5949bd22e 100644
--- a/tests/many_to_one/tests.py
+++ b/tests/many_to_one/tests.py
@@ -2,12 +2,13 @@ from copy import deepcopy
import datetime
from django.core.exceptions import MultipleObjectsReturned, FieldError
-from django.db import transaction
+from django.db import models, transaction
from django.test import TestCase
from django.utils import six
from django.utils.translation import ugettext_lazy
-from .models import Article, Reporter
+from .models import (Article, Reporter, First, Third, Parent, Child,
+ ToFieldChild, Category, Record, Relation, School, Student)
class ManyToOneTests(TestCase):
@@ -374,12 +375,6 @@ class ManyToOneTests(TestCase):
self.assertQuerysetEqual(Reporter.objects.all(), [])
self.assertQuerysetEqual(Article.objects.all(), [])
- def test_regression_12876(self):
- # Regression for #12876 -- Model methods that include queries that
- # recursive don't cause recursion depth problems under deepcopy.
- self.r.cached_query = Article.objects.filter(reporter=self.r)
- self.assertEqual(repr(deepcopy(self.r)), "<Reporter: John Smith>")
-
def test_explicit_fk(self):
# Create a new Article with get_or_create using an explicit value
# for a ForeignKey.
@@ -411,6 +406,12 @@ class ManyToOneTests(TestCase):
repr(Article.objects.get(reporter_id=self.r2.id,
pub_date=datetime.date(2011, 5, 7))))
+ def test_deepcopy_and_circular_references(self):
+ # Regression for #12876 -- Model methods that include queries that
+ # recursive don't cause recursion depth problems under deepcopy.
+ self.r.cached_query = Article.objects.filter(reporter=self.r)
+ self.assertEqual(repr(deepcopy(self.r)), "<Reporter: John Smith>")
+
def test_manager_class_caching(self):
r1 = Reporter.objects.create(first_name='Mike')
r2 = Reporter.objects.create(first_name='John')
@@ -443,3 +444,154 @@ class ManyToOneTests(TestCase):
expected_message % ', '.join(['EXTRA'] + Article._meta.get_all_field_names()),
Article.objects.extra(select={'EXTRA': 'EXTRA_SELECT'}).values_list,
'notafield')
+
+ def test_fk_assignment_and_related_object_cache(self):
+ # Tests of ForeignKey assignment and the related-object cache (see #6886).
+
+ p = Parent.objects.create(name="Parent")
+ c = Child.objects.create(name="Child", parent=p)
+
+ # Look up the object again so that we get a "fresh" object.
+ c = Child.objects.get(name="Child")
+ p = c.parent
+
+ # Accessing the related object again returns the exactly same object.
+ self.assertTrue(c.parent is p)
+
+ # But if we kill the cache, we get a new object.
+ del c._parent_cache
+ self.assertFalse(c.parent is p)
+
+ # Assigning a new object results in that object getting cached immediately.
+ p2 = Parent.objects.create(name="Parent 2")
+ c.parent = p2
+ self.assertTrue(c.parent is p2)
+
+ # Assigning None succeeds if field is null=True.
+ p.bestchild = None
+ self.assertTrue(p.bestchild is None)
+
+ # bestchild should still be None after saving.
+ p.save()
+ self.assertTrue(p.bestchild is None)
+
+ # bestchild should still be None after fetching the object again.
+ p = Parent.objects.get(name="Parent")
+ self.assertTrue(p.bestchild is None)
+
+ # Assigning None fails: Child.parent is null=False.
+ self.assertRaises(ValueError, setattr, c, "parent", None)
+
+ # You also can't assign an object of the wrong type here
+ self.assertRaises(ValueError, setattr, c, "parent", First(id=1, second=1))
+
+ # Nor can you explicitly assign None to Child.parent during object
+ # creation (regression for #9649).
+ self.assertRaises(ValueError, Child, name='xyzzy', parent=None)
+ self.assertRaises(ValueError, Child.objects.create, name='xyzzy', parent=None)
+
+ # Creation using keyword argument should cache the related object.
+ p = Parent.objects.get(name="Parent")
+ c = Child(parent=p)
+ self.assertTrue(c.parent is p)
+
+ # Creation using keyword argument and unsaved related instance (#8070).
+ p = Parent()
+ with self.assertRaisesMessage(ValueError,
+ 'Cannot assign "%r": "%s" instance isn\'t saved in the database.'
+ % (p, Child.parent.field.rel.to._meta.object_name)):
+ Child(parent=p)
+
+ with self.assertRaisesMessage(ValueError,
+ 'Cannot assign "%r": "%s" instance isn\'t saved in the database.'
+ % (p, Child.parent.field.rel.to._meta.object_name)):
+ ToFieldChild(parent=p)
+
+ # Creation using attname keyword argument and an id will cause the
+ # related object to be fetched.
+ p = Parent.objects.get(name="Parent")
+ c = Child(parent_id=p.id)
+ self.assertFalse(c.parent is p)
+ self.assertEqual(c.parent, p)
+
+ def test_multiple_foreignkeys(self):
+ # Test of multiple ForeignKeys to the same model (bug #7125).
+ c1 = Category.objects.create(name='First')
+ c2 = Category.objects.create(name='Second')
+ c3 = Category.objects.create(name='Third')
+ r1 = Record.objects.create(category=c1)
+ r2 = Record.objects.create(category=c1)
+ r3 = Record.objects.create(category=c2)
+ r4 = Record.objects.create(category=c2)
+ r5 = Record.objects.create(category=c3)
+ Relation.objects.create(left=r1, right=r2)
+ Relation.objects.create(left=r3, right=r4)
+ Relation.objects.create(left=r1, right=r3)
+ Relation.objects.create(left=r5, right=r2)
+ Relation.objects.create(left=r3, right=r2)
+
+ q1 = Relation.objects.filter(left__category__name__in=['First'], right__category__name__in=['Second'])
+ self.assertQuerysetEqual(q1, ["<Relation: First - Second>"])
+
+ q2 = Category.objects.filter(record__left_set__right__category__name='Second').order_by('name')
+ self.assertQuerysetEqual(q2, ["<Category: First>", "<Category: Second>"])
+
+ p = Parent.objects.create(name="Parent")
+ c = Child.objects.create(name="Child", parent=p)
+ self.assertRaises(ValueError, Child.objects.create, name="Grandchild", parent=c)
+
+ def test_fk_instantiation_outside_model(self):
+ # Regression for #12190 -- Should be able to instantiate a FK outside
+ # of a model, and interrogate its related field.
+ cat = models.ForeignKey(Category)
+ self.assertEqual('id', cat.rel.get_related_field().name)
+
+ def test_relation_unsaved(self):
+ # Test that the <field>_set manager does not join on Null value fields (#17541)
+ Third.objects.create(name='Third 1')
+ Third.objects.create(name='Third 2')
+ th = Third(name="testing")
+ # The object isn't saved an thus the relation field is null - we won't even
+ # execute a query in this case.
+ with self.assertNumQueries(0):
+ self.assertEqual(th.child_set.count(), 0)
+ th.save()
+ # Now the model is saved, so we will need to execute an query.
+ with self.assertNumQueries(1):
+ self.assertEqual(th.child_set.count(), 0)
+
+ def test_related_object(self):
+ public_school = School.objects.create(is_public=True)
+ public_student = Student.objects.create(school=public_school)
+
+ private_school = School.objects.create(is_public=False)
+ private_student = Student.objects.create(school=private_school)
+
+ # Only one school is available via all() due to the custom default manager.
+ self.assertQuerysetEqual(
+ School.objects.all(),
+ ["<School: School object>"]
+ )
+
+ self.assertEqual(public_student.school, public_school)
+
+ # Make sure the base manager is used so that an student can still access
+ # its related school even if the default manager doesn't normally
+ # allow it.
+ self.assertEqual(private_student.school, private_school)
+
+ # If the manager is marked "use_for_related_fields", it'll get used instead
+ # of the "bare" queryset. Usually you'd define this as a property on the class,
+ # but this approximates that in a way that's easier in tests.
+ School.objects.use_for_related_fields = True
+ try:
+ private_student = Student.objects.get(pk=private_student.pk)
+ self.assertRaises(School.DoesNotExist, lambda: private_student.school)
+ finally:
+ School.objects.use_for_related_fields = False
+
+ def test_hasattr_related_object(self):
+ # The exception raised on attribute access when a related object
+ # doesn't exist should be an instance of a subclass of `AttributeError`
+ # refs #21563
+ self.assertFalse(hasattr(Article(), 'reporter'))
diff --git a/tests/many_to_one_null/models.py b/tests/many_to_one_null/models.py
index 16ee56cec6..3b03c42105 100644
--- a/tests/many_to_one_null/models.py
+++ b/tests/many_to_one_null/models.py
@@ -27,3 +27,11 @@ class Article(models.Model):
def __str__(self):
return self.headline
+
+
+class Car(models.Model):
+ make = models.CharField(max_length=100, null=True, unique=True)
+
+
+class Driver(models.Model):
+ car = models.ForeignKey(Car, to_field='make', null=True, related_name='drivers')
diff --git a/tests/many_to_one_null/tests.py b/tests/many_to_one_null/tests.py
index 2463065c9d..a72b821f88 100644
--- a/tests/many_to_one_null/tests.py
+++ b/tests/many_to_one_null/tests.py
@@ -2,7 +2,7 @@ from __future__ import unicode_literals
from django.test import TestCase
-from .models import Reporter, Article
+from .models import Article, Car, Driver, Reporter
class ManyToOneNullTests(TestCase):
@@ -105,3 +105,10 @@ class ManyToOneNullTests(TestCase):
with self.assertNumQueries(1):
r.article_set.clear()
self.assertEqual(r.article_set.count(), 0)
+
+ def test_related_null_to_field(self):
+ c1 = Car.objects.create()
+ d1 = Driver.objects.create()
+ self.assertIs(d1.car, None)
+ with self.assertNumQueries(0):
+ self.assertEqual(list(c1.drivers.all()), [])
diff --git a/tests/many_to_one_regress/__init__.py b/tests/many_to_one_regress/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
--- a/tests/many_to_one_regress/__init__.py
+++ /dev/null
diff --git a/tests/many_to_one_regress/models.py b/tests/many_to_one_regress/models.py
deleted file mode 100644
index f1239dbef2..0000000000
--- a/tests/many_to_one_regress/models.py
+++ /dev/null
@@ -1,69 +0,0 @@
-"""
-Regression tests for a few ForeignKey bugs.
-"""
-from __future__ import unicode_literals
-
-from django.db import models
-from django.utils.encoding import python_2_unicode_compatible
-
-# If ticket #1578 ever slips back in, these models will not be able to be
-# created (the field names being lower-cased versions of their opposite
-# classes is important here).
-
-
-class First(models.Model):
- second = models.IntegerField()
-
-
-class Second(models.Model):
- first = models.ForeignKey(First, related_name='the_first')
-
-
-# Protect against repetition of #1839, #2415 and #2536.
-class Third(models.Model):
- name = models.CharField(max_length=20)
- third = models.ForeignKey('self', null=True, related_name='child_set')
-
-
-class Parent(models.Model):
- name = models.CharField(max_length=20, unique=True)
- bestchild = models.ForeignKey('Child', null=True, related_name='favored_by')
-
-
-class Child(models.Model):
- name = models.CharField(max_length=20)
- parent = models.ForeignKey(Parent)
-
-
-class ToFieldChild(models.Model):
- parent = models.ForeignKey(Parent, to_field='name')
-
-
-# Multiple paths to the same model (#7110, #7125)
-@python_2_unicode_compatible
-class Category(models.Model):
- name = models.CharField(max_length=20)
-
- def __str__(self):
- return self.name
-
-
-class Record(models.Model):
- category = models.ForeignKey(Category)
-
-
-@python_2_unicode_compatible
-class Relation(models.Model):
- left = models.ForeignKey(Record, related_name='left_set')
- right = models.ForeignKey(Record, related_name='right_set')
-
- def __str__(self):
- return "%s - %s" % (self.left.category.name, self.right.category.name)
-
-
-class Car(models.Model):
- make = models.CharField(max_length=100, null=True, unique=True)
-
-
-class Driver(models.Model):
- car = models.ForeignKey(Car, to_field='make', null=True, related_name='drivers')
diff --git a/tests/many_to_one_regress/tests.py b/tests/many_to_one_regress/tests.py
deleted file mode 100644
index f8e4d96363..0000000000
--- a/tests/many_to_one_regress/tests.py
+++ /dev/null
@@ -1,137 +0,0 @@
-from __future__ import unicode_literals
-
-from django.db import models
-from django.test import TestCase
-
-from .models import (
- First, Third, Parent, Child, ToFieldChild, Category, Record, Relation, Car, Driver)
-
-
-class ManyToOneRegressionTests(TestCase):
- def test_object_creation(self):
- Third.objects.create(id='3', name='An example')
- parent = Parent(name='fred')
- parent.save()
- Child.objects.create(name='bam-bam', parent=parent)
-
- def test_fk_assignment_and_related_object_cache(self):
- # Tests of ForeignKey assignment and the related-object cache (see #6886).
-
- p = Parent.objects.create(name="Parent")
- c = Child.objects.create(name="Child", parent=p)
-
- # Look up the object again so that we get a "fresh" object.
- c = Child.objects.get(name="Child")
- p = c.parent
-
- # Accessing the related object again returns the exactly same object.
- self.assertTrue(c.parent is p)
-
- # But if we kill the cache, we get a new object.
- del c._parent_cache
- self.assertFalse(c.parent is p)
-
- # Assigning a new object results in that object getting cached immediately.
- p2 = Parent.objects.create(name="Parent 2")
- c.parent = p2
- self.assertTrue(c.parent is p2)
-
- # Assigning None succeeds if field is null=True.
- p.bestchild = None
- self.assertTrue(p.bestchild is None)
-
- # bestchild should still be None after saving.
- p.save()
- self.assertTrue(p.bestchild is None)
-
- # bestchild should still be None after fetching the object again.
- p = Parent.objects.get(name="Parent")
- self.assertTrue(p.bestchild is None)
-
- # Assigning None fails: Child.parent is null=False.
- self.assertRaises(ValueError, setattr, c, "parent", None)
-
- # You also can't assign an object of the wrong type here
- self.assertRaises(ValueError, setattr, c, "parent", First(id=1, second=1))
-
- # Nor can you explicitly assign None to Child.parent during object
- # creation (regression for #9649).
- self.assertRaises(ValueError, Child, name='xyzzy', parent=None)
- self.assertRaises(ValueError, Child.objects.create, name='xyzzy', parent=None)
-
- # Creation using keyword argument should cache the related object.
- p = Parent.objects.get(name="Parent")
- c = Child(parent=p)
- self.assertTrue(c.parent is p)
-
- # Creation using keyword argument and unsaved related instance (#8070).
- p = Parent()
- with self.assertRaisesMessage(ValueError,
- 'Cannot assign "%r": "%s" instance isn\'t saved in the database.'
- % (p, Child.parent.field.rel.to._meta.object_name)):
- Child(parent=p)
-
- with self.assertRaisesMessage(ValueError,
- 'Cannot assign "%r": "%s" instance isn\'t saved in the database.'
- % (p, Child.parent.field.rel.to._meta.object_name)):
- ToFieldChild(parent=p)
-
- # Creation using attname keyword argument and an id will cause the
- # related object to be fetched.
- p = Parent.objects.get(name="Parent")
- c = Child(parent_id=p.id)
- self.assertFalse(c.parent is p)
- self.assertEqual(c.parent, p)
-
- def test_multiple_foreignkeys(self):
- # Test of multiple ForeignKeys to the same model (bug #7125).
- c1 = Category.objects.create(name='First')
- c2 = Category.objects.create(name='Second')
- c3 = Category.objects.create(name='Third')
- r1 = Record.objects.create(category=c1)
- r2 = Record.objects.create(category=c1)
- r3 = Record.objects.create(category=c2)
- r4 = Record.objects.create(category=c2)
- r5 = Record.objects.create(category=c3)
- Relation.objects.create(left=r1, right=r2)
- Relation.objects.create(left=r3, right=r4)
- Relation.objects.create(left=r1, right=r3)
- Relation.objects.create(left=r5, right=r2)
- Relation.objects.create(left=r3, right=r2)
-
- q1 = Relation.objects.filter(left__category__name__in=['First'], right__category__name__in=['Second'])
- self.assertQuerysetEqual(q1, ["<Relation: First - Second>"])
-
- q2 = Category.objects.filter(record__left_set__right__category__name='Second').order_by('name')
- self.assertQuerysetEqual(q2, ["<Category: First>", "<Category: Second>"])
-
- p = Parent.objects.create(name="Parent")
- c = Child.objects.create(name="Child", parent=p)
- self.assertRaises(ValueError, Child.objects.create, name="Grandchild", parent=c)
-
- def test_fk_instantiation_outside_model(self):
- # Regression for #12190 -- Should be able to instantiate a FK outside
- # of a model, and interrogate its related field.
- cat = models.ForeignKey(Category)
- self.assertEqual('id', cat.rel.get_related_field().name)
-
- def test_relation_unsaved(self):
- # Test that the <field>_set manager does not join on Null value fields (#17541)
- Third.objects.create(name='Third 1')
- Third.objects.create(name='Third 2')
- th = Third(name="testing")
- # The object isn't saved an thus the relation field is null - we won't even
- # execute a query in this case.
- with self.assertNumQueries(0):
- self.assertEqual(th.child_set.count(), 0)
- th.save()
- # Now the model is saved, so we will need to execute an query.
- with self.assertNumQueries(1):
- self.assertEqual(th.child_set.count(), 0)
-
- def test_related_null_to_field(self):
- c1 = Car.objects.create()
- d1 = Driver.objects.create()
- self.assertIs(d1.car, None)
- with self.assertNumQueries(0):
- self.assertEqual(list(c1.drivers.all()), [])
diff --git a/tests/reverse_single_related/__init__.py b/tests/reverse_single_related/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
--- a/tests/reverse_single_related/__init__.py
+++ /dev/null
diff --git a/tests/reverse_single_related/models.py b/tests/reverse_single_related/models.py
deleted file mode 100644
index c7ec2edc6b..0000000000
--- a/tests/reverse_single_related/models.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from django.db import models
-
-
-class SourceManager(models.Manager):
- def get_queryset(self):
- return super(SourceManager, self).get_queryset().filter(is_public=True)
-
-
-class Source(models.Model):
- is_public = models.BooleanField(default=False)
- objects = SourceManager()
-
-
-class Item(models.Model):
- source = models.ForeignKey(Source)
diff --git a/tests/reverse_single_related/tests.py b/tests/reverse_single_related/tests.py
deleted file mode 100644
index 2a8b96ddbc..0000000000
--- a/tests/reverse_single_related/tests.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from django.test import TestCase
-
-from .models import Source, Item
-
-
-class ReverseSingleRelatedTests(TestCase):
- """
- Regression tests for an object that cannot access a single related
- object due to a restrictive default manager.
- """
-
- def test_reverse_single_related(self):
-
- public_source = Source.objects.create(is_public=True)
- public_item = Item.objects.create(source=public_source)
-
- private_source = Source.objects.create(is_public=False)
- private_item = Item.objects.create(source=private_source)
-
- # Only one source is available via all() due to the custom default manager.
- self.assertQuerysetEqual(
- Source.objects.all(),
- ["<Source: Source object>"]
- )
-
- self.assertEqual(public_item.source, public_source)
-
- # Make sure that an item can still access its related source even if the default
- # manager doesn't normally allow it.
- self.assertEqual(private_item.source, private_source)
-
- # If the manager is marked "use_for_related_fields", it'll get used instead
- # of the "bare" queryset. Usually you'd define this as a property on the class,
- # but this approximates that in a way that's easier in tests.
- Source.objects.use_for_related_fields = True
- try:
- private_item = Item.objects.get(pk=private_item.pk)
- self.assertRaises(Source.DoesNotExist, lambda: private_item.source)
- finally:
- Source.objects.use_for_related_fields = False
-
- def test_hasattr_single_related(self):
- # The exception raised on attribute access when a related object
- # doesn't exist should be an instance of a subclass of `AttributeError`
- # refs #21563
- self.assertFalse(hasattr(Item(), 'source'))