<commit_before>from __future__ import division, unicode_literals
from future.builtins import str

from copy import copy

from django.contrib.contenttypes.fields import GenericRelation
from django.core.exceptions import ImproperlyConfigured, AppRegistryNotReady
from django.db.models import IntegerField, CharField, FloatField
from django.db.models.signals import post_save, post_delete

from mezzanine.utils.models import lazy_model_ops


class BaseGenericRelation(GenericRelation):
    """
    Extends ``GenericRelation`` to:

    - Add a consistent default value for ``object_id_field`` and
      check for a ``default_related_model`` attribute which can be
      defined on subclasses as a default for the ``to`` argument.

    - Add one or more custom fields to the model that the relation
      field is applied to, and then call a ``related_items_changed``
      method each time related items are saved or deleted, so that a
      calculated value can be stored against the custom fields since
      aggregates aren't available for GenericRelation instances.

    """

    # Mapping of field names to model fields that will be added.
    fields = {}

    def __init__(self, *args, **kwargs):
        """
        Set up some defaults and check for a ``default_related_model``
        attribute for the ``to`` argument.
        """
        kwargs.setdefault("object_id_field", "object_pk")
        to = getattr(self, "default_related_model", None)
        # Avoid having both a positional arg and a keyword arg for
        # the parameter ``to``
        if to and not args:
            kwargs.setdefault("to", to)
        try:
            # Check if ``related_model`` has been modified by a subclass
            self.related_model
        except (AppRegistryNotReady, AttributeError):
            # if not, all is good
            super(BaseGenericRelation, self).__init__(*args, **kwargs)
        else:
            # otherwise, warn the user to stick to the new (as of 4.0)
            # ``default_related_model`` attribute
            raise ImproperlyConfigured("BaseGenericRelation changed the "
                "way it handled a default ``related_model`` in mezzanine "
                "4.0. Please override ``default_related_model`` instead "
                "and do not tamper with django's ``related_model`` "
                "property anymore.")

    def contribute_to_class(self, cls, name):
        """
        Add each of the names and fields in the ``fields`` attribute
        to the model the relationship field is applied to, and set up
        the related item save and delete signals for calling
        ``related_items_changed``.
        """
        for field in cls._meta.many_to_many:
            if isinstance(field, self.__class__):
                e = "Multiple %s fields are not supported (%s.%s, %s.%s)" % (
                    self.__class__.__name__, cls.__name__, cls.__name__,
                    name, field.name)
                raise ImproperlyConfigured(e)
        self.related_field_name = name
        super(BaseGenericRelation, self).contribute_to_class(cls, name)
        # Not applicable to abstract classes, and in fact will break.
        if not cls._meta.abstract:
            for (name_string, field) in self.fields.items():
                if "%s" in name_string:
                    name_string = name_string % name
                # In Django 1.6, add_to_class will be called on a
                # parent model's field more than once, so
                # contribute_to_class needs to be idempotent. We
                # don't call get_all_field_names() which fill the app
                # cache get_fields_with_model() is safe.
                try:
                    # Django >= 1.8
                    extant_fields = cls._meta._forward_fields_map
                except AttributeError:
                    # Django <= 1.7
                    extant_fields = (i.name for i in cls._meta.fields)
                if name_string in extant_fields:
                    continue
                if field.verbose_name is None:
                    field.verbose_name = self.verbose_name
                cls.add_to_class(name_string, copy(field))
            # Add a getter function to the model we can use to retrieve
            # the field/manager by name.
            getter_name = "get_%s_name" % self.__class__.__name__.lower()
            cls.add_to_class(getter_name, lambda self: name)

            def connect_save(sender):
                post_save.connect(self._related_items_changed, sender=sender)

            def connect_delete(sender):
                post_delete.connect(self._related_items_changed, sender=sender)

            lazy_model_ops.add(connect_save, self.rel.to)
            lazy_model_ops.add(connect_delete, self.rel.to)

    def _related_items_changed(self, **kwargs):
        """
        Ensure that the given related item is actually for the model
        this field applies to, and pass the instance to the real
        ``related_items_changed`` handler.
        """
        for_model = kwargs["instance"].content_type.model_class()
        if issubclass(for_model, self.model):
            instance_id = kwargs["instance"].object_pk
            try:
                instance = for_model.objects.get(id=instance_id)<commit_msg>Handle None as content_type subclass without breaking<commit_after>from __future__ import division, unicode_literals
from future.builtins import str

from copy import copy

from django.contrib.contenttypes.fields import GenericRelation
from django.core.exceptions import ImproperlyConfigured, AppRegistryNotReady
from django.db.models import IntegerField, CharField, FloatField
from django.db.models.signals import post_save, post_delete

from mezzanine.utils.models import lazy_model_ops


class BaseGenericRelation(GenericRelation):
    """
    Extends ``GenericRelation`` to:

    - Add a consistent default value for ``object_id_field`` and
      check for a ``default_related_model`` attribute which can be
      defined on subclasses as a default for the ``to`` argument.

    - Add one or more custom fields to the model that the relation
      field is applied to, and then call a ``related_items_changed``
      method each time related items are saved or deleted, so that a
      calculated value can be stored against the custom fields since
      aggregates aren't available for GenericRelation instances.

    """

    # Mapping of field names to model fields that will be added.
    fields = {}

    def __init__(self, *args, **kwargs):
        """
        Set up some defaults and check for a ``default_related_model``
        attribute for the ``to`` argument.
        """
        kwargs.setdefault("object_id_field", "object_pk")
        to = getattr(self, "default_related_model", None)
        # Avoid having both a positional arg and a keyword arg for
        # the parameter ``to``
        if to and not args:
            kwargs.setdefault("to", to)
        try:
            # Check if ``related_model`` has been modified by a subclass
            self.related_model
        except (AppRegistryNotReady, AttributeError):
            # if not, all is good
            super(BaseGenericRelation, self).__init__(*args, **kwargs)
        else:
            # otherwise, warn the user to stick to the new (as of 4.0)
            # ``default_related_model`` attribute
            raise ImproperlyConfigured("BaseGenericRelation changed the "
                "way it handled a default ``related_model`` in mezzanine "
                "4.0. Please override ``default_related_model`` instead "
                "and do not tamper with django's ``related_model`` "
                "property anymore.")

    def contribute_to_class(self, cls, name):
        """
        Add each of the names and fields in the ``fields`` attribute
        to the model the relationship field is applied to, and set up
        the related item save and delete signals for calling
        ``related_items_changed``.
        """
        for field in cls._meta.many_to_many:
            if isinstance(field, self.__class__):
                e = "Multiple %s fields are not supported (%s.%s, %s.%s)" % (
                    self.__class__.__name__, cls.__name__, cls.__name__,
                    name, field.name)
                raise ImproperlyConfigured(e)
        self.related_field_name = name
        super(BaseGenericRelation, self).contribute_to_class(cls, name)
        # Not applicable to abstract classes, and in fact will break.
        if not cls._meta.abstract:
            for (name_string, field) in self.fields.items():
                if "%s" in name_string:
                    name_string = name_string % name
                # In Django 1.6, add_to_class will be called on a
                # parent model's field more than once, so
                # contribute_to_class needs to be idempotent. We
                # don't call get_all_field_names() which fill the app
                # cache get_fields_with_model() is safe.
                try:
                    # Django >= 1.8
                    extant_fields = cls._meta._forward_fields_map
                except AttributeError:
                    # Django <= 1.7
                    extant_fields = (i.name for i in cls._meta.fields)
                if name_string in extant_fields:
                    continue
                if field.verbose_name is None:
                    field.verbose_name = self.verbose_name
                cls.add_to_class(name_string, copy(field))
            # Add a getter function to the model we can use to retrieve
            # the field/manager by name.
            getter_name = "get_%s_name" % self.__class__.__name__.lower()
            cls.add_to_class(getter_name, lambda self: name)

            def connect_save(sender):
                post_save.connect(self._related_items_changed, sender=sender)

            def connect_delete(sender):
                post_delete.connect(self._related_items_changed, sender=sender)

            lazy_model_ops.add(connect_save, self.rel.to)
            lazy_model_ops.add(connect_delete, self.rel.to)

    def _related_items_changed(self, **kwargs):
        """
        Ensure that the given related item is actually for the model
        this field applies to, and pass the instance to the real
        ``related_items_changed`` handler.
        """
        for_model = kwargs["instance"].content_type.model_class()
        if for_model and issubclass(for_model, self.model):
            instance_id = kwargs["instance"].object_pk
            try:
                instance = for_model.objects.get(id=instance_id)
{'fraction_non_alphanumeric': 0.0753514526710403, 'fraction_numerical': 0.0018744142455482662, 'mean_word_length': 2.2016201620162015, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, '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': '213073', 'n_tokens_mistral': 2816, 'n_tokens_neox': 2721, 'n_words': 927}