<commit_before># -*- coding: utf-8 -*-
""" QuerySet for PolymorphicModel
    Please see README.rst or DOCS.rst or http://chrisglass.github.com/django_polymorphic/
"""
from __future__ import absolute_import

from collections import defaultdict

import django
from django.db.models.query import QuerySet
from django.contrib.contenttypes.models import ContentType
from django.utils import six

from .query_translate import translate_polymorphic_filter_definitions_in_kwargs, translate_polymorphic_filter_definitions_in_args
from .query_translate import translate_polymorphic_field_path

# chunk-size: maximum number of objects requested per db-request
# by the polymorphic queryset.iterator() implementation; we use the same chunk size as Django
try:
    from django.db.models.query import CHUNK_SIZE               # this is 100 for Django 1.1/1.2
except ImportError:
    # CHUNK_SIZE was removed in Django 1.6
    CHUNK_SIZE = 100
Polymorphic_QuerySet_objects_per_request = CHUNK_SIZE


def transmogrify(cls, obj):
    """
    Upcast a class to a different type without asking questions.
    """
    if not '__init__' in obj.__dict__:
        # Just assign __class__ to a different value.
        new = obj
        new.__class__ = cls
    else:
        # Run constructor, reassign values
        new = cls()
        for k, v in obj.__dict__.items():
            new.__dict__[k] = v
    return new


###################################################################################
# PolymorphicQuerySet

def _query_annotations(query):
    try:
        return query.annotations
    except AttributeError:
        # Django < 1.8
        return query.aggregates


class PolymorphicQuerySet(QuerySet):
    """
    QuerySet for PolymorphicModel

    Contains the core functionality for PolymorphicModel

    Usually not explicitly needed, except if a custom queryset class
    is to be used.
    """

    def __init__(self, *args, **kwargs):
        "init our queryset object member variables"
        self.polymorphic_disabled = False
        super(PolymorphicQuerySet, self).__init__(*args, **kwargs)

    def _clone(self, *args, **kwargs):
        "Django's _clone only copies its own variables, so we need to copy ours here"
        new = super(PolymorphicQuerySet, self)._clone(*args, **kwargs)
        new.polymorphic_disabled = self.polymorphic_disabled
        return new

    if django.VERSION >= (1, 7):
        def as_manager(cls):
            # Make sure the Django 1.7 way of creating managers works.
            from .managers import PolymorphicManager
            manager = PolymorphicManager.from_queryset(cls)()
            manager._built_with_as_manager = True
            return manager
        as_manager.queryset_only = True
        as_manager = classmethod(as_manager)

    def non_polymorphic(self):
        """switch off polymorphic behaviour for this query.
        When the queryset is evaluated, only objects of the type of the
        base class used for this query are returned."""
        qs = self._clone()
        qs.polymorphic_disabled = True
        return qs

    def instance_of(self, *args):
        """Filter the queryset to only include the classes in args (and their subclasses).
        Implementation in _translate_polymorphic_filter_defnition."""
        return self.filter(instance_of=args)

    def not_instance_of(self, *args):
        """Filter the queryset to exclude the classes in args (and their subclasses).
        Implementation in _translate_polymorphic_filter_defnition."""
        return self.filter(not_instance_of=args)

    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'
        return super(PolymorphicQuerySet, self)._filter_or_exclude(negate, *(list(args) + additional_args), **kwargs)

    def order_by(self, *args, **kwargs):
        """translate the field paths in the args, then call vanilla order_by."""
        new_args = [translate_polymorphic_field_path(self.model, a) for a in args]
        return super(PolymorphicQuerySet, self).order_by(*new_args, **kwargs)

    def _process_aggregate_args(self, args, kwargs):
        """for aggregate and annotate kwargs: allow ModelX___field syntax for kwargs, forbid it for args.
        Modifies kwargs if needed (these are Aggregate objects, we translate the lookup member variable)"""

        def patch_lookup(a):
            if django.VERSION < (1, 8):
                a.lookup = translate_polymorphic_field_path(self.model, a.lookup)
            else:
                # With Django > 1.8, the field on which the aggregate operates is
                # stored inside a query expression.
                if hasattr(a, 'source_expressions'):
                    a.source_expressions[0].name = translate_polymorphic_field_path(
                        self.model, a.source_expressions[0].name)

        get_lookup = lambda a: a.lookup if django.VERSION < (1, 8) else a.source_expressions[0].name

        for a in args:
            assert '___' not in get_lookup(a), 'PolymorphicModel: annotate()/aggregate(): ___ model lookup supported for keyword arguments only'
        for a in six.itervalues(kwargs):
            patch_lookup(a)

    def annotate(self, *args, **kwargs):
        """translate the polymorphic field paths in the kwargs, then call vanilla annotate.
        _get_real_instances will do the rest of the job after executing the query."""
        self._process_aggregate_args(args, kwargs)
        return super(PolymorphicQuerySet, self).annotate(*args, **kwargs)

    def aggregate(self, *args, **kwargs):
        """translate the polymorphic field paths in the kwargs, then call vanilla aggregate.
        We need no polymorphic object retrieval for aggregate => switch it off."""
        self._process_aggregate_args(args, kwargs)
        qs = self.non_polymorphic()
        return super(PolymorphicQuerySet, qs).aggregate(*args, **kwargs)

    if django.VERSION >= (1, 9):
        # On Django < 1.9, 'qs.values(...)' returned a new special ValuesQuerySet
        # object, which our polymorphic modifications didn't apply to.
        # Starting with Django 1.9, the copy returned by 'qs.values(...)' has the
        # same class as 'qs', so our polymorphic modifications would apply.
        # We want to leave values queries untouched, so we set 'polymorphic_disabled'.
        def _values(self, *args, **kwargs):
            clone = super(PolymorphicQuerySet, self)._values(*args, **kwargs)
            clone.polymorphic_disabled = True
            return clone

    # Since django_polymorphic 'V1.0 beta2', extra() always returns polymorphic results.<commit_msg>Support Django 1.8 complex expressions on aggregate/annotate<commit_after># -*- coding: utf-8 -*-
""" QuerySet for PolymorphicModel
    Please see README.rst or DOCS.rst or http://chrisglass.github.com/django_polymorphic/
"""
from __future__ import absolute_import

from collections import defaultdict

import django
from django.db.models.query import QuerySet, Q
from django.contrib.contenttypes.models import ContentType
from django.utils import six

from .query_translate import translate_polymorphic_filter_definitions_in_kwargs, translate_polymorphic_filter_definitions_in_args
from .query_translate import translate_polymorphic_field_path, translate_polymorphic_Q_object

# chunk-size: maximum number of objects requested per db-request
# by the polymorphic queryset.iterator() implementation; we use the same chunk size as Django
try:
    from django.db.models.query import CHUNK_SIZE               # this is 100 for Django 1.1/1.2
except ImportError:
    # CHUNK_SIZE was removed in Django 1.6
    CHUNK_SIZE = 100
Polymorphic_QuerySet_objects_per_request = CHUNK_SIZE


def transmogrify(cls, obj):
    """
    Upcast a class to a different type without asking questions.
    """
    if not '__init__' in obj.__dict__:
        # Just assign __class__ to a different value.
        new = obj
        new.__class__ = cls
    else:
        # Run constructor, reassign values
        new = cls()
        for k, v in obj.__dict__.items():
            new.__dict__[k] = v
    return new


###################################################################################
# PolymorphicQuerySet

def _query_annotations(query):
    try:
        return query.annotations
    except AttributeError:
        # Django < 1.8
        return query.aggregates


class PolymorphicQuerySet(QuerySet):
    """
    QuerySet for PolymorphicModel

    Contains the core functionality for PolymorphicModel

    Usually not explicitly needed, except if a custom queryset class
    is to be used.
    """

    def __init__(self, *args, **kwargs):
        "init our queryset object member variables"
        self.polymorphic_disabled = False
        super(PolymorphicQuerySet, self).__init__(*args, **kwargs)

    def _clone(self, *args, **kwargs):
        "Django's _clone only copies its own variables, so we need to copy ours here"
        new = super(PolymorphicQuerySet, self)._clone(*args, **kwargs)
        new.polymorphic_disabled = self.polymorphic_disabled
        return new

    if django.VERSION >= (1, 7):
        def as_manager(cls):
            # Make sure the Django 1.7 way of creating managers works.
            from .managers import PolymorphicManager
            manager = PolymorphicManager.from_queryset(cls)()
            manager._built_with_as_manager = True
            return manager
        as_manager.queryset_only = True
        as_manager = classmethod(as_manager)

    def non_polymorphic(self):
        """switch off polymorphic behaviour for this query.
        When the queryset is evaluated, only objects of the type of the
        base class used for this query are returned."""
        qs = self._clone()
        qs.polymorphic_disabled = True
        return qs

    def instance_of(self, *args):
        """Filter the queryset to only include the classes in args (and their subclasses).
        Implementation in _translate_polymorphic_filter_defnition."""
        return self.filter(instance_of=args)

    def not_instance_of(self, *args):
        """Filter the queryset to exclude the classes in args (and their subclasses).
        Implementation in _translate_polymorphic_filter_defnition."""
        return self.filter(not_instance_of=args)

    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'
        return super(PolymorphicQuerySet, self)._filter_or_exclude(negate, *(list(args) + additional_args), **kwargs)

    def order_by(self, *args, **kwargs):
        """translate the field paths in the args, then call vanilla order_by."""
        new_args = [translate_polymorphic_field_path(self.model, a) for a in args]
        return super(PolymorphicQuerySet, self).order_by(*new_args, **kwargs)

    def _process_aggregate_args(self, args, kwargs):
        """for aggregate and annotate kwargs: allow ModelX___field syntax for kwargs, forbid it for args.
        Modifies kwargs if needed (these are Aggregate objects, we translate the lookup member variable)"""

        def patch_lookup_lt_18(a):
            a.lookup = translate_polymorphic_field_path(self.model, a.lookup)

                    
        def patch_lookup_gte_18(a):
            # With Django > 1.8, the field on which the aggregate operates is
            # stored inside a complex query expression.
            if isinstance(a, Q):
                translate_polymorphic_Q_object(self.model, a)
            elif hasattr(a, 'get_source_expressions'):
                for source_expression in a.get_source_expressions():
                    patch_lookup_gte_18(source_expression)
            else:
                a.name = translate_polymorphic_field_path(self.model, a.name)

        get_lookup = lambda a: a.lookup if django.VERSION < (1, 8) else a.source_expressions[0].name

        for a in args:
            assert '___' not in get_lookup(a), 'PolymorphicModel: annotate()/aggregate(): ___ model lookup supported for keyword arguments only'
        for a in six.itervalues(kwargs):
            patch_lookup = patch_lookup_lt_18 if django.VERSION < (1, 8) else patch_lookup_gte_18
            patch_lookup(a)

    def annotate(self, *args, **kwargs):
        """translate the polymorphic field paths in the kwargs, then call vanilla annotate.
        _get_real_instances will do the rest of the job after executing the query."""
        self._process_aggregate_args(args, kwargs)
        return super(PolymorphicQuerySet, self).annotate(*args, **kwargs)

    def aggregate(self, *args, **kwargs):
        """translate the polymorphic field paths in the kwargs, then call vanilla aggregate.
        We need no polymorphic object retrieval for aggregate => switch it off."""
        self._process_aggregate_args(args, kwargs)
        qs = self.non_polymorphic()
        return super(PolymorphicQuerySet, qs).aggregate(*args, **kwargs)

    if django.VERSION >= (1, 9):
        # On Django < 1.9, 'qs.values(...)' returned a new special ValuesQuerySet
        # object, which our polymorphic modifications didn't apply to.
        # Starting with Django 1.9, the copy returned by 'qs.values(...)' has the
        # same class as 'qs', so our polymorphic modifications would apply.
        # We want to leave values queries untouched, so we set 'polymorphic_disabled'.
        def _values(self, *args, **kwargs):
            clone = super(PolymorphicQuerySet, self)._values(*args, **kwargs)
            clone.polymorphic_disabled = True
            return clone

    # Since django_polymorphic 'V1.0 beta2', extra() always returns polymorphic results.
{'fraction_non_alphanumeric': 0.09119763013118917, 'fraction_numerical': 0.005924672027084215, 'mean_word_length': 3.074425287356322, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3996810', 'n_tokens_mistral': 4085, 'n_tokens_neox': 3815, 'n_words': 1259}