<commit_before>
from django.contrib.contenttypes.generic import GenericRelation
from django.db.models import IntegerField, CharField
from django.db.models.signals import post_save, post_delete


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

    - Add a consistent default value for ``object_id_field`` and
      check for a ``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 ``related_model``
        attribute for the ``to`` argument.
        """
        kwargs.setdefault("object_id_field", "object_pk")
        to = getattr(self, "related_model", None)
        if to:
            kwargs.setdefault("to", to)
        super(BaseGenericRelation, self).__init__(*args, **kwargs)

    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``.
        """
        super(BaseGenericRelation, self).contribute_to_class(cls, name)
        self.related_field_name = 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
                if not field.verbose_name:
                    field.verbose_name = self.verbose_name
                cls.add_to_class(name_string, field)
            post_save.connect(self._related_items_changed, self.rel.to, True)
            post_delete.connect(self._related_items_changed, self.rel.to, True)

    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 = self.model.objects.get(id=kwargs["instance"].object_pk)
            if hasattr(instance, "get_content_model"):
                instance = instance.get_content_model()<commit_msg>Applied workaround for unexplainable Django issue where certain signals get lost.<commit_after>
from uuid import uuid4

from django.contrib.contenttypes.generic import GenericRelation
from django.db.models import IntegerField, CharField
from django.db.models.signals import post_save, post_delete


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

    - Add a consistent default value for ``object_id_field`` and
      check for a ``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 ``related_model``
        attribute for the ``to`` argument.
        """
        kwargs.setdefault("object_id_field", "object_pk")
        to = getattr(self, "related_model", None)
        if to:
            kwargs.setdefault("to", to)
        super(BaseGenericRelation, self).__init__(*args, **kwargs)
        self._save_id = uuid4()
        self._delete_id = uuid4()

    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``.
        """
        super(BaseGenericRelation, self).contribute_to_class(cls, name)
        self.related_field_name = 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
                if not field.verbose_name:
                    field.verbose_name = self.verbose_name
                cls.add_to_class(name_string, field)
            # For some unknown reason the signal won't be triggered
            # if given a sender arg, particularly when running
            # Cartridge with the field ContentPage.keywords - so
            # instead of specifying self.rel.to as the sender, we
            # check for it inside the signal itself.
            post_save.connect(self._related_items_changed)
            post_delete.connect(self._related_items_changed)

    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.
        """
        # Manually check that the instance matches the relation,
        # since we don't specify a sender for the signal.
        if not isinstance(kwargs["instance"], self.rel.to):
            return
        for_model = kwargs["instance"].content_type.model_class()
        if issubclass(for_model, self.model):
            instance = self.model.objects.get(id=kwargs["instance"].object_pk)
            if hasattr(instance, "get_content_model"):
                instance = instance.get_content_model()
{'fraction_non_alphanumeric': 0.07977970102281667, 'fraction_numerical': 0.00047206923682140047, 'mean_word_length': 2.5808450704225354, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '7362155', 'n_tokens_mistral': 1647, 'n_tokens_neox': 1625, 'n_words': 587}