Merge pull request #4861 from netbox-community/django-31

Upgrade to Django 3.1 (v2.9)
This commit is contained in:
Jeremy Stretch 2020-07-20 12:47:49 -04:00 committed by GitHub
commit de6202c160
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 314 additions and 119 deletions

View File

@ -19,7 +19,7 @@ from . import serializers
class ProviderViewSet(CustomFieldModelViewSet):
queryset = Provider.objects.prefetch_related('tags').annotate(
circuit_count=Count('circuits')
)
).order_by(*Provider._meta.ordering)
serializer_class = serializers.ProviderSerializer
filterset_class = filters.ProviderFilterSet
@ -41,7 +41,7 @@ class ProviderViewSet(CustomFieldModelViewSet):
class CircuitTypeViewSet(ModelViewSet):
queryset = CircuitType.objects.annotate(
circuit_count=Count('circuits')
)
).order_by(*CircuitType._meta.ordering)
serializer_class = serializers.CircuitTypeSerializer
filterset_class = filters.CircuitTypeFilterSet

View File

@ -0,0 +1,18 @@
# Generated by Django 3.1b1 on 2020-07-16 15:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0018_standardize_description'),
]
operations = [
migrations.AlterField(
model_name='circuittermination',
name='connection_status',
field=models.BooleanField(blank=True, null=True),
),
]

View File

@ -275,9 +275,10 @@ class CircuitTermination(CableTermination):
blank=True,
null=True
)
connection_status = models.NullBooleanField(
connection_status = models.BooleanField(
choices=CONNECTION_STATUS_CHOICES,
blank=True
blank=True,
null=True
)
port_speed = models.PositiveIntegerField(
verbose_name='Port speed (Kbps)'

View File

@ -21,7 +21,7 @@ from .models import Circuit, CircuitTermination, CircuitType, Provider
#
class ProviderListView(ObjectListView):
queryset = Provider.objects.annotate(count_circuits=Count('circuits'))
queryset = Provider.objects.annotate(count_circuits=Count('circuits')).order_by(*Provider._meta.ordering)
filterset = filters.ProviderFilterSet
filterset_form = forms.ProviderFilterForm
table = tables.ProviderTable
@ -73,14 +73,14 @@ class ProviderBulkImportView(BulkImportView):
class ProviderBulkEditView(BulkEditView):
queryset = Provider.objects.annotate(count_circuits=Count('circuits'))
queryset = Provider.objects.annotate(count_circuits=Count('circuits')).order_by(*Provider._meta.ordering)
filterset = filters.ProviderFilterSet
table = tables.ProviderTable
form = forms.ProviderBulkEditForm
class ProviderBulkDeleteView(BulkDeleteView):
queryset = Provider.objects.annotate(count_circuits=Count('circuits'))
queryset = Provider.objects.annotate(count_circuits=Count('circuits')).order_by(*Provider._meta.ordering)
filterset = filters.ProviderFilterSet
table = tables.ProviderTable
@ -90,7 +90,7 @@ class ProviderBulkDeleteView(BulkDeleteView):
#
class CircuitTypeListView(ObjectListView):
queryset = CircuitType.objects.annotate(circuit_count=Count('circuits'))
queryset = CircuitType.objects.annotate(circuit_count=Count('circuits')).order_by(*CircuitType._meta.ordering)
table = tables.CircuitTypeTable
@ -110,7 +110,7 @@ class CircuitTypeBulkImportView(BulkImportView):
class CircuitTypeBulkDeleteView(BulkDeleteView):
queryset = CircuitType.objects.annotate(circuit_count=Count('circuits'))
queryset = CircuitType.objects.annotate(circuit_count=Count('circuits')).order_by(*CircuitType._meta.ordering)
table = tables.CircuitTypeTable

View File

@ -74,8 +74,12 @@ class CableTraceMixin(object):
#
class RegionViewSet(ModelViewSet):
queryset = Region.objects.annotate(
site_count=Count('sites')
queryset = Region.objects.add_related_count(
Region.objects.all(),
Site,
'region',
'site_count',
cumulative=True
)
serializer_class = serializers.RegionSerializer
filterset_class = filters.RegionFilterSet
@ -95,7 +99,7 @@ class SiteViewSet(CustomFieldModelViewSet):
vlan_count=get_subquery(VLAN, 'site'),
circuit_count=get_subquery(Circuit, 'terminations__site'),
virtualmachine_count=get_subquery(VirtualMachine, 'cluster__site'),
)
).order_by(*Site._meta.ordering)
serializer_class = serializers.SiteSerializer
filterset_class = filters.SiteFilterSet
@ -115,9 +119,13 @@ class SiteViewSet(CustomFieldModelViewSet):
#
class RackGroupViewSet(ModelViewSet):
queryset = RackGroup.objects.prefetch_related('site').annotate(
rack_count=Count('racks')
)
queryset = RackGroup.objects.add_related_count(
RackGroup.objects.all(),
Rack,
'group',
'rack_count',
cumulative=True
).prefetch_related('site')
serializer_class = serializers.RackGroupSerializer
filterset_class = filters.RackGroupFilterSet
@ -129,7 +137,7 @@ class RackGroupViewSet(ModelViewSet):
class RackRoleViewSet(ModelViewSet):
queryset = RackRole.objects.annotate(
rack_count=Count('racks')
)
).order_by(*RackRole._meta.ordering)
serializer_class = serializers.RackRoleSerializer
filterset_class = filters.RackRoleFilterSet
@ -144,7 +152,7 @@ class RackViewSet(CustomFieldModelViewSet):
).annotate(
device_count=get_subquery(Device, 'rack'),
powerfeed_count=get_subquery(PowerFeed, 'rack')
)
).order_by(*Rack._meta.ordering)
serializer_class = serializers.RackSerializer
filterset_class = filters.RackFilterSet
@ -217,7 +225,7 @@ class ManufacturerViewSet(ModelViewSet):
devicetype_count=get_subquery(DeviceType, 'manufacturer'),
inventoryitem_count=get_subquery(InventoryItem, 'manufacturer'),
platform_count=get_subquery(Platform, 'manufacturer')
)
).order_by(*Manufacturer._meta.ordering)
serializer_class = serializers.ManufacturerSerializer
filterset_class = filters.ManufacturerFilterSet
@ -229,7 +237,7 @@ class ManufacturerViewSet(ModelViewSet):
class DeviceTypeViewSet(CustomFieldModelViewSet):
queryset = DeviceType.objects.prefetch_related('manufacturer', 'tags').annotate(
device_count=Count('instances')
)
).order_by(*DeviceType._meta.ordering)
serializer_class = serializers.DeviceTypeSerializer
filterset_class = filters.DeviceTypeFilterSet
@ -294,7 +302,7 @@ class DeviceRoleViewSet(ModelViewSet):
queryset = DeviceRole.objects.annotate(
device_count=get_subquery(Device, 'device_role'),
virtualmachine_count=get_subquery(VirtualMachine, 'role')
)
).order_by(*DeviceRole._meta.ordering)
serializer_class = serializers.DeviceRoleSerializer
filterset_class = filters.DeviceRoleFilterSet
@ -307,7 +315,7 @@ class PlatformViewSet(ModelViewSet):
queryset = Platform.objects.annotate(
device_count=get_subquery(Device, 'platform'),
virtualmachine_count=get_subquery(VirtualMachine, 'platform')
)
).order_by(*Platform._meta.ordering)
serializer_class = serializers.PlatformSerializer
filterset_class = filters.PlatformFilterSet
@ -583,7 +591,7 @@ class CableViewSet(ModelViewSet):
class VirtualChassisViewSet(ModelViewSet):
queryset = VirtualChassis.objects.prefetch_related('tags').annotate(
member_count=Count('members')
)
).order_by(*VirtualChassis._meta.ordering)
serializer_class = serializers.VirtualChassisSerializer
filterset_class = filters.VirtualChassisFilterSet
@ -597,7 +605,7 @@ class PowerPanelViewSet(ModelViewSet):
'site', 'rack_group'
).annotate(
powerfeed_count=Count('powerfeeds')
)
).order_by(*PowerPanel._meta.ordering)
serializer_class = serializers.PowerPanelSerializer
filterset_class = filters.PowerPanelFilterSet

View File

@ -0,0 +1,43 @@
# Generated by Django 3.1b1 on 2020-07-16 15:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dcim', '0112_standardize_components'),
]
operations = [
migrations.AlterField(
model_name='consoleport',
name='connection_status',
field=models.BooleanField(blank=True, null=True),
),
migrations.AlterField(
model_name='consoleserverport',
name='connection_status',
field=models.BooleanField(blank=True, null=True),
),
migrations.AlterField(
model_name='interface',
name='connection_status',
field=models.BooleanField(blank=True, null=True),
),
migrations.AlterField(
model_name='powerfeed',
name='connection_status',
field=models.BooleanField(blank=True, null=True),
),
migrations.AlterField(
model_name='poweroutlet',
name='connection_status',
field=models.BooleanField(blank=True, null=True),
),
migrations.AlterField(
model_name='powerport',
name='connection_status',
field=models.BooleanField(blank=True, null=True),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.1b1 on 2020-07-16 16:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dcim', '0113_nullbooleanfield_to_booleanfield'),
]
operations = [
migrations.AlterField(
model_name='device',
name='local_context_data',
field=models.JSONField(blank=True, null=True),
),
migrations.AlterField(
model_name='platform',
name='napalm_args',
field=models.JSONField(blank=True, null=True),
),
]

View File

@ -6,7 +6,7 @@ from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.fields import ArrayField, JSONField
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
@ -1280,7 +1280,7 @@ class Platform(ChangeLoggedModel):
verbose_name='NAPALM driver',
help_text='The name of the NAPALM driver to use when interacting with devices'
)
napalm_args = JSONField(
napalm_args = models.JSONField(
blank=True,
null=True,
verbose_name='NAPALM arguments',
@ -1905,9 +1905,10 @@ class PowerFeed(ChangeLoggedModel, CableTermination, CustomFieldModel):
blank=True,
null=True
)
connection_status = models.NullBooleanField(
connection_status = models.BooleanField(
choices=CONNECTION_STATUS_CHOICES,
blank=True
blank=True,
null=True
)
name = models.CharField(
max_length=50

View File

@ -264,9 +264,10 @@ class ConsolePort(CableTermination, ComponentModel):
blank=True,
null=True
)
connection_status = models.NullBooleanField(
connection_status = models.BooleanField(
choices=CONNECTION_STATUS_CHOICES,
blank=True
blank=True,
null=True
)
tags = TaggableManager(through=TaggedItem)
@ -304,9 +305,10 @@ class ConsoleServerPort(CableTermination, ComponentModel):
blank=True,
help_text='Physical port type'
)
connection_status = models.NullBooleanField(
connection_status = models.BooleanField(
choices=CONNECTION_STATUS_CHOICES,
blank=True
blank=True,
null=True
)
tags = TaggableManager(through=TaggedItem)
@ -370,9 +372,10 @@ class PowerPort(CableTermination, ComponentModel):
blank=True,
null=True
)
connection_status = models.NullBooleanField(
connection_status = models.BooleanField(
choices=CONNECTION_STATUS_CHOICES,
blank=True
blank=True,
null=True
)
tags = TaggableManager(through=TaggedItem)
@ -505,9 +508,10 @@ class PowerOutlet(CableTermination, ComponentModel):
blank=True,
help_text="Phase (for three-phase feeds)"
)
connection_status = models.NullBooleanField(
connection_status = models.BooleanField(
choices=CONNECTION_STATUS_CHOICES,
blank=True
blank=True,
null=True
)
tags = TaggableManager(through=TaggedItem)
@ -598,9 +602,10 @@ class Interface(CableTermination, ComponentModel, BaseInterface):
blank=True,
null=True
)
connection_status = models.NullBooleanField(
connection_status = models.BooleanField(
choices=CONNECTION_STATUS_CHOICES,
blank=True
blank=True,
null=True
)
lag = models.ForeignKey(
to='self',

View File

@ -133,7 +133,13 @@ class RegionBulkImportView(BulkImportView):
class RegionBulkDeleteView(BulkDeleteView):
queryset = Region.objects.all()
queryset = Region.objects.add_related_count(
Region.objects.all(),
Site,
'region',
'site_count',
cumulative=True
)
filterset = filters.RegionFilterSet
table = tables.RegionTable
@ -238,7 +244,13 @@ class RackGroupBulkImportView(BulkImportView):
class RackGroupBulkDeleteView(BulkDeleteView):
queryset = RackGroup.objects.prefetch_related('site').annotate(rack_count=Count('racks'))
queryset = RackGroup.objects.add_related_count(
RackGroup.objects.all(),
Rack,
'group',
'rack_count',
cumulative=True
).prefetch_related('site')
filterset = filters.RackGroupFilterSet
table = tables.RackGroupTable
@ -248,7 +260,7 @@ class RackGroupBulkDeleteView(BulkDeleteView):
#
class RackRoleListView(ObjectListView):
queryset = RackRole.objects.annotate(rack_count=Count('racks'))
queryset = RackRole.objects.annotate(rack_count=Count('racks')).order_by(*RackRole._meta.ordering)
table = tables.RackRoleTable
@ -268,7 +280,7 @@ class RackRoleBulkImportView(BulkImportView):
class RackRoleBulkDeleteView(BulkDeleteView):
queryset = RackRole.objects.annotate(rack_count=Count('racks'))
queryset = RackRole.objects.annotate(rack_count=Count('racks')).order_by(*RackRole._meta.ordering)
table = tables.RackRoleTable
@ -281,7 +293,7 @@ class RackListView(ObjectListView):
'site', 'group', 'tenant', 'role', 'devices__device_type'
).annotate(
device_count=Count('devices')
)
).order_by(*Rack._meta.ordering)
filterset = filters.RackFilterSet
filterset_form = forms.RackFilterForm
table = tables.RackDetailTable
@ -465,7 +477,7 @@ class ManufacturerListView(ObjectListView):
devicetype_count=Count('device_types', distinct=True),
inventoryitem_count=Count('inventory_items', distinct=True),
platform_count=Count('platforms', distinct=True),
)
).order_by(*Manufacturer._meta.ordering)
table = tables.ManufacturerTable
@ -485,7 +497,9 @@ class ManufacturerBulkImportView(BulkImportView):
class ManufacturerBulkDeleteView(BulkDeleteView):
queryset = Manufacturer.objects.annotate(devicetype_count=Count('device_types'))
queryset = Manufacturer.objects.annotate(
devicetype_count=Count('device_types')
).order_by(*Manufacturer._meta.ordering)
table = tables.ManufacturerTable
@ -494,7 +508,9 @@ class ManufacturerBulkDeleteView(BulkDeleteView):
#
class DeviceTypeListView(ObjectListView):
queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(instance_count=Count('instances'))
queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(
instance_count=Count('instances')
).order_by(*DeviceType._meta.ordering)
filterset = filters.DeviceTypeFilterSet
filterset_form = forms.DeviceTypeFilterForm
table = tables.DeviceTypeTable
@ -602,14 +618,18 @@ class DeviceTypeImportView(ObjectImportView):
class DeviceTypeBulkEditView(BulkEditView):
queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(instance_count=Count('instances'))
queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(
instance_count=Count('instances')
).order_by(*DeviceType._meta.ordering)
filterset = filters.DeviceTypeFilterSet
table = tables.DeviceTypeTable
form = forms.DeviceTypeBulkEditForm
class DeviceTypeBulkDeleteView(BulkDeleteView):
queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(instance_count=Count('instances'))
queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(
instance_count=Count('instances')
).order_by(*DeviceType._meta.ordering)
filterset = filters.DeviceTypeFilterSet
table = tables.DeviceTypeTable
@ -2152,7 +2172,9 @@ class InterfaceConnectionsListView(ObjectListView):
#
class VirtualChassisListView(ObjectListView):
queryset = VirtualChassis.objects.prefetch_related('master').annotate(member_count=Count('members'))
queryset = VirtualChassis.objects.prefetch_related('master').annotate(
member_count=Count('members')
).order_by(*VirtualChassis._meta.ordering)
table = tables.VirtualChassisTable
filterset = filters.VirtualChassisFilterSet
filterset_form = forms.VirtualChassisFilterForm
@ -2385,7 +2407,7 @@ class PowerPanelListView(ObjectListView):
'site', 'rack_group'
).annotate(
powerfeed_count=Count('powerfeeds')
)
).order_by(*PowerPanel._meta.ordering)
filterset = filters.PowerPanelFilterSet
filterset_form = forms.PowerPanelFilterForm
table = tables.PowerPanelTable
@ -2437,7 +2459,7 @@ class PowerPanelBulkDeleteView(BulkDeleteView):
'site', 'rack_group'
).annotate(
rack_count=Count('powerfeeds')
)
).order_by(*PowerPanel._meta.ordering)
filterset = filters.PowerPanelFilterSet
table = tables.PowerPanelTable

View File

@ -0,0 +1,28 @@
# Generated by Django 3.1b1 on 2020-07-16 16:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('extras', '0045_configcontext_changelog'),
]
operations = [
migrations.AlterField(
model_name='configcontext',
name='data',
field=models.JSONField(),
),
migrations.AlterField(
model_name='jobresult',
name='data',
field=models.JSONField(blank=True, null=True),
),
migrations.AlterField(
model_name='objectchange',
name='object_data',
field=models.JSONField(editable=False),
),
]

View File

@ -1,7 +1,6 @@
from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.fields import JSONField
from django.db import models
from django.urls import reverse
@ -104,7 +103,7 @@ class ObjectChange(models.Model):
max_length=200,
editable=False
)
object_data = JSONField(
object_data = models.JSONField(
editable=False
)

View File

@ -5,7 +5,6 @@ from collections import OrderedDict
from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.fields import JSONField
from django.core.validators import ValidationError
from django.db import models
from django.http import HttpResponse
@ -499,7 +498,7 @@ class ConfigContext(ChangeLoggedModel):
related_name='+',
blank=True
)
data = JSONField()
data = models.JSONField()
objects = ConfigContextQuerySet.as_manager()
@ -526,7 +525,7 @@ class ConfigContextModel(models.Model):
A model which includes local configuration context data. This local data will override any inherited data from
ConfigContexts.
"""
local_context_data = JSONField(
local_context_data = models.JSONField(
blank=True,
null=True,
)
@ -627,7 +626,7 @@ class JobResult(models.Model):
choices=JobResultStatusChoices,
default=JobResultStatusChoices.STATUS_PENDING
)
data = JSONField(
data = models.JSONField(
null=True,
blank=True
)

View File

@ -11,7 +11,7 @@ from django import forms
from django.conf import settings
from django.core.validators import RegexValidator
from django.db import transaction
from django.utils.decorators import classproperty
from django.utils.functional import classproperty
from django_rq import job
from extras.api.serializers import ScriptOutputSerializer

View File

@ -24,7 +24,7 @@ class VRFViewSet(CustomFieldModelViewSet):
queryset = VRF.objects.prefetch_related('tenant').prefetch_related('tags').annotate(
ipaddress_count=get_subquery(IPAddress, 'vrf'),
prefix_count=get_subquery(Prefix, 'vrf')
)
).order_by(*VRF._meta.ordering)
serializer_class = serializers.VRFSerializer
filterset_class = filters.VRFFilterSet
@ -36,7 +36,7 @@ class VRFViewSet(CustomFieldModelViewSet):
class RIRViewSet(ModelViewSet):
queryset = RIR.objects.annotate(
aggregate_count=Count('aggregates')
)
).order_by(*RIR._meta.ordering)
serializer_class = serializers.RIRSerializer
filterset_class = filters.RIRFilterSet
@ -59,7 +59,7 @@ class RoleViewSet(ModelViewSet):
queryset = Role.objects.annotate(
prefix_count=get_subquery(Prefix, 'role'),
vlan_count=get_subquery(VLAN, 'role')
)
).order_by(*Role._meta.ordering)
serializer_class = serializers.RoleSerializer
filterset_class = filters.RoleFilterSet
@ -246,7 +246,7 @@ class IPAddressViewSet(CustomFieldModelViewSet):
class VLANGroupViewSet(ModelViewSet):
queryset = VLANGroup.objects.prefetch_related('site').annotate(
vlan_count=Count('vlans')
)
).order_by(*VLANGroup._meta.ordering)
serializer_class = serializers.VLANGroupSerializer
filterset_class = filters.VLANGroupFilterSet
@ -260,7 +260,7 @@ class VLANViewSet(CustomFieldModelViewSet):
'site', 'group', 'tenant', 'role', 'tags'
).annotate(
prefix_count=get_subquery(Prefix, 'vlan')
)
).order_by(*VLAN._meta.ordering)
serializer_class = serializers.VLANSerializer
filterset_class = filters.VLANFilterSet

View File

@ -78,7 +78,7 @@ class VRFBulkDeleteView(BulkDeleteView):
#
class RIRListView(ObjectListView):
queryset = RIR.objects.annotate(aggregate_count=Count('aggregates'))
queryset = RIR.objects.annotate(aggregate_count=Count('aggregates')).order_by(*RIR._meta.ordering)
filterset = filters.RIRFilterSet
filterset_form = forms.RIRFilterForm
table = tables.RIRDetailTable
@ -171,7 +171,7 @@ class RIRBulkImportView(BulkImportView):
class RIRBulkDeleteView(BulkDeleteView):
queryset = RIR.objects.annotate(aggregate_count=Count('aggregates'))
queryset = RIR.objects.annotate(aggregate_count=Count('aggregates')).order_by(*RIR._meta.ordering)
filterset = filters.RIRFilterSet
table = tables.RIRTable
@ -183,7 +183,7 @@ class RIRBulkDeleteView(BulkDeleteView):
class AggregateListView(ObjectListView):
queryset = Aggregate.objects.prefetch_related('rir').annotate(
child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
)
).order_by(*Aggregate._meta.ordering)
filterset = filters.AggregateFilterSet
filterset_form = forms.AggregateFilterForm
table = tables.AggregateDetailTable
@ -650,7 +650,9 @@ class IPAddressBulkDeleteView(BulkDeleteView):
#
class VLANGroupListView(ObjectListView):
queryset = VLANGroup.objects.prefetch_related('site').annotate(vlan_count=Count('vlans'))
queryset = VLANGroup.objects.prefetch_related('site').annotate(
vlan_count=Count('vlans')
).order_by(*VLANGroup._meta.ordering)
filterset = filters.VLANGroupFilterSet
filterset_form = forms.VLANGroupFilterForm
table = tables.VLANGroupTable
@ -672,7 +674,9 @@ class VLANGroupBulkImportView(BulkImportView):
class VLANGroupBulkDeleteView(BulkDeleteView):
queryset = VLANGroup.objects.prefetch_related('site').annotate(vlan_count=Count('vlans'))
queryset = VLANGroup.objects.prefetch_related('site').annotate(
vlan_count=Count('vlans')
).order_by(*VLANGroup._meta.ordering)
filterset = filters.VLANGroupFilterSet
table = tables.VLANGroupTable

View File

@ -46,7 +46,9 @@ SEARCH_MAX_RESULTS = 15
SEARCH_TYPES = OrderedDict((
# Circuits
('provider', {
'queryset': Provider.objects.annotate(count_circuits=Count('circuits')),
'queryset': Provider.objects.annotate(
count_circuits=Count('circuits')
).order_by(*Provider._meta.ordering),
'filterset': ProviderFilterSet,
'table': ProviderTable,
'url': 'circuits:provider_list',
@ -73,13 +75,17 @@ SEARCH_TYPES = OrderedDict((
'url': 'dcim:rack_list',
}),
('rackgroup', {
'queryset': RackGroup.objects.prefetch_related('site').annotate(rack_count=Count('racks')),
'queryset': RackGroup.objects.prefetch_related('site').annotate(
rack_count=Count('racks')
).order_by(*RackGroup._meta.ordering),
'filterset': RackGroupFilterSet,
'table': RackGroupTable,
'url': 'dcim:rackgroup_list',
}),
('devicetype', {
'queryset': DeviceType.objects.prefetch_related('manufacturer').annotate(instance_count=Count('instances')),
'queryset': DeviceType.objects.prefetch_related('manufacturer').annotate(
instance_count=Count('instances')
).order_by(*DeviceType._meta.ordering),
'filterset': DeviceTypeFilterSet,
'table': DeviceTypeTable,
'url': 'dcim:devicetype_list',
@ -93,7 +99,9 @@ SEARCH_TYPES = OrderedDict((
'url': 'dcim:device_list',
}),
('virtualchassis', {
'queryset': VirtualChassis.objects.prefetch_related('master').annotate(member_count=Count('members')),
'queryset': VirtualChassis.objects.prefetch_related('master').annotate(
member_count=Count('members')
).order_by(*VirtualChassis._meta.ordering),
'filterset': VirtualChassisFilterSet,
'table': VirtualChassisTable,
'url': 'dcim:virtualchassis_list',

View File

@ -27,7 +27,7 @@ ERR_PRIVKEY_INVALID = "Invalid private key."
class SecretRoleViewSet(ModelViewSet):
queryset = SecretRole.objects.annotate(
secret_count=Count('secrets')
)
).order_by(*SecretRole._meta.ordering)
serializer_class = serializers.SecretRoleSerializer
filterset_class = filters.SecretRoleFilterSet

View File

@ -29,7 +29,7 @@ def get_session_key(request):
#
class SecretRoleListView(ObjectListView):
queryset = SecretRole.objects.annotate(secret_count=Count('secrets'))
queryset = SecretRole.objects.annotate(secret_count=Count('secrets')).order_by(*SecretRole._meta.ordering)
table = tables.SecretRoleTable
@ -49,7 +49,7 @@ class SecretRoleBulkImportView(BulkImportView):
class SecretRoleBulkDeleteView(BulkDeleteView):
queryset = SecretRole.objects.annotate(secret_count=Count('secrets'))
queryset = SecretRole.objects.annotate(secret_count=Count('secrets')).order_by(*SecretRole._meta.ordering)
table = tables.SecretRoleTable

View File

@ -9,7 +9,7 @@
{% endif %}
{% for p in page.smart_pages %}
{% if p %}
<li{% ifequal page.number p %} class="active"{% endifequal %}><a href="{% querystring request page=p %}">{{ p }}</a></li>
<li{% if page.number == p %} class="active"{% endif %}><a href="{% querystring request page=p %}">{{ p }}</a></li>
{% else %}
<li class="disabled"><span>&hellip;</span></li>
{% endif %}

View File

@ -9,21 +9,21 @@
<div class="row">
<div class="col-sm-3 col-md-2 col-md-offset-1">
<ul class="nav nav-pills nav-stacked">
<li{% ifequal active_tab "profile" %} class="active"{% endifequal %}>
<li{% if active_tab == "profile" %} class="active"{% endif %}>
<a href="{% url 'user:profile' %}">Profile</a>
</li>
<li{% ifequal active_tab "preferences" %} class="active"{% endifequal %}>
<li{% if active_tab == "preferences" %} class="active"{% endif %}>
<a href="{% url 'user:preferences' %}">Preferences</a>
</li>
{% if not request.user.ldap_username %}
<li{% ifequal active_tab "change_password" %} class="active"{% endifequal %}>
<li{% if active_tab == "change_password" %} class="active"{% endif %}>
<a href="{% url 'user:change_password' %}">Change Password</a>
</li>
{% endif %}
<li{% ifequal active_tab "api_tokens" %} class="active"{% endifequal %}>
<li{% if active_tab == "api_tokens" %} class="active"{% endif %}>
<a href="{% url 'user:token_list' %}">API Tokens</a>
</li>
<li{% ifequal active_tab "userkey" %} class="active"{% endifequal %}>
<li{% if active_tab == "userkey" %} class="active"{% endif %}>
<a href="{% url 'user:userkey' %}">User Key</a>
</li>
</ul>

View File

@ -15,8 +15,12 @@ from . import serializers
#
class TenantGroupViewSet(ModelViewSet):
queryset = TenantGroup.objects.annotate(
tenant_count=get_subquery(Tenant, 'group')
queryset = TenantGroup.objects.add_related_count(
TenantGroup.objects.all(),
Tenant,
'group',
'tenant_count',
cumulative=True
)
serializer_class = serializers.TenantGroupSerializer
filterset_class = filters.TenantGroupFilterSet

View File

@ -43,7 +43,13 @@ class TenantGroupBulkImportView(BulkImportView):
class TenantGroupBulkDeleteView(BulkDeleteView):
queryset = TenantGroup.objects.annotate(tenant_count=Count('tenants'))
queryset = TenantGroup.objects.add_related_count(
TenantGroup.objects.all(),
Tenant,
'group',
'tenant_count',
cumulative=True
)
table = tables.TenantGroupTable

View File

@ -0,0 +1,23 @@
# Generated by Django 3.1b1 on 2020-07-16 16:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0009_replicate_permissions'),
]
operations = [
migrations.AlterField(
model_name='objectpermission',
name='constraints',
field=models.JSONField(blank=True, null=True),
),
migrations.AlterField(
model_name='userconfig',
name='data',
field=models.JSONField(default=dict),
),
]

View File

@ -3,7 +3,7 @@ import os
from django.contrib.auth.models import Group, User
from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.fields import ArrayField, JSONField
from django.contrib.postgres.fields import ArrayField
from django.core.validators import MinLengthValidator
from django.db import models
from django.db.models.signals import post_save
@ -56,7 +56,7 @@ class UserConfig(models.Model):
on_delete=models.CASCADE,
related_name='config'
)
data = JSONField(
data = models.JSONField(
default=dict
)
@ -265,7 +265,7 @@ class ObjectPermission(models.Model):
base_field=models.CharField(max_length=30),
help_text="The list of actions granted by this permission"
)
constraints = JSONField(
constraints = models.JSONField(
blank=True,
null=True,
help_text="Queryset filter matching the applicable objects of the selected type(s)"

View File

@ -345,10 +345,9 @@ class ModelViewSet(_ModelViewSet):
try:
return super().dispatch(request, *args, **kwargs)
except ProtectedError as e:
models = [
'{} ({})'.format(o, o._meta) for o in e.protected_objects.all()
]
msg = 'Unable to delete object. The following dependent objects were found: {}'.format(', '.join(models))
protected_objects = list(e.protected_objects)
msg = f'Unable to delete object. {len(protected_objects)} dependent objects were found: '
msg += ', '.join([f'{obj} ({obj.pk})' for obj in protected_objects])
logger.warning(msg)
return self.finalize_response(
request,

View File

@ -7,31 +7,17 @@ def handle_protectederror(obj, request, e):
"""
Generate a user-friendly error message in response to a ProtectedError exception.
"""
try:
dep_class = e.protected_objects[0]._meta.verbose_name_plural
except IndexError:
raise e
# Grammar for single versus multiple triggering objects
if type(obj) in (list, tuple):
err_message = "Unable to delete the requested {}. The following dependent {} were found: ".format(
obj[0]._meta.verbose_name_plural,
dep_class,
)
else:
err_message = "Unable to delete {} {}. The following dependent {} were found: ".format(
obj._meta.verbose_name,
obj,
dep_class,
)
protected_objects = list(e.protected_objects)
err_message = f"Unable to delete {obj._meta.verbose_name} <strong>{obj}</strong>. " \
f"{len(protected_objects)} dependent objects were found: "
# Append dependent objects to error message
dependent_objects = []
for obj in e.protected_objects:
for dependent in protected_objects:
if hasattr(obj, 'get_absolute_url'):
dependent_objects.append('<a href="{}">{}</a>'.format(obj.get_absolute_url(), escape(obj)))
dependent_objects.append(f'<a href="{dependent.get_absolute_url()}">{escape(dependent)}</a>')
else:
dependent_objects.append(str(obj))
dependent_objects.append(str(dependent))
err_message += ', '.join(dependent_objects)
messages.error(request, mark_safe(err_message))

View File

@ -8,7 +8,7 @@ import yaml
from django import forms
from django.conf import settings
from django.contrib.postgres.forms import SimpleArrayField
from django.contrib.postgres.forms.jsonb import JSONField as _JSONField, InvalidJSONInput
from django.forms.fields import JSONField as _JSONField, InvalidJSONInput
from django.core.exceptions import MultipleObjectsReturned
from django.db.models import Count
from django.forms import BoundField

View File

@ -22,7 +22,7 @@ from . import serializers
class ClusterTypeViewSet(ModelViewSet):
queryset = ClusterType.objects.annotate(
cluster_count=Count('clusters')
)
).order_by(*ClusterType._meta.ordering)
serializer_class = serializers.ClusterTypeSerializer
filterset_class = filters.ClusterTypeFilterSet
@ -30,7 +30,7 @@ class ClusterTypeViewSet(ModelViewSet):
class ClusterGroupViewSet(ModelViewSet):
queryset = ClusterGroup.objects.annotate(
cluster_count=Count('clusters')
)
).order_by(*ClusterGroup._meta.ordering)
serializer_class = serializers.ClusterGroupSerializer
filterset_class = filters.ClusterGroupFilterSet
@ -41,7 +41,7 @@ class ClusterViewSet(CustomFieldModelViewSet):
).annotate(
device_count=get_subquery(Device, 'cluster'),
virtualmachine_count=get_subquery(VirtualMachine, 'cluster')
)
).order_by(*Cluster._meta.ordering)
serializer_class = serializers.ClusterSerializer
filterset_class = filters.ClusterFilterSet

View File

@ -0,0 +1,18 @@
# Generated by Django 3.1b1 on 2020-07-16 16:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('virtualization', '0016_replicate_interfaces'),
]
operations = [
migrations.AlterField(
model_name='virtualmachine',
name='local_context_data',
field=models.JSONField(blank=True, null=True),
),
]

View File

@ -22,7 +22,7 @@ from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterf
#
class ClusterTypeListView(ObjectListView):
queryset = ClusterType.objects.annotate(cluster_count=Count('clusters'))
queryset = ClusterType.objects.annotate(cluster_count=Count('clusters')).order_by(*ClusterType._meta.ordering)
table = tables.ClusterTypeTable
@ -42,7 +42,7 @@ class ClusterTypeBulkImportView(BulkImportView):
class ClusterTypeBulkDeleteView(BulkDeleteView):
queryset = ClusterType.objects.annotate(cluster_count=Count('clusters'))
queryset = ClusterType.objects.annotate(cluster_count=Count('clusters')).order_by(*ClusterType._meta.ordering)
table = tables.ClusterTypeTable
@ -51,7 +51,7 @@ class ClusterTypeBulkDeleteView(BulkDeleteView):
#
class ClusterGroupListView(ObjectListView):
queryset = ClusterGroup.objects.annotate(cluster_count=Count('clusters'))
queryset = ClusterGroup.objects.annotate(cluster_count=Count('clusters')).order_by(*ClusterGroup._meta.ordering)
table = tables.ClusterGroupTable
@ -71,7 +71,7 @@ class ClusterGroupBulkImportView(BulkImportView):
class ClusterGroupBulkDeleteView(BulkDeleteView):
queryset = ClusterGroup.objects.annotate(cluster_count=Count('clusters'))
queryset = ClusterGroup.objects.annotate(cluster_count=Count('clusters')).order_by(*ClusterGroup._meta.ordering)
table = tables.ClusterGroupTable

View File

@ -1,4 +1,4 @@
Django>=3.0,<3.1
Django==3.1rc1
django-cacheops==5.0.1
django-cors-headers==3.4.0
django-debug-toolbar==2.2