def m2m_changed_handler(sender, *args, **kwargs):
action = kwargs['<STR_LIT:action>']<EOL>instance = kwargs['<STR_LIT>']<EOL>logger.debug("<STR_LIT>"%(sender, args, kwargs))<EOL>bulk = []<EOL>if is_df(instance) and (action in ['<STR_LIT>', '<STR_LIT>']):<EOL><INDENT>pk_set = list(kwargs.get('<STR_LIT>') or [])<EOL>relation_name = sender._meta.db_table.replace(sender._meta.app_label + '<STR_LIT:_>' + instance.__class__.__name__.lower() + '<STR_LIT:_>', '<STR_LIT>')<EOL>relations = {k.name:k for k in m2m_relations(instance)}<EOL>field = relations[relation_name]<EOL>for pk in pk_set:<EOL><INDENT>related_instance = get_relation(relations[relation_name]).objects.get(pk=pk)<EOL>changes = {field.name: {'<STR_LIT>': [pk],<EOL>'<STR_LIT>': six.text_type(related_instance)}}<EOL>bulk.append(History.objects.add(<EOL>action=action,<EOL>changes=changes,<EOL>model=instance,<EOL>commit=False,))<EOL>field = get_field(field)<EOL>changes = {field.name: {'<STR_LIT>': [instance.pk],<EOL>'<STR_LIT>': six.text_type(instance),<EOL>'<STR_LIT>': True,}}<EOL>bulk.append(History.objects.add(<EOL>action='<STR_LIT>' if action in ['<STR_LIT>'] else '<STR_LIT>',<EOL>changes=changes,<EOL>model=related_instance,<EOL>commit=False,))<EOL><DEDENT><DEDENT>if is_df(instance) and (action in ['<STR_LIT>']):<EOL><INDENT>relations = instance._meta.get_all_related_many_to_many_objects()<EOL>for relation in relations:<EOL><INDENT>instances = get_m2m_reverse_instances(instance, relation)<EOL>field = get_model_relation_by_instance(kwargs['<STR_LIT>'], instance)<EOL>changes = {field.name: {'<STR_LIT>': [instance.pk],<EOL>'<STR_LIT>': six.text_type(instance)}}<EOL>for k in instances:<EOL><INDENT>bulk.append(History.objects.add(<EOL>action=action,<EOL>changes=changes,<EOL>model=k,<EOL>commit=False,))<EOL><DEDENT><DEDENT><DEDENT>if bulk:<EOL><INDENT>History.objects.bulk_create(bulk)<EOL><DEDENT>
A model's save() never gets called on ManyToManyField changes, m2m_changed-signal is sent.
sender = dynamically generated model in m2m-table
instance = parent
related_instance = instance being m2m'ed