diff --git a/passbook/core/migrations/0004_auto_20200703_2213.py b/passbook/core/migrations/0004_auto_20200703_2213.py new file mode 100644 index 000000000..b56c3d95c --- /dev/null +++ b/passbook/core/migrations/0004_auto_20200703_2213.py @@ -0,0 +1,28 @@ +# Generated by Django 3.0.7 on 2020-07-03 22:13 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("passbook_core", "0003_default_user"), + ] + + operations = [ + migrations.AlterModelOptions( + name="application", + options={ + "verbose_name": "Application", + "verbose_name_plural": "Applications", + }, + ), + migrations.AlterModelOptions( + name="user", + options={ + "permissions": (("reset_user_password", "Reset Password"),), + "verbose_name": "User", + "verbose_name_plural": "Users", + }, + ), + ] diff --git a/passbook/core/models.py b/passbook/core/models.py index a632c8369..e660ab53a 100644 --- a/passbook/core/models.py +++ b/passbook/core/models.py @@ -71,6 +71,8 @@ class User(GuardianUserMixin, AbstractUser): class Meta: permissions = (("reset_user_password", "Reset Password"),) + verbose_name = _("User") + verbose_name_plural = _("Users") class Provider(models.Model): @@ -121,6 +123,11 @@ class Application(PolicyBindingModel): def __str__(self): return self.name + class Meta: + + verbose_name = _("Application") + verbose_name_plural = _("Applications") + class Source(PolicyBindingModel): """Base Authentication source, i.e. an OAuth Provider, SAML Remote or LDAP Server""" diff --git a/passbook/lib/widgets.py b/passbook/lib/widgets.py new file mode 100644 index 000000000..5d026b488 --- /dev/null +++ b/passbook/lib/widgets.py @@ -0,0 +1,28 @@ +"""Utility Widgets""" +from functools import partial +from itertools import groupby +from operator import attrgetter + +from django.forms.models import ModelChoiceField, ModelChoiceIterator + + +class GroupedModelChoiceIterator(ModelChoiceIterator): + """ModelChoiceField which groups objects by their verbose_name""" + + def __iter__(self): + if self.field.empty_label is not None: + yield ("", self.field.empty_label) + queryset = self.queryset + # Can't use iterator() when queryset uses prefetch_related() + if not queryset._prefetch_related_lookups: + queryset = queryset.iterator() + # We can't use DB-level sorting as we sort by subclass + queryset = sorted(queryset, key=lambda x: x._meta.verbose_name) + for group, objs in groupby(queryset, key=lambda x: x._meta.verbose_name): + yield (group, [self.choice(obj) for obj in objs]) + + +class GroupedModelChoiceField(ModelChoiceField): + """ModelChoiceField which groups objects by their verbose_name""" + + iterator = GroupedModelChoiceIterator diff --git a/passbook/policies/forms.py b/passbook/policies/forms.py index 0378e2c58..6a961ac86 100644 --- a/passbook/policies/forms.py +++ b/passbook/policies/forms.py @@ -1,7 +1,9 @@ """General fields""" + from django import forms -from passbook.policies.models import PolicyBinding, PolicyBindingModel +from passbook.lib.widgets import GroupedModelChoiceField +from passbook.policies.models import Policy, PolicyBinding, PolicyBindingModel GENERAL_FIELDS = ["name"] GENERAL_SERIALIZER_FIELDS = ["pk", "name"] @@ -10,10 +12,11 @@ GENERAL_SERIALIZER_FIELDS = ["pk", "name"] class PolicyBindingForm(forms.ModelForm): """Form to edit Policy to PolicyBindingModel Binding""" - target = forms.ModelChoiceField( + target = GroupedModelChoiceField( queryset=PolicyBindingModel.objects.all().select_subclasses(), to_field_name="pbm_uuid", ) + policy = GroupedModelChoiceField(queryset=Policy.objects.all().select_subclasses(),) class Meta: diff --git a/swagger.yaml b/swagger.yaml index b2a42b3da..83f7b7047 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -210,7 +210,7 @@ paths: parameters: - name: pbm_uuid in: path - description: A UUID string identifying this application. + description: A UUID string identifying this Application. required: true type: string format: uuid @@ -479,7 +479,7 @@ paths: parameters: - name: id in: path - description: A unique integer value identifying this user. + description: A unique integer value identifying this User. required: true type: integer /flows/bindings/: