[Django]-Is there a Django 1.7+ replacement for South's add_introspection_rules()?

8👍

As Phillip mentions in the comments, deconstruct() is the official way to handle custom fields in django migrations.

To go on to complete the request for clarification… It would appear that there are already a couple of examples of code out there written to handle both. For example, this excerpt (to handle the on parameter for ExclusiveBooleanField) is taken from django-exclusivebooleanfield:

from django.db import models, transaction
from django.db.models import Q

from six import string_types
from six.moves import reduce


try:
    transaction_context = transaction.atomic
except AttributeError:
    transaction_context = transaction.commit_on_success


class ExclusiveBooleanField(models.BooleanField):
    """
    Usage:

    class MyModel(models.Model):
        the_one = ExclusiveBooleanField()


    class MyModel(models.Model):
        field_1 = ForeignKey()
        field_2 = CharField()
        the_one = ExclusiveBooleanField(on=('field_1', 'field_2'))
        # `on` is a bit like a unique constraint, value of field
        # is only exclusive for rows with same value of the on fields
    """
    def __init__(self, on=None, *args, **kwargs):
        if isinstance(on, string_types):
            on = (on, )
        self._on_fields = on or ()
        super(ExclusiveBooleanField, self).__init__(*args, **kwargs)

    def contribute_to_class(self, cls, name):
        super(ExclusiveBooleanField, self).contribute_to_class(cls, name)
        models.signals.class_prepared.connect(self._replace_save, sender=cls)

    def deconstruct(self):
        """
        to support Django 1.7 migrations, see also the add_introspection_rules
        section at bottom of this file for South + earlier Django versions
        """
        name, path, args, kwargs = super(
            ExclusiveBooleanField, self).deconstruct()
        if self._on_fields:
            kwargs['on'] = self._on_fields
        return name, path, args, kwargs

    def _replace_save(self, sender, **kwargs):
        old_save = sender.save
        field_name = self.name
        on_fields = self._on_fields

        def new_save(self, *args, **kwargs):
            def reducer(left, right):
                return left & Q(**{right: getattr(self, right)})

            with transaction_context():
                if getattr(self, field_name) is True:
                    f_args = reduce(reducer, on_fields, Q())
                    u_args = {field_name: False}
                    sender._default_manager.filter(f_args).update(**u_args)
                old_save(self, *args, **kwargs)
        new_save.alters_data = True

        sender.save = new_save


try:
    from south.modelsinspector import add_introspection_rules
    add_introspection_rules(
        rules=[
            (
                (ExclusiveBooleanField,),
                [],
                {"on": ["_on_fields", {"default": tuple()}]},
            )
        ],
        patterns=[
            'exclusivebooleanfield\.fields\.ExclusiveBooleanField',
        ]
    )
except ImportError:
    pass

Leave a comment