Skip to content

Commit

Permalink
Initial work on implementing django-taggit for #132
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremystretch committed May 8, 2018
1 parent 57f6d22 commit b0dafcf
Show file tree
Hide file tree
Showing 13 changed files with 68 additions and 12 deletions.
18 changes: 12 additions & 6 deletions netbox/dcim/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator
from taggit.models import Tag

from circuits.models import Circuit, CircuitTermination
from dcim.constants import (
Expand All @@ -21,7 +22,8 @@
from tenancy.api.serializers import NestedTenantSerializer
from users.api.serializers import NestedUserSerializer
from utilities.api import (
ChoiceFieldSerializer, SerializedPKRelatedField, TimeZoneField, ValidatedModelSerializer, WritableNestedSerializer,
ChoiceFieldSerializer, SerializedPKRelatedField, TagField, TimeZoneField, ValidatedModelSerializer,
WritableNestedSerializer,
)
from virtualization.models import Cluster

Expand Down Expand Up @@ -55,14 +57,15 @@ class SiteSerializer(CustomFieldModelSerializer):
region = NestedRegionSerializer(required=False, allow_null=True)
tenant = NestedTenantSerializer(required=False, allow_null=True)
time_zone = TimeZoneField(required=False)
tags = TagField(queryset=Tag.objects.all(), required=False, many=True)

class Meta:
model = Site
fields = [
'id', 'name', 'slug', 'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description',
'physical_address', 'shipping_address', 'contact_name', 'contact_phone', 'contact_email', 'comments',
'custom_fields', 'created', 'last_updated', 'count_prefixes', 'count_vlans', 'count_racks', 'count_devices',
'count_circuits',
'tags', 'custom_fields', 'created', 'last_updated', 'count_prefixes', 'count_vlans', 'count_racks',
'count_devices', 'count_circuits',
]


Expand Down Expand Up @@ -124,12 +127,13 @@ class RackSerializer(CustomFieldModelSerializer):
role = NestedRackRoleSerializer(required=False, allow_null=True)
type = ChoiceFieldSerializer(choices=RACK_TYPE_CHOICES, required=False)
width = ChoiceFieldSerializer(choices=RACK_WIDTH_CHOICES, required=False)
tags = TagField(queryset=Tag.objects.all(), required=False, many=True)

class Meta:
model = Rack
fields = [
'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'role', 'serial', 'type', 'width',
'u_height', 'desc_units', 'comments', 'custom_fields', 'created', 'last_updated',
'u_height', 'desc_units', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
# Omit the UniqueTogetherValidator that would be automatically added to validate (site, facility_id). This
# prevents facility_id from being interpreted as a required field.
Expand Down Expand Up @@ -223,12 +227,13 @@ class DeviceTypeSerializer(CustomFieldModelSerializer):
interface_ordering = ChoiceFieldSerializer(choices=IFACE_ORDERING_CHOICES, required=False)
subdevice_role = ChoiceFieldSerializer(choices=SUBDEVICE_ROLE_CHOICES, required=False)
instance_count = serializers.IntegerField(source='instances.count', read_only=True)
tags = TagField(queryset=Tag.objects.all(), required=False, many=True)

class Meta:
model = DeviceType
fields = [
'id', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'interface_ordering',
'is_console_server', 'is_pdu', 'is_network_device', 'subdevice_role', 'comments', 'custom_fields',
'is_console_server', 'is_pdu', 'is_network_device', 'subdevice_role', 'comments', 'tags', 'custom_fields',
'instance_count',
]

Expand Down Expand Up @@ -401,13 +406,14 @@ class DeviceSerializer(CustomFieldModelSerializer):
parent_device = serializers.SerializerMethodField()
cluster = NestedClusterSerializer(required=False, allow_null=True)
virtual_chassis = DeviceVirtualChassisSerializer(required=False, allow_null=True)
tags = TagField(queryset=Tag.objects.all(), required=False, many=True)

class Meta:
model = Device
fields = [
'id', 'name', 'display_name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag',
'site', 'rack', 'position', 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4', 'primary_ip6',
'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'custom_fields', 'created',
'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'tags', 'custom_fields', 'created',
'last_updated',
]
validators = []
Expand Down
18 changes: 13 additions & 5 deletions netbox/dcim/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from django.contrib.postgres.forms.array import SimpleArrayField
from django.db.models import Count, Q
from mptt.forms import TreeNodeChoiceField
from taggit.forms import TagField
from timezone_field import TimeZoneFormField

from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
Expand Down Expand Up @@ -108,12 +109,14 @@ class SiteForm(BootstrapMixin, TenancyForm, CustomFieldForm):
region = TreeNodeChoiceField(queryset=Region.objects.all(), required=False)
slug = SlugField()
comments = CommentField()
tags = TagField(required=False)

class Meta:
model = Site
fields = [
'name', 'slug', 'status', 'region', 'tenant_group', 'tenant', 'facility', 'asn', 'time_zone', 'description',
'physical_address', 'shipping_address', 'contact_name', 'contact_phone', 'contact_email', 'comments',
'tags',
]
widgets = {
'physical_address': SmallTextarea(attrs={'rows': 3}),
Expand Down Expand Up @@ -274,12 +277,13 @@ class RackForm(BootstrapMixin, TenancyForm, CustomFieldForm):
)
)
comments = CommentField()
tags = TagField(required=False)

class Meta:
model = Rack
fields = [
'site', 'group', 'name', 'facility_id', 'tenant_group', 'tenant', 'role', 'serial', 'type', 'width',
'u_height', 'desc_units', 'comments',
'u_height', 'desc_units', 'comments', 'tags',
]
help_texts = {
'site': "The site at which the rack exists",
Expand Down Expand Up @@ -485,11 +489,14 @@ class Meta:

class DeviceTypeForm(BootstrapMixin, CustomFieldForm):
slug = SlugField(slug_source='model')
tags = TagField(required=False)

class Meta:
model = DeviceType
fields = ['manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'is_console_server',
'is_pdu', 'is_network_device', 'subdevice_role', 'interface_ordering', 'comments']
fields = [
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'is_console_server', 'is_pdu',
'is_network_device', 'subdevice_role', 'interface_ordering', 'comments', 'tags',
]
labels = {
'interface_ordering': 'Order interfaces by',
}
Expand Down Expand Up @@ -772,12 +779,13 @@ class DeviceForm(BootstrapMixin, TenancyForm, CustomFieldForm):
)
)
comments = CommentField()
tags = TagField(required=False)

class Meta:
model = Device
fields = [
'name', 'device_role', 'device_type', 'serial', 'asset_tag', 'site', 'rack', 'position', 'face', 'status',
'platform', 'primary_ip4', 'primary_ip6', 'tenant_group', 'tenant', 'comments',
'name', 'device_role', 'tags', 'device_type', 'serial', 'asset_tag', 'site', 'rack', 'position', 'face',
'status', 'platform', 'primary_ip4', 'primary_ip6', 'tenant_group', 'tenant', 'comments',
]
help_texts = {
'device_role': "The function this device serves",
Expand Down
6 changes: 6 additions & 0 deletions netbox/dcim/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible
from mptt.models import MPTTModel, TreeForeignKey
from taggit.managers import TaggableManager
from timezone_field import TimeZoneField

from circuits.models import Circuit
Expand Down Expand Up @@ -161,6 +162,7 @@ class Site(CreatedUpdatedModel, CustomFieldModel):
)

objects = SiteManager()
tags = TaggableManager()

csv_headers = [
'name', 'slug', 'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description', 'physical_address',
Expand Down Expand Up @@ -388,6 +390,7 @@ class Rack(CreatedUpdatedModel, CustomFieldModel):
)

objects = RackManager()
tags = TaggableManager()

csv_headers = [
'site', 'group_name', 'name', 'facility_id', 'tenant', 'role', 'type', 'serial', 'width', 'u_height',
Expand Down Expand Up @@ -746,6 +749,8 @@ class DeviceType(models.Model, CustomFieldModel):
object_id_field='obj_id'
)

tags = TaggableManager()

csv_headers = [
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'is_console_server',
'is_pdu', 'is_network_device', 'subdevice_role', 'interface_ordering', 'comments',
Expand Down Expand Up @@ -1231,6 +1236,7 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
)

objects = DeviceManager()
tags = TaggableManager()

csv_headers = [
'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', 'status',
Expand Down
1 change: 1 addition & 0 deletions netbox/netbox/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
'django_tables2',
'mptt',
'rest_framework',
'taggit',
'timezone_field',
'circuits',
'dcim',
Expand Down
4 changes: 4 additions & 0 deletions netbox/templates/dcim/device.html
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@
{% endif %}
</td>
</tr>
<tr>
<td>Tags</td>
<td>{{ device.tags.all|join:" " }}</td>
</tr>
</table>
</div>
{% if vc_members %}
Expand Down
1 change: 1 addition & 0 deletions netbox/templates/dcim/device_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<div class="panel-body">
{% render_field form.name %}
{% render_field form.device_role %}
{% render_field form.tags %}
</div>
</div>
<div class="panel panel-default">
Expand Down
4 changes: 4 additions & 0 deletions netbox/templates/dcim/devicetype.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ <h1>{% block title %}{{ devicetype.manufacturer }} {{ devicetype.model }}{% endb
<td>Interface Ordering</td>
<td>{{ devicetype.get_interface_ordering_display }}</td>
</tr>
<tr>
<td>Tags</td>
<td>{{ devicetype.tags.all|join:" " }}</td>
</tr>
<tr>
<td>Instances</td>
<td><a href="{% url 'dcim:device_list' %}?device_type_id={{ devicetype.pk }}">{{ devicetype.instances.count }}</a></td>
Expand Down
1 change: 1 addition & 0 deletions netbox/templates/dcim/devicetype_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
{% render_field form.u_height %}
{% render_field form.is_full_depth %}
{% render_field form.interface_ordering %}
{% render_field form.tags %}
</div>
</div>
<div class="panel panel-default">
Expand Down
4 changes: 4 additions & 0 deletions netbox/templates/dcim/rack.html
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ <h1>{% block title %}Rack {{ rack }}{% endblock %}</h1>
{% endif %}
</td>
</tr>
<tr>
<td>Tags</td>
<td>{{ rack.tags.all|join:" " }}</td>
</tr>
<tr>
<td>Devices</td>
<td>
Expand Down
1 change: 1 addition & 0 deletions netbox/templates/dcim/rack_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
{% render_field form.group %}
{% render_field form.role %}
{% render_field form.serial %}
{% render_field form.tags %}
</div>
</div>
<div class="panel panel-default">
Expand Down
4 changes: 4 additions & 0 deletions netbox/templates/dcim/site.html
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ <h1>{% block title %}{{ site }}{% endblock %}</h1>
{% endif %}
</td>
</tr>
<tr>
<td>Tags</td>
<td>{{ site.tags.all|join:" " }}</td>
</tr>
</table>
</div>
<div class="panel panel-default">
Expand Down
1 change: 1 addition & 0 deletions netbox/templates/dcim/site_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
{% render_field form.asn %}
{% render_field form.time_zone %}
{% render_field form.description %}
{% render_field form.tags %}
</div>
</div>
<div class="panel panel-default">
Expand Down
17 changes: 16 additions & 1 deletion netbox/utilities/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from rest_framework.permissions import BasePermission
from rest_framework.relations import PrimaryKeyRelatedField
from rest_framework.response import Response
from rest_framework.serializers import Field, ModelSerializer, ValidationError
from rest_framework.serializers import Field, ModelSerializer, RelatedField, ValidationError
from rest_framework.viewsets import GenericViewSet, ViewSet

WRITE_OPERATIONS = ['create', 'update', 'partial_update', 'delete']
Expand Down Expand Up @@ -42,6 +42,21 @@ def has_permission(self, request, view):
# Fields
#

class TagField(RelatedField):
"""
Represent a writable list of Tags associated with an object (use with many=True).
"""

def to_internal_value(self, data):
obj = self.parent.parent.instance
content_type = ContentType.objects.get_for_model(obj)
tag, _ = Tag.objects.get_or_create(content_type=content_type, object_id=obj.pk, name=data)
return tag

def to_representation(self, value):
return value.name


class ChoiceFieldSerializer(Field):
"""
Represent a ChoiceField as {'value': <DB value>, 'label': <string>}.
Expand Down

0 comments on commit b0dafcf

Please sign in to comment.