m2m_changed callback.
    The idea is to get the model and the instance of the object being tracked,
    and the different objects being added/removed. It is then send to the
    ``_create_tracked_field_m2m`` method to extract the proper attribute for
    the TrackedFieldModification.

def tracking_m2m(
        sender, instance, action, reverse, model, pk_set, using, **kwargs
):
    """
    m2m_changed callback.
    The idea is to get the model and the instance of the object being tracked,
    and the different objects being added/removed. It is then send to the
    ``_create_tracked_field_m2m`` method to extract the proper attribute for
    the TrackedFieldModification.
    """
    action_event = {
        'pre_clear': 'CLEAR',
        'pre_add': 'ADD',
        'pre_remove': 'REMOVE',
    }
    if (action not in action_event.keys()):
        return
    if reverse:
        if action == 'pre_clear':
            # It will actually be a remove of ``instance`` on every
            # tracked object being related
            action = 'pre_remove'
            # pk_set is None for clear events, we need to get objects' pk.
            field = _get_m2m_field(model, sender)
            field = model._meta.get_field(field).remote_field.get_accessor_name()
            pk_set = set([obj.id for obj in getattr(instance, field).all()])
        # Create an event for each object being tracked
        for pk in pk_set:
            tracked_instance = model.objects.get(pk=pk)
            objects = [instance]
            _create_tracked_event_m2m(
                model, tracked_instance, sender, objects, action_event[action]
            )
    else:
        # Get the model of the object being tracked
        tracked_model = instance._meta.model
        objects = []
        if pk_set is not None:
            objects = [model.objects.get(pk=pk) for pk in pk_set]
        _create_tracked_event_m2m(
            tracked_model, instance, sender, objects, action_event[action]
        )