Projects management with Model Mixins¶
Mixins allows to create shared apps based on django-groups-manager. The mixins approach has pros and cons.
- Pros:
- models are completely customizable (add all fields you need);
- all fields are in the same table (with subclassed models, only extra fields are stored in the subclass table);
- better for shared applications (the “original” django-groups-manager tables don’t share entries from different models).
- Cons:
- all external foreign keys must be declared in the concrete model;
- all signals must be declared with concrete models.
Model mixins example¶
The following models allow to manage a set of Organizations with related members (from organization
app).
In this example, a last_edit_date
is added to models, and member display name has the user email (if defined).
1) Define models in models.py:
from groups_manager.models import GroupMixin, MemberMixin, GroupMemberMixin, GroupMemberRoleMixin, \
GroupEntity, GroupType, \
group_save, group_delete, member_save, member_delete, group_member_save, group_member_delete
class OrganizationMemberRole(GroupMemberRoleMixin):
pass
class OrganizationGroupMember(GroupMemberMixin):
group = models.ForeignKey('OrganizationGroup', related_name='group_membership')
member = models.ForeignKey('OrganizationMember', related_name='group_membership')
roles = models.ManyToManyField(OrganizationMemberRole, blank=True)
class OrganizationGroup(GroupMixin):
last_edit_date = models.DateTimeField(auto_now=True, null=True)
short_name = models.CharField(max_length=50, default='', blank=True)
country = CountryField(null=True, blank=True)
city = models.CharField(max_length=200, blank=True, default='')
group_type = models.ForeignKey(GroupType, null=True, blank=True, on_delete=models.SET_NULL,
related_name='%(app_label)s_%(class)s_set')
group_entities = models.ManyToManyField(GroupEntity, null=True, blank=True,
related_name='%(app_label)s_%(class)s_set')
django_group = models.ForeignKey(DjangoGroup, null=True, blank=True, on_delete=models.SET_NULL)
group_members = models.ManyToManyField('OrganizationMember', through=OrganizationGroupMember,
through_fields=('group', 'member'),
related_name='%(app_label)s_%(class)s_set')
class Meta:
permissions = (('manage_organization', 'Manage Organization'),
('view_organization', 'View Organization'))
class GroupsManagerMeta:
member_model = 'organizations.OrganizationMember'
group_member_model = 'organizations.OrganizationGroupMember'
def save(self, *args, **kwargs):
if not self.short_name:
self.short_name = self.name
super(OrganizationGroup, self).save(*args, **kwargs)
@property
def members_names(self):
return [member.full_name for member in self.group_members.all()]
class OrganizationMember(MemberMixin):
last_edit_date = models.DateTimeField(auto_now=True, null=True)
django_user = models.ForeignKey(DjangoUser, null=True, blank=True, on_delete=models.SET_NULL,
related_name='%(app_label)s_%(class)s_set')
class GroupsManagerMeta:
group_model = 'organizations.OrganizationGroup'
group_member_model = 'organizations.OrganizationGroupMember'
def __unicode__(self):
if self.email:
return '%s (%s)' % (self.full_name, self.email)
return self.full_name
def __str__(self):
if self.email:
return '%s (%s)' % (self.full_name, self.email)
return self.full_name
2) Connect creation and deletion signals to the models
(This step is required if you want to sync with django auth models):
post_save.connect(group_save, sender=OrganizationGroup)
post_delete.connect(group_delete, sender=OrganizationGroup)
post_save.connect(member_save, sender=OrganizationMember)
post_delete.connect(member_delete, sender=OrganizationMember)
post_save.connect(group_member_save, sender=OrganizationGroupMember)
post_delete.connect(group_member_delete, sender=OrganizationGroupMember)
3) Customize the flag for AUTH_MODEL_SYNC
If you plan to create a reusable app and to let users decide if sync or not with Django auth models
independently from groups_manager
settings, you should define a separated function that
returns the boolean value from your own settings:
def organization_with_mixin_get_auth_models_sync_func(instance):
return organization.SETTINGS['DJANGO_AUTH_MODEL_SYNC'] # example
def organization_group_member_save(*args, **kwargs):
group_member_save(*args, get_auth_models_sync_func=organization_get_auth_models_sync_func, **kwargs)
def organization_group_member_delete(*args, **kwargs):
group_member_delete(*args, get_auth_models_sync_func=organization_get_auth_models_sync_func, **kwargs)
post_save.connect(organization_group_member_save, sender=OrganizationGroupMember)
post_delete.connect(organization_group_member_delete, sender=OrganizationGroupMember)
Note
The full tested example is available in repository source code, testproject
‘s tests.py
under test_model_mixins
method.