diff --git a/netbox/netbox/authentication.py b/netbox/netbox/authentication.py index 10555b373..c70c68bc0 100644 --- a/netbox/netbox/authentication.py +++ b/netbox/netbox/authentication.py @@ -4,13 +4,13 @@ from collections import defaultdict from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.auth.backends import ModelBackend, RemoteUserBackend as _RemoteUserBackend -from django.contrib.auth.models import Group, AnonymousUser +from django.contrib.auth.models import AnonymousUser from django.core.exceptions import ImproperlyConfigured from django.db.models import Q from django.utils.translation import gettext_lazy as _ from users.constants import CONSTRAINT_TOKEN_USER -from users.models import ObjectPermission +from users.models import Group, ObjectPermission from utilities.permissions import ( permission_is_exempt, qs_filter_from_constraints, resolve_permission, resolve_permission_ct, ) diff --git a/netbox/netbox/navigation/menu.py b/netbox/netbox/navigation/menu.py index 2dba76e72..621bd4f5d 100644 --- a/netbox/netbox/navigation/menu.py +++ b/netbox/netbox/navigation/menu.py @@ -392,19 +392,19 @@ ADMIN_MENU = Menu( ), # Proxy model for auth.Group MenuItem( - link=f'users:netboxgroup_list', + link=f'users:group_list', link_text=_('Groups'), permissions=[f'auth.view_group'], staff_only=True, buttons=( MenuItemButton( - link=f'users:netboxgroup_add', + link=f'users:group_add', title='Add', icon_class='mdi mdi-plus-thick', permissions=[f'auth.add_group'] ), MenuItemButton( - link=f'users:netboxgroup_import', + link=f'users:group_import', title='Import', icon_class='mdi mdi-upload', permissions=[f'auth.add_group'] diff --git a/netbox/netbox/tests/test_authentication.py b/netbox/netbox/tests/test_authentication.py index 1804087d1..6a894edcd 100644 --- a/netbox/netbox/tests/test_authentication.py +++ b/netbox/netbox/tests/test_authentication.py @@ -2,7 +2,6 @@ import datetime from django.conf import settings from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group from django.contrib.contenttypes.models import ContentType from django.test import Client from django.test.utils import override_settings @@ -12,7 +11,7 @@ from rest_framework.test import APIClient from dcim.models import Site from ipam.models import Prefix -from users.models import ObjectPermission, Token +from users.models import Group, ObjectPermission, Token from utilities.testing import TestCase from utilities.testing.api import APITestCase diff --git a/netbox/templates/users/group.html b/netbox/templates/users/group.html index 27b4707fb..d3f02af12 100644 --- a/netbox/templates/users/group.html +++ b/netbox/templates/users/group.html @@ -24,7 +24,7 @@
{% trans "Users" %}
- {% for user in object.user_set.all %} + {% for user in object.users.all %} {{ user }} {% empty %}
{% trans "None" %}
diff --git a/netbox/templates/users/objectpermission.html b/netbox/templates/users/objectpermission.html index 9a222ba80..3e8b71327 100644 --- a/netbox/templates/users/objectpermission.html +++ b/netbox/templates/users/objectpermission.html @@ -82,7 +82,7 @@
{% trans "Assigned Groups" %}
{% for group in object.groups.all %} - {{ group }} + {{ group }} {% empty %}
{% trans "None" %}
{% endfor %} diff --git a/netbox/templates/users/user.html b/netbox/templates/users/user.html index 3b08f98d9..0dd12fb52 100644 --- a/netbox/templates/users/user.html +++ b/netbox/templates/users/user.html @@ -53,7 +53,7 @@
{% trans "Assigned Groups" %}
{% for group in object.groups.all %} - {{ group }} + {{ group }} {% empty %}
{% trans "None" %}
{% endfor %} diff --git a/netbox/users/api/nested_serializers.py b/netbox/users/api/nested_serializers.py index 5e15fa41a..552c24906 100644 --- a/netbox/users/api/nested_serializers.py +++ b/netbox/users/api/nested_serializers.py @@ -1,5 +1,4 @@ from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group from django.contrib.contenttypes.models import ContentType from drf_spectacular.utils import extend_schema_field from drf_spectacular.types import OpenApiTypes @@ -7,7 +6,7 @@ from rest_framework import serializers from netbox.api.fields import ContentTypeField from netbox.api.serializers import WritableNestedSerializer -from users.models import ObjectPermission, Token +from users.models import Group, ObjectPermission, Token __all__ = [ 'NestedGroupSerializer', diff --git a/netbox/users/api/serializers.py b/netbox/users/api/serializers.py index 0eef61dc8..b9bd55e75 100644 --- a/netbox/users/api/serializers.py +++ b/netbox/users/api/serializers.py @@ -1,7 +1,6 @@ from django.conf import settings from django.contrib.auth import authenticate from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group from django.contrib.contenttypes.models import ContentType from drf_spectacular.utils import extend_schema_field from drf_spectacular.types import OpenApiTypes @@ -10,7 +9,7 @@ from rest_framework.exceptions import AuthenticationFailed, PermissionDenied from netbox.api.fields import ContentTypeField, IPNetworkSerializer, SerializedPKRelatedField from netbox.api.serializers import ValidatedModelSerializer -from users.models import ObjectPermission, Token +from users.models import Group, ObjectPermission, Token from .nested_serializers import * diff --git a/netbox/users/api/views.py b/netbox/users/api/views.py index 895600822..412bccf59 100644 --- a/netbox/users/api/views.py +++ b/netbox/users/api/views.py @@ -1,11 +1,9 @@ import logging -from django.contrib.auth import authenticate + from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group from django.db.models import Count -from drf_spectacular.utils import extend_schema from drf_spectacular.types import OpenApiTypes -from rest_framework.exceptions import AuthenticationFailed +from drf_spectacular.utils import extend_schema from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.routers import APIRootView @@ -15,7 +13,7 @@ from rest_framework.viewsets import ViewSet from netbox.api.viewsets import NetBoxModelViewSet from users import filtersets -from users.models import ObjectPermission, Token, UserConfig +from users.models import Group, ObjectPermission, Token, UserConfig from utilities.querysets import RestrictedQuerySet from utilities.utils import deepmerge from . import serializers @@ -40,7 +38,7 @@ class UserViewSet(NetBoxModelViewSet): class GroupViewSet(NetBoxModelViewSet): - queryset = RestrictedQuerySet(model=Group).annotate(user_count=Count('user')).order_by('name') + queryset = Group.objects.annotate(user_count=Count('user')) serializer_class = serializers.GroupSerializer filterset_class = filtersets.GroupFilterSet diff --git a/netbox/users/filtersets.py b/netbox/users/filtersets.py index 0f590e012..5dbca7738 100644 --- a/netbox/users/filtersets.py +++ b/netbox/users/filtersets.py @@ -1,11 +1,10 @@ import django_filters from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group from django.db.models import Q from django.utils.translation import gettext as _ from netbox.filtersets import BaseFilterSet -from users.models import ObjectPermission, Token +from users.models import Group, ObjectPermission, Token __all__ = ( 'GroupFilterSet', diff --git a/netbox/users/forms/bulk_import.py b/netbox/users/forms/bulk_import.py index 055998c69..cbaa1ad76 100644 --- a/netbox/users/forms/bulk_import.py +++ b/netbox/users/forms/bulk_import.py @@ -14,7 +14,7 @@ __all__ = ( class GroupImportForm(CSVModelForm): class Meta: - model = NetBoxGroup + model = Group fields = ( 'name', ) diff --git a/netbox/users/forms/filtersets.py b/netbox/users/forms/filtersets.py index c127e2144..23bbe45e1 100644 --- a/netbox/users/forms/filtersets.py +++ b/netbox/users/forms/filtersets.py @@ -1,11 +1,10 @@ from django import forms from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group from django.utils.translation import gettext_lazy as _ from netbox.forms import NetBoxModelFilterSetForm from netbox.forms.mixins import SavedFiltersMixin -from users.models import NetBoxGroup, User, ObjectPermission, Token +from users.models import Group, ObjectPermission, Token, User from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm from utilities.forms.fields import DynamicModelMultipleChoiceField from utilities.forms.widgets import DateTimePicker @@ -19,7 +18,7 @@ __all__ = ( class GroupFilterForm(NetBoxModelFilterSetForm): - model = NetBoxGroup + model = Group fieldsets = ( (None, ('q', 'filter_id',)), ) diff --git a/netbox/users/forms/model_forms.py b/netbox/users/forms/model_forms.py index 8875dc7f0..2a024bf47 100644 --- a/netbox/users/forms/model_forms.py +++ b/netbox/users/forms/model_forms.py @@ -1,7 +1,6 @@ from django import forms from django.conf import settings from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.forms import SimpleArrayField from django.core.exceptions import FieldError @@ -253,7 +252,7 @@ class GroupForm(forms.ModelForm): ) class Meta: - model = NetBoxGroup + model = Group fields = [ 'name', 'users', 'object_permissions', ] @@ -263,14 +262,14 @@ class GroupForm(forms.ModelForm): # Populate assigned users and permissions if self.instance.pk: - self.fields['users'].initial = self.instance.user_set.values_list('id', flat=True) + self.fields['users'].initial = self.instance.users.values_list('id', flat=True) self.fields['object_permissions'].initial = self.instance.object_permissions.values_list('id', flat=True) def save(self, *args, **kwargs): instance = super().save(*args, **kwargs) # Update assigned users and permissions - instance.user_set.set(self.cleaned_data['users']) + instance.users.set(self.cleaned_data['users']) instance.object_permissions.set(self.cleaned_data['object_permissions']) return instance diff --git a/netbox/users/graphql/schema.py b/netbox/users/graphql/schema.py index f033a535a..84ae0c975 100644 --- a/netbox/users/graphql/schema.py +++ b/netbox/users/graphql/schema.py @@ -1,10 +1,10 @@ import graphene - from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group + from netbox.graphql.fields import ObjectField, ObjectListField -from .types import * +from users.models import Group from utilities.graphql_optimizer import gql_query_optimizer +from .types import * class UsersQuery(graphene.ObjectType): diff --git a/netbox/users/graphql/types.py b/netbox/users/graphql/types.py index 4254f1791..58d211028 100644 --- a/netbox/users/graphql/types.py +++ b/netbox/users/graphql/types.py @@ -1,8 +1,8 @@ from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group from graphene_django import DjangoObjectType from users import filtersets +from users.models import Group from utilities.querysets import RestrictedQuerySet __all__ = ( diff --git a/netbox/users/migrations/0005_alter_user_table.py b/netbox/users/migrations/0005_alter_user_table.py index 6c4a815dd..e07db6875 100644 --- a/netbox/users/migrations/0005_alter_user_table.py +++ b/netbox/users/migrations/0005_alter_user_table.py @@ -1,5 +1,3 @@ -# Generated by Django 5.0.1 on 2024-01-31 23:18 - from django.db import migrations @@ -27,12 +25,26 @@ class Migration(migrations.Migration): ] operations = [ - # 0001_squashed had model with db_table=auth_user - now we switch it - # to None to use the default Django resolution (users.user) + # The User table was originally created as 'auth_user'. Now we nullify the model's + # db_table option, so that it defaults to the app & model name (users_user). This + # causes the database table to be renamed. migrations.AlterModelTable( name='user', table=None, ), + + # Rename auth_user_* sequences + migrations.RunSQL("ALTER TABLE auth_user_groups_id_seq RENAME TO users_user_groups_id_seq"), + migrations.RunSQL("ALTER TABLE auth_user_id_seq RENAME TO users_user_id_seq"), + migrations.RunSQL("ALTER TABLE auth_user_user_permissions_id_seq RENAME TO users_user_user_permissions_id_seq"), + + # Rename auth_user_* indexes + migrations.RunSQL("ALTER INDEX auth_user_pkey RENAME TO users_user_pkey"), + # Hash is deterministic; generated via schema_editor._create_index_name() + migrations.RunSQL("ALTER INDEX auth_user_username_6821ab7c_like RENAME TO users_user_username_06e46fe6_like"), + migrations.RunSQL("ALTER INDEX auth_user_username_key RENAME TO users_user_username_key"), + + # Update ContentTypes migrations.RunPython( code=update_content_types, reverse_code=migrations.RunPython.noop diff --git a/netbox/users/migrations/0006_custom_group_model.py b/netbox/users/migrations/0006_custom_group_model.py new file mode 100644 index 000000000..282da3ce0 --- /dev/null +++ b/netbox/users/migrations/0006_custom_group_model.py @@ -0,0 +1,80 @@ +import users.models +from django.db import migrations, models + + +def update_custom_fields(apps, schema_editor): + """ + Update any CustomFields referencing the old Group model to use the new model. + """ + ContentType = apps.get_model('contenttypes', 'ContentType') + CustomField = apps.get_model('extras', 'CustomField') + Group = apps.get_model('users', 'Group') + + if old_ct := ContentType.objects.filter(app_label='users', model='netboxgroup').first(): + new_ct = ContentType.objects.get_for_model(Group) + CustomField.objects.filter(object_type=old_ct).update(object_type=new_ct) + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0005_alter_user_table'), + ] + + operations = [ + # Create the new Group model & table + migrations.CreateModel( + name='Group', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=150, unique=True)), + ('description', models.CharField(blank=True, max_length=200)), + ('permissions', models.ManyToManyField(blank=True, related_name='groups', related_query_name='group', to='auth.permission')), + ], + options={ + 'verbose_name': 'group', + 'verbose_name_plural': 'groups', + }, + managers=[ + ('objects', users.models.NetBoxGroupManager()), + ], + ), + + # Copy existing groups from the old table into the new one + migrations.RunSQL( + "INSERT INTO users_group (SELECT id, name, '' AS description FROM auth_group)" + ), + + # Update the sequence for group ID values + migrations.RunSQL( + "SELECT setval('users_group_id_seq', (SELECT MAX(id) FROM users_group))" + ), + + # Update the "groups" M2M fields on User & ObjectPermission + migrations.AlterField( + model_name='user', + name='groups', + field=models.ManyToManyField(blank=True, related_name='users', related_query_name='user', to='users.group'), + ), + migrations.AlterField( + model_name='objectpermission', + name='groups', + field=models.ManyToManyField(blank=True, related_name='object_permissions', to='users.group'), + ), + + # Delete groups from the old table + migrations.RunSQL( + "DELETE from auth_group" + ), + + # Update custom fields + migrations.RunPython( + code=update_custom_fields, + reverse_code=migrations.RunPython.noop + ), + + # Delete the proxy model + migrations.DeleteModel( + name='NetBoxGroup', + ), + ] diff --git a/netbox/users/models.py b/netbox/users/models.py index 5e817be0b..19d6013c7 100644 --- a/netbox/users/models.py +++ b/netbox/users/models.py @@ -4,7 +4,12 @@ import os from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.auth.models import ( - AbstractUser, Group, GroupManager, User as DjangoUser, UserManager as DjangoUserManager + AbstractUser, + Group as DjangoGroup, + GroupManager, + Permission, + User as DjangoUser, + UserManager as DjangoUserManager ) from django.contrib.postgres.fields import ArrayField from django.core.exceptions import ValidationError @@ -25,7 +30,7 @@ from utilities.utils import flatten_dict from .constants import * __all__ = ( - 'NetBoxGroup', + 'Group', 'ObjectPermission', 'Token', 'User', @@ -33,22 +38,61 @@ __all__ = ( ) -# -# Proxies for Django's User and Group models -# +class NetBoxGroupManager(GroupManager.from_queryset(RestrictedQuerySet)): + pass + + +class Group(models.Model): + name = models.CharField( + verbose_name=_('name'), + max_length=150, + unique=True + ) + description = models.CharField( + verbose_name=_('description'), + max_length=200, + blank=True + ) + + # Replicate legacy Django permissions support from stock Group model + # to ensure authentication backend compatibility + permissions = models.ManyToManyField( + Permission, + verbose_name=_("permissions"), + blank=True, + related_name='groups', + related_query_name='group' + ) + + objects = NetBoxGroupManager() + + class Meta: + verbose_name = _('group') + verbose_name_plural = _('groups') + + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse('users:group', args=[self.pk]) + + def natural_key(self): + return (self.name,) + class UserManager(DjangoUserManager.from_queryset(RestrictedQuerySet)): pass -class NetBoxGroupManager(GroupManager.from_queryset(RestrictedQuerySet)): - pass - - class User(AbstractUser): - """ - Proxy contrib.auth.models.User for the UI - """ + groups = models.ManyToManyField( + to='users.Group', + verbose_name=_('groups'), + blank=True, + related_name='users', + related_query_name='user' + ) + objects = UserManager() class Meta: @@ -68,22 +112,6 @@ class User(AbstractUser): raise ValidationError(_("A user with this username already exists.")) -class NetBoxGroup(Group): - """ - Proxy contrib.auth.models.User for the UI - """ - objects = NetBoxGroupManager() - - class Meta: - proxy = True - ordering = ('name',) - verbose_name = _('group') - verbose_name_plural = _('groups') - - def get_absolute_url(self): - return reverse('users:netboxgroup', args=[self.pk]) - - # # User preferences # @@ -360,7 +388,7 @@ class ObjectPermission(models.Model): related_name='object_permissions' ) groups = models.ManyToManyField( - to=Group, + to='users.Group', blank=True, related_name='object_permissions' ) diff --git a/netbox/users/tables.py b/netbox/users/tables.py index 781660817..813d729c9 100644 --- a/netbox/users/tables.py +++ b/netbox/users/tables.py @@ -3,7 +3,7 @@ from django.utils.translation import gettext as _ from account.tables import UserTokenTable from netbox.tables import NetBoxTable, columns -from users.models import NetBoxGroup, User, ObjectPermission, Token +from users.models import Group, ObjectPermission, Token, User __all__ = ( 'GroupTable', @@ -33,7 +33,7 @@ class UserTable(NetBoxTable): ) groups = columns.ManyToManyColumn( verbose_name=_('Groups'), - linkify_item=('users:netboxgroup', {'pk': tables.A('pk')}) + linkify_item=('users:group', {'pk': tables.A('pk')}) ) is_active = columns.BooleanColumn( verbose_name=_('Is Active'), @@ -67,7 +67,7 @@ class GroupTable(NetBoxTable): ) class Meta(NetBoxTable.Meta): - model = NetBoxGroup + model = Group fields = ( 'pk', 'id', 'name', 'users_count', ) @@ -107,7 +107,7 @@ class ObjectPermissionTable(NetBoxTable): ) groups = columns.ManyToManyColumn( verbose_name=_('Groups'), - linkify_item=('users:netboxgroup', {'pk': tables.A('pk')}) + linkify_item=('users:group', {'pk': tables.A('pk')}) ) actions = columns.ActionsColumn( actions=('edit', 'delete'), diff --git a/netbox/users/tests/test_api.py b/netbox/users/tests/test_api.py index 40a3edf31..51fc21c97 100644 --- a/netbox/users/tests/test_api.py +++ b/netbox/users/tests/test_api.py @@ -1,9 +1,8 @@ from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group from django.contrib.contenttypes.models import ContentType from django.urls import reverse -from users.models import ObjectPermission, Token +from users.models import Group, ObjectPermission, Token from utilities.testing import APIViewTestCases, APITestCase, create_test_user from utilities.utils import deepmerge diff --git a/netbox/users/tests/test_filtersets.py b/netbox/users/tests/test_filtersets.py index 38a0df813..5d373628f 100644 --- a/netbox/users/tests/test_filtersets.py +++ b/netbox/users/tests/test_filtersets.py @@ -1,13 +1,12 @@ import datetime from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group from django.contrib.contenttypes.models import ContentType from django.test import TestCase from django.utils.timezone import make_aware from users import filtersets -from users.models import ObjectPermission, Token +from users.models import Group, ObjectPermission, Token from utilities.testing import BaseFilterSetTests User = get_user_model() diff --git a/netbox/users/tests/test_views.py b/netbox/users/tests/test_views.py index 259b7a857..27d2aeab1 100644 --- a/netbox/users/tests/test_views.py +++ b/netbox/users/tests/test_views.py @@ -1,4 +1,3 @@ -from django.contrib.auth.models import Group from django.contrib.contenttypes.models import ContentType from users.models import * @@ -70,7 +69,7 @@ class GroupTestCase( ViewTestCases.BulkImportObjectsViewTestCase, ViewTestCases.BulkDeleteObjectsViewTestCase, ): - model = NetBoxGroup + model = Group maxDiff = None @classmethod diff --git a/netbox/users/urls.py b/netbox/users/urls.py index 486a0c771..adfeba378 100644 --- a/netbox/users/urls.py +++ b/netbox/users/urls.py @@ -23,11 +23,11 @@ urlpatterns = [ path('users//', include(get_model_urls('users', 'user'))), # Groups - path('groups/', views.GroupListView.as_view(), name='netboxgroup_list'), - path('groups/add/', views.GroupEditView.as_view(), name='netboxgroup_add'), - path('groups/import/', views.GroupBulkImportView.as_view(), name='netboxgroup_import'), - path('groups/delete/', views.GroupBulkDeleteView.as_view(), name='netboxgroup_bulk_delete'), - path('groups//', include(get_model_urls('users', 'netboxgroup'))), + path('groups/', views.GroupListView.as_view(), name='group_list'), + path('groups/add/', views.GroupEditView.as_view(), name='group_add'), + path('groups/import/', views.GroupBulkImportView.as_view(), name='group_import'), + path('groups/delete/', views.GroupBulkDeleteView.as_view(), name='group_bulk_delete'), + path('groups//', include(get_model_urls('users', 'group'))), # Permissions path('permissions/', views.ObjectPermissionListView.as_view(), name='objectpermission_list'), diff --git a/netbox/users/views.py b/netbox/users/views.py index 324125604..662e5e573 100644 --- a/netbox/users/views.py +++ b/netbox/users/views.py @@ -5,7 +5,7 @@ from extras.tables import ObjectChangeTable from netbox.views import generic from utilities.views import register_model_view from . import filtersets, forms, tables -from .models import NetBoxGroup, User, ObjectPermission, Token +from .models import Group, User, ObjectPermission, Token # @@ -110,36 +110,36 @@ class UserBulkDeleteView(generic.BulkDeleteView): # class GroupListView(generic.ObjectListView): - queryset = NetBoxGroup.objects.annotate(users_count=Count('user')) + queryset = Group.objects.annotate(users_count=Count('user')) filterset = filtersets.GroupFilterSet filterset_form = forms.GroupFilterForm table = tables.GroupTable -@register_model_view(NetBoxGroup) +@register_model_view(Group) class GroupView(generic.ObjectView): - queryset = NetBoxGroup.objects.all() + queryset = Group.objects.all() template_name = 'users/group.html' -@register_model_view(NetBoxGroup, 'edit') +@register_model_view(Group, 'edit') class GroupEditView(generic.ObjectEditView): - queryset = NetBoxGroup.objects.all() + queryset = Group.objects.all() form = forms.GroupForm -@register_model_view(NetBoxGroup, 'delete') +@register_model_view(Group, 'delete') class GroupDeleteView(generic.ObjectDeleteView): - queryset = NetBoxGroup.objects.all() + queryset = Group.objects.all() class GroupBulkImportView(generic.BulkImportView): - queryset = NetBoxGroup.objects.all() + queryset = Group.objects.all() model_form = forms.GroupImportForm class GroupBulkDeleteView(generic.BulkDeleteView): - queryset = NetBoxGroup.objects.annotate(users_count=Count('user')) + queryset = Group.objects.annotate(users_count=Count('user')) filterset = filtersets.GroupFilterSet table = tables.GroupTable diff --git a/netbox/utilities/api.py b/netbox/utilities/api.py index a13e62bfd..25a350c81 100644 --- a/netbox/utilities/api.py +++ b/netbox/utilities/api.py @@ -31,23 +31,13 @@ def get_serializer_for_model(model, prefix=''): """ Dynamically resolve and return the appropriate serializer for a model. """ - app_name, model_name = model._meta.label.split('.') - # Serializers for Django's auth models are in the users app - if app_name == 'auth': - app_name = 'users' - # Account for changes using Proxy model - if app_name == 'users': - if model_name == 'NetBoxUser': - model_name = 'User' - elif model_name == 'NetBoxGroup': - model_name = 'Group' - - serializer_name = f'{app_name}.api.serializers.{prefix}{model_name}Serializer' + app_label, model_name = model._meta.label.split('.') + serializer_name = f'{app_label}.api.serializers.{prefix}{model_name}Serializer' try: return dynamic_import(serializer_name) except AttributeError: raise SerializerNotFound( - f"Could not determine serializer for {app_name}.{model_name} with prefix '{prefix}'" + f"Could not determine serializer for {app_label}.{model_name} with prefix '{prefix}'" ) diff --git a/netbox/utilities/utils.py b/netbox/utilities/utils.py index bd03ae4b8..5a25b4465 100644 --- a/netbox/utilities/utils.py +++ b/netbox/utilities/utils.py @@ -1,11 +1,12 @@ import datetime import decimal import json -import nh3 import re from decimal import Decimal from itertools import count, groupby +from urllib.parse import urlencode +import nh3 from django.contrib.contenttypes.models import ContentType from django.core import serializers from django.db.models import Count, ManyToOneRel, OuterRef, Subquery @@ -23,7 +24,6 @@ from dcim.choices import CableLengthUnitChoices, WeightUnitChoices from extras.utils import is_taggable from netbox.config import get_config from netbox.plugins import PluginConfig -from urllib.parse import urlencode from utilities.constants import HTTP_REQUEST_META_SAFE_COPY from .constants import HTML_ALLOWED_ATTRIBUTES, HTML_ALLOWED_TAGS @@ -48,26 +48,16 @@ def get_viewname(model, action=None, rest_api=False): model_name = model._meta.model_name if rest_api: + viewname = f'{app_label}-api:{model_name}' if is_plugin: - viewname = f'plugins-api:{app_label}-api:{model_name}' - else: - # Alter the app_label for group and user model_name to point to users app - if app_label == 'auth' and model_name in ['group', 'user']: - app_label = 'users' - if app_label == 'users' and model._meta.proxy and model_name in ['netboxuser', 'netboxgroup']: - model_name = model._meta.proxy_for_model._meta.model_name - - viewname = f'{app_label}-api:{model_name}' - # Append the action, if any + viewname = f'plugins-api:{viewname}' if action: viewname = f'{viewname}-{action}' else: viewname = f'{app_label}:{model_name}' - # Prepend the plugins namespace if this is a plugin model if is_plugin: viewname = f'plugins:{viewname}' - # Append the action, if any if action: viewname = f'{viewname}_{action}'