In Django I want to track how the groups are assigned to User, right now we are assigning group to user through Django Admin. I tried to use m2m_changed signal on User.groups.through table but In Django admin when group assigned to User then it first clears out existing groups of user and add updated groups so it creates unnecessary call to "pre_clear" and "post_clear" action of signal so because of it signal is not working in all cases.
Can anyone suggest me the way to override Django User save method and handle changed group in that method? or any other way to track user group assignment?
I am using Django 1.6
With Django, you can create groups to class users and assign permissions to each group so when creating users, you can just assign the user to a group and, in turn, the user has all the permissions from that group. To create a group, you need the Group model from django. contrib. auth.
is_active. Boolean. Designates whether this user account should be considered active. We recommend that you set this flag to False instead of deleting accounts; that way, if your applications have any foreign keys to users, the foreign keys won't break.
Django uses UserAdmin to render the nice admin look for User model. By just using this in our admin.py -file, we can get the same look for our model. from django.contrib.auth.admin import UserAdmin admin.site.register(MyUser, UserAdmin)
Groups are a means of categorizing users. This allows for granting permissions to a specific group. It also saves a lot of time, as compared to granting permissions to each individual user. A user may belong to any number of groups and automatically has all the permissions granted to that group.
That's a known problem of Django and it was fixed only in 1.9: https://docs.djangoproject.com/en/1.9/releases/1.9/#related-set-direct-assignment.
Direct assignment of related objects in the ORM used to perform a clear() followed by a call to add(). This caused needlessly large data changes and prevented using the m2m_changed signal to track individual changes in many-to-many relations.
Direct assignment now relies on the the new set() method on related managers which by default only processes changes between the existing related set and the one that’s newly assigned. The previous behavior can be restored by replacing direct assignment by a call to set() with the keyword argument clear=True.
ModelForm, and therefore ModelAdmin, internally rely on direct assignment for many-to-many relations and as a consequence now use the new behavior.
If you were manually using user.groups.add(foo)
or user.groups.remove(foo)
then your current signal receiver should work fine. But django admin uses assignment so it causes clear and set on every save from django-admin. To fix this problem without updating to django 1.9 you have to process both pre_clear
and post_add
: store cleared pks
to some "special" attribute of instance in preclear and then calculate what was added or removed.
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With