Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

User group assignment track in django admin

Tags:

python

django

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

like image 844
Yogesh Avatar asked Feb 10 '16 09:02

Yogesh


People also ask

How do I access groups in Django?

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.

What is Is_active in Django?

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.

What is UserAdmin in Django?

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)

What are groups in admin panel in Django?

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.


1 Answers

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()
like image 70
imposeren Avatar answered Nov 16 '22 03:11

imposeren