1
0
mirror of https://github.com/django/django.git synced 2024-12-28 12:06:22 +00:00

[1.8.x] Refs #24299 -- Made contenttypes migrations signal handler more robust.

Backport of d392c1e150 from master
This commit is contained in:
Sergey Fedoseev 2015-02-08 00:31:12 +05:00 committed by Tim Graham
parent b7cf99a8c3
commit 870d900cdc
2 changed files with 38 additions and 13 deletions

View File

@ -4,7 +4,7 @@ import warnings
from django.apps import apps from django.apps import apps
from django.db import models 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.deprecation import RemovedInDjango20Warning
from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -59,10 +59,18 @@ class ContentTypeManager(models.Manager):
# The ContentType entry was not found in the cache, therefore we # The ContentType entry was not found in the cache, therefore we
# proceed to load or create it. # proceed to load or create it.
try: try:
# We start with get() and not get_or_create() in order to use try:
# the db_for_read (see #20401). # We start with get() and not get_or_create() in order to use
ct = self.get(app_label=opts.app_label, model=opts.model_name) # the db_for_read (see #20401).
except (OperationalError, ProgrammingError): ct = self.get(app_label=opts.app_label, model=opts.model_name)
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.
ct, created = self.get_or_create(
app_label=opts.app_label,
model=opts.model_name,
)
except (OperationalError, ProgrammingError, IntegrityError):
# It's possible to migrate a single app before contenttypes, # It's possible to migrate a single app before contenttypes,
# as it's not a required initial dependency (it's contrib!) # as it's not a required initial dependency (it's contrib!)
# Have a nice error for this. # Have a nice error for this.
@ -70,13 +78,6 @@ class ContentTypeManager(models.Manager):
"Error creating new content types. Please make sure contenttypes " "Error creating new content types. Please make sure contenttypes "
"is migrated before trying to migrate apps individually." "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.
ct, created = self.get_or_create(
app_label=opts.app_label,
model=opts.model_name,
)
self._add_to_cache(self.db, ct) self._add_to_cache(self.db, ct)
return ct return ct

View File

@ -5,8 +5,9 @@ import warnings
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.views import shortcut from django.contrib.contenttypes.views import shortcut
from django.contrib.sites.shortcuts import get_current_site 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.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 django.utils import six
from .models import ( from .models import (
@ -264,3 +265,26 @@ class ContentTypesTests(TestCase):
"ContentType.name field doesn't exist any longer. Please remove it from your code." "ContentType.name field doesn't exist any longer. Please remove it from your code."
) )
self.assertTrue(ContentType.objects.filter(model='OldModel').exists()) 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)