Refs #18586 -- Split up model_inheritance.ModelInheritanceTest
This commit is contained in:
parent
49d034fff2
commit
2cd19f3738
|
@ -49,9 +49,16 @@ class ModelInheritanceTests(TestCase):
|
|||
# doesn't exist as a model).
|
||||
self.assertRaises(AttributeError, lambda: CommonInfo.objects.all())
|
||||
|
||||
def test_multiple_table(self):
|
||||
post = Post.objects.create(title="Lorem Ipsum")
|
||||
def test_reverse_relation_for_different_hierarchy_tree(self):
|
||||
# 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.
|
||||
post = Post.objects.create(title="Lorem Ipsum")
|
||||
post.attached_comment_set.create(content="Save $ on V1agr@", is_spam=True)
|
||||
post.attached_link_set.create(
|
||||
content="The Web framework for perfections with deadlines.",
|
||||
|
@ -64,47 +71,7 @@ class ModelInheritanceTests(TestCase):
|
|||
AttributeError, getattr, post, "attached_%(class)s_set"
|
||||
)
|
||||
|
||||
# The Place/Restaurant/ItalianRestaurant models all exist as
|
||||
# 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")
|
||||
)
|
||||
|
||||
def test_meta_fields_and_ordering(self):
|
||||
# Make sure Restaurant and ItalianRestaurant have the right fields in
|
||||
# the right order.
|
||||
self.assertEqual(
|
||||
|
@ -119,14 +86,91 @@ class ModelInheritanceTests(TestCase):
|
|||
)
|
||||
self.assertEqual(Restaurant._meta.ordering, ["-rating"])
|
||||
|
||||
# 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_custompk_m2m(self):
|
||||
b = Base.objects.create()
|
||||
b.titles.add(Title.objects.create(title="foof"))
|
||||
s = SubBase.objects.create(sub_id=b.id)
|
||||
b = Base.objects.get(pk=s.id)
|
||||
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.
|
||||
self.assertQuerysetEqual(
|
||||
Restaurant.objects.filter(name="Demon Dogs"), [
|
||||
|
@ -135,21 +179,24 @@ class ModelInheritanceTests(TestCase):
|
|||
attrgetter("name")
|
||||
)
|
||||
self.assertQuerysetEqual(
|
||||
ItalianRestaurant.objects.filter(address="1234 W. Elm"), [
|
||||
ItalianRestaurant.objects.filter(address="1234 W. Ash"), [
|
||||
"Ristorante Miron",
|
||||
],
|
||||
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.
|
||||
p = Place.objects.get(name="Demon Dogs")
|
||||
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
|
||||
# OneToOneField, you can get from the parent to the child by using the
|
||||
# child's name.
|
||||
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(
|
||||
Place.objects.get(name="Ristorante Miron").restaurant.italianrestaurant,
|
||||
|
@ -160,29 +207,35 @@ class ModelInheritanceTests(TestCase):
|
|||
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
|
||||
# restaurant.
|
||||
self.assertRaises(
|
||||
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
|
||||
# not exist.
|
||||
self.assertRaises(
|
||||
Place.DoesNotExist,
|
||||
ItalianRestaurant.objects.get, name="The Noodle Void"
|
||||
)
|
||||
|
||||
def test_inherited_multiple_objects_returned_exception(self):
|
||||
# MultipleObjectsReturned is also inherited.
|
||||
self.assertRaises(
|
||||
Place.MultipleObjectsReturned,
|
||||
Restaurant.objects.get, id__lt=12321
|
||||
)
|
||||
|
||||
def test_related_objects_for_inherited_models(self):
|
||||
# Related objects work just as they normally do.
|
||||
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.customers = [ir]
|
||||
s2.customers = [self.italian_restaurant]
|
||||
|
||||
# This won't work because the Place we select is not a Restaurant (it's
|
||||
# a Supplier).
|
||||
|
@ -193,7 +246,7 @@ class ModelInheritanceTests(TestCase):
|
|||
|
||||
self.assertEqual(p.supplier, s1)
|
||||
self.assertQuerysetEqual(
|
||||
ir.provider.order_by("-name"), [
|
||||
self.italian_restaurant.provider.order_by("-name"), [
|
||||
"Luigi's Pasta",
|
||||
"Joe's Chickens"
|
||||
],
|
||||
|
@ -217,7 +270,7 @@ class ModelInheritanceTests(TestCase):
|
|||
name="Main St", address="111 Main St", main_site=s1
|
||||
)
|
||||
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(
|
||||
|
@ -225,6 +278,7 @@ class ModelInheritanceTests(TestCase):
|
|||
"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
|
||||
# once (although it executed multiple SQL queries to do so).
|
||||
rows = Restaurant.objects.filter(
|
||||
|
@ -234,18 +288,20 @@ class ModelInheritanceTests(TestCase):
|
|||
)
|
||||
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.assertEqual(r1.name, "Demon Puppies")
|
||||
|
||||
def test_values_works_on_parent_model_fields(self):
|
||||
# The values() command also works on fields from parent models.
|
||||
self.assertQuerysetEqual(
|
||||
ItalianRestaurant.objects.values("name", "rating"), [
|
||||
{"rating": 4, "name": "Ristorante Miron"}
|
||||
{"rating": 4, "name": "Ristorante Miron"},
|
||||
],
|
||||
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
|
||||
# were a normal part of the model.
|
||||
self.assertNumQueries(
|
||||
|
@ -260,22 +316,6 @@ class ModelInheritanceTests(TestCase):
|
|||
#23370 - Should be able to defer child fields when using
|
||||
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
|
||||
.select_related("italianrestaurant")
|
||||
.defer("italianrestaurant__serves_gnocchi")
|
||||
|
@ -292,91 +332,53 @@ class ModelInheritanceTests(TestCase):
|
|||
self.assertEqual(qs[1].italianrestaurant.name, 'Ristorante Miron')
|
||||
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):
|
||||
"""
|
||||
Test that update queries do not generate non-necessary queries.
|
||||
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):
|
||||
ir.save()
|
||||
self.italian_restaurant.save()
|
||||
|
||||
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_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
|
||||
def test_filter_inherited_on_null(self):
|
||||
# Refs #12567
|
||||
Supplier.objects.create(
|
||||
name="Central market",
|
||||
address="610 some street",
|
||||
)
|
||||
self.assertQuerysetEqual(
|
||||
Place.objects.filter(supplier__isnull=True),
|
||||
[Place.objects.get(pk=r.pk)],
|
||||
lambda x: x
|
||||
Place.objects.filter(supplier__isnull=False), [
|
||||
"Central market",
|
||||
],
|
||||
attrgetter("name")
|
||||
)
|
||||
self.assertQuerysetEqual(
|
||||
Place.objects.exclude(supplier__isnull=False),
|
||||
[Place.objects.get(pk=r.pk)],
|
||||
lambda x: x
|
||||
)
|
||||
self.assertQuerysetEqual(
|
||||
Place.objects.exclude(supplier__isnull=True),
|
||||
[Place.objects.get(pk=s.pk)],
|
||||
lambda x: x
|
||||
Place.objects.filter(supplier__isnull=True).order_by("name"), [
|
||||
"Demon Dogs",
|
||||
"Ristorante Miron",
|
||||
],
|
||||
attrgetter("name")
|
||||
)
|
||||
|
||||
def test_custompk_m2m(self):
|
||||
b = Base.objects.create()
|
||||
b.titles.add(Title.objects.create(title="foof"))
|
||||
s = SubBase.objects.create(sub_id=b.id)
|
||||
b = Base.objects.get(pk=s.id)
|
||||
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).
|
||||
def test_exclude_inherited_on_null(self):
|
||||
# Refs #12567
|
||||
Supplier.objects.create(
|
||||
name="Central market",
|
||||
address="610 some street",
|
||||
)
|
||||
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):
|
||||
|
|
Loading…
Reference in New Issue