Это известная проблема Джанго и она была установлена только в версии 1.9: https://docs.djangoproject.com/en/1.9/releases/1.9/#related-set-direct-assignment.
Прямое присвоение связанных объектов в ORM, используемых для выполнения операции clear(), за которым следует вызов для добавления(). Это вызвало ненужные большие данные изменений и предотвратило использование сигнала m2m_changed для отслеживания отдельных изменений во многих отношениях.
Прямое присвоение теперь основано на новом методе set() для связанных менеджеров , который по умолчанию обрабатывает только изменения существующего связанного набора и того, который был недавно назначен. Предыдущее поведение может быть восстановлено путем замены прямого назначения вызовом set() с аргументом ключевого слова clear = True.
ModelForm, и поэтому ModelAdmin, внутренне полагаются на прямое назначение для отношений «многие ко многим» и, как следствие, теперь используют новое поведение .
Если вы вручную использовали user.groups.add(foo)
или user.groups.remove(foo)
, то ваш приемник текущего сигнала должен работать нормально. Но администратор django использует назначение, поэтому он становится понятным и устанавливается при каждом сохранении из django-admin. Чтобы исправить эту проблему без обновления django 1.9, вам необходимо обработать как pre_clear
, так и post_add
: сохраните очищенный pks
до некоторого «специального» атрибута экземпляра в преклирете, а затем вычислите, что было добавлено или удалено.
def pre_clear_handler(sender, *args, **kwargs):
# ...
instance._cleared_groups_pk_set = set([group.pk for group in instance.groups.all()]) # or `set(instance.groups.all().values_list('pk', flat=True))
tracker = SomeTrackingModel()
tracker.removed_group_ids = instance._cleared_groups_pk_set
tracker.save() # post_add may not be called if groups are fully cleared
instance._changes_tracker_helper = tracker
def post_add_handler(sender, *args, **kwargs):
# ...
prev_groups_pk_set = getattr(instance, '_cleared_groups_pk_set', None)
if prev_groups_pk_set is not None:
not_realy_added_pk_set = kwargs['pk_set']
removed_pks = prev_groups_pk_set - not_realy_added_pk_set
realy_added_pks = not_realy_added_pk_set - prev_groups_pk_set
else:
removed_pks = set()
realy_added_pks = kwargs['pk_set']
tracker = getattr(instance, '_changes_tracker_helper', SomeTrackingModel())
tracker.removed_group_ids = removed_pks
tracker.added_group_ids = realy_added_pks
tracker.save()
Я думаю, вы могли бы показать какой-то код, чтобы другие могли лучше понять вас и опубликовать решение. – pktangyue