mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
* Rename sequences & indexes after renaming users table * Migrate from auth.Group to a custom group model * Delete original groups from auth_group table * Update object & multi-object custom fields referencing the Group model * Fix ContentType resolution * Clean up obsolete logic for view/serializer resolution
This commit is contained in:
@ -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,
|
||||
)
|
||||
|
@ -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']
|
||||
|
@ -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
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
<div class="card">
|
||||
<h5 class="card-header">{% trans "Users" %}</h5>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for user in object.user_set.all %}
|
||||
{% for user in object.users.all %}
|
||||
<a href="{% url 'users:user' pk=user.pk %}" class="list-group-item list-group-item-action">{{ user }}</a>
|
||||
{% empty %}
|
||||
<div class="list-group-item text-muted">{% trans "None" %}</div>
|
||||
|
@ -82,7 +82,7 @@
|
||||
<h5 class="card-header">{% trans "Assigned Groups" %}</h5>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for group in object.groups.all %}
|
||||
<a href="{% url 'users:netboxgroup' pk=group.pk %}" class="list-group-item list-group-item-action">{{ group }}</a>
|
||||
<a href="{% url 'users:group' pk=group.pk %}" class="list-group-item list-group-item-action">{{ group }}</a>
|
||||
{% empty %}
|
||||
<div class="list-group-item text-muted">{% trans "None" %}</div>
|
||||
{% endfor %}
|
||||
|
@ -53,7 +53,7 @@
|
||||
<h5 class="card-header">{% trans "Assigned Groups" %}</h5>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for group in object.groups.all %}
|
||||
<a href="{% url 'users:netboxgroup' pk=group.pk %}" class="list-group-item list-group-item-action">{{ group }}</a>
|
||||
<a href="{% url 'users:group' pk=group.pk %}" class="list-group-item list-group-item-action">{{ group }}</a>
|
||||
{% empty %}
|
||||
<div class="list-group-item text-muted">{% trans "None" %}</div>
|
||||
{% endfor %}
|
||||
|
@ -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',
|
||||
|
@ -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 *
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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',
|
||||
|
@ -14,7 +14,7 @@ __all__ = (
|
||||
class GroupImportForm(CSVModelForm):
|
||||
|
||||
class Meta:
|
||||
model = NetBoxGroup
|
||||
model = Group
|
||||
fields = (
|
||||
'name',
|
||||
)
|
||||
|
@ -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',)),
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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__ = (
|
||||
|
@ -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
|
||||
|
80
netbox/users/migrations/0006_custom_group_model.py
Normal file
80
netbox/users/migrations/0006_custom_group_model.py
Normal file
@ -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',
|
||||
),
|
||||
]
|
@ -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'
|
||||
)
|
||||
|
@ -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'),
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -23,11 +23,11 @@ urlpatterns = [
|
||||
path('users/<int:pk>/', 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/<int:pk>/', 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/<int:pk>/', include(get_model_urls('users', 'group'))),
|
||||
|
||||
# Permissions
|
||||
path('permissions/', views.ObjectPermissionListView.as_view(), name='objectpermission_list'),
|
||||
|
@ -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
|
||||
|
||||
|
@ -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}'"
|
||||
)
|
||||
|
||||
|
||||
|
@ -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}'
|
||||
|
||||
|
Reference in New Issue
Block a user