Fixed #20401 -- ContentTypeManager.get_for_model reads from db_for_read.

Thanks Simon Charette and Tim Graham for the reviews.
This commit is contained in:
Antonis Christofides 2013-11-24 18:12:46 +01:00 committed by Loic Bistuer
parent d240b29c08
commit 62f9508ade
2 changed files with 53 additions and 7 deletions

View File

@ -38,18 +38,27 @@ class ContentTypeManager(models.Manager):
"""
opts = self._get_opts(model, for_concrete_model)
try:
ct = self._get_from_cache(opts)
return self._get_from_cache(opts)
except KeyError:
# Load or create the ContentType entry. The smart_text() is
# needed around opts.verbose_name_raw because name_raw might be a
# django.utils.functional.__proxy__ object.
pass
# The ContentType entry was not found in the cache, therefore we
# proceed to load or create it.
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 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.
# The smart_text() is needed around opts.verbose_name_raw because
# name_raw might be a django.utils.functional.__proxy__ object.
ct, created = self.get_or_create(
app_label=opts.app_label,
model=opts.model_name,
defaults={'name': smart_text(opts.verbose_name_raw)},
)
self._add_to_cache(self.db, ct)
self._add_to_cache(self.db, ct)
return ct
def get_for_models(self, *models, **kwargs):

View File

@ -7,7 +7,7 @@ from django.contrib.contenttypes.fields import (
)
from django.contrib.contenttypes.models import ContentType
from django.core import checks
from django.db import models
from django.db import connections, models, router
from django.test import TestCase
from django.test.utils import override_settings
from django.utils.encoding import force_str
@ -335,3 +335,40 @@ class GenericRelationshipTests(IsolatedModelsTestCase):
)
]
self.assertEqual(errors, expected)
class TestRouter(object):
def db_for_read(self, model, **hints):
return 'other'
def db_for_write(self, model, **hints):
return 'default'
class ContentTypesMultidbTestCase(TestCase):
def setUp(self):
self.old_routers = router.routers
router.routers = [TestRouter()]
# 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 tearDown(self):
router.routers = self.old_routers
def test_multidb(self):
"""
Test that, when using multiple databases, we use the db_for_read (see
#20401).
"""
ContentType.objects.clear_cache()
with self.assertNumQueries(0, using='default'), \
self.assertNumQueries(1, using='other'):
ContentType.objects.get_for_model(Author)