Kaydet (Commit) 52edc160 authored tarafından Andrew Godwin's avatar Andrew Godwin

Add more stringent M2M tests and fix the bug they exposed

üst 5b522cd8
...@@ -9,7 +9,7 @@ from django.conf import settings ...@@ -9,7 +9,7 @@ from django.conf import settings
from django.db.models.fields.related import ManyToManyRel from django.db.models.fields.related import ManyToManyRel
from django.db.models.fields import AutoField, FieldDoesNotExist from django.db.models.fields import AutoField, FieldDoesNotExist
from django.db.models.fields.proxy import OrderWrt from django.db.models.fields.proxy import OrderWrt
from django.db.models.loading import get_models, app_cache_ready, cache from django.db.models.loading import app_cache_ready, cache
from django.utils import six from django.utils import six
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.encoding import force_text, smart_text, python_2_unicode_compatible from django.utils.encoding import force_text, smart_text, python_2_unicode_compatible
...@@ -495,7 +495,7 @@ class Options(object): ...@@ -495,7 +495,7 @@ class Options(object):
cache[obj] = model cache[obj] = model
# Collect also objects which are in relation to some proxy child/parent of self. # Collect also objects which are in relation to some proxy child/parent of self.
proxy_cache = cache.copy() proxy_cache = cache.copy()
for klass in get_models(include_auto_created=True, only_installed=False): for klass in self.app_cache.get_models(include_auto_created=True, only_installed=False):
if not klass._meta.swapped: if not klass._meta.swapped:
for f in klass._meta.local_fields: for f in klass._meta.local_fields:
if f.rel and not isinstance(f.rel.to, six.string_types) and f.generate_reverse_relation: if f.rel and not isinstance(f.rel.to, six.string_types) and f.generate_reverse_relation:
...@@ -538,7 +538,7 @@ class Options(object): ...@@ -538,7 +538,7 @@ class Options(object):
cache[obj] = parent cache[obj] = parent
else: else:
cache[obj] = model cache[obj] = model
for klass in get_models(only_installed=False): for klass in self.app_cache.get_models(only_installed=False):
if not klass._meta.swapped: if not klass._meta.swapped:
for f in klass._meta.local_many_to_many: for f in klass._meta.local_many_to_many:
if (f.rel if (f.rel
......
...@@ -116,7 +116,7 @@ class OperationTests(MigrationTestBase): ...@@ -116,7 +116,7 @@ class OperationTests(MigrationTestBase):
""" """
project_state = self.set_up_test_model("test_adflmm", second_model=True) project_state = self.set_up_test_model("test_adflmm", second_model=True)
# Test the state alteration # Test the state alteration
operation = migrations.AddField("Pony", "stables", models.ManyToManyField("Stable")) operation = migrations.AddField("Pony", "stables", models.ManyToManyField("Stable", related_name="ponies"))
new_state = project_state.clone() new_state = project_state.clone()
operation.state_forwards("test_adflmm", new_state) operation.state_forwards("test_adflmm", new_state)
self.assertEqual(len(new_state.models["test_adflmm", "pony"].fields), 4) self.assertEqual(len(new_state.models["test_adflmm", "pony"].fields), 4)
...@@ -126,6 +126,13 @@ class OperationTests(MigrationTestBase): ...@@ -126,6 +126,13 @@ class OperationTests(MigrationTestBase):
operation.database_forwards("test_adflmm", editor, project_state, new_state) operation.database_forwards("test_adflmm", editor, project_state, new_state)
self.assertTableExists("test_adflmm_pony_stables") self.assertTableExists("test_adflmm_pony_stables")
self.assertColumnNotExists("test_adflmm_pony", "stables") self.assertColumnNotExists("test_adflmm_pony", "stables")
# Make sure the M2M field actually works
app_cache = new_state.render()
Pony = app_cache.get_model("test_adflmm", "Pony")
p = Pony.objects.create(pink=False, weight=4.55)
p.stables.create()
self.assertEqual(p.stables.count(), 1)
p.stables.all().delete()
# And test reversal # And test reversal
with connection.schema_editor() as editor: with connection.schema_editor() as editor:
operation.database_backwards("test_adflmm", editor, new_state, project_state) operation.database_backwards("test_adflmm", editor, new_state, project_state)
......
...@@ -37,7 +37,7 @@ class BookWithM2M(models.Model): ...@@ -37,7 +37,7 @@ class BookWithM2M(models.Model):
author = models.ForeignKey(Author) author = models.ForeignKey(Author)
title = models.CharField(max_length=100, db_index=True) title = models.CharField(max_length=100, db_index=True)
pub_date = models.DateTimeField() pub_date = models.DateTimeField()
tags = models.ManyToManyField("Tag", related_name="books") tags = models.ManyToManyField("TagM2MTest", related_name="books")
class Meta: class Meta:
app_cache = new_app_cache app_cache = new_app_cache
...@@ -62,6 +62,14 @@ class Tag(models.Model): ...@@ -62,6 +62,14 @@ class Tag(models.Model):
app_cache = new_app_cache app_cache = new_app_cache
class TagM2MTest(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
class Meta:
app_cache = new_app_cache
class TagIndexed(models.Model): class TagIndexed(models.Model):
title = models.CharField(max_length=255) title = models.CharField(max_length=255)
slug = models.SlugField(unique=True) slug = models.SlugField(unique=True)
......
...@@ -6,7 +6,7 @@ from django.db import connection, DatabaseError, IntegrityError ...@@ -6,7 +6,7 @@ from django.db import connection, DatabaseError, IntegrityError
from django.db.models.fields import IntegerField, TextField, CharField, SlugField from django.db.models.fields import IntegerField, TextField, CharField, SlugField
from django.db.models.fields.related import ManyToManyField, ForeignKey from django.db.models.fields.related import ManyToManyField, ForeignKey
from django.db.transaction import atomic from django.db.transaction import atomic
from .models import Author, AuthorWithM2M, Book, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagUniqueRename, UniqueTest from .models import Author, AuthorWithM2M, Book, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest
class SchemaTests(TransactionTestCase): class SchemaTests(TransactionTestCase):
...@@ -20,7 +20,7 @@ class SchemaTests(TransactionTestCase): ...@@ -20,7 +20,7 @@ class SchemaTests(TransactionTestCase):
available_apps = [] available_apps = []
models = [Author, AuthorWithM2M, Book, BookWithSlug, BookWithM2M, Tag, TagUniqueRename, UniqueTest] models = [Author, AuthorWithM2M, Book, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest]
no_table_strings = ["no such table", "unknown table", "does not exist"] no_table_strings = ["no such table", "unknown table", "does not exist"]
# Utility functions # Utility functions
...@@ -234,7 +234,7 @@ class SchemaTests(TransactionTestCase): ...@@ -234,7 +234,7 @@ class SchemaTests(TransactionTestCase):
editor.create_model(BookWithM2M) editor.create_model(BookWithM2M)
# Ensure there is now an m2m table there # Ensure there is now an m2m table there
columns = self.column_classes(BookWithM2M._meta.get_field_by_name("tags")[0].rel.through) columns = self.column_classes(BookWithM2M._meta.get_field_by_name("tags")[0].rel.through)
self.assertEqual(columns['tag_id'][0], "IntegerField") self.assertEqual(columns['tagm2mtest_id'][0], "IntegerField")
def test_m2m(self): def test_m2m(self):
""" """
...@@ -243,9 +243,9 @@ class SchemaTests(TransactionTestCase): ...@@ -243,9 +243,9 @@ class SchemaTests(TransactionTestCase):
# Create the tables # Create the tables
with connection.schema_editor() as editor: with connection.schema_editor() as editor:
editor.create_model(AuthorWithM2M) editor.create_model(AuthorWithM2M)
editor.create_model(Tag) editor.create_model(TagM2MTest)
# Create an M2M field # Create an M2M field
new_field = ManyToManyField("schema.Tag", related_name="authors") new_field = ManyToManyField("schema.TagM2MTest", related_name="authors")
new_field.contribute_to_class(AuthorWithM2M, "tags") new_field.contribute_to_class(AuthorWithM2M, "tags")
try: try:
# Ensure there's no m2m table there # Ensure there's no m2m table there
...@@ -258,7 +258,7 @@ class SchemaTests(TransactionTestCase): ...@@ -258,7 +258,7 @@ class SchemaTests(TransactionTestCase):
) )
# Ensure there is now an m2m table there # Ensure there is now an m2m table there
columns = self.column_classes(new_field.rel.through) columns = self.column_classes(new_field.rel.through)
self.assertEqual(columns['tag_id'][0], "IntegerField") self.assertEqual(columns['tagm2mtest_id'][0], "IntegerField")
# Remove the M2M table again # Remove the M2M table again
with connection.schema_editor() as editor: with connection.schema_editor() as editor:
editor.remove_field( editor.remove_field(
...@@ -279,17 +279,17 @@ class SchemaTests(TransactionTestCase): ...@@ -279,17 +279,17 @@ class SchemaTests(TransactionTestCase):
with connection.schema_editor() as editor: with connection.schema_editor() as editor:
editor.create_model(Author) editor.create_model(Author)
editor.create_model(BookWithM2M) editor.create_model(BookWithM2M)
editor.create_model(Tag) editor.create_model(TagM2MTest)
editor.create_model(UniqueTest) editor.create_model(UniqueTest)
# Ensure the M2M exists and points to Tag # Ensure the M2M exists and points to TagM2MTest
constraints = connection.introspection.get_constraints(connection.cursor(), BookWithM2M._meta.get_field_by_name("tags")[0].rel.through._meta.db_table) constraints = connection.introspection.get_constraints(connection.cursor(), BookWithM2M._meta.get_field_by_name("tags")[0].rel.through._meta.db_table)
if connection.features.supports_foreign_keys: if connection.features.supports_foreign_keys:
for name, details in constraints.items(): for name, details in constraints.items():
if details['columns'] == ["tag_id"] and details['foreign_key']: if details['columns'] == ["tagm2mtest_id"] and details['foreign_key']:
self.assertEqual(details['foreign_key'], ('schema_tag', 'id')) self.assertEqual(details['foreign_key'], ('schema_tagm2mtest', 'id'))
break break
else: else:
self.fail("No FK constraint for tag_id found") self.fail("No FK constraint for tagm2mtest_id found")
# Repoint the M2M # Repoint the M2M
new_field = ManyToManyField(UniqueTest) new_field = ManyToManyField(UniqueTest)
new_field.contribute_to_class(BookWithM2M, "uniques") new_field.contribute_to_class(BookWithM2M, "uniques")
...@@ -310,7 +310,7 @@ class SchemaTests(TransactionTestCase): ...@@ -310,7 +310,7 @@ class SchemaTests(TransactionTestCase):
self.assertEqual(details['foreign_key'], ('schema_uniquetest', 'id')) self.assertEqual(details['foreign_key'], ('schema_uniquetest', 'id'))
break break
else: else:
self.fail("No FK constraint for tag_id found") self.fail("No FK constraint for uniquetest_id found")
finally: finally:
# Cleanup model states # Cleanup model states
BookWithM2M._meta.local_many_to_many.remove(new_field) BookWithM2M._meta.local_many_to_many.remove(new_field)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment