Split up contenttypes_tests.
This commit is contained in:
parent
29f607927f
commit
aa14528910
|
@ -0,0 +1,218 @@
|
|||
from unittest import mock
|
||||
|
||||
from django.contrib.contenttypes.fields import (
|
||||
GenericForeignKey, GenericRelation,
|
||||
)
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core import checks
|
||||
from django.db import models
|
||||
from django.test import SimpleTestCase, override_settings
|
||||
from django.test.utils import isolate_apps
|
||||
|
||||
|
||||
@isolate_apps('contenttypes_tests', attr_name='apps')
|
||||
class GenericForeignKeyTests(SimpleTestCase):
|
||||
|
||||
def test_missing_content_type_field(self):
|
||||
class TaggedItem(models.Model):
|
||||
# no content_type field
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey()
|
||||
|
||||
expected = [
|
||||
checks.Error(
|
||||
"The GenericForeignKey content type references the nonexistent "
|
||||
"field 'TaggedItem.content_type'.",
|
||||
obj=TaggedItem.content_object,
|
||||
id='contenttypes.E002',
|
||||
)
|
||||
]
|
||||
self.assertEqual(TaggedItem.content_object.check(), expected)
|
||||
|
||||
def test_invalid_content_type_field(self):
|
||||
class Model(models.Model):
|
||||
content_type = models.IntegerField() # should be ForeignKey
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey('content_type', 'object_id')
|
||||
|
||||
self.assertEqual(Model.content_object.check(), [
|
||||
checks.Error(
|
||||
"'Model.content_type' is not a ForeignKey.",
|
||||
hint=(
|
||||
"GenericForeignKeys must use a ForeignKey to "
|
||||
"'contenttypes.ContentType' as the 'content_type' field."
|
||||
),
|
||||
obj=Model.content_object,
|
||||
id='contenttypes.E003',
|
||||
)
|
||||
])
|
||||
|
||||
def test_content_type_field_pointing_to_wrong_model(self):
|
||||
class Model(models.Model):
|
||||
content_type = models.ForeignKey('self', models.CASCADE) # should point to ContentType
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey('content_type', 'object_id')
|
||||
|
||||
self.assertEqual(Model.content_object.check(), [
|
||||
checks.Error(
|
||||
"'Model.content_type' is not a ForeignKey to 'contenttypes.ContentType'.",
|
||||
hint=(
|
||||
"GenericForeignKeys must use a ForeignKey to "
|
||||
"'contenttypes.ContentType' as the 'content_type' field."
|
||||
),
|
||||
obj=Model.content_object,
|
||||
id='contenttypes.E004',
|
||||
)
|
||||
])
|
||||
|
||||
def test_missing_object_id_field(self):
|
||||
class TaggedItem(models.Model):
|
||||
content_type = models.ForeignKey(ContentType, models.CASCADE)
|
||||
# missing object_id field
|
||||
content_object = GenericForeignKey()
|
||||
|
||||
self.assertEqual(TaggedItem.content_object.check(), [
|
||||
checks.Error(
|
||||
"The GenericForeignKey object ID references the nonexistent "
|
||||
"field 'object_id'.",
|
||||
obj=TaggedItem.content_object,
|
||||
id='contenttypes.E001',
|
||||
)
|
||||
])
|
||||
|
||||
def test_field_name_ending_with_underscore(self):
|
||||
class Model(models.Model):
|
||||
content_type = models.ForeignKey(ContentType, models.CASCADE)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object_ = GenericForeignKey('content_type', 'object_id')
|
||||
|
||||
self.assertEqual(Model.content_object_.check(), [
|
||||
checks.Error(
|
||||
'Field names must not end with an underscore.',
|
||||
obj=Model.content_object_,
|
||||
id='fields.E001',
|
||||
)
|
||||
])
|
||||
|
||||
@override_settings(INSTALLED_APPS=['django.contrib.auth', 'django.contrib.contenttypes', 'contenttypes_tests'])
|
||||
def test_generic_foreign_key_checks_are_performed(self):
|
||||
class Model(models.Model):
|
||||
content_object = GenericForeignKey()
|
||||
|
||||
with mock.patch.object(GenericForeignKey, 'check') as check:
|
||||
checks.run_checks(app_configs=self.apps.get_app_configs())
|
||||
check.assert_called_once_with()
|
||||
|
||||
|
||||
@isolate_apps('contenttypes_tests')
|
||||
class GenericRelationTests(SimpleTestCase):
|
||||
|
||||
def test_valid_generic_relationship(self):
|
||||
class TaggedItem(models.Model):
|
||||
content_type = models.ForeignKey(ContentType, models.CASCADE)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey()
|
||||
|
||||
class Bookmark(models.Model):
|
||||
tags = GenericRelation('TaggedItem')
|
||||
|
||||
self.assertEqual(Bookmark.tags.field.check(), [])
|
||||
|
||||
def test_valid_generic_relationship_with_explicit_fields(self):
|
||||
class TaggedItem(models.Model):
|
||||
custom_content_type = models.ForeignKey(ContentType, models.CASCADE)
|
||||
custom_object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey('custom_content_type', 'custom_object_id')
|
||||
|
||||
class Bookmark(models.Model):
|
||||
tags = GenericRelation(
|
||||
'TaggedItem',
|
||||
content_type_field='custom_content_type',
|
||||
object_id_field='custom_object_id',
|
||||
)
|
||||
|
||||
self.assertEqual(Bookmark.tags.field.check(), [])
|
||||
|
||||
def test_pointing_to_missing_model(self):
|
||||
class Model(models.Model):
|
||||
rel = GenericRelation('MissingModel')
|
||||
|
||||
self.assertEqual(Model.rel.field.check(), [
|
||||
checks.Error(
|
||||
"Field defines a relation with model 'MissingModel', "
|
||||
"which is either not installed, or is abstract.",
|
||||
obj=Model.rel.field,
|
||||
id='fields.E300',
|
||||
)
|
||||
])
|
||||
|
||||
def test_valid_self_referential_generic_relationship(self):
|
||||
class Model(models.Model):
|
||||
rel = GenericRelation('Model')
|
||||
content_type = models.ForeignKey(ContentType, models.CASCADE)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey('content_type', 'object_id')
|
||||
|
||||
self.assertEqual(Model.rel.field.check(), [])
|
||||
|
||||
def test_missing_generic_foreign_key(self):
|
||||
class TaggedItem(models.Model):
|
||||
content_type = models.ForeignKey(ContentType, models.CASCADE)
|
||||
object_id = models.PositiveIntegerField()
|
||||
|
||||
class Bookmark(models.Model):
|
||||
tags = GenericRelation('TaggedItem')
|
||||
|
||||
self.assertEqual(Bookmark.tags.field.check(), [
|
||||
checks.Error(
|
||||
"The GenericRelation defines a relation with the model "
|
||||
"'contenttypes_tests.TaggedItem', but that model does not have a "
|
||||
"GenericForeignKey.",
|
||||
obj=Bookmark.tags.field,
|
||||
id='contenttypes.E004',
|
||||
)
|
||||
])
|
||||
|
||||
@override_settings(TEST_SWAPPED_MODEL='contenttypes_tests.Replacement')
|
||||
def test_pointing_to_swapped_model(self):
|
||||
class Replacement(models.Model):
|
||||
pass
|
||||
|
||||
class SwappedModel(models.Model):
|
||||
content_type = models.ForeignKey(ContentType, models.CASCADE)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey()
|
||||
|
||||
class Meta:
|
||||
swappable = 'TEST_SWAPPED_MODEL'
|
||||
|
||||
class Model(models.Model):
|
||||
rel = GenericRelation('SwappedModel')
|
||||
|
||||
self.assertEqual(Model.rel.field.check(), [
|
||||
checks.Error(
|
||||
"Field defines a relation with the model "
|
||||
"'contenttypes_tests.SwappedModel', "
|
||||
"which has been swapped out.",
|
||||
hint="Update the relation to point at 'settings.TEST_SWAPPED_MODEL'.",
|
||||
obj=Model.rel.field,
|
||||
id='fields.E301',
|
||||
)
|
||||
])
|
||||
|
||||
def test_field_name_ending_with_underscore(self):
|
||||
class TaggedItem(models.Model):
|
||||
content_type = models.ForeignKey(ContentType, models.CASCADE)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey()
|
||||
|
||||
class InvalidBookmark(models.Model):
|
||||
tags_ = GenericRelation('TaggedItem')
|
||||
|
||||
self.assertEqual(InvalidBookmark.tags_.field.check(), [
|
||||
checks.Error(
|
||||
'Field names must not end with an underscore.',
|
||||
obj=InvalidBookmark.tags_.field,
|
||||
id='fields.E001',
|
||||
)
|
||||
])
|
|
@ -0,0 +1,13 @@
|
|||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.db import models
|
||||
from django.test import SimpleTestCase
|
||||
from django.test.utils import isolate_apps
|
||||
|
||||
|
||||
@isolate_apps('contenttypes_tests')
|
||||
class GenericForeignKeyTests(SimpleTestCase):
|
||||
|
||||
def test_str(self):
|
||||
class Model(models.Model):
|
||||
field = GenericForeignKey()
|
||||
self.assertEqual(str(Model.field), 'contenttypes_tests.Model.field')
|
|
@ -0,0 +1,65 @@
|
|||
from unittest import mock
|
||||
|
||||
from django.apps.registry import Apps, apps
|
||||
from django.contrib.contenttypes import management as contenttypes_management
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.management import call_command
|
||||
from django.test import TestCase
|
||||
from django.test.utils import captured_stdout
|
||||
|
||||
from .models import ModelWithNullFKToSite, Post
|
||||
|
||||
|
||||
class UpdateContentTypesTests(TestCase):
|
||||
def setUp(self):
|
||||
self.before_count = ContentType.objects.count()
|
||||
self.content_type = ContentType.objects.create(app_label='contenttypes_tests', model='Fake')
|
||||
self.app_config = apps.get_app_config('contenttypes_tests')
|
||||
|
||||
def test_interactive_true_with_dependent_objects(self):
|
||||
"""
|
||||
interactive mode of remove_stale_contenttypes (the default) deletes
|
||||
stale contenttypes and warn of dependent objects.
|
||||
"""
|
||||
post = Post.objects.create(title='post', content_type=self.content_type)
|
||||
# A related object is needed to show that a custom collector with
|
||||
# can_fast_delete=False is needed.
|
||||
ModelWithNullFKToSite.objects.create(post=post)
|
||||
with mock.patch('builtins.input', return_value='yes'):
|
||||
with captured_stdout() as stdout:
|
||||
call_command('remove_stale_contenttypes', verbosity=2, stdout=stdout)
|
||||
self.assertEqual(Post.objects.count(), 0)
|
||||
output = stdout.getvalue()
|
||||
self.assertIn('- Content type for contenttypes_tests.Fake', output)
|
||||
self.assertIn('- 1 contenttypes_tests.Post object(s)', output)
|
||||
self.assertIn('- 1 contenttypes_tests.ModelWithNullFKToSite', output)
|
||||
self.assertIn('Deleting stale content type', output)
|
||||
self.assertEqual(ContentType.objects.count(), self.before_count)
|
||||
|
||||
def test_interactive_true_without_dependent_objects(self):
|
||||
"""
|
||||
interactive mode of remove_stale_contenttypes (the default) deletes
|
||||
stale contenttypes even if there aren't any dependent objects.
|
||||
"""
|
||||
with mock.patch('builtins.input', return_value='yes'):
|
||||
with captured_stdout() as stdout:
|
||||
call_command('remove_stale_contenttypes', verbosity=2)
|
||||
self.assertIn("Deleting stale content type", stdout.getvalue())
|
||||
self.assertEqual(ContentType.objects.count(), self.before_count)
|
||||
|
||||
def test_interactive_false(self):
|
||||
"""
|
||||
non-interactive mode of remove_stale_contenttypes doesn't delete
|
||||
stale content types.
|
||||
"""
|
||||
with captured_stdout() as stdout:
|
||||
call_command('remove_stale_contenttypes', interactive=False, verbosity=2)
|
||||
self.assertIn("Stale content types remain.", stdout.getvalue())
|
||||
self.assertEqual(ContentType.objects.count(), self.before_count + 1)
|
||||
|
||||
def test_unavailable_content_type_model(self):
|
||||
"""A ContentType isn't created if the model isn't available."""
|
||||
apps = Apps()
|
||||
with self.assertNumQueries(0):
|
||||
contenttypes_management.create_contenttypes(self.app_config, interactive=False, verbosity=0, apps=apps)
|
||||
self.assertEqual(ContentType.objects.count(), self.before_count + 1)
|
|
@ -1,11 +1,12 @@
|
|||
from django.contrib.contenttypes.models import ContentType, ContentTypeManager
|
||||
from django.contrib.contenttypes.views import shortcut
|
||||
from django.contrib.sites.shortcuts import get_current_site
|
||||
from django.db import connections
|
||||
from django.http import Http404, HttpRequest
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
from .models import (
|
||||
ConcreteModel, FooWithBrokenAbsoluteUrl, FooWithoutUrl, FooWithUrl,
|
||||
Author, ConcreteModel, FooWithBrokenAbsoluteUrl, FooWithoutUrl, FooWithUrl,
|
||||
ProxyModel,
|
||||
)
|
||||
|
||||
|
@ -20,11 +21,10 @@ class ContentTypesTests(TestCase):
|
|||
|
||||
def test_lookup_cache(self):
|
||||
"""
|
||||
Make sure that the content type cache (see ContentTypeManager)
|
||||
works correctly. Lookups for a particular content type -- by model, ID
|
||||
or natural key -- should hit the database only on the first lookup.
|
||||
The content type cache (see ContentTypeManager) works correctly.
|
||||
Lookups for a particular content type -- by model, ID, or natural key
|
||||
-- should hit the database only on the first lookup.
|
||||
"""
|
||||
|
||||
# At this point, a lookup for a ContentType should hit the DB
|
||||
with self.assertNumQueries(1):
|
||||
ContentType.objects.get_for_model(ContentType)
|
||||
|
@ -244,8 +244,35 @@ class ContentTypesTests(TestCase):
|
|||
self.assertEqual(str(ct), 'OldModel')
|
||||
self.assertIsNone(ct.model_class())
|
||||
|
||||
# Make sure stale ContentTypes can be fetched like any other object.
|
||||
# Before Django 1.6 this caused a NoneType error in the caching mechanism.
|
||||
# Instead, just return the ContentType object and let the app detect stale states.
|
||||
# Stale ContentTypes can be fetched like any other object.
|
||||
ct_fetched = ContentType.objects.get_for_id(ct.pk)
|
||||
self.assertIsNone(ct_fetched.model_class())
|
||||
|
||||
|
||||
class TestRouter:
|
||||
def db_for_read(self, model, **hints):
|
||||
return 'other'
|
||||
|
||||
def db_for_write(self, model, **hints):
|
||||
return 'default'
|
||||
|
||||
|
||||
@override_settings(DATABASE_ROUTERS=[TestRouter()])
|
||||
class ContentTypesMultidbTests(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# When a test starts executing, only the "default" database is
|
||||
# connected. Connect to the "other" database here because otherwise it
|
||||
# will be connected later when it's queried. Some database backends
|
||||
# perform extra queries upon connecting (MySQL executes
|
||||
# "SET SQL_AUTO_IS_NULL = 0"), which will affect assertNumQueries().
|
||||
connections['other'].ensure_connection()
|
||||
|
||||
def test_multidb(self):
|
||||
"""
|
||||
When using multiple databases, ContentType.objects.get_for_model() uses
|
||||
db_for_read().
|
||||
"""
|
||||
ContentType.objects.clear_cache()
|
||||
with self.assertNumQueries(0, using='default'), self.assertNumQueries(1, using='other'):
|
||||
ContentType.objects.get_for_model(Author)
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
from django.apps.registry import apps
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes import management as contenttypes_management
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.management import call_command
|
||||
from django.db import migrations, models
|
||||
from django.test import TransactionTestCase, override_settings
|
||||
|
||||
|
||||
@override_settings(
|
||||
MIGRATION_MODULES=dict(
|
||||
settings.MIGRATION_MODULES,
|
||||
contenttypes_tests='contenttypes_tests.operations_migrations',
|
||||
),
|
||||
)
|
||||
class ContentTypeOperationsTests(TransactionTestCase):
|
||||
available_apps = [
|
||||
'contenttypes_tests',
|
||||
'django.contrib.contenttypes',
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
app_config = apps.get_app_config('contenttypes_tests')
|
||||
models.signals.post_migrate.connect(self.assertOperationsInjected, sender=app_config)
|
||||
|
||||
def tearDown(self):
|
||||
app_config = apps.get_app_config('contenttypes_tests')
|
||||
models.signals.post_migrate.disconnect(self.assertOperationsInjected, sender=app_config)
|
||||
|
||||
def assertOperationsInjected(self, plan, **kwargs):
|
||||
for migration, _backward in plan:
|
||||
operations = iter(migration.operations)
|
||||
for operation in operations:
|
||||
if isinstance(operation, migrations.RenameModel):
|
||||
next_operation = next(operations)
|
||||
self.assertIsInstance(next_operation, contenttypes_management.RenameContentType)
|
||||
self.assertEqual(next_operation.app_label, migration.app_label)
|
||||
self.assertEqual(next_operation.old_model, operation.old_name_lower)
|
||||
self.assertEqual(next_operation.new_model, operation.new_name_lower)
|
||||
|
||||
def test_existing_content_type_rename(self):
|
||||
ContentType.objects.create(app_label='contenttypes_tests', model='foo')
|
||||
call_command('migrate', 'contenttypes_tests', database='default', interactive=False, verbosity=0,)
|
||||
self.assertFalse(ContentType.objects.filter(app_label='contenttypes_tests', model='foo').exists())
|
||||
self.assertTrue(ContentType.objects.filter(app_label='contenttypes_tests', model='renamedfoo').exists())
|
||||
call_command('migrate', 'contenttypes_tests', 'zero', database='default', interactive=False, verbosity=0)
|
||||
self.assertTrue(ContentType.objects.filter(app_label='contenttypes_tests', model='foo').exists())
|
||||
self.assertFalse(ContentType.objects.filter(app_label='contenttypes_tests', model='renamedfoo').exists())
|
||||
|
||||
def test_missing_content_type_rename_ignore(self):
|
||||
call_command('migrate', 'contenttypes_tests', database='default', interactive=False, verbosity=0,)
|
||||
self.assertFalse(ContentType.objects.filter(app_label='contenttypes_tests', model='foo').exists())
|
||||
self.assertTrue(ContentType.objects.filter(app_label='contenttypes_tests', model='renamedfoo').exists())
|
||||
call_command('migrate', 'contenttypes_tests', 'zero', database='default', interactive=False, verbosity=0)
|
||||
self.assertTrue(ContentType.objects.filter(app_label='contenttypes_tests', model='foo').exists())
|
||||
self.assertFalse(ContentType.objects.filter(app_label='contenttypes_tests', model='renamedfoo').exists())
|
||||
|
||||
def test_content_type_rename_conflict(self):
|
||||
ContentType.objects.create(app_label='contenttypes_tests', model='foo')
|
||||
ContentType.objects.create(app_label='contenttypes_tests', model='renamedfoo')
|
||||
call_command('migrate', 'contenttypes_tests', database='default', interactive=False, verbosity=0)
|
||||
self.assertTrue(ContentType.objects.filter(app_label='contenttypes_tests', model='foo').exists())
|
||||
self.assertTrue(ContentType.objects.filter(app_label='contenttypes_tests', model='renamedfoo').exists())
|
||||
call_command('migrate', 'contenttypes_tests', 'zero', database='default', interactive=False, verbosity=0)
|
||||
self.assertTrue(ContentType.objects.filter(app_label='contenttypes_tests', model='foo').exists())
|
||||
self.assertTrue(ContentType.objects.filter(app_label='contenttypes_tests', model='renamedfoo').exists())
|
|
@ -0,0 +1,121 @@
|
|||
import datetime
|
||||
from unittest import mock
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.sites.models import Site
|
||||
from django.db import models
|
||||
from django.test import TestCase, override_settings
|
||||
from django.test.utils import isolate_apps
|
||||
|
||||
from .models import (
|
||||
Article, Author, ModelWithNullFKToSite, SchemeIncludedURL,
|
||||
Site as MockSite,
|
||||
)
|
||||
|
||||
|
||||
@override_settings(ROOT_URLCONF='contenttypes_tests.urls')
|
||||
class ContentTypesViewsTests(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
# Don't use the manager to ensure the site exists with pk=1, regardless
|
||||
# of whether or not it already exists.
|
||||
cls.site1 = Site(pk=1, domain='testserver', name='testserver')
|
||||
cls.site1.save()
|
||||
cls.author1 = Author.objects.create(name='Boris')
|
||||
cls.article1 = Article.objects.create(
|
||||
title='Old Article', slug='old_article', author=cls.author1,
|
||||
date_created=datetime.datetime(2001, 1, 1, 21, 22, 23),
|
||||
)
|
||||
cls.article2 = Article.objects.create(
|
||||
title='Current Article', slug='current_article', author=cls.author1,
|
||||
date_created=datetime.datetime(2007, 9, 17, 21, 22, 23),
|
||||
)
|
||||
cls.article3 = Article.objects.create(
|
||||
title='Future Article', slug='future_article', author=cls.author1,
|
||||
date_created=datetime.datetime(3000, 1, 1, 21, 22, 23),
|
||||
)
|
||||
cls.scheme1 = SchemeIncludedURL.objects.create(url='http://test_scheme_included_http/')
|
||||
cls.scheme2 = SchemeIncludedURL.objects.create(url='https://test_scheme_included_https/')
|
||||
cls.scheme3 = SchemeIncludedURL.objects.create(url='//test_default_scheme_kept/')
|
||||
|
||||
def setUp(self):
|
||||
Site.objects.clear_cache()
|
||||
|
||||
def test_shortcut_with_absolute_url(self):
|
||||
"Can view a shortcut for an Author object that has a get_absolute_url method"
|
||||
for obj in Author.objects.all():
|
||||
short_url = '/shortcut/%s/%s/' % (ContentType.objects.get_for_model(Author).id, obj.pk)
|
||||
response = self.client.get(short_url)
|
||||
self.assertRedirects(response, 'http://testserver%s' % obj.get_absolute_url(), target_status_code=404)
|
||||
|
||||
def test_shortcut_with_absolute_url_including_scheme(self):
|
||||
"""
|
||||
Can view a shortcut when object's get_absolute_url returns a full URL
|
||||
the tested URLs are: "http://...", "https://..." and "//..."
|
||||
"""
|
||||
for obj in SchemeIncludedURL.objects.all():
|
||||
short_url = '/shortcut/%s/%s/' % (ContentType.objects.get_for_model(SchemeIncludedURL).id, obj.pk)
|
||||
response = self.client.get(short_url)
|
||||
self.assertRedirects(response, obj.get_absolute_url(), fetch_redirect_response=False)
|
||||
|
||||
def test_shortcut_no_absolute_url(self):
|
||||
"""
|
||||
Shortcuts for an object that has no get_absolute_url() method raise
|
||||
404.
|
||||
"""
|
||||
for obj in Article.objects.all():
|
||||
short_url = '/shortcut/%s/%s/' % (ContentType.objects.get_for_model(Article).id, obj.pk)
|
||||
response = self.client.get(short_url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_wrong_type_pk(self):
|
||||
short_url = '/shortcut/%s/%s/' % (ContentType.objects.get_for_model(Author).id, 'nobody/expects')
|
||||
response = self.client.get(short_url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_shortcut_bad_pk(self):
|
||||
short_url = '/shortcut/%s/%s/' % (ContentType.objects.get_for_model(Author).id, '42424242')
|
||||
response = self.client.get(short_url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_nonint_content_type(self):
|
||||
an_author = Author.objects.all()[0]
|
||||
short_url = '/shortcut/%s/%s/' % ('spam', an_author.pk)
|
||||
response = self.client.get(short_url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_bad_content_type(self):
|
||||
an_author = Author.objects.all()[0]
|
||||
short_url = '/shortcut/%s/%s/' % (42424242, an_author.pk)
|
||||
response = self.client.get(short_url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
@mock.patch('django.apps.apps.get_model')
|
||||
def test_shortcut_view_with_null_site_fk(self, get_model):
|
||||
"""
|
||||
The shortcut view works if a model's ForeignKey to site is None.
|
||||
"""
|
||||
get_model.side_effect = lambda *args, **kwargs: MockSite if args[0] == 'sites.Site' else ModelWithNullFKToSite
|
||||
|
||||
obj = ModelWithNullFKToSite.objects.create(title='title')
|
||||
url = '/shortcut/%s/%s/' % (ContentType.objects.get_for_model(ModelWithNullFKToSite).id, obj.pk)
|
||||
response = self.client.get(url)
|
||||
self.assertRedirects(response, '%s' % obj.get_absolute_url(), fetch_redirect_response=False)
|
||||
|
||||
@isolate_apps('contenttypes_tests')
|
||||
def test_create_contenttype_on_the_spot(self):
|
||||
"""
|
||||
ContentTypeManager.get_for_model() creates the corresponding content
|
||||
type if it doesn't exist in the database.
|
||||
"""
|
||||
class ModelCreatedOnTheFly(models.Model):
|
||||
name = models.CharField()
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'a model created on the fly'
|
||||
|
||||
ct = ContentType.objects.get_for_model(ModelCreatedOnTheFly)
|
||||
self.assertEqual(ct.app_label, 'contenttypes_tests')
|
||||
self.assertEqual(ct.model, 'modelcreatedonthefly')
|
||||
self.assertEqual(str(ct), 'modelcreatedonthefly')
|
|
@ -1,530 +0,0 @@
|
|||
import datetime
|
||||
from unittest import mock
|
||||
|
||||
from django.apps.registry import Apps, apps
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes import management as contenttypes_management
|
||||
from django.contrib.contenttypes.fields import (
|
||||
GenericForeignKey, GenericRelation,
|
||||
)
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core import checks, management
|
||||
from django.core.management import call_command
|
||||
from django.db import connections, migrations, models
|
||||
from django.test import (
|
||||
SimpleTestCase, TestCase, TransactionTestCase, override_settings,
|
||||
)
|
||||
from django.test.utils import captured_stdout, isolate_apps
|
||||
|
||||
from .models import (
|
||||
Article, Author, ModelWithNullFKToSite, Post, SchemeIncludedURL,
|
||||
Site as MockSite,
|
||||
)
|
||||
|
||||
|
||||
@override_settings(ROOT_URLCONF='contenttypes_tests.urls')
|
||||
class ContentTypesViewsTests(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
# don't use the manager because we want to ensure the site exists
|
||||
# with pk=1, regardless of whether or not it already exists.
|
||||
cls.site1 = Site(pk=1, domain='testserver', name='testserver')
|
||||
cls.site1.save()
|
||||
cls.author1 = Author.objects.create(name='Boris')
|
||||
cls.article1 = Article.objects.create(
|
||||
title='Old Article', slug='old_article', author=cls.author1,
|
||||
date_created=datetime.datetime(2001, 1, 1, 21, 22, 23)
|
||||
)
|
||||
cls.article2 = Article.objects.create(
|
||||
title='Current Article', slug='current_article', author=cls.author1,
|
||||
date_created=datetime.datetime(2007, 9, 17, 21, 22, 23)
|
||||
)
|
||||
cls.article3 = Article.objects.create(
|
||||
title='Future Article', slug='future_article', author=cls.author1,
|
||||
date_created=datetime.datetime(3000, 1, 1, 21, 22, 23)
|
||||
)
|
||||
cls.scheme1 = SchemeIncludedURL.objects.create(url='http://test_scheme_included_http/')
|
||||
cls.scheme2 = SchemeIncludedURL.objects.create(url='https://test_scheme_included_https/')
|
||||
cls.scheme3 = SchemeIncludedURL.objects.create(url='//test_default_scheme_kept/')
|
||||
|
||||
def setUp(self):
|
||||
Site.objects.clear_cache()
|
||||
|
||||
def test_shortcut_with_absolute_url(self):
|
||||
"Can view a shortcut for an Author object that has a get_absolute_url method"
|
||||
for obj in Author.objects.all():
|
||||
short_url = '/shortcut/%s/%s/' % (ContentType.objects.get_for_model(Author).id, obj.pk)
|
||||
response = self.client.get(short_url)
|
||||
self.assertRedirects(response, 'http://testserver%s' % obj.get_absolute_url(),
|
||||
status_code=302, target_status_code=404)
|
||||
|
||||
def test_shortcut_with_absolute_url_including_scheme(self):
|
||||
"""
|
||||
Can view a shortcut when object's get_absolute_url returns a full URL
|
||||
the tested URLs are: "http://...", "https://..." and "//..."
|
||||
"""
|
||||
for obj in SchemeIncludedURL.objects.all():
|
||||
short_url = '/shortcut/%s/%s/' % (ContentType.objects.get_for_model(SchemeIncludedURL).id, obj.pk)
|
||||
response = self.client.get(short_url)
|
||||
self.assertRedirects(response, obj.get_absolute_url(),
|
||||
status_code=302,
|
||||
fetch_redirect_response=False)
|
||||
|
||||
def test_shortcut_no_absolute_url(self):
|
||||
"Shortcuts for an object that has no get_absolute_url method raises 404"
|
||||
for obj in Article.objects.all():
|
||||
short_url = '/shortcut/%s/%s/' % (ContentType.objects.get_for_model(Article).id, obj.pk)
|
||||
response = self.client.get(short_url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_wrong_type_pk(self):
|
||||
short_url = '/shortcut/%s/%s/' % (ContentType.objects.get_for_model(Author).id, 'nobody/expects')
|
||||
response = self.client.get(short_url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_shortcut_bad_pk(self):
|
||||
short_url = '/shortcut/%s/%s/' % (ContentType.objects.get_for_model(Author).id, '42424242')
|
||||
response = self.client.get(short_url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_nonint_content_type(self):
|
||||
an_author = Author.objects.all()[0]
|
||||
short_url = '/shortcut/%s/%s/' % ('spam', an_author.pk)
|
||||
response = self.client.get(short_url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_bad_content_type(self):
|
||||
an_author = Author.objects.all()[0]
|
||||
short_url = '/shortcut/%s/%s/' % (42424242, an_author.pk)
|
||||
response = self.client.get(short_url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
@mock.patch('django.apps.apps.get_model')
|
||||
def test_shortcut_view_with_null_site_fk(self, get_model):
|
||||
"""
|
||||
The shortcut view works if a model's ForeignKey to site is None.
|
||||
"""
|
||||
get_model.side_effect = lambda *args, **kwargs: MockSite if args[0] == 'sites.Site' else ModelWithNullFKToSite
|
||||
|
||||
obj = ModelWithNullFKToSite.objects.create(title='title')
|
||||
url = '/shortcut/%s/%s/' % (ContentType.objects.get_for_model(ModelWithNullFKToSite).id, obj.pk)
|
||||
response = self.client.get(url)
|
||||
self.assertRedirects(
|
||||
response, '%s' % obj.get_absolute_url(),
|
||||
fetch_redirect_response=False,
|
||||
)
|
||||
|
||||
def test_create_contenttype_on_the_spot(self):
|
||||
"""
|
||||
Make sure ContentTypeManager.get_for_model creates the corresponding
|
||||
content type if it doesn't exist in the database (for some reason).
|
||||
"""
|
||||
|
||||
class ModelCreatedOnTheFly(models.Model):
|
||||
name = models.CharField()
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'a model created on the fly'
|
||||
app_label = 'my_great_app'
|
||||
apps = Apps()
|
||||
|
||||
ct = ContentType.objects.get_for_model(ModelCreatedOnTheFly)
|
||||
self.assertEqual(ct.app_label, 'my_great_app')
|
||||
self.assertEqual(ct.model, 'modelcreatedonthefly')
|
||||
self.assertEqual(str(ct), 'modelcreatedonthefly')
|
||||
|
||||
|
||||
@override_settings(SILENCED_SYSTEM_CHECKS=['fields.W342']) # ForeignKey(unique=True)
|
||||
@isolate_apps('contenttypes_tests', attr_name='apps')
|
||||
class GenericForeignKeyTests(SimpleTestCase):
|
||||
|
||||
def test_str(self):
|
||||
class Model(models.Model):
|
||||
field = GenericForeignKey()
|
||||
self.assertEqual(str(Model.field), "contenttypes_tests.Model.field")
|
||||
|
||||
def test_missing_content_type_field(self):
|
||||
class TaggedItem(models.Model):
|
||||
# no content_type field
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey()
|
||||
|
||||
errors = TaggedItem.content_object.check()
|
||||
expected = [
|
||||
checks.Error(
|
||||
"The GenericForeignKey content type references the nonexistent field 'TaggedItem.content_type'.",
|
||||
obj=TaggedItem.content_object,
|
||||
id='contenttypes.E002',
|
||||
)
|
||||
]
|
||||
self.assertEqual(errors, expected)
|
||||
|
||||
def test_invalid_content_type_field(self):
|
||||
class Model(models.Model):
|
||||
content_type = models.IntegerField() # should be ForeignKey
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey(
|
||||
'content_type', 'object_id')
|
||||
|
||||
errors = Model.content_object.check()
|
||||
expected = [
|
||||
checks.Error(
|
||||
"'Model.content_type' is not a ForeignKey.",
|
||||
hint=(
|
||||
"GenericForeignKeys must use a ForeignKey to "
|
||||
"'contenttypes.ContentType' as the 'content_type' field."
|
||||
),
|
||||
obj=Model.content_object,
|
||||
id='contenttypes.E003',
|
||||
)
|
||||
]
|
||||
self.assertEqual(errors, expected)
|
||||
|
||||
def test_content_type_field_pointing_to_wrong_model(self):
|
||||
class Model(models.Model):
|
||||
content_type = models.ForeignKey('self', models.CASCADE) # should point to ContentType
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey(
|
||||
'content_type', 'object_id')
|
||||
|
||||
errors = Model.content_object.check()
|
||||
expected = [
|
||||
checks.Error(
|
||||
"'Model.content_type' is not a ForeignKey to 'contenttypes.ContentType'.",
|
||||
hint=(
|
||||
"GenericForeignKeys must use a ForeignKey to "
|
||||
"'contenttypes.ContentType' as the 'content_type' field."
|
||||
),
|
||||
obj=Model.content_object,
|
||||
id='contenttypes.E004',
|
||||
)
|
||||
]
|
||||
self.assertEqual(errors, expected)
|
||||
|
||||
def test_missing_object_id_field(self):
|
||||
class TaggedItem(models.Model):
|
||||
content_type = models.ForeignKey(ContentType, models.CASCADE)
|
||||
# missing object_id field
|
||||
content_object = GenericForeignKey()
|
||||
|
||||
errors = TaggedItem.content_object.check()
|
||||
expected = [
|
||||
checks.Error(
|
||||
"The GenericForeignKey object ID references the nonexistent field 'object_id'.",
|
||||
obj=TaggedItem.content_object,
|
||||
id='contenttypes.E001',
|
||||
)
|
||||
]
|
||||
self.assertEqual(errors, expected)
|
||||
|
||||
def test_field_name_ending_with_underscore(self):
|
||||
class Model(models.Model):
|
||||
content_type = models.ForeignKey(ContentType, models.CASCADE)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object_ = GenericForeignKey(
|
||||
'content_type', 'object_id')
|
||||
|
||||
errors = Model.content_object_.check()
|
||||
expected = [
|
||||
checks.Error(
|
||||
'Field names must not end with an underscore.',
|
||||
obj=Model.content_object_,
|
||||
id='fields.E001',
|
||||
)
|
||||
]
|
||||
self.assertEqual(errors, expected)
|
||||
|
||||
@override_settings(INSTALLED_APPS=['django.contrib.auth', 'django.contrib.contenttypes', 'contenttypes_tests'])
|
||||
def test_generic_foreign_key_checks_are_performed(self):
|
||||
class Model(models.Model):
|
||||
content_object = GenericForeignKey()
|
||||
|
||||
with mock.patch.object(GenericForeignKey, 'check') as check:
|
||||
checks.run_checks(app_configs=self.apps.get_app_configs())
|
||||
check.assert_called_once_with()
|
||||
|
||||
|
||||
@isolate_apps('contenttypes_tests')
|
||||
class GenericRelationshipTests(SimpleTestCase):
|
||||
|
||||
def test_valid_generic_relationship(self):
|
||||
class TaggedItem(models.Model):
|
||||
content_type = models.ForeignKey(ContentType, models.CASCADE)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey()
|
||||
|
||||
class Bookmark(models.Model):
|
||||
tags = GenericRelation('TaggedItem')
|
||||
|
||||
errors = Bookmark.tags.field.check()
|
||||
self.assertEqual(errors, [])
|
||||
|
||||
def test_valid_generic_relationship_with_explicit_fields(self):
|
||||
class TaggedItem(models.Model):
|
||||
custom_content_type = models.ForeignKey(ContentType, models.CASCADE)
|
||||
custom_object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey(
|
||||
'custom_content_type', 'custom_object_id')
|
||||
|
||||
class Bookmark(models.Model):
|
||||
tags = GenericRelation(
|
||||
'TaggedItem',
|
||||
content_type_field='custom_content_type',
|
||||
object_id_field='custom_object_id',
|
||||
)
|
||||
|
||||
errors = Bookmark.tags.field.check()
|
||||
self.assertEqual(errors, [])
|
||||
|
||||
def test_pointing_to_missing_model(self):
|
||||
class Model(models.Model):
|
||||
rel = GenericRelation('MissingModel')
|
||||
|
||||
errors = Model.rel.field.check()
|
||||
expected = [
|
||||
checks.Error(
|
||||
"Field defines a relation with model 'MissingModel', "
|
||||
"which is either not installed, or is abstract.",
|
||||
obj=Model.rel.field,
|
||||
id='fields.E300',
|
||||
)
|
||||
]
|
||||
self.assertEqual(errors, expected)
|
||||
|
||||
def test_valid_self_referential_generic_relationship(self):
|
||||
class Model(models.Model):
|
||||
rel = GenericRelation('Model')
|
||||
content_type = models.ForeignKey(ContentType, models.CASCADE)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey(
|
||||
'content_type', 'object_id')
|
||||
|
||||
errors = Model.rel.field.check()
|
||||
self.assertEqual(errors, [])
|
||||
|
||||
def test_missing_generic_foreign_key(self):
|
||||
class TaggedItem(models.Model):
|
||||
content_type = models.ForeignKey(ContentType, models.CASCADE)
|
||||
object_id = models.PositiveIntegerField()
|
||||
|
||||
class Bookmark(models.Model):
|
||||
tags = GenericRelation('TaggedItem')
|
||||
|
||||
errors = Bookmark.tags.field.check()
|
||||
expected = [
|
||||
checks.Error(
|
||||
"The GenericRelation defines a relation with the model "
|
||||
"'contenttypes_tests.TaggedItem', but that model does not have a "
|
||||
"GenericForeignKey.",
|
||||
obj=Bookmark.tags.field,
|
||||
id='contenttypes.E004',
|
||||
)
|
||||
]
|
||||
self.assertEqual(errors, expected)
|
||||
|
||||
@override_settings(TEST_SWAPPED_MODEL='contenttypes_tests.Replacement')
|
||||
def test_pointing_to_swapped_model(self):
|
||||
class Replacement(models.Model):
|
||||
pass
|
||||
|
||||
class SwappedModel(models.Model):
|
||||
content_type = models.ForeignKey(ContentType, models.CASCADE)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey()
|
||||
|
||||
class Meta:
|
||||
swappable = 'TEST_SWAPPED_MODEL'
|
||||
|
||||
class Model(models.Model):
|
||||
rel = GenericRelation('SwappedModel')
|
||||
|
||||
errors = Model.rel.field.check()
|
||||
expected = [
|
||||
checks.Error(
|
||||
"Field defines a relation with the model "
|
||||
"'contenttypes_tests.SwappedModel', "
|
||||
"which has been swapped out.",
|
||||
hint="Update the relation to point at 'settings.TEST_SWAPPED_MODEL'.",
|
||||
obj=Model.rel.field,
|
||||
id='fields.E301',
|
||||
)
|
||||
]
|
||||
self.assertEqual(errors, expected)
|
||||
|
||||
def test_field_name_ending_with_underscore(self):
|
||||
class TaggedItem(models.Model):
|
||||
content_type = models.ForeignKey(ContentType, models.CASCADE)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey()
|
||||
|
||||
class InvalidBookmark(models.Model):
|
||||
tags_ = GenericRelation('TaggedItem')
|
||||
|
||||
errors = InvalidBookmark.tags_.field.check()
|
||||
expected = [
|
||||
checks.Error(
|
||||
'Field names must not end with an underscore.',
|
||||
obj=InvalidBookmark.tags_.field,
|
||||
id='fields.E001',
|
||||
)
|
||||
]
|
||||
self.assertEqual(errors, expected)
|
||||
|
||||
|
||||
class UpdateContentTypesTests(TestCase):
|
||||
def setUp(self):
|
||||
self.before_count = ContentType.objects.count()
|
||||
self.content_type = ContentType.objects.create(app_label='contenttypes_tests', model='Fake')
|
||||
self.app_config = apps.get_app_config('contenttypes_tests')
|
||||
|
||||
def test_interactive_true_with_dependent_objects(self):
|
||||
"""
|
||||
interactive mode of remove_stale_contenttypes (the default) should
|
||||
delete stale contenttypes and warn of dependent objects.
|
||||
"""
|
||||
post = Post.objects.create(title='post', content_type=self.content_type)
|
||||
# A related object is needed to show that a custom collector with
|
||||
# can_fast_delete=False is needed.
|
||||
ModelWithNullFKToSite.objects.create(post=post)
|
||||
with mock.patch('builtins.input', return_value='yes'):
|
||||
with captured_stdout() as stdout:
|
||||
call_command('remove_stale_contenttypes', verbosity=2, stdout=stdout)
|
||||
self.assertEqual(Post.objects.count(), 0)
|
||||
output = stdout.getvalue()
|
||||
self.assertIn('- Content type for contenttypes_tests.Fake', output)
|
||||
self.assertIn('- 1 contenttypes_tests.Post object(s)', output)
|
||||
self.assertIn('- 1 contenttypes_tests.ModelWithNullFKToSite', output)
|
||||
self.assertIn('Deleting stale content type', output)
|
||||
self.assertEqual(ContentType.objects.count(), self.before_count)
|
||||
|
||||
def test_interactive_true_without_dependent_objects(self):
|
||||
"""
|
||||
interactive mode of remove_stale_contenttypes (the default) should
|
||||
delete stale contenttypes even if there aren't any dependent objects.
|
||||
"""
|
||||
with mock.patch('builtins.input', return_value='yes'):
|
||||
with captured_stdout() as stdout:
|
||||
call_command('remove_stale_contenttypes', verbosity=2)
|
||||
self.assertIn("Deleting stale content type", stdout.getvalue())
|
||||
self.assertEqual(ContentType.objects.count(), self.before_count)
|
||||
|
||||
def test_interactive_false(self):
|
||||
"""
|
||||
non-interactive mode of remove_stale_contenttypes shouldn't delete
|
||||
stale content types.
|
||||
"""
|
||||
with captured_stdout() as stdout:
|
||||
call_command('remove_stale_contenttypes', interactive=False, verbosity=2)
|
||||
self.assertIn("Stale content types remain.", stdout.getvalue())
|
||||
self.assertEqual(ContentType.objects.count(), self.before_count + 1)
|
||||
|
||||
def test_unavailable_content_type_model(self):
|
||||
"""
|
||||
A ContentType shouldn't be created if the model isn't available.
|
||||
"""
|
||||
apps = Apps()
|
||||
with self.assertNumQueries(0):
|
||||
contenttypes_management.create_contenttypes(self.app_config, interactive=False, verbosity=0, apps=apps)
|
||||
self.assertEqual(ContentType.objects.count(), self.before_count + 1)
|
||||
|
||||
|
||||
class TestRouter:
|
||||
def db_for_read(self, model, **hints):
|
||||
return 'other'
|
||||
|
||||
def db_for_write(self, model, **hints):
|
||||
return 'default'
|
||||
|
||||
|
||||
@override_settings(DATABASE_ROUTERS=[TestRouter()])
|
||||
class ContentTypesMultidbTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# Whenever a test starts executing, only the "default" database is
|
||||
# connected. We explicitly connect to the "other" database here. If we
|
||||
# don't do it, then it will be implicitly connected later when we query
|
||||
# it, but in that case some database backends may automatically perform
|
||||
# extra queries upon connecting (notably mysql executes
|
||||
# "SET SQL_AUTO_IS_NULL = 0"), which will affect assertNumQueries().
|
||||
connections['other'].ensure_connection()
|
||||
|
||||
def test_multidb(self):
|
||||
"""
|
||||
When using multiple databases, ContentType.objects.get_for_model() uses
|
||||
db_for_read().
|
||||
"""
|
||||
ContentType.objects.clear_cache()
|
||||
|
||||
with self.assertNumQueries(0, using='default'), \
|
||||
self.assertNumQueries(1, using='other'):
|
||||
ContentType.objects.get_for_model(Author)
|
||||
|
||||
|
||||
@override_settings(
|
||||
MIGRATION_MODULES=dict(settings.MIGRATION_MODULES, contenttypes_tests='contenttypes_tests.operations_migrations'),
|
||||
)
|
||||
class ContentTypeOperationsTests(TransactionTestCase):
|
||||
available_apps = [
|
||||
'contenttypes_tests',
|
||||
'django.contrib.contenttypes',
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
app_config = apps.get_app_config('contenttypes_tests')
|
||||
models.signals.post_migrate.connect(self.assertOperationsInjected, sender=app_config)
|
||||
|
||||
def tearDown(self):
|
||||
app_config = apps.get_app_config('contenttypes_tests')
|
||||
models.signals.post_migrate.disconnect(self.assertOperationsInjected, sender=app_config)
|
||||
|
||||
def assertOperationsInjected(self, plan, **kwargs):
|
||||
for migration, _backward in plan:
|
||||
operations = iter(migration.operations)
|
||||
for operation in operations:
|
||||
if isinstance(operation, migrations.RenameModel):
|
||||
next_operation = next(operations)
|
||||
self.assertIsInstance(next_operation, contenttypes_management.RenameContentType)
|
||||
self.assertEqual(next_operation.app_label, migration.app_label)
|
||||
self.assertEqual(next_operation.old_model, operation.old_name_lower)
|
||||
self.assertEqual(next_operation.new_model, operation.new_name_lower)
|
||||
|
||||
def test_existing_content_type_rename(self):
|
||||
ContentType.objects.create(app_label='contenttypes_tests', model='foo')
|
||||
management.call_command(
|
||||
'migrate', 'contenttypes_tests', database='default', interactive=False, verbosity=0,
|
||||
)
|
||||
self.assertFalse(ContentType.objects.filter(app_label='contenttypes_tests', model='foo').exists())
|
||||
self.assertTrue(ContentType.objects.filter(app_label='contenttypes_tests', model='renamedfoo').exists())
|
||||
management.call_command(
|
||||
'migrate', 'contenttypes_tests', 'zero', database='default', interactive=False, verbosity=0,
|
||||
)
|
||||
self.assertTrue(ContentType.objects.filter(app_label='contenttypes_tests', model='foo').exists())
|
||||
self.assertFalse(ContentType.objects.filter(app_label='contenttypes_tests', model='renamedfoo').exists())
|
||||
|
||||
def test_missing_content_type_rename_ignore(self):
|
||||
management.call_command(
|
||||
'migrate', 'contenttypes_tests', database='default', interactive=False, verbosity=0,
|
||||
)
|
||||
self.assertFalse(ContentType.objects.filter(app_label='contenttypes_tests', model='foo').exists())
|
||||
self.assertTrue(ContentType.objects.filter(app_label='contenttypes_tests', model='renamedfoo').exists())
|
||||
management.call_command(
|
||||
'migrate', 'contenttypes_tests', 'zero', database='default', interactive=False, verbosity=0,
|
||||
)
|
||||
self.assertTrue(ContentType.objects.filter(app_label='contenttypes_tests', model='foo').exists())
|
||||
self.assertFalse(ContentType.objects.filter(app_label='contenttypes_tests', model='renamedfoo').exists())
|
||||
|
||||
def test_content_type_rename_conflict(self):
|
||||
ContentType.objects.create(app_label='contenttypes_tests', model='foo')
|
||||
ContentType.objects.create(app_label='contenttypes_tests', model='renamedfoo')
|
||||
management.call_command(
|
||||
'migrate', 'contenttypes_tests', database='default', interactive=False, verbosity=0,
|
||||
)
|
||||
self.assertTrue(ContentType.objects.filter(app_label='contenttypes_tests', model='foo').exists())
|
||||
self.assertTrue(ContentType.objects.filter(app_label='contenttypes_tests', model='renamedfoo').exists())
|
||||
management.call_command(
|
||||
'migrate', 'contenttypes_tests', 'zero', database='default', interactive=False, verbosity=0,
|
||||
)
|
||||
self.assertTrue(ContentType.objects.filter(app_label='contenttypes_tests', model='foo').exists())
|
||||
self.assertTrue(ContentType.objects.filter(app_label='contenttypes_tests', model='renamedfoo').exists())
|
Loading…
Reference in New Issue