Refs #18586 -- Split up model_inheritance.ModelInheritanceTest

This commit is contained in:
Alexander Shchapov 2014-12-03 23:24:42 +02:00 committed by Tim Graham
parent 49d034fff2
commit 2cd19f3738
1 changed files with 147 additions and 145 deletions

View File

@ -49,9 +49,16 @@ class ModelInheritanceTests(TestCase):
# doesn't exist as a model). # doesn't exist as a model).
self.assertRaises(AttributeError, lambda: CommonInfo.objects.all()) self.assertRaises(AttributeError, lambda: CommonInfo.objects.all())
def test_multiple_table(self): def test_reverse_relation_for_different_hierarchy_tree(self):
post = Post.objects.create(title="Lorem Ipsum") # Even though p.supplier for a Place 'p' (a parent of a Supplier), a
# Restaurant object cannot access that reverse relation, since it's not
# part of the Place-Supplier Hierarchy.
self.assertQuerysetEqual(Place.objects.filter(supplier__name="foo"), [])
self.assertRaises(FieldError, Restaurant.objects.filter, supplier__name="foo")
def test_model_with_distinct_accessors(self):
# The Post model has distinct accessors for the Comment and Link models. # The Post model has distinct accessors for the Comment and Link models.
post = Post.objects.create(title="Lorem Ipsum")
post.attached_comment_set.create(content="Save $ on V1agr@", is_spam=True) post.attached_comment_set.create(content="Save $ on V1agr@", is_spam=True)
post.attached_link_set.create( post.attached_link_set.create(
content="The Web framework for perfections with deadlines.", content="The Web framework for perfections with deadlines.",
@ -64,47 +71,7 @@ class ModelInheritanceTests(TestCase):
AttributeError, getattr, post, "attached_%(class)s_set" AttributeError, getattr, post, "attached_%(class)s_set"
) )
# The Place/Restaurant/ItalianRestaurant models all exist as def test_meta_fields_and_ordering(self):
# independent models. However, the subclasses also have transparent
# access to the fields of their ancestors.
# Create a couple of Places.
Place.objects.create(name="Master Shakes", address="666 W. Jersey")
Place.objects.create(name="Ace Hardware", address="1013 N. Ashland")
# Test constructor for Restaurant.
r = Restaurant.objects.create(
name="Demon Dogs",
address="944 W. Fullerton",
serves_hot_dogs=True,
serves_pizza=False,
rating=2
)
# Test the constructor for ItalianRestaurant.
c = Chef.objects.create(name="Albert")
ir = ItalianRestaurant.objects.create(
name="Ristorante Miron",
address="1234 W. Ash",
serves_hot_dogs=False,
serves_pizza=False,
serves_gnocchi=True,
rating=4,
chef=c
)
self.assertQuerysetEqual(
ItalianRestaurant.objects.filter(address="1234 W. Ash"), [
"Ristorante Miron",
],
attrgetter("name")
)
ir.address = "1234 W. Elm"
ir.save()
self.assertQuerysetEqual(
ItalianRestaurant.objects.filter(address="1234 W. Elm"), [
"Ristorante Miron",
],
attrgetter("name")
)
# Make sure Restaurant and ItalianRestaurant have the right fields in # Make sure Restaurant and ItalianRestaurant have the right fields in
# the right order. # the right order.
self.assertEqual( self.assertEqual(
@ -119,14 +86,91 @@ class ModelInheritanceTests(TestCase):
) )
self.assertEqual(Restaurant._meta.ordering, ["-rating"]) self.assertEqual(Restaurant._meta.ordering, ["-rating"])
# Even though p.supplier for a Place 'p' (a parent of a Supplier), a def test_custompk_m2m(self):
# Restaurant object cannot access that reverse relation, since it's not b = Base.objects.create()
# part of the Place-Supplier Hierarchy. b.titles.add(Title.objects.create(title="foof"))
self.assertQuerysetEqual(Place.objects.filter(supplier__name="foo"), []) s = SubBase.objects.create(sub_id=b.id)
self.assertRaises( b = Base.objects.get(pk=s.id)
FieldError, Restaurant.objects.filter, supplier__name="foo" self.assertNotEqual(b.pk, s.pk)
# Low-level test for related_val
self.assertEqual(s.titles.related_val, (s.id,))
# Higher level test for correct query values (title foof not
# accidentally found).
self.assertQuerysetEqual(s.titles.all(), [])
def test_update_parent_filtering(self):
"""
Test that updating a field of a model subclass doesn't issue an UPDATE
query constrained by an inner query.
Refs #10399
"""
supplier = Supplier.objects.create(
name='Central market',
address='610 some street',
)
# Capture the expected query in a database agnostic way
with CaptureQueriesContext(connection) as captured_queries:
Place.objects.filter(pk=supplier.pk).update(name=supplier.name)
expected_sql = captured_queries[0]['sql']
# Capture the queries executed when a subclassed model instance is saved.
with CaptureQueriesContext(connection) as captured_queries:
supplier.save(update_fields=('name',))
for query in captured_queries:
sql = query['sql']
if 'UPDATE' in sql:
self.assertEqual(expected_sql, sql)
def test_eq(self):
# Equality doesn't transfer in multitable inheritance.
self.assertNotEqual(Place(id=1), Restaurant(id=1))
self.assertNotEqual(Restaurant(id=1), Place(id=1))
def test_mixin_init(self):
m = MixinModel()
self.assertEqual(m.other_attr, 1)
class ModelInheritanceDataTests(TestCase):
@classmethod
def setUpTestData(cls):
cls.restaurant = Restaurant.objects.create(
name="Demon Dogs",
address="944 W. Fullerton",
serves_hot_dogs=True,
serves_pizza=False,
rating=2,
) )
chef = Chef.objects.create(name="Albert")
cls.italian_restaurant = ItalianRestaurant.objects.create(
name="Ristorante Miron",
address="1234 W. Ash",
serves_hot_dogs=False,
serves_pizza=False,
serves_gnocchi=True,
rating=4,
chef=chef,
)
def test_filter_inherited_model(self):
self.assertQuerysetEqual(
ItalianRestaurant.objects.filter(address="1234 W. Ash"), [
"Ristorante Miron",
],
attrgetter("name")
)
def test_update_inherited_model(self):
self.italian_restaurant.address = "1234 W. Elm"
self.italian_restaurant.save()
self.assertQuerysetEqual(
ItalianRestaurant.objects.filter(address="1234 W. Elm"), [
"Ristorante Miron",
],
attrgetter("name")
)
def test_parent_fields_available_for_filtering_in_child_model(self):
# Parent fields can be used directly in filters on the child model. # Parent fields can be used directly in filters on the child model.
self.assertQuerysetEqual( self.assertQuerysetEqual(
Restaurant.objects.filter(name="Demon Dogs"), [ Restaurant.objects.filter(name="Demon Dogs"), [
@ -135,21 +179,24 @@ class ModelInheritanceTests(TestCase):
attrgetter("name") attrgetter("name")
) )
self.assertQuerysetEqual( self.assertQuerysetEqual(
ItalianRestaurant.objects.filter(address="1234 W. Elm"), [ ItalianRestaurant.objects.filter(address="1234 W. Ash"), [
"Ristorante Miron", "Ristorante Miron",
], ],
attrgetter("name") attrgetter("name")
) )
def test_filter_on_parent_returns_object_of_parent_type(self):
# Filters against the parent model return objects of the parent's type. # Filters against the parent model return objects of the parent's type.
p = Place.objects.get(name="Demon Dogs") p = Place.objects.get(name="Demon Dogs")
self.assertIs(type(p), Place) self.assertIs(type(p), Place)
def test_parent_child_one_to_one_link(self):
# Since the parent and child are linked by an automatically created # Since the parent and child are linked by an automatically created
# OneToOneField, you can get from the parent to the child by using the # OneToOneField, you can get from the parent to the child by using the
# child's name. # child's name.
self.assertEqual( self.assertEqual(
p.restaurant, Restaurant.objects.get(name="Demon Dogs") Place.objects.get(name="Demon Dogs").restaurant,
Restaurant.objects.get(name="Demon Dogs")
) )
self.assertEqual( self.assertEqual(
Place.objects.get(name="Ristorante Miron").restaurant.italianrestaurant, Place.objects.get(name="Ristorante Miron").restaurant.italianrestaurant,
@ -160,29 +207,35 @@ class ModelInheritanceTests(TestCase):
ItalianRestaurant.objects.get(name="Ristorante Miron") ItalianRestaurant.objects.get(name="Ristorante Miron")
) )
def test_parent_child_one_to_one_link_on_nonrelated_objects(self):
# This won't work because the Demon Dogs restaurant is not an Italian # This won't work because the Demon Dogs restaurant is not an Italian
# restaurant. # restaurant.
self.assertRaises( self.assertRaises(
ItalianRestaurant.DoesNotExist, ItalianRestaurant.DoesNotExist,
lambda: p.restaurant.italianrestaurant lambda: Place.objects.get(name="Demon Dogs").restaurant.italianrestaurant
) )
def test_inherited_does_not_exist_exception(self):
# An ItalianRestaurant which does not exist is also a Place which does # An ItalianRestaurant which does not exist is also a Place which does
# not exist. # not exist.
self.assertRaises( self.assertRaises(
Place.DoesNotExist, Place.DoesNotExist,
ItalianRestaurant.objects.get, name="The Noodle Void" ItalianRestaurant.objects.get, name="The Noodle Void"
) )
def test_inherited_multiple_objects_returned_exception(self):
# MultipleObjectsReturned is also inherited. # MultipleObjectsReturned is also inherited.
self.assertRaises( self.assertRaises(
Place.MultipleObjectsReturned, Place.MultipleObjectsReturned,
Restaurant.objects.get, id__lt=12321 Restaurant.objects.get, id__lt=12321
) )
def test_related_objects_for_inherited_models(self):
# Related objects work just as they normally do. # Related objects work just as they normally do.
s1 = Supplier.objects.create(name="Joe's Chickens", address="123 Sesame St") s1 = Supplier.objects.create(name="Joe's Chickens", address="123 Sesame St")
s1.customers = [r, ir] s1.customers = [self.restaurant, self.italian_restaurant]
s2 = Supplier.objects.create(name="Luigi's Pasta", address="456 Sesame St") s2 = Supplier.objects.create(name="Luigi's Pasta", address="456 Sesame St")
s2.customers = [ir] s2.customers = [self.italian_restaurant]
# This won't work because the Place we select is not a Restaurant (it's # This won't work because the Place we select is not a Restaurant (it's
# a Supplier). # a Supplier).
@ -193,7 +246,7 @@ class ModelInheritanceTests(TestCase):
self.assertEqual(p.supplier, s1) self.assertEqual(p.supplier, s1)
self.assertQuerysetEqual( self.assertQuerysetEqual(
ir.provider.order_by("-name"), [ self.italian_restaurant.provider.order_by("-name"), [
"Luigi's Pasta", "Luigi's Pasta",
"Joe's Chickens" "Joe's Chickens"
], ],
@ -217,7 +270,7 @@ class ModelInheritanceTests(TestCase):
name="Main St", address="111 Main St", main_site=s1 name="Main St", address="111 Main St", main_site=s1
) )
ParkingLot.objects.create( ParkingLot.objects.create(
name="Well Lit", address="124 Sesame St", main_site=ir name="Well Lit", address="124 Sesame St", main_site=self.italian_restaurant
) )
self.assertEqual( self.assertEqual(
@ -225,6 +278,7 @@ class ModelInheritanceTests(TestCase):
"Ristorante Miron" "Ristorante Miron"
) )
def test_update_works_on_parent_and_child_models_at_once(self):
# The update() command can update fields in parent and child classes at # The update() command can update fields in parent and child classes at
# once (although it executed multiple SQL queries to do so). # once (although it executed multiple SQL queries to do so).
rows = Restaurant.objects.filter( rows = Restaurant.objects.filter(
@ -234,18 +288,20 @@ class ModelInheritanceTests(TestCase):
) )
self.assertEqual(rows, 1) self.assertEqual(rows, 1)
r1 = Restaurant.objects.get(pk=r.pk) r1 = Restaurant.objects.get(pk=self.restaurant.pk)
self.assertFalse(r1.serves_hot_dogs) self.assertFalse(r1.serves_hot_dogs)
self.assertEqual(r1.name, "Demon Puppies") self.assertEqual(r1.name, "Demon Puppies")
def test_values_works_on_parent_model_fields(self):
# The values() command also works on fields from parent models. # The values() command also works on fields from parent models.
self.assertQuerysetEqual( self.assertQuerysetEqual(
ItalianRestaurant.objects.values("name", "rating"), [ ItalianRestaurant.objects.values("name", "rating"), [
{"rating": 4, "name": "Ristorante Miron"} {"rating": 4, "name": "Ristorante Miron"},
], ],
lambda o: o lambda o: o
) )
def test_select_related_works_on_parent_model_fields(self):
# select_related works with fields from the parent object as if they # select_related works with fields from the parent object as if they
# were a normal part of the model. # were a normal part of the model.
self.assertNumQueries( self.assertNumQueries(
@ -260,22 +316,6 @@ class ModelInheritanceTests(TestCase):
#23370 - Should be able to defer child fields when using #23370 - Should be able to defer child fields when using
select_related() from parent to child. select_related() from parent to child.
""" """
Restaurant.objects.create(
name="Demon Dogs",
address="944 W. Fullerton",
serves_hot_dogs=True,
serves_pizza=False,
rating=2,
)
ItalianRestaurant.objects.create(
name="Ristorante Miron",
address="1234 W. Ash",
serves_hot_dogs=False,
serves_pizza=False,
serves_gnocchi=True,
rating=4,
)
qs = (Restaurant.objects qs = (Restaurant.objects
.select_related("italianrestaurant") .select_related("italianrestaurant")
.defer("italianrestaurant__serves_gnocchi") .defer("italianrestaurant__serves_gnocchi")
@ -292,91 +332,53 @@ class ModelInheritanceTests(TestCase):
self.assertEqual(qs[1].italianrestaurant.name, 'Ristorante Miron') self.assertEqual(qs[1].italianrestaurant.name, 'Ristorante Miron')
self.assertEqual(qs[1].italianrestaurant.rating, 4) self.assertEqual(qs[1].italianrestaurant.rating, 4)
def test_mixin_init(self):
m = MixinModel()
self.assertEqual(m.other_attr, 1)
def test_update_query_counts(self): def test_update_query_counts(self):
""" """
Test that update queries do not generate non-necessary queries. Test that update queries do not generate non-necessary queries.
Refs #18304. Refs #18304.
""" """
c = Chef.objects.create(name="Albert")
ir = ItalianRestaurant.objects.create(
name="Ristorante Miron",
address="1234 W. Ash",
serves_hot_dogs=False,
serves_pizza=False,
serves_gnocchi=True,
rating=4,
chef=c
)
with self.assertNumQueries(3): with self.assertNumQueries(3):
ir.save() self.italian_restaurant.save()
def test_update_parent_filtering(self): def test_filter_inherited_on_null(self):
""" # Refs #12567
Test that updating a field of a model subclass doesn't issue an UPDATE Supplier.objects.create(
query constrained by an inner query. name="Central market",
Refs #10399 address="610 some street",
"""
supplier = Supplier.objects.create(
name='Central market',
address='610 some street'
)
# Capture the expected query in a database agnostic way
with CaptureQueriesContext(connection) as captured_queries:
Place.objects.filter(pk=supplier.pk).update(name=supplier.name)
expected_sql = captured_queries[0]['sql']
# Capture the queries executed when a subclassed model instance is saved.
with CaptureQueriesContext(connection) as captured_queries:
supplier.save(update_fields=('name',))
for query in captured_queries:
sql = query['sql']
if 'UPDATE' in sql:
self.assertEqual(expected_sql, sql)
def test_eq(self):
# Equality doesn't transfer in multitable inheritance.
self.assertNotEqual(Place(id=1), Restaurant(id=1))
self.assertNotEqual(Restaurant(id=1), Place(id=1))
def test_ticket_12567(self):
r = Restaurant.objects.create(name='n1', address='a1')
s = Supplier.objects.create(name='s1', address='a2')
self.assertQuerysetEqual(
Place.objects.filter(supplier__isnull=False),
[Place.objects.get(pk=s.pk)],
lambda x: x
) )
self.assertQuerysetEqual( self.assertQuerysetEqual(
Place.objects.filter(supplier__isnull=True), Place.objects.filter(supplier__isnull=False), [
[Place.objects.get(pk=r.pk)], "Central market",
lambda x: x ],
attrgetter("name")
) )
self.assertQuerysetEqual( self.assertQuerysetEqual(
Place.objects.exclude(supplier__isnull=False), Place.objects.filter(supplier__isnull=True).order_by("name"), [
[Place.objects.get(pk=r.pk)], "Demon Dogs",
lambda x: x "Ristorante Miron",
) ],
self.assertQuerysetEqual( attrgetter("name")
Place.objects.exclude(supplier__isnull=True),
[Place.objects.get(pk=s.pk)],
lambda x: x
) )
def test_custompk_m2m(self): def test_exclude_inherited_on_null(self):
b = Base.objects.create() # Refs #12567
b.titles.add(Title.objects.create(title="foof")) Supplier.objects.create(
s = SubBase.objects.create(sub_id=b.id) name="Central market",
b = Base.objects.get(pk=s.id) address="610 some street",
self.assertNotEqual(b.pk, s.pk) )
# Low-level test for related_val
self.assertEqual(s.titles.related_val, (s.id,))
# Higher level test for correct query values (title foof not
# accidentally found).
self.assertQuerysetEqual( self.assertQuerysetEqual(
s.titles.all(), []) Place.objects.exclude(supplier__isnull=False).order_by("name"), [
"Demon Dogs",
"Ristorante Miron",
],
attrgetter("name")
)
self.assertQuerysetEqual(
Place.objects.exclude(supplier__isnull=True), [
"Central market",
],
attrgetter("name")
)
class InheritanceSameModelNameTests(TransactionTestCase): class InheritanceSameModelNameTests(TransactionTestCase):