mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-26 18:38:38 -06:00
Add attributes property on ModuleType
This commit is contained in:
parent
69e67d0258
commit
3cda074cbd
@ -436,7 +436,7 @@ class ModuleTypeForm(NetBoxModelForm):
|
||||
|
||||
fieldsets = (
|
||||
FieldSet('profile', 'manufacturer', 'model', 'part_number', 'description', 'tags', name=_('Module Type')),
|
||||
FieldSet('attributes', name=_('Profile Attributes')),
|
||||
FieldSet('attribute_data', name=_('Profile Attributes')),
|
||||
FieldSet('airflow', 'weight', 'weight_unit', name=_('Hardware')),
|
||||
)
|
||||
|
||||
@ -444,7 +444,7 @@ class ModuleTypeForm(NetBoxModelForm):
|
||||
model = ModuleType
|
||||
fields = [
|
||||
'profile', 'manufacturer', 'model', 'part_number', 'description', 'airflow', 'weight', 'weight_unit',
|
||||
'attributes', 'comments', 'tags',
|
||||
'attribute_data', 'comments', 'tags',
|
||||
]
|
||||
|
||||
|
||||
|
@ -36,7 +36,7 @@ class Migration(migrations.Migration):
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='moduletype',
|
||||
name='attributes',
|
||||
name='attribute_data',
|
||||
field=models.JSONField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
|
@ -93,7 +93,7 @@ class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
attributes = models.JSONField(
|
||||
attribute_data = models.JSONField(
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name=_('attributes')
|
||||
@ -122,19 +122,32 @@ class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
|
||||
def full_name(self):
|
||||
return f"{self.manufacturer} {self.model}"
|
||||
|
||||
@property
|
||||
def attributes(self):
|
||||
"""
|
||||
Returns a human-friendly representation of the attributes defined for a ModuleType according to its profile.
|
||||
"""
|
||||
if self.profile is None or not self.profile.schema:
|
||||
return {}
|
||||
attrs = {}
|
||||
for name, options in self.profile.schema.get('properties', {}).items():
|
||||
key = options.get('title', name)
|
||||
attrs[key] = self.attribute_data.get(name)
|
||||
return dict(sorted(attrs.items()))
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
# Validate any attributes against the assigned profile's schema
|
||||
if self.profile:
|
||||
try:
|
||||
jsonschema.validate(self.attributes, schema=self.profile.schema)
|
||||
jsonschema.validate(self.attribute_data, schema=self.profile.schema)
|
||||
except JSONValidationError as e:
|
||||
raise ValidationError({
|
||||
'attributes': _("Invalid schema: {error}").format(error=e)
|
||||
})
|
||||
else:
|
||||
self.attributes = None
|
||||
self.attribute_data = None
|
||||
|
||||
def to_yaml(self):
|
||||
data = {
|
||||
|
@ -1,7 +1,7 @@
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
import django_tables2 as tables
|
||||
|
||||
from dcim.models import Module, ModuleType
|
||||
from dcim.models import Module, ModuleType, ModuleTypeProfile
|
||||
from netbox.tables import NetBoxTable, columns
|
||||
from .template_code import WEIGHT
|
||||
|
||||
@ -13,15 +13,19 @@ __all__ = (
|
||||
|
||||
|
||||
class ModuleTypeProfileTable(NetBoxTable):
|
||||
name = tables.Column(
|
||||
verbose_name=_('Name'),
|
||||
linkify=True
|
||||
)
|
||||
comments = columns.MarkdownColumn(
|
||||
verbose_name=_('Comments'),
|
||||
)
|
||||
tags = columns.TagColumn(
|
||||
url_name='dcim:moduletype_list'
|
||||
url_name='dcim:moduletypeprofile_list'
|
||||
)
|
||||
|
||||
class Meta(NetBoxTable.Meta):
|
||||
model = ModuleType
|
||||
model = ModuleTypeProfile
|
||||
fields = (
|
||||
'pk', 'id', 'name', 'description', 'comments', 'tags', 'created', 'last_updated',
|
||||
)
|
||||
@ -43,6 +47,12 @@ class ModuleTypeTable(NetBoxTable):
|
||||
linkify=True,
|
||||
verbose_name=_('Module Type')
|
||||
)
|
||||
weight = columns.TemplateColumn(
|
||||
verbose_name=_('Weight'),
|
||||
template_code=WEIGHT,
|
||||
order_by=('_abs_weight', 'weight_unit')
|
||||
)
|
||||
attributes = columns.DictColumn()
|
||||
instance_count = columns.LinkedCountColumn(
|
||||
viewname='dcim:module_list',
|
||||
url_params={'module_type_id': 'pk'},
|
||||
@ -54,17 +64,12 @@ class ModuleTypeTable(NetBoxTable):
|
||||
tags = columns.TagColumn(
|
||||
url_name='dcim:moduletype_list'
|
||||
)
|
||||
weight = columns.TemplateColumn(
|
||||
verbose_name=_('Weight'),
|
||||
template_code=WEIGHT,
|
||||
order_by=('_abs_weight', 'weight_unit')
|
||||
)
|
||||
|
||||
class Meta(NetBoxTable.Meta):
|
||||
model = ModuleType
|
||||
fields = (
|
||||
'pk', 'id', 'model', 'profile', 'manufacturer', 'part_number', 'airflow', 'weight', 'description',
|
||||
'comments', 'tags', 'created', 'last_updated',
|
||||
'attributes', 'comments', 'tags', 'created', 'last_updated',
|
||||
)
|
||||
default_columns = (
|
||||
'pk', 'model', 'profile', 'manufacturer', 'part_number',
|
||||
|
@ -35,6 +35,7 @@ __all__ = (
|
||||
'ContentTypesColumn',
|
||||
'CustomFieldColumn',
|
||||
'CustomLinkColumn',
|
||||
'DictColumn',
|
||||
'DistanceColumn',
|
||||
'DurationColumn',
|
||||
'LinkedCountColumn',
|
||||
@ -707,3 +708,14 @@ class DistanceColumn(TemplateColumn):
|
||||
|
||||
def __init__(self, template_code=template_code, order_by='_abs_distance', **kwargs):
|
||||
super().__init__(template_code=template_code, order_by=order_by, **kwargs)
|
||||
|
||||
|
||||
class DictColumn(tables.Column):
|
||||
"""
|
||||
Render a dictionary of data in a simple key: value format, one pair per line.
|
||||
"""
|
||||
def render(self, value):
|
||||
output = '<br />'.join([
|
||||
f'{escape(k)}: {escape(v)}' for k, v in value.items()
|
||||
])
|
||||
return mark_safe(output)
|
||||
|
@ -25,7 +25,7 @@
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Profile" %}</th>
|
||||
<td>{{ object.profile|linkify }}</td>
|
||||
<td>{{ object.profile|linkify|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Manufacturer" %}</th>
|
||||
@ -64,6 +64,27 @@
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Attributes" %}</h2>
|
||||
{% if not object.profile %}
|
||||
<div class="card-body text-muted">
|
||||
{% trans "No profile assigned" %}
|
||||
</div>
|
||||
{% elif object.attributes %}
|
||||
<table class="table table-hover attr-table">
|
||||
{% for k, v in object.attributes.items %}
|
||||
<tr>
|
||||
<th scope="row">{{ k }}</th>
|
||||
<td>{{ v|placeholder }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="card-body text-muted">
|
||||
{% trans "None" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'inc/panels/related_objects.html' %}
|
||||
{% include 'inc/panels/custom_fields.html' %}
|
||||
{% include 'inc/panels/image_attachments.html' %}
|
||||
|
@ -28,8 +28,11 @@
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "Schema" %}</h2>
|
||||
<pre>{{ object.schema|json }}</pre>
|
||||
<h2 class="card-header d-flex justify-content-between">
|
||||
{% trans "Schema" %}
|
||||
{% copy_content 'profile_schema' %}
|
||||
</h2>
|
||||
<pre id="profile_schema">{{ object.schema|json }}</pre>
|
||||
</div>
|
||||
{% include 'inc/panels/related_objects.html' %}
|
||||
{% include 'inc/panels/custom_fields.html' %}
|
||||
|
Loading…
Reference in New Issue
Block a user