Refs #24299 -- Made contenttypes migrations signal handler more robust.
This commit is contained in:
parent
d538e37e1b
commit
d392c1e150
|
@ -4,7 +4,7 @@ import warnings
|
|||
|
||||
from django.apps import apps
|
||||
from django.db import models
|
||||
from django.db.utils import OperationalError, ProgrammingError
|
||||
from django.db.utils import IntegrityError, OperationalError, ProgrammingError
|
||||
from django.utils.deprecation import RemovedInDjango20Warning
|
||||
from django.utils.encoding import force_text, python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
@ -58,18 +58,11 @@ class ContentTypeManager(models.Manager):
|
|||
|
||||
# The ContentType entry was not found in the cache, therefore we
|
||||
# proceed to load or create it.
|
||||
try:
|
||||
try:
|
||||
# We start with get() and not get_or_create() in order to use
|
||||
# the db_for_read (see #20401).
|
||||
ct = self.get(app_label=opts.app_label, model=opts.model_name)
|
||||
except (OperationalError, ProgrammingError):
|
||||
# It's possible to migrate a single app before contenttypes,
|
||||
# as it's not a required initial dependency (it's contrib!)
|
||||
# Have a nice error for this.
|
||||
raise RuntimeError(
|
||||
"Error creating new content types. Please make sure contenttypes "
|
||||
"is migrated before trying to migrate apps individually."
|
||||
)
|
||||
except self.model.DoesNotExist:
|
||||
# Not found in the database; we proceed to create it. This time we
|
||||
# use get_or_create to take care of any race conditions.
|
||||
|
@ -77,6 +70,14 @@ class ContentTypeManager(models.Manager):
|
|||
app_label=opts.app_label,
|
||||
model=opts.model_name,
|
||||
)
|
||||
except (OperationalError, ProgrammingError, IntegrityError):
|
||||
# It's possible to migrate a single app before contenttypes,
|
||||
# as it's not a required initial dependency (it's contrib!)
|
||||
# Have a nice error for this.
|
||||
raise RuntimeError(
|
||||
"Error creating new content types. Please make sure contenttypes "
|
||||
"is migrated before trying to migrate apps individually."
|
||||
)
|
||||
self._add_to_cache(self.db, ct)
|
||||
return ct
|
||||
|
||||
|
|
|
@ -5,8 +5,9 @@ import warnings
|
|||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.contenttypes.views import shortcut
|
||||
from django.contrib.sites.shortcuts import get_current_site
|
||||
from django.db.utils import IntegrityError, OperationalError, ProgrammingError
|
||||
from django.http import Http404, HttpRequest
|
||||
from django.test import TestCase, override_settings
|
||||
from django.test import TestCase, mock, override_settings
|
||||
from django.utils import six
|
||||
|
||||
from .models import (
|
||||
|
@ -264,3 +265,26 @@ class ContentTypesTests(TestCase):
|
|||
"ContentType.name field doesn't exist any longer. Please remove it from your code."
|
||||
)
|
||||
self.assertTrue(ContentType.objects.filter(model='OldModel').exists())
|
||||
|
||||
@mock.patch('django.contrib.contenttypes.models.ContentTypeManager.get_or_create')
|
||||
@mock.patch('django.contrib.contenttypes.models.ContentTypeManager.get')
|
||||
def test_message_if_get_for_model_fails(self, mocked_get, mocked_get_or_create):
|
||||
"""
|
||||
Check that `RuntimeError` with nice error message is raised if
|
||||
`get_for_model` fails because of database errors.
|
||||
"""
|
||||
|
||||
def _test_message(mocked_method):
|
||||
for ExceptionClass in (IntegrityError, OperationalError, ProgrammingError):
|
||||
mocked_method.side_effect = ExceptionClass
|
||||
with self.assertRaisesMessage(
|
||||
RuntimeError,
|
||||
"Error creating new content types. Please make sure contenttypes "
|
||||
"is migrated before trying to migrate apps individually."
|
||||
):
|
||||
ContentType.objects.get_for_model(ContentType)
|
||||
|
||||
_test_message(mocked_get)
|
||||
|
||||
mocked_get.side_effect = ContentType.DoesNotExist
|
||||
_test_message(mocked_get_or_create)
|
||||
|
|
Loading…
Reference in New Issue