Skip to content

Commit

Permalink
feat: implements view for design model
Browse files Browse the repository at this point in the history
  • Loading branch information
Kircheneer committed Aug 7, 2023
1 parent 24a473a commit 02e78c4
Show file tree
Hide file tree
Showing 17 changed files with 1,173 additions and 843 deletions.
Empty file added design_builder/api/__init__.py
Empty file.
17 changes: 17 additions & 0 deletions design_builder/api/nested_serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Nested serializers for design builder."""
from nautobot.core.api import BaseModelSerializer
from rest_framework.relations import HyperlinkedIdentityField

from design_builder.models import Design


class NestedDesignSerializer(BaseModelSerializer):
"""Nested serializer for the design model."""

url = HyperlinkedIdentityField(view_name="plugins-api:design_builder-api:design-detail")

class Meta:
"""Nested serializer options for the design model."""

model = Design
fields = ["id", "url", "name"]
27 changes: 27 additions & 0 deletions design_builder/api/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Serializers for design builder."""
from nautobot.apps.api import NautobotModelSerializer, TaggedModelSerializerMixin
from rest_framework.relations import HyperlinkedIdentityField

from design_builder.models import Design

# We need to import the nested serializers, so they can be auto-discovered as the serializer for the ?brief
# URL parameter variant.
from design_builder.api.nested_serializers import ( # pylint: disable=unused-import # noqa: F401
NestedDesignSerializer,
)


class DesignSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
"""Serializer for the design model."""

url = HyperlinkedIdentityField(view_name="plugins-api:design_builder-api:design-detail")

class Meta:
"""Serializer options for the design model."""

model = Design
fields = [
"id",
"url",
"name",
]
9 changes: 9 additions & 0 deletions design_builder/api/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""API URLs for design builder."""
from nautobot.core.api import OrderedDefaultRouter
from design_builder.api.views import DesignViewSet

router = OrderedDefaultRouter()

router.register("designs", DesignViewSet)

urlpatterns = router.urls
14 changes: 14 additions & 0 deletions design_builder/api/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""UI Views for design builder."""
from nautobot.extras.api.views import NautobotModelViewSet

from design_builder.api.serializers import DesignSerializer
from design_builder.filters import DesignFilterSet
from design_builder.models import Design


class DesignViewSet(NautobotModelViewSet):
"""API views for the design model."""

queryset = Design.objects.all()
serializer_class = DesignSerializer
filterset_class = DesignFilterSet
19 changes: 19 additions & 0 deletions design_builder/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from nautobot.apps.filters import NautobotFilterSet, NaturalKeyOrPKMultipleChoiceFilter
from nautobot.extras.models import Job

from design_builder.models import Design


class DesignFilterSet(NautobotFilterSet):
"""Filter set for the design model."""

job = NaturalKeyOrPKMultipleChoiceFilter(
queryset=Job.objects.all(),
label="Job (ID or slug)",
)

class Meta:
"""Meta attributes for filter."""

model = Design
fields = ["id", "job"]
12 changes: 12 additions & 0 deletions design_builder/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from nautobot.extras.forms import NautobotFilterForm, NautobotModelForm
from nautobot.extras.models import Job
from nautobot.utilities.forms import TagFilterField, DynamicModelChoiceField

from design_builder.models import Design


class DesignFilterForm(NautobotFilterForm):
model = Design

job = DynamicModelChoiceField(queryset=Job.objects.all())
tag = TagFilterField(model)
12 changes: 8 additions & 4 deletions design_builder/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 3.2.20 on 2023-07-28 18:51
# Generated by Django 3.2.20 on 2023-08-07 18:25

import django.core.serializers.json
from django.db import migrations, models
Expand All @@ -14,8 +14,8 @@ class Migration(migrations.Migration):
initial = True

dependencies = [
("extras", "0058_jobresult_add_time_status_idxs"),
("contenttypes", "0002_remove_content_type_name"),
("extras", "0058_jobresult_add_time_status_idxs"),
]

operations = [
Expand All @@ -36,7 +36,7 @@ class Migration(migrations.Migration):
),
(
"job",
models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to="extras.job"),
models.ForeignKey(editable=False, on_delete=django.db.models.deletion.PROTECT, to="extras.job"),
),
(
"status",
Expand Down Expand Up @@ -71,12 +71,16 @@ class Migration(migrations.Migration):
models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
),
("name", models.CharField(max_length=100)),
("owner", models.CharField(max_length=100)),
("first_implemented", models.DateTimeField(blank=True, null=True)),
("last_implemented", models.DateTimeField(blank=True, null=True)),
(
"design",
models.ForeignKey(
editable=False, on_delete=django.db.models.deletion.PROTECT, to="design_builder.design"
editable=False,
on_delete=django.db.models.deletion.PROTECT,
related_name="instances",
to="design_builder.design",
),
),
("tags", taggit.managers.TaggableManager(through="extras.TaggedItem", to="extras.Tag")),
Expand Down
4 changes: 2 additions & 2 deletions design_builder/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def name(self):

def get_absolute_url(self):
"""Return detail view for Designs."""
return reverse("plugins:design_builder:design", args=[self.name])
return reverse("plugins:design_builder:design", args=[self.pk])

def __str__(self):
"""Stringify instance."""
Expand All @@ -115,7 +115,7 @@ class DesignInstance(PrimaryModel):

# TODO: add version field to indicate which version of a design
# this instance is on. (future feature)
design = models.ForeignKey(to=Design, on_delete=models.PROTECT, editable=False)
design = models.ForeignKey(to=Design, on_delete=models.PROTECT, editable=False, related_name="instances")
name = models.CharField(max_length=100)
owner = models.CharField(max_length=100)
first_implemented = models.DateTimeField(blank=True, null=True)
Expand Down
27 changes: 27 additions & 0 deletions design_builder/navigation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from nautobot.apps.ui import (
NavMenuGroup,
NavMenuItem,
NavMenuTab,
)


menu_items = (
NavMenuTab(
name="Jobs",
weight=150,
groups=(
NavMenuGroup(
name="Designs",
weight=100,
items=(
NavMenuItem(
link="plugins:design_builder:design_list",
name="Designs",
permissions=["design_builder.view_designs"],
buttons=(),
),
),
),
),
),
)
21 changes: 21 additions & 0 deletions design_builder/tables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Tables for design builder."""
from django_tables2 import Column
from django_tables2.utils import Accessor
from nautobot.apps.tables import StatusTableMixin, BaseTable
from nautobot.utilities.tables import ToggleColumn

from design_builder.models import Design


class DesignTable(StatusTableMixin, BaseTable):
"""Table for list view."""

pk = ToggleColumn()
job = Column(linkify=True)
name = Column(linkify=True)

class Meta(BaseTable.Meta):
"""Meta attributes."""

model = Design
fields = ("pk", "name", "job", "status")
22 changes: 22 additions & 0 deletions design_builder/templates/design_builder/design_retrieve.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{% extends 'generic/object_retrieve.html' %}
{% load helpers %}

{% block content_left_page %}
<div class="panel panel-default">
<div class="panel-heading">
<strong>Design</strong>
</div>
<table class="table table-hover panel-body attr-table">
<tr>
<td>Status</td>
<td>
<span class="label" style="color: {{ object.status.color|fgcolor }}; background-color: #{{ object.status.color }}">{{ object.get_status_display }}</span>
</td>
</tr>
<tr>
<td>Job</td>
<td>{{ object.job|hyperlinked_object }}</td>
</tr>
</table>
</div>
{% endblock content_left_page %}
29 changes: 29 additions & 0 deletions design_builder/tests/test_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import unittest

from nautobot.extras.models import Job
from nautobot.utilities.testing import APIViewTestCases

from design_builder.models import Design


class DesignTest(
APIViewTestCases.GetObjectViewTestCase,
APIViewTestCases.ListObjectsViewTestCase,
APIViewTestCases.NotesURLViewTestCase,
):
model = Design
brief_fields = ["display", "id", "name", "url"]

@classmethod
def setUpTestData(cls):
job_1 = Job.objects.create(name="Fake Design Job 1")
job_2 = Job.objects.create(name="Fake Design Job 2")
job_3 = Job.objects.create(name="Fake Design Job 3")
Design.objects.create(job=job_1)
Design.objects.create(job=job_2)
Design.objects.create(job=job_3)

@unittest.skip
def test_list_objects_unknown_filter_no_strict_filtering(self):
"""Currently broken, don't know why."""
pass
28 changes: 28 additions & 0 deletions design_builder/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import unittest

from django.test.utils import override_settings
from nautobot.extras.models import Job
from nautobot.utilities.testing import ViewTestCases

from design_builder.models import Design


class DesignTestCase(
ViewTestCases.GetObjectViewTestCase,
ViewTestCases.GetObjectChangelogViewTestCase,
ViewTestCases.GetObjectNotesViewTestCase,
ViewTestCases.ListObjectsViewTestCase,
):
model = Design

@classmethod
def setUpTestData(cls):
job_1 = Job.objects.create(name="Fake Design Job 1")
job_2 = Job.objects.create(name="Fake Design Job 2")
Design.objects.create(job=job_1)
Design.objects.create(job=job_2)

@unittest.skip
def test_list_objects_unknown_filter_no_strict_filtering(self):
"""Broken, don't know why."""
pass
9 changes: 9 additions & 0 deletions design_builder/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""UI URLs for design builder."""
from nautobot.core.views.routers import NautobotUIViewSetRouter

from design_builder.views import DesignUIViewSet

router = NautobotUIViewSetRouter()
router.register("designs", DesignUIViewSet)

urlpatterns = router.urls
30 changes: 30 additions & 0 deletions design_builder/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""UI Views for design builder."""
from nautobot.core.views.mixins import (
ObjectDetailViewMixin,
ObjectListViewMixin,
ObjectChangeLogViewMixin,
ObjectNotesViewMixin,
)

from design_builder.api.serializers import DesignSerializer
from design_builder.filters import DesignFilterSet
from design_builder.forms import DesignFilterForm
from design_builder.models import Design
from design_builder.tables import DesignTable


class DesignUIViewSet(
ObjectDetailViewMixin,
ObjectListViewMixin,
ObjectChangeLogViewMixin,
ObjectNotesViewMixin,
):
"""UI views for the design model."""

filterset_class = DesignFilterSet
filterset_form_class = DesignFilterForm
queryset = Design.objects.all()
serializer_class = DesignSerializer
table_class = DesignTable
action_buttons = ()
lookup_field = "pk"
Loading

0 comments on commit 02e78c4

Please sign in to comment.