#216 Use self._state.db instead of `using` kwarg in model methods.
Thanks to @vdboor for the suggestion. Also: - Add missing `using` kwargs in query_translate functions. - Add a couple unit tests for non-default database functionality.fix_request_path_info
parent
2f11cb6ffd
commit
4aece2b5d3
|
|
@ -87,12 +87,12 @@ class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
|
|||
def save(self, *args, **kwargs):
|
||||
"""Overridden model save function which supports the polymorphism
|
||||
functionality (through pre_save_polymorphic)."""
|
||||
using = kwargs.get('using', DEFAULT_DB_ALIAS)
|
||||
using = kwargs.get('using', self._state.db or DEFAULT_DB_ALIAS)
|
||||
self.pre_save_polymorphic(using=using)
|
||||
return super(PolymorphicModel, self).save(*args, **kwargs)
|
||||
save.alters_data = True
|
||||
|
||||
def get_real_instance_class(self, using=DEFAULT_DB_ALIAS):
|
||||
def get_real_instance_class(self):
|
||||
"""
|
||||
Normally not needed.
|
||||
If a non-polymorphic manager (like base_objects) has been used to
|
||||
|
|
@ -105,7 +105,7 @@ class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
|
|||
# Note that model_class() can return None for stale content types;
|
||||
# when the content type record still exists but no longer refers to an existing model.
|
||||
try:
|
||||
model = ContentType.objects.db_manager(using).get_for_id(self.polymorphic_ctype_id).model_class()
|
||||
model = ContentType.objects.db_manager(self._state.db).get_for_id(self.polymorphic_ctype_id).model_class()
|
||||
except AttributeError:
|
||||
# Django <1.6 workaround
|
||||
return None
|
||||
|
|
@ -120,28 +120,28 @@ class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
|
|||
))
|
||||
return model
|
||||
|
||||
def get_real_concrete_instance_class_id(self, using=DEFAULT_DB_ALIAS):
|
||||
model_class = self.get_real_instance_class(using=using)
|
||||
def get_real_concrete_instance_class_id(self):
|
||||
model_class = self.get_real_instance_class()
|
||||
if model_class is None:
|
||||
return None
|
||||
return ContentType.objects.db_manager(using).get_for_model(model_class, for_concrete_model=True).pk
|
||||
return ContentType.objects.db_manager(self._state.db).get_for_model(model_class, for_concrete_model=True).pk
|
||||
|
||||
def get_real_concrete_instance_class(self, using=DEFAULT_DB_ALIAS):
|
||||
model_class = self.get_real_instance_class(using=using)
|
||||
def get_real_concrete_instance_class(self):
|
||||
model_class = self.get_real_instance_class()
|
||||
if model_class is None:
|
||||
return None
|
||||
return ContentType.objects.db_manager(using).get_for_model(model_class, for_concrete_model=True).model_class()
|
||||
return ContentType.objects.db_manager(self._state.db).get_for_model(model_class, for_concrete_model=True).model_class()
|
||||
|
||||
def get_real_instance(self, using=DEFAULT_DB_ALIAS):
|
||||
def get_real_instance(self):
|
||||
"""Normally not needed.
|
||||
If a non-polymorphic manager (like base_objects) has been used to
|
||||
retrieve objects, then the complete object with it's real class/type
|
||||
and all fields may be retrieved with this method.
|
||||
Each method call executes one db query (if necessary)."""
|
||||
real_model = self.get_real_instance_class(using=using)
|
||||
real_model = self.get_real_instance_class()
|
||||
if real_model == self.__class__:
|
||||
return self
|
||||
return real_model.objects.db_manager(using).get(pk=self.pk)
|
||||
return real_model.objects.db_manager(self._state.db).get(pk=self.pk)
|
||||
|
||||
def __init__(self, * args, ** kwargs):
|
||||
"""Replace Django's inheritance accessor member functions for our model
|
||||
|
|
|
|||
|
|
@ -112,8 +112,8 @@ class PolymorphicQuerySet(QuerySet):
|
|||
|
||||
def _filter_or_exclude(self, negate, *args, **kwargs):
|
||||
"We override this internal Django functon as it is used for all filter member functions."
|
||||
translate_polymorphic_filter_definitions_in_args(self.model, args) # the Q objects
|
||||
additional_args = translate_polymorphic_filter_definitions_in_kwargs(self.model, kwargs) # filter_field='data'
|
||||
translate_polymorphic_filter_definitions_in_args(self.model, args, using=self._db) # the Q objects
|
||||
additional_args = translate_polymorphic_filter_definitions_in_kwargs(self.model, kwargs, using=self._db) # filter_field='data'
|
||||
return super(PolymorphicQuerySet, self)._filter_or_exclude(negate, *(list(args) + additional_args), **kwargs)
|
||||
|
||||
def order_by(self, *args, **kwargs):
|
||||
|
|
@ -325,8 +325,8 @@ class PolymorphicQuerySet(QuerySet):
|
|||
results[base_object.pk] = base_object
|
||||
|
||||
else:
|
||||
real_concrete_class = base_object.get_real_instance_class(using=self._db)
|
||||
real_concrete_class_id = base_object.get_real_concrete_instance_class_id(using=self._db)
|
||||
real_concrete_class = base_object.get_real_instance_class()
|
||||
real_concrete_class_id = base_object.get_real_concrete_instance_class_id()
|
||||
|
||||
if real_concrete_class_id is None:
|
||||
# Dealing with a stale content type
|
||||
|
|
@ -345,7 +345,7 @@ class PolymorphicQuerySet(QuerySet):
|
|||
# Then we copy the extra() select fields from the base objects to the real objects.
|
||||
# TODO: defer(), only(): support for these would be around here
|
||||
for real_concrete_class, idlist in idlist_per_model.items():
|
||||
real_objects = real_concrete_class.base_objects.filter(**{
|
||||
real_objects = real_concrete_class.base_objects.db_manager(self._db).filter(**{
|
||||
('%s__in' % pk_name): idlist,
|
||||
})
|
||||
real_objects.query.select_related = self.query.select_related # copy select related configuration to new qs
|
||||
|
|
@ -372,7 +372,7 @@ class PolymorphicQuerySet(QuerySet):
|
|||
|
||||
for real_object in real_objects:
|
||||
o_pk = getattr(real_object, pk_name)
|
||||
real_class = real_object.get_real_instance_class(using=self._db)
|
||||
real_class = real_object.get_real_instance_class()
|
||||
|
||||
# If the real class is a proxy, upcast it
|
||||
if real_class != real_concrete_class:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ from functools import reduce
|
|||
# functionality to filters and Q objects.
|
||||
# Probably a more general queryset enhancement class could be made out of them.
|
||||
|
||||
def translate_polymorphic_filter_definitions_in_kwargs(queryset_model, kwargs):
|
||||
def translate_polymorphic_filter_definitions_in_kwargs(queryset_model, kwargs, using=DEFAULT_DB_ALIAS):
|
||||
"""
|
||||
Translate the keyword argument list for PolymorphicQuerySet.filter()
|
||||
|
||||
|
|
@ -51,7 +51,6 @@ def translate_polymorphic_filter_definitions_in_kwargs(queryset_model, kwargs):
|
|||
Returns: a list of non-keyword-arguments (Q objects) to be added to the filter() query.
|
||||
"""
|
||||
additional_args = []
|
||||
using = kwargs.get('using', DEFAULT_DB_ALIAS)
|
||||
for field_path, val in kwargs.copy().items(): # Python 3 needs copy
|
||||
|
||||
new_expr = _translate_polymorphic_filter_definition(queryset_model, field_path, val, using=using)
|
||||
|
|
@ -90,7 +89,7 @@ def translate_polymorphic_Q_object(queryset_model, potential_q_object, using=DEF
|
|||
return potential_q_object
|
||||
|
||||
|
||||
def translate_polymorphic_filter_definitions_in_args(queryset_model, args):
|
||||
def translate_polymorphic_filter_definitions_in_args(queryset_model, args, using=DEFAULT_DB_ALIAS):
|
||||
"""
|
||||
Translate the non-keyword argument list for PolymorphicQuerySet.filter()
|
||||
|
||||
|
|
@ -104,7 +103,7 @@ def translate_polymorphic_filter_definitions_in_args(queryset_model, args):
|
|||
"""
|
||||
|
||||
for q in args:
|
||||
translate_polymorphic_Q_object(queryset_model, q)
|
||||
translate_polymorphic_Q_object(queryset_model, q, using=using)
|
||||
|
||||
|
||||
def _translate_polymorphic_filter_definition(queryset_model, field_path, field_val, using=DEFAULT_DB_ALIAS):
|
||||
|
|
|
|||
|
|
@ -430,6 +430,8 @@ class PolymorphicTests(TestCase):
|
|||
The test suite
|
||||
"""
|
||||
|
||||
multi_db = True
|
||||
|
||||
def test_annotate_aggregate_order(self):
|
||||
# create a blog of type BlogA
|
||||
# create two blog entries in BlogA
|
||||
|
|
@ -1162,6 +1164,45 @@ class PolymorphicTests(TestCase):
|
|||
result = DateModel.objects.annotate(val=DateTime('date', 'day', utc))
|
||||
self.assertEqual(list(result), [])
|
||||
|
||||
def test_save_to_non_default_database(self):
|
||||
Model2A.objects.db_manager('secondary').create(field1='A1')
|
||||
Model2C(field1='C1', field2='C2', field3='C3').save(using='secondary')
|
||||
Model2B.objects.create(field1='B1', field2='B2')
|
||||
Model2D(field1='D1', field2='D2', field3='D3', field4='D4').save('secondary')
|
||||
|
||||
default_objects = list(Model2A.objects.order_by('id'))
|
||||
self.assertEqual(len(default_objects), 2)
|
||||
self.assertEqual(repr(default_objects[0]), '<Model2B: id 1, field1 (CharField), field2 (CharField)>')
|
||||
self.assertEqual(repr(default_objects[1]), '<Model2D: id 2, field1 (CharField), field2 (CharField), field3 (CharField), field4 (CharField)>')
|
||||
|
||||
secondary_objects = list(Model2A.objects.db_manager('secondary').order_by('id'))
|
||||
self.assertEqual(len(secondary_objects), 2)
|
||||
self.assertEqual(repr(secondary_objects[0]), '<Model2A: id 1, field1 (CharField)>')
|
||||
self.assertEqual(repr(secondary_objects[1]), '<Model2C: id 2, field1 (CharField), field2 (CharField), field3 (CharField)>')
|
||||
|
||||
def test_instance_of_filter_on_non_default_database(self):
|
||||
Base.objects.db_manager('secondary').create(field_b='B1')
|
||||
ModelX.objects.db_manager('secondary').create(field_b='B', field_x='X')
|
||||
ModelY.objects.db_manager('secondary').create(field_b='Y', field_y='Y')
|
||||
|
||||
objects = Base.objects.db_manager('secondary').filter(instance_of=Base)
|
||||
self.assertEqual(len(objects), 3)
|
||||
self.assertEqual(repr(objects[0]), '<Base: id 1, field_b (CharField)>')
|
||||
self.assertEqual(repr(objects[1]), '<ModelX: id 2, field_b (CharField), field_x (CharField)>')
|
||||
self.assertEqual(repr(objects[2]), '<ModelY: id 3, field_b (CharField), field_y (CharField)>')
|
||||
|
||||
objects = Base.objects.db_manager('secondary').filter(instance_of=ModelX)
|
||||
self.assertEqual(len(objects), 1)
|
||||
self.assertEqual(repr(objects[0]), '<ModelX: id 2, field_b (CharField), field_x (CharField)>')
|
||||
|
||||
objects = Base.objects.db_manager('secondary').filter(instance_of=ModelY)
|
||||
self.assertEqual(len(objects), 1)
|
||||
self.assertEqual(repr(objects[0]), '<ModelY: id 3, field_b (CharField), field_y (CharField)>')
|
||||
|
||||
objects = Base.objects.db_manager('secondary').filter(Q(instance_of=ModelX) | Q(instance_of=ModelY))
|
||||
self.assertEqual(len(objects), 2)
|
||||
self.assertEqual(repr(objects[0]), '<ModelX: id 2, field_b (CharField), field_x (CharField)>')
|
||||
self.assertEqual(repr(objects[1]), '<ModelY: id 3, field_b (CharField), field_y (CharField)>')
|
||||
|
||||
class RegressionTests(TestCase):
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@ if not settings.configured:
|
|||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': ':memory:'
|
||||
},
|
||||
'secondary': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': ':memory:'
|
||||
}
|
||||
},
|
||||
TEMPLATE_LOADERS=(
|
||||
|
|
|
|||
Loading…
Reference in New Issue