mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-21 11:37:21 -06:00
commit
23f6832d9c
@ -96,6 +96,7 @@ class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
|||||||
def search(self, queryset, value):
|
def search(self, queryset, value):
|
||||||
return queryset.filter(
|
return queryset.filter(
|
||||||
Q(cid__icontains=value) |
|
Q(cid__icontains=value) |
|
||||||
Q(xconnect_id__icontains=value) |
|
Q(terminations__xconnect_id__icontains=value) |
|
||||||
|
Q(terminations__pp_info__icontains=value) |
|
||||||
Q(comments__icontains=value)
|
Q(comments__icontains=value)
|
||||||
)
|
)
|
||||||
|
@ -5,6 +5,7 @@ from django.db import models
|
|||||||
from dcim.fields import ASNField
|
from dcim.fields import ASNField
|
||||||
from extras.models import CustomFieldModel, CustomFieldValue
|
from extras.models import CustomFieldModel, CustomFieldValue
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
|
from utilities.utils import csv_format
|
||||||
from utilities.models import CreatedUpdatedModel
|
from utilities.models import CreatedUpdatedModel
|
||||||
|
|
||||||
|
|
||||||
@ -57,10 +58,10 @@ class Provider(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
return reverse('circuits:provider', args=[self.slug])
|
return reverse('circuits:provider', args=[self.slug])
|
||||||
|
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
return ','.join([
|
return csv_format([
|
||||||
self.name,
|
self.name,
|
||||||
self.slug,
|
self.slug,
|
||||||
str(self.asn) if self.asn else '',
|
self.asn,
|
||||||
self.account,
|
self.account,
|
||||||
self.portal_url,
|
self.portal_url,
|
||||||
])
|
])
|
||||||
@ -68,7 +69,7 @@ class Provider(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
|
|
||||||
class CircuitType(models.Model):
|
class CircuitType(models.Model):
|
||||||
"""
|
"""
|
||||||
Circuits can be orgnanized by their functional role. For example, a user might wish to define CircuitTypes named
|
Circuits can be organized by their functional role. For example, a user might wish to define CircuitTypes named
|
||||||
"Long Haul," "Metro," or "Out-of-Band".
|
"Long Haul," "Metro," or "Out-of-Band".
|
||||||
"""
|
"""
|
||||||
name = models.CharField(max_length=50, unique=True)
|
name = models.CharField(max_length=50, unique=True)
|
||||||
@ -110,13 +111,13 @@ class Circuit(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
return reverse('circuits:circuit', args=[self.pk])
|
return reverse('circuits:circuit', args=[self.pk])
|
||||||
|
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
return ','.join([
|
return csv_format([
|
||||||
self.cid,
|
self.cid,
|
||||||
self.provider.name,
|
self.provider.name,
|
||||||
self.type.name,
|
self.type.name,
|
||||||
self.tenant.name if self.tenant else '',
|
self.tenant.name if self.tenant else None,
|
||||||
self.install_date.isoformat() if self.install_date else '',
|
self.install_date.isoformat() if self.install_date else None,
|
||||||
str(self.commit_rate) if self.commit_rate else '',
|
self.commit_rate,
|
||||||
])
|
])
|
||||||
|
|
||||||
def _get_termination(self, side):
|
def _get_termination(self, side):
|
||||||
|
@ -118,11 +118,13 @@ class RackUnitListView(APIView):
|
|||||||
|
|
||||||
rack = get_object_or_404(Rack, pk=pk)
|
rack = get_object_or_404(Rack, pk=pk)
|
||||||
face = request.GET.get('face', 0)
|
face = request.GET.get('face', 0)
|
||||||
try:
|
exclude_pk = request.GET.get('exclude', None)
|
||||||
exclude = int(request.GET.get('exclude', None))
|
if exclude_pk is not None:
|
||||||
except ValueError:
|
try:
|
||||||
exclude = None
|
exclude_pk = int(exclude_pk)
|
||||||
elevation = rack.get_rack_units(face, exclude)
|
except ValueError:
|
||||||
|
exclude_pk = None
|
||||||
|
elevation = rack.get_rack_units(face, exclude_pk)
|
||||||
|
|
||||||
# Serialize Devices within the rack elevation
|
# Serialize Devices within the rack elevation
|
||||||
for u in elevation:
|
for u in elevation:
|
||||||
|
@ -16,6 +16,7 @@ from tenancy.models import Tenant
|
|||||||
from utilities.fields import ColorField, NullableCharField
|
from utilities.fields import ColorField, NullableCharField
|
||||||
from utilities.managers import NaturalOrderByManager
|
from utilities.managers import NaturalOrderByManager
|
||||||
from utilities.models import CreatedUpdatedModel
|
from utilities.models import CreatedUpdatedModel
|
||||||
|
from utilities.utils import csv_format
|
||||||
|
|
||||||
from .fields import ASNField, MACAddressField
|
from .fields import ASNField, MACAddressField
|
||||||
|
|
||||||
@ -263,12 +264,12 @@ class Site(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
return reverse('dcim:site', args=[self.slug])
|
return reverse('dcim:site', args=[self.slug])
|
||||||
|
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
return ','.join([
|
return csv_format([
|
||||||
self.name,
|
self.name,
|
||||||
self.slug,
|
self.slug,
|
||||||
self.tenant.name if self.tenant else '',
|
self.tenant.name if self.tenant else None,
|
||||||
self.facility,
|
self.facility,
|
||||||
str(self.asn) if self.asn else '',
|
self.asn,
|
||||||
self.contact_name,
|
self.contact_name,
|
||||||
self.contact_phone,
|
self.contact_phone,
|
||||||
self.contact_email,
|
self.contact_email,
|
||||||
@ -398,17 +399,17 @@ class Rack(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
return ','.join([
|
return csv_format([
|
||||||
self.site.name,
|
self.site.name,
|
||||||
self.group.name if self.group else '',
|
self.group.name if self.group else None,
|
||||||
self.name,
|
self.name,
|
||||||
self.facility_id or '',
|
self.facility_id,
|
||||||
self.tenant.name if self.tenant else '',
|
self.tenant.name if self.tenant else None,
|
||||||
self.role.name if self.role else '',
|
self.role.name if self.role else None,
|
||||||
self.get_type_display() if self.type else '',
|
self.get_type_display() if self.type else None,
|
||||||
str(self.width),
|
self.width,
|
||||||
str(self.u_height),
|
self.u_height,
|
||||||
'True' if self.desc_units else '',
|
self.desc_units,
|
||||||
])
|
])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -910,19 +911,19 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
Device.objects.filter(parent_bay__device=self).update(rack=self.rack)
|
Device.objects.filter(parent_bay__device=self).update(rack=self.rack)
|
||||||
|
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
return ','.join([
|
return csv_format([
|
||||||
self.name or '',
|
self.name or '',
|
||||||
self.device_role.name,
|
self.device_role.name,
|
||||||
self.tenant.name if self.tenant else '',
|
self.tenant.name if self.tenant else None,
|
||||||
self.device_type.manufacturer.name,
|
self.device_type.manufacturer.name,
|
||||||
self.device_type.model,
|
self.device_type.model,
|
||||||
self.platform.name if self.platform else '',
|
self.platform.name if self.platform else None,
|
||||||
self.serial,
|
self.serial,
|
||||||
self.asset_tag if self.asset_tag else '',
|
self.asset_tag,
|
||||||
self.rack.site.name,
|
self.rack.site.name,
|
||||||
self.rack.name,
|
self.rack.name,
|
||||||
str(self.position) if self.position else '',
|
self.position,
|
||||||
self.get_face_display() or '',
|
self.get_face_display(),
|
||||||
])
|
])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -991,9 +992,9 @@ class ConsolePort(models.Model):
|
|||||||
|
|
||||||
# Used for connections export
|
# Used for connections export
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
return ','.join([
|
return csv_format([
|
||||||
self.cs_port.device.identifier if self.cs_port else '',
|
self.cs_port.device.identifier if self.cs_port else None,
|
||||||
self.cs_port.name if self.cs_port else '',
|
self.cs_port.name if self.cs_port else None,
|
||||||
self.device.identifier,
|
self.device.identifier,
|
||||||
self.name,
|
self.name,
|
||||||
self.get_connection_status_display(),
|
self.get_connection_status_display(),
|
||||||
@ -1055,10 +1056,10 @@ class PowerPort(models.Model):
|
|||||||
return self.device.get_absolute_url()
|
return self.device.get_absolute_url()
|
||||||
|
|
||||||
# Used for connections export
|
# Used for connections export
|
||||||
def to_csv(self):
|
def csv_format(self):
|
||||||
return ','.join([
|
return ','.join([
|
||||||
self.power_outlet.device.identifier if self.power_outlet else '',
|
self.power_outlet.device.identifier if self.power_outlet else None,
|
||||||
self.power_outlet.name if self.power_outlet else '',
|
self.power_outlet.name if self.power_outlet else None,
|
||||||
self.device.identifier,
|
self.device.identifier,
|
||||||
self.name,
|
self.name,
|
||||||
self.get_connection_status_display(),
|
self.get_connection_status_display(),
|
||||||
@ -1196,7 +1197,7 @@ class InterfaceConnection(models.Model):
|
|||||||
|
|
||||||
# Used for connections export
|
# Used for connections export
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
return ','.join([
|
return csv_format([
|
||||||
self.interface_a.device.identifier,
|
self.interface_a.device.identifier,
|
||||||
self.interface_a.name,
|
self.interface_a.name,
|
||||||
self.interface_b.device.identifier,
|
self.interface_b.device.identifier,
|
||||||
|
@ -34,9 +34,9 @@ def get_custom_fields_for_model(content_type, filterable_only=False, bulk_edit=F
|
|||||||
(0, 'False'),
|
(0, 'False'),
|
||||||
)
|
)
|
||||||
if cf.default.lower() in ['true', 'yes', '1']:
|
if cf.default.lower() in ['true', 'yes', '1']:
|
||||||
initial = True
|
initial = 1
|
||||||
elif cf.default.lower() in ['false', 'no', '0']:
|
elif cf.default.lower() in ['false', 'no', '0']:
|
||||||
initial = False
|
initial = 0
|
||||||
else:
|
else:
|
||||||
initial = None
|
initial = None
|
||||||
field = forms.NullBooleanField(required=cf.required, initial=initial,
|
field = forms.NullBooleanField(required=cf.required, initial=initial,
|
||||||
|
@ -126,7 +126,7 @@ class PrefixFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site (slug)',
|
label='Site (slug)',
|
||||||
)
|
)
|
||||||
vlan_id = django_filters.ModelMultipleChoiceFilter(
|
vlan_id = NullableModelMultipleChoiceFilter(
|
||||||
name='vlan',
|
name='vlan',
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
label='VLAN (ID)',
|
label='VLAN (ID)',
|
||||||
|
@ -13,6 +13,7 @@ from extras.models import CustomFieldModel, CustomFieldValue
|
|||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.models import CreatedUpdatedModel
|
from utilities.models import CreatedUpdatedModel
|
||||||
from utilities.sql import NullsFirstQuerySet
|
from utilities.sql import NullsFirstQuerySet
|
||||||
|
from utilities.utils import csv_format
|
||||||
|
|
||||||
from .fields import IPNetworkField, IPAddressField
|
from .fields import IPNetworkField, IPAddressField
|
||||||
|
|
||||||
@ -95,11 +96,11 @@ class VRF(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
return reverse('ipam:vrf', args=[self.pk])
|
return reverse('ipam:vrf', args=[self.pk])
|
||||||
|
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
return ','.join([
|
return csv_format([
|
||||||
self.name,
|
self.name,
|
||||||
self.rd,
|
self.rd,
|
||||||
self.tenant.name if self.tenant else '',
|
self.tenant.name if self.tenant else None,
|
||||||
'True' if self.enforce_unique else '',
|
self.enforce_unique,
|
||||||
self.description,
|
self.description,
|
||||||
])
|
])
|
||||||
|
|
||||||
@ -183,10 +184,10 @@ class Aggregate(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
super(Aggregate, self).save(*args, **kwargs)
|
super(Aggregate, self).save(*args, **kwargs)
|
||||||
|
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
return ','.join([
|
return csv_format([
|
||||||
str(self.prefix),
|
self.prefix,
|
||||||
self.rir.name,
|
self.rir.name,
|
||||||
self.date_added.isoformat() if self.date_added else '',
|
self.date_added.isoformat() if self.date_added else None,
|
||||||
self.description,
|
self.description,
|
||||||
])
|
])
|
||||||
|
|
||||||
@ -319,16 +320,16 @@ class Prefix(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
super(Prefix, self).save(*args, **kwargs)
|
super(Prefix, self).save(*args, **kwargs)
|
||||||
|
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
return ','.join([
|
return csv_format([
|
||||||
str(self.prefix),
|
self.prefix,
|
||||||
self.vrf.rd if self.vrf else '',
|
self.vrf.rd if self.vrf else None,
|
||||||
self.tenant.name if self.tenant else '',
|
self.tenant.name if self.tenant else None,
|
||||||
self.site.name if self.site else '',
|
self.site.name if self.site else None,
|
||||||
self.vlan.group.name if self.vlan and self.vlan.group else '',
|
self.vlan.group.name if self.vlan and self.vlan.group else None,
|
||||||
str(self.vlan.vid) if self.vlan else '',
|
self.vlan.vid if self.vlan else None,
|
||||||
self.get_status_display(),
|
self.get_status_display(),
|
||||||
self.role.name if self.role else '',
|
self.role.name if self.role else None,
|
||||||
'True' if self.is_pool else '',
|
self.is_pool,
|
||||||
self.description,
|
self.description,
|
||||||
])
|
])
|
||||||
|
|
||||||
@ -432,14 +433,14 @@ class IPAddress(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
elif self.family == 6 and getattr(self, 'primary_ip6_for', False):
|
elif self.family == 6 and getattr(self, 'primary_ip6_for', False):
|
||||||
is_primary = True
|
is_primary = True
|
||||||
|
|
||||||
return ','.join([
|
return csv_format([
|
||||||
str(self.address),
|
self.address,
|
||||||
self.vrf.rd if self.vrf else '',
|
self.vrf.rd if self.vrf else None,
|
||||||
self.tenant.name if self.tenant else '',
|
self.tenant.name if self.tenant else None,
|
||||||
self.get_status_display(),
|
self.get_status_display(),
|
||||||
self.device.identifier if self.device else '',
|
self.device.identifier if self.device else None,
|
||||||
self.interface.name if self.interface else '',
|
self.interface.name if self.interface else None,
|
||||||
'True' if is_primary else '',
|
is_primary,
|
||||||
self.description,
|
self.description,
|
||||||
])
|
])
|
||||||
|
|
||||||
@ -523,14 +524,14 @@ class VLAN(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
return ','.join([
|
return csv_format([
|
||||||
self.site.name,
|
self.site.name,
|
||||||
self.group.name if self.group else '',
|
self.group.name if self.group else None,
|
||||||
str(self.vid),
|
self.vid,
|
||||||
self.name,
|
self.name,
|
||||||
self.tenant.name if self.tenant else '',
|
self.tenant.name if self.tenant else None,
|
||||||
self.get_status_display(),
|
self.get_status_display(),
|
||||||
self.role.name if self.role else '',
|
self.role.name if self.role else None,
|
||||||
self.description,
|
self.description,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ except ImportError:
|
|||||||
"the documentation.")
|
"the documentation.")
|
||||||
|
|
||||||
|
|
||||||
VERSION = '1.8.0'
|
VERSION = '1.8.1'
|
||||||
|
|
||||||
# Import local configuration
|
# Import local configuration
|
||||||
for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']:
|
for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']:
|
||||||
|
@ -4,6 +4,7 @@ from django.db import models
|
|||||||
|
|
||||||
from extras.models import CustomFieldModel, CustomFieldValue
|
from extras.models import CustomFieldModel, CustomFieldValue
|
||||||
from utilities.models import CreatedUpdatedModel
|
from utilities.models import CreatedUpdatedModel
|
||||||
|
from utilities.utils import csv_format
|
||||||
|
|
||||||
|
|
||||||
class TenantGroup(models.Model):
|
class TenantGroup(models.Model):
|
||||||
@ -45,9 +46,9 @@ class Tenant(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
return reverse('tenancy:tenant', args=[self.slug])
|
return reverse('tenancy:tenant', args=[self.slug])
|
||||||
|
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
return ','.join([
|
return csv_format([
|
||||||
self.name,
|
self.name,
|
||||||
self.slug,
|
self.slug,
|
||||||
self.group.name if self.group else '',
|
self.group.name if self.group else None,
|
||||||
self.description,
|
self.description,
|
||||||
])
|
])
|
||||||
|
15
netbox/utilities/utils.py
Normal file
15
netbox/utilities/utils.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
def csv_format(data):
|
||||||
|
"""
|
||||||
|
Encapsulate any data which contains a comma within double quotes.
|
||||||
|
"""
|
||||||
|
csv = []
|
||||||
|
for d in data:
|
||||||
|
if d in [None, False]:
|
||||||
|
csv.append(u'')
|
||||||
|
elif type(d) not in (str, unicode):
|
||||||
|
csv.append(u'{}'.format(d))
|
||||||
|
elif u',' in d:
|
||||||
|
csv.append(u'"{}"'.format(d))
|
||||||
|
else:
|
||||||
|
csv.append(d)
|
||||||
|
return u','.join(csv)
|
@ -48,8 +48,6 @@ class ObjectListView(View):
|
|||||||
table: The django-tables2 Table used to render the objects list
|
table: The django-tables2 Table used to render the objects list
|
||||||
edit_permissions: Editing controls are displayed only if the user has these permissions
|
edit_permissions: Editing controls are displayed only if the user has these permissions
|
||||||
template_name: The name of the template
|
template_name: The name of the template
|
||||||
redirect_on_single_result: If True and the queryset returns only a single object, the user is automatically
|
|
||||||
redirected to that object
|
|
||||||
"""
|
"""
|
||||||
queryset = None
|
queryset = None
|
||||||
filter = None
|
filter = None
|
||||||
@ -57,7 +55,6 @@ class ObjectListView(View):
|
|||||||
table = None
|
table = None
|
||||||
edit_permissions = []
|
edit_permissions = []
|
||||||
template_name = None
|
template_name = None
|
||||||
redirect_on_single_result = True
|
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
|
||||||
@ -95,13 +92,6 @@ class ObjectListView(View):
|
|||||||
.format(self.queryset.model._meta.verbose_name_plural)
|
.format(self.queryset.model._meta.verbose_name_plural)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
# Attempt to redirect automatically if the search query returns a single result
|
|
||||||
if self.redirect_on_single_result and self.queryset.count() == 1 and request.GET:
|
|
||||||
try:
|
|
||||||
return HttpResponseRedirect(self.queryset[0].get_absolute_url())
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Provide a hook to tweak the queryset based on the request immediately prior to rendering the object list
|
# Provide a hook to tweak the queryset based on the request immediately prior to rendering the object list
|
||||||
self.queryset = self.alter_queryset(request)
|
self.queryset = self.alter_queryset(request)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user