Replace instance_of OR query with IN statement
parent
3d014a482c
commit
9042fdd689
|
|
@ -5,11 +5,11 @@ PolymorphicQuerySet support functions
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
from functools import reduce
|
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models import Q
|
||||||
from django.db.models.fields.related import ForeignObjectRel, RelatedField
|
from django.db.models.fields.related import ForeignObjectRel, RelatedField
|
||||||
from django.db.utils import DEFAULT_DB_ALIAS
|
from django.db.utils import DEFAULT_DB_ALIAS
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
@ -105,9 +105,9 @@ def _translate_polymorphic_filter_definition(queryset_model, field_path, field_v
|
||||||
# handle instance_of expressions or alternatively,
|
# handle instance_of expressions or alternatively,
|
||||||
# if this is a normal Django filter expression, return None
|
# if this is a normal Django filter expression, return None
|
||||||
if field_path == 'instance_of':
|
if field_path == 'instance_of':
|
||||||
return _create_model_filter_Q(field_val, using=using)
|
return create_instanceof_q(field_val, using=using)
|
||||||
elif field_path == 'not_instance_of':
|
elif field_path == 'not_instance_of':
|
||||||
return _create_model_filter_Q(field_val, not_instance_of=True, using=using)
|
return create_instanceof_q(field_val, not_instance_of=True, using=using)
|
||||||
elif '___' not in field_path:
|
elif '___' not in field_path:
|
||||||
return None # no change
|
return None # no change
|
||||||
|
|
||||||
|
|
@ -214,7 +214,7 @@ def translate_polymorphic_field_path(queryset_model, field_path):
|
||||||
return newpath
|
return newpath
|
||||||
|
|
||||||
|
|
||||||
def _create_model_filter_Q(modellist, not_instance_of=False, using=DEFAULT_DB_ALIAS):
|
def create_instanceof_q(modellist, not_instance_of=False, using=DEFAULT_DB_ALIAS):
|
||||||
"""
|
"""
|
||||||
Helper function for instance_of / not_instance_of
|
Helper function for instance_of / not_instance_of
|
||||||
Creates and returns a Q object that filters for the models in modellist,
|
Creates and returns a Q object that filters for the models in modellist,
|
||||||
|
|
@ -226,27 +226,32 @@ def _create_model_filter_Q(modellist, not_instance_of=False, using=DEFAULT_DB_AL
|
||||||
efficiently however (regarding the resulting sql), should an optimization
|
efficiently however (regarding the resulting sql), should an optimization
|
||||||
be needed.
|
be needed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not modellist:
|
if not modellist:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
if not isinstance(modellist, (list, tuple)):
|
||||||
from .models import PolymorphicModel
|
from .models import PolymorphicModel
|
||||||
|
|
||||||
if type(modellist) != list and type(modellist) != tuple:
|
|
||||||
if issubclass(modellist, PolymorphicModel):
|
if issubclass(modellist, PolymorphicModel):
|
||||||
modellist = [modellist]
|
modellist = [modellist]
|
||||||
else:
|
else:
|
||||||
assert False, 'PolymorphicModel: instance_of expects a list of (polymorphic) models or a single (polymorphic) model'
|
raise TypeError(
|
||||||
|
'PolymorphicModel: instance_of expects a list of (polymorphic) '
|
||||||
|
'models or a single (polymorphic) model'
|
||||||
|
)
|
||||||
|
|
||||||
def q_class_with_subclasses(model):
|
contenttype_ids = _get_mro_content_type_ids(modellist, using)
|
||||||
q = models.Q(polymorphic_ctype=ContentType.objects.db_manager(using).get_for_model(model, for_concrete_model=False))
|
q = Q(polymorphic_ctype__in=sorted(contenttype_ids))
|
||||||
for subclass in model.__subclasses__():
|
if not_instance_of:
|
||||||
q = q | q_class_with_subclasses(subclass)
|
q = ~q
|
||||||
return q
|
return q
|
||||||
|
|
||||||
qlist = [q_class_with_subclasses(m) for m in modellist]
|
|
||||||
|
|
||||||
q_ored = reduce(lambda a, b: a | b, qlist)
|
def _get_mro_content_type_ids(models, using):
|
||||||
if not_instance_of:
|
contenttype_ids = set()
|
||||||
q_ored = ~q_ored
|
for model in models:
|
||||||
return q_ored
|
ct = ContentType.objects.db_manager(using).get_for_model(model, for_concrete_model=False)
|
||||||
|
contenttype_ids.add(ct.pk)
|
||||||
|
subclasses = model.__subclasses__()
|
||||||
|
if subclasses:
|
||||||
|
contenttype_ids.update(_get_mro_content_type_ids(subclasses, using))
|
||||||
|
return contenttype_ids
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import re
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import django
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Case, Count, Q, When
|
from django.db.models import Case, Count, Q, When
|
||||||
from django.test import TestCase, TransactionTestCase
|
from django.test import TestCase, TransactionTestCase
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
|
from polymorphic import query_translate
|
||||||
from polymorphic.managers import PolymorphicManager
|
from polymorphic.managers import PolymorphicManager
|
||||||
from polymorphic.models import PolymorphicTypeUndefined
|
from polymorphic.models import PolymorphicTypeUndefined
|
||||||
from polymorphic.tests.models import (
|
from polymorphic.tests.models import (
|
||||||
|
|
@ -343,6 +344,14 @@ class PolymorphicTests(TransactionTestCase):
|
||||||
ordered=False,
|
ordered=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_create_instanceof_q(self):
|
||||||
|
q = query_translate.create_instanceof_q([Model2B])
|
||||||
|
expected = sorted([
|
||||||
|
ContentType.objects.get_for_model(m).pk
|
||||||
|
for m in [Model2B, Model2C, Model2D]
|
||||||
|
])
|
||||||
|
self.assertEqual(dict(q.children), dict(polymorphic_ctype__in=expected))
|
||||||
|
|
||||||
def test_base_manager(self):
|
def test_base_manager(self):
|
||||||
def base_manager(model):
|
def base_manager(model):
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue