mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 09:51:22 -06:00
Closes #7744: Add status field to Location
This commit is contained in:
parent
4587b83d85
commit
e4aa933d57
@ -2,5 +2,4 @@
|
|||||||
|
|
||||||
Racks and devices can be grouped by location within a site. A location may represent a floor, room, cage, or similar organizational unit. Locations can be nested to form a hierarchy. For example, you may have floors within a site, and rooms within a floor.
|
Racks and devices can be grouped by location within a site. A location may represent a floor, room, cage, or similar organizational unit. Locations can be nested to form a hierarchy. For example, you may have floors within a site, and rooms within a floor.
|
||||||
|
|
||||||
Each location must have a name that is unique within its parent site and location, if any.
|
Each location must have a name that is unique within its parent site and location, if any, and must be assigned an operational status. (The set of available statuses is configurable.)
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
* [#1202](https://github.com/netbox-community/netbox/issues/1202) - Support overlapping assignment of NAT IP addresses
|
* [#1202](https://github.com/netbox-community/netbox/issues/1202) - Support overlapping assignment of NAT IP addresses
|
||||||
* [#4350](https://github.com/netbox-community/netbox/issues/4350) - Illustrate reservations vertically alongside rack elevations
|
* [#4350](https://github.com/netbox-community/netbox/issues/4350) - Illustrate reservations vertically alongside rack elevations
|
||||||
* [#5303](https://github.com/netbox-community/netbox/issues/5303) - A virtual machine may be assigned to a site and/or cluster
|
* [#5303](https://github.com/netbox-community/netbox/issues/5303) - A virtual machine may be assigned to a site and/or cluster
|
||||||
|
* [#7744](https://github.com/netbox-community/netbox/issues/7744) - Add `status` field to Location
|
||||||
* [#8222](https://github.com/netbox-community/netbox/issues/8222) - Enable the assignment of a VM to a specific host device within a cluster
|
* [#8222](https://github.com/netbox-community/netbox/issues/8222) - Enable the assignment of a VM to a specific host device within a cluster
|
||||||
* [#8471](https://github.com/netbox-community/netbox/issues/8471) - Add `status` field to Cluster
|
* [#8471](https://github.com/netbox-community/netbox/issues/8471) - Add `status` field to Cluster
|
||||||
* [#8495](https://github.com/netbox-community/netbox/issues/8495) - Enable custom field grouping
|
* [#8495](https://github.com/netbox-community/netbox/issues/8495) - Enable custom field grouping
|
||||||
@ -36,7 +37,9 @@
|
|||||||
* dcim.DeviceType
|
* dcim.DeviceType
|
||||||
* The `u_height` field has been changed from an integer to a decimal
|
* The `u_height` field has been changed from an integer to a decimal
|
||||||
* dcim.Interface
|
* dcim.Interface
|
||||||
* Added the option `poe_mode` and `poe_type` fields
|
* Added the optional `poe_mode` and `poe_type` fields
|
||||||
|
* dcim.Location
|
||||||
|
* Added required `status` field (default value: `active`)
|
||||||
* dcim.Rack
|
* dcim.Rack
|
||||||
* The `elevation` endpoint now includes half-height rack units, and utilizes decimal values for the ID and name of each unit
|
* The `elevation` endpoint now includes half-height rack units, and utilizes decimal values for the ID and name of each unit
|
||||||
* extras.CustomField
|
* extras.CustomField
|
||||||
|
@ -151,6 +151,7 @@ class LocationSerializer(NestedGroupModelSerializer):
|
|||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:location-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:location-detail')
|
||||||
site = NestedSiteSerializer()
|
site = NestedSiteSerializer()
|
||||||
parent = NestedLocationSerializer(required=False, allow_null=True)
|
parent = NestedLocationSerializer(required=False, allow_null=True)
|
||||||
|
status = ChoiceField(choices=LocationStatusChoices, required=False)
|
||||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
rack_count = serializers.IntegerField(read_only=True)
|
rack_count = serializers.IntegerField(read_only=True)
|
||||||
device_count = serializers.IntegerField(read_only=True)
|
device_count = serializers.IntegerField(read_only=True)
|
||||||
@ -158,8 +159,8 @@ class LocationSerializer(NestedGroupModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Location
|
model = Location
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'slug', 'site', 'parent', 'tenant', 'description', 'tags', 'custom_fields',
|
'id', 'url', 'display', 'name', 'slug', 'site', 'parent', 'status', 'tenant', 'description', 'tags',
|
||||||
'created', 'last_updated', 'rack_count', 'device_count', '_depth',
|
'custom_fields', 'created', 'last_updated', 'rack_count', 'device_count', '_depth',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,6 +23,28 @@ class SiteStatusChoices(ChoiceSet):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Locations
|
||||||
|
#
|
||||||
|
|
||||||
|
class LocationStatusChoices(ChoiceSet):
|
||||||
|
key = 'Location.status'
|
||||||
|
|
||||||
|
STATUS_PLANNED = 'planned'
|
||||||
|
STATUS_STAGING = 'staging'
|
||||||
|
STATUS_ACTIVE = 'active'
|
||||||
|
STATUS_DECOMMISSIONING = 'decommissioning'
|
||||||
|
STATUS_RETIRED = 'retired'
|
||||||
|
|
||||||
|
CHOICES = [
|
||||||
|
(STATUS_PLANNED, 'Planned', 'cyan'),
|
||||||
|
(STATUS_STAGING, 'Staging', 'blue'),
|
||||||
|
(STATUS_ACTIVE, 'Active', 'green'),
|
||||||
|
(STATUS_DECOMMISSIONING, 'Decommissioning', 'yellow'),
|
||||||
|
(STATUS_RETIRED, 'Retired', 'red'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Racks
|
# Racks
|
||||||
#
|
#
|
||||||
|
@ -216,10 +216,14 @@ class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, OrganizationalM
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Location (slug)',
|
label='Location (slug)',
|
||||||
)
|
)
|
||||||
|
status = django_filters.MultipleChoiceFilter(
|
||||||
|
choices=LocationStatusChoices,
|
||||||
|
null_value=None
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Location
|
model = Location
|
||||||
fields = ['id', 'name', 'slug', 'description']
|
fields = ['id', 'name', 'slug', 'status', 'description']
|
||||||
|
|
||||||
def search(self, queryset, name, value):
|
def search(self, queryset, name, value):
|
||||||
if not value.strip():
|
if not value.strip():
|
||||||
|
@ -158,6 +158,12 @@ class LocationBulkEditForm(NetBoxModelBulkEditForm):
|
|||||||
'site_id': '$site'
|
'site_id': '$site'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
status = forms.ChoiceField(
|
||||||
|
choices=add_blank_choice(LocationStatusChoices),
|
||||||
|
required=False,
|
||||||
|
initial='',
|
||||||
|
widget=StaticSelect()
|
||||||
|
)
|
||||||
tenant = DynamicModelChoiceField(
|
tenant = DynamicModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -169,7 +175,7 @@ class LocationBulkEditForm(NetBoxModelBulkEditForm):
|
|||||||
|
|
||||||
model = Location
|
model = Location
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('site', 'parent', 'tenant', 'description')),
|
(None, ('site', 'parent', 'status', 'tenant', 'description')),
|
||||||
)
|
)
|
||||||
nullable_fields = ('parent', 'tenant', 'description')
|
nullable_fields = ('parent', 'tenant', 'description')
|
||||||
|
|
||||||
|
@ -124,6 +124,10 @@ class LocationCSVForm(NetBoxModelCSVForm):
|
|||||||
'invalid_choice': 'Location not found.',
|
'invalid_choice': 'Location not found.',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
status = CSVChoiceField(
|
||||||
|
choices=LocationStatusChoices,
|
||||||
|
help_text='Operational status'
|
||||||
|
)
|
||||||
tenant = CSVModelChoiceField(
|
tenant = CSVModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -133,7 +137,7 @@ class LocationCSVForm(NetBoxModelCSVForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Location
|
model = Location
|
||||||
fields = ('site', 'parent', 'name', 'slug', 'tenant', 'description')
|
fields = ('site', 'parent', 'name', 'slug', 'status', 'tenant', 'description')
|
||||||
|
|
||||||
|
|
||||||
class RackRoleCSVForm(NetBoxModelCSVForm):
|
class RackRoleCSVForm(NetBoxModelCSVForm):
|
||||||
|
@ -166,7 +166,7 @@ class LocationFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelF
|
|||||||
model = Location
|
model = Location
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('q', 'tag')),
|
(None, ('q', 'tag')),
|
||||||
('Parent', ('region_id', 'site_group_id', 'site_id', 'parent_id')),
|
('Attributes', ('region_id', 'site_group_id', 'site_id', 'parent_id', 'status')),
|
||||||
('Tenant', ('tenant_group_id', 'tenant_id')),
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
('Contacts', ('contact', 'contact_role', 'contact_group')),
|
('Contacts', ('contact', 'contact_role', 'contact_group')),
|
||||||
)
|
)
|
||||||
@ -198,6 +198,10 @@ class LocationFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelF
|
|||||||
},
|
},
|
||||||
label=_('Parent')
|
label=_('Parent')
|
||||||
)
|
)
|
||||||
|
status = MultipleChoiceField(
|
||||||
|
choices=LocationStatusChoices,
|
||||||
|
required=False
|
||||||
|
)
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ class LocationForm(TenancyForm, NetBoxModelForm):
|
|||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
('Location', (
|
('Location', (
|
||||||
'region', 'site_group', 'site', 'parent', 'name', 'slug', 'description', 'tags',
|
'region', 'site_group', 'site', 'parent', 'name', 'slug', 'status', 'description', 'tags',
|
||||||
)),
|
)),
|
||||||
('Tenancy', ('tenant_group', 'tenant')),
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
)
|
)
|
||||||
@ -202,8 +202,12 @@ class LocationForm(TenancyForm, NetBoxModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Location
|
model = Location
|
||||||
fields = (
|
fields = (
|
||||||
'region', 'site_group', 'site', 'parent', 'name', 'slug', 'description', 'tenant_group', 'tenant', 'tags',
|
'region', 'site_group', 'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant',
|
||||||
|
'tags',
|
||||||
)
|
)
|
||||||
|
widgets = {
|
||||||
|
'status': StaticSelect(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class RackRoleForm(NetBoxModelForm):
|
class RackRoleForm(NetBoxModelForm):
|
||||||
|
18
netbox/dcim/migrations/0156_location_status.py
Normal file
18
netbox/dcim/migrations/0156_location_status.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.0.5 on 2022-06-22 17:10
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0155_interface_poe_mode_type'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='location',
|
||||||
|
name='status',
|
||||||
|
field=models.CharField(default='active', max_length=50),
|
||||||
|
),
|
||||||
|
]
|
@ -341,6 +341,11 @@ class Location(NestedGroupModel):
|
|||||||
null=True,
|
null=True,
|
||||||
db_index=True
|
db_index=True
|
||||||
)
|
)
|
||||||
|
status = models.CharField(
|
||||||
|
max_length=50,
|
||||||
|
choices=LocationStatusChoices,
|
||||||
|
default=LocationStatusChoices.STATUS_ACTIVE
|
||||||
|
)
|
||||||
tenant = models.ForeignKey(
|
tenant = models.ForeignKey(
|
||||||
to='tenancy.Tenant',
|
to='tenancy.Tenant',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
@ -367,7 +372,7 @@ class Location(NestedGroupModel):
|
|||||||
to='extras.ImageAttachment'
|
to='extras.ImageAttachment'
|
||||||
)
|
)
|
||||||
|
|
||||||
clone_fields = ['site', 'parent', 'tenant', 'description']
|
clone_fields = ['site', 'parent', 'status', 'tenant', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['site', 'name']
|
ordering = ['site', 'name']
|
||||||
@ -409,6 +414,9 @@ class Location(NestedGroupModel):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('dcim:location', args=[self.pk])
|
return reverse('dcim:location', args=[self.pk])
|
||||||
|
|
||||||
|
def get_status_color(self):
|
||||||
|
return LocationStatusChoices.colors.get(self.status)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
super().clean()
|
super().clean()
|
||||||
|
|
||||||
|
@ -126,6 +126,7 @@ class LocationTable(NetBoxTable):
|
|||||||
site = tables.Column(
|
site = tables.Column(
|
||||||
linkify=True
|
linkify=True
|
||||||
)
|
)
|
||||||
|
status = columns.ChoiceFieldColumn()
|
||||||
tenant = TenantColumn()
|
tenant = TenantColumn()
|
||||||
rack_count = columns.LinkedCountColumn(
|
rack_count = columns.LinkedCountColumn(
|
||||||
viewname='dcim:rack_list',
|
viewname='dcim:rack_list',
|
||||||
@ -150,7 +151,7 @@ class LocationTable(NetBoxTable):
|
|||||||
class Meta(NetBoxTable.Meta):
|
class Meta(NetBoxTable.Meta):
|
||||||
model = Location
|
model = Location
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'name', 'site', 'tenant', 'rack_count', 'device_count', 'description', 'slug', 'contacts',
|
'pk', 'id', 'name', 'site', 'status', 'tenant', 'rack_count', 'device_count', 'description', 'slug',
|
||||||
'tags', 'actions', 'created', 'last_updated',
|
'contacts', 'tags', 'actions', 'created', 'last_updated',
|
||||||
)
|
)
|
||||||
default_columns = ('pk', 'name', 'site', 'tenant', 'rack_count', 'device_count', 'description')
|
default_columns = ('pk', 'name', 'site', 'status', 'tenant', 'rack_count', 'device_count', 'description')
|
||||||
|
@ -197,13 +197,13 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
|
|||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
parent_locations = (
|
parent_locations = (
|
||||||
Location.objects.create(site=sites[0], name='Parent Location 1', slug='parent-location-1'),
|
Location.objects.create(site=sites[0], name='Parent Location 1', slug='parent-location-1', status=LocationStatusChoices.STATUS_ACTIVE),
|
||||||
Location.objects.create(site=sites[1], name='Parent Location 2', slug='parent-location-2'),
|
Location.objects.create(site=sites[1], name='Parent Location 2', slug='parent-location-2', status=LocationStatusChoices.STATUS_ACTIVE),
|
||||||
)
|
)
|
||||||
|
|
||||||
Location.objects.create(site=sites[0], name='Location 1', slug='location-1', parent=parent_locations[0])
|
Location.objects.create(site=sites[0], name='Location 1', slug='location-1', parent=parent_locations[0], status=LocationStatusChoices.STATUS_ACTIVE)
|
||||||
Location.objects.create(site=sites[0], name='Location 2', slug='location-2', parent=parent_locations[0])
|
Location.objects.create(site=sites[0], name='Location 2', slug='location-2', parent=parent_locations[0], status=LocationStatusChoices.STATUS_ACTIVE)
|
||||||
Location.objects.create(site=sites[0], name='Location 3', slug='location-3', parent=parent_locations[0])
|
Location.objects.create(site=sites[0], name='Location 3', slug='location-3', parent=parent_locations[0], status=LocationStatusChoices.STATUS_ACTIVE)
|
||||||
|
|
||||||
cls.create_data = [
|
cls.create_data = [
|
||||||
{
|
{
|
||||||
@ -211,18 +211,21 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
|
|||||||
'slug': 'test-location-4',
|
'slug': 'test-location-4',
|
||||||
'site': sites[1].pk,
|
'site': sites[1].pk,
|
||||||
'parent': parent_locations[1].pk,
|
'parent': parent_locations[1].pk,
|
||||||
|
'status': LocationStatusChoices.STATUS_PLANNED,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Test Location 5',
|
'name': 'Test Location 5',
|
||||||
'slug': 'test-location-5',
|
'slug': 'test-location-5',
|
||||||
'site': sites[1].pk,
|
'site': sites[1].pk,
|
||||||
'parent': parent_locations[1].pk,
|
'parent': parent_locations[1].pk,
|
||||||
|
'status': LocationStatusChoices.STATUS_PLANNED,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Test Location 6',
|
'name': 'Test Location 6',
|
||||||
'slug': 'test-location-6',
|
'slug': 'test-location-6',
|
||||||
'site': sites[1].pk,
|
'site': sites[1].pk,
|
||||||
'parent': parent_locations[1].pk,
|
'parent': parent_locations[1].pk,
|
||||||
|
'status': LocationStatusChoices.STATUS_PLANNED,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -265,9 +265,9 @@ class LocationTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
location.save()
|
location.save()
|
||||||
|
|
||||||
locations = (
|
locations = (
|
||||||
Location(name='Location 1', slug='location-1', site=sites[0], parent=parent_locations[0], description='A'),
|
Location(name='Location 1', slug='location-1', site=sites[0], parent=parent_locations[0], status=LocationStatusChoices.STATUS_PLANNED, description='A'),
|
||||||
Location(name='Location 2', slug='location-2', site=sites[1], parent=parent_locations[1], description='B'),
|
Location(name='Location 2', slug='location-2', site=sites[1], parent=parent_locations[1], status=LocationStatusChoices.STATUS_STAGING, description='B'),
|
||||||
Location(name='Location 3', slug='location-3', site=sites[2], parent=parent_locations[2], description='C'),
|
Location(name='Location 3', slug='location-3', site=sites[2], parent=parent_locations[2], status=LocationStatusChoices.STATUS_DECOMMISSIONING, description='C'),
|
||||||
)
|
)
|
||||||
for location in locations:
|
for location in locations:
|
||||||
location.save()
|
location.save()
|
||||||
@ -280,6 +280,10 @@ class LocationTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
params = {'slug': ['location-1', 'location-2']}
|
params = {'slug': ['location-1', 'location-2']}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_status(self):
|
||||||
|
params = {'status': [LocationStatusChoices.STATUS_PLANNED, LocationStatusChoices.STATUS_STAGING]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_description(self):
|
def test_description(self):
|
||||||
params = {'description': ['A', 'B']}
|
params = {'description': ['A', 'B']}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
@ -175,9 +175,9 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
|||||||
tenant = Tenant.objects.create(name='Tenant 1', slug='tenant-1')
|
tenant = Tenant.objects.create(name='Tenant 1', slug='tenant-1')
|
||||||
|
|
||||||
locations = (
|
locations = (
|
||||||
Location(name='Location 1', slug='location-1', site=site, tenant=tenant),
|
Location(name='Location 1', slug='location-1', site=site, status=LocationStatusChoices.STATUS_ACTIVE, tenant=tenant),
|
||||||
Location(name='Location 2', slug='location-2', site=site, tenant=tenant),
|
Location(name='Location 2', slug='location-2', site=site, status=LocationStatusChoices.STATUS_ACTIVE, tenant=tenant),
|
||||||
Location(name='Location 3', slug='location-3', site=site, tenant=tenant),
|
Location(name='Location 3', slug='location-3', site=site, status=LocationStatusChoices.STATUS_ACTIVE, tenant=tenant),
|
||||||
)
|
)
|
||||||
for location in locations:
|
for location in locations:
|
||||||
location.save()
|
location.save()
|
||||||
@ -188,16 +188,17 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
|||||||
'name': 'Location X',
|
'name': 'Location X',
|
||||||
'slug': 'location-x',
|
'slug': 'location-x',
|
||||||
'site': site.pk,
|
'site': site.pk,
|
||||||
|
'status': LocationStatusChoices.STATUS_PLANNED,
|
||||||
'tenant': tenant.pk,
|
'tenant': tenant.pk,
|
||||||
'description': 'A new location',
|
'description': 'A new location',
|
||||||
'tags': [t.pk for t in tags],
|
'tags': [t.pk for t in tags],
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
"site,tenant,name,slug,description",
|
"site,tenant,name,slug,status,description",
|
||||||
"Site 1,Tenant 1,Location 4,location-4,Fourth location",
|
"Site 1,Tenant 1,Location 4,location-4,planned,Fourth location",
|
||||||
"Site 1,Tenant 1,Location 5,location-5,Fifth location",
|
"Site 1,Tenant 1,Location 5,location-5,planned,Fifth location",
|
||||||
"Site 1,Tenant 1,Location 6,location-6,Sixth location",
|
"Site 1,Tenant 1,Location 6,location-6,planned,Sixth location",
|
||||||
)
|
)
|
||||||
|
|
||||||
cls.bulk_edit_data = {
|
cls.bulk_edit_data = {
|
||||||
|
@ -43,6 +43,10 @@
|
|||||||
<th scope="row">Parent</th>
|
<th scope="row">Parent</th>
|
||||||
<td>{{ object.parent|linkify|placeholder }}</td>
|
<td>{{ object.parent|linkify|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Status</th>
|
||||||
|
<td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Tenant</th>
|
<th scope="row">Tenant</th>
|
||||||
<td>
|
<td>
|
||||||
|
Loading…
Reference in New Issue
Block a user