from django import forms from django.contrib.auth import get_user_model from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase from utils.forms import KeepUnselectableModelFormMixin User = get_user_model() class KeepUnselectableModelFormMixinTests(TestCase): def _get_form_cls(self): """ We recreate a new form class for each test, because `queryset` may not be reevaluate and miss the permissions created by `setUp`. """ class ExampleForm(KeepUnselectableModelFormMixin, forms.ModelForm): user_permissions = forms.ModelMultipleChoiceField( queryset=Permission.objects.filter( codename__startswith='selec'), ) keep_unselectable_fields = ['user_permissions'] class Meta: model = User fields = ('username', 'user_permissions') return ExampleForm def setUp(self): ct = ContentType.objects.get_for_model(Permission) self.unselec_perm1 = Permission.objects.create( content_type=ct, codename='unselec_perm1', name='Unselectable 1') self.unselec_perm2 = Permission.objects.create( content_type=ct, codename='unselec_perm2', name='Unselectable 2') # These two perms are the only selectable permissions from # 'permissions' field of ExampleForm. self.selec_perm1 = Permission.objects.create( content_type=ct, codename='selec_perm1', name='Selectable 1') self.selec_perm2 = Permission.objects.create( content_type=ct, codename='selec_perm2', name='Selectable 2') def test_creation(self): """ The mixin functions properly when instance is being created. """ ExampleForm = self._get_form_cls() data = { 'username': 'user', 'user_permissions': [self.selec_perm1.pk], } form = ExampleForm(data) instance = form.save() self.assertQuerysetEqual( instance.user_permissions.all(), map(repr, [self.selec_perm1]), ) def test_creation_commit_false(self): """ When instance is being created and 'save' method is called with 'commit=False', 'save_m2m' method functions properly. https://docs.djangoproject.com/en/1.11/topics/forms/modelforms/#the-save-method """ ExampleForm = self._get_form_cls() data = { 'username': 'user', 'user_permissions': [self.selec_perm1.pk], } form = ExampleForm(data) instance = form.save(commit=False) with self.assertRaises(User.DoesNotExist): User.objects.get(username='user') instance.save() form.save_m2m() self.assertQuerysetEqual( instance.user_permissions.all(), map(repr, [self.selec_perm1]), ) def test_existing(self): """ Unselectable items of an instance are kept. """ instance = User.objects.create(username='user') # Link instance with an unselectable and a selectable permissions. instance.user_permissions.add(self.unselec_perm1, self.selec_perm2) ExampleForm = self._get_form_cls() data = { 'username': 'user', 'user_permissions': [self.selec_perm1.pk], } form = ExampleForm(data, instance=instance) instance = form.save() self.assertQuerysetEqual( instance.user_permissions.all(), map(repr, [self.selec_perm1, self.unselec_perm1]), ) def test_existing_commit_false(self): """ When 'save' is called with 'commit=False', unselectable items of an instance are kept by 'save_m2m'. """ instance = User.objects.create(username='user') # Link instance with an unselectable and a selectable permissions. instance.user_permissions.add(self.unselec_perm1, self.selec_perm2) ExampleForm = self._get_form_cls() data = { 'username': 'changed', 'user_permissions': [self.selec_perm1.pk], } form = ExampleForm(data, instance=instance) instance = form.save(commit=False) with self.assertRaises(User.DoesNotExist): User.objects.get(username='changed') instance.save() form.save_m2m() self.assertQuerysetEqual( instance.user_permissions.all(), map(repr, [self.selec_perm1, self.unselec_perm1]), )