Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Views #64

Merged
merged 2 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added design_builder/api/__init__.py
Empty file.
41 changes: 41 additions & 0 deletions design_builder/api/nested_serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Nested serializers for design builder."""
from nautobot.core.api import BaseModelSerializer
from rest_framework.relations import HyperlinkedIdentityField

from design_builder.models import Design, DesignInstance, Journal


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"]


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

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

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

model = DesignInstance
fields = ["id", "url", "name"]


class NestedJournalSerializer(BaseModelSerializer):
"""Nested serializer for the journal model."""

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

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

model = Journal
fields = ["id", "url"]
89 changes: 89 additions & 0 deletions design_builder/api/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"""Serializers for design builder."""
from django.contrib.contenttypes.models import ContentType
from drf_spectacular.utils import extend_schema_field
from nautobot.apps.api import NautobotModelSerializer, TaggedModelSerializerMixin
from nautobot.core.api import ContentTypeField
from nautobot.extras.api.nested_serializers import NestedJobResultSerializer
from nautobot.utilities.api import get_serializer_for_model
from rest_framework.fields import SerializerMethodField, DictField
from rest_framework.relations import HyperlinkedIdentityField

from design_builder.models import Design, DesignInstance, Journal, JournalEntry

from design_builder.api.nested_serializers import (
NestedDesignSerializer,
NestedDesignInstanceSerializer,
NestedJournalSerializer,
)


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",
]


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

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

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

model = DesignInstance
fields = [
"id",
"url",
"design",
"name",
"owner",
"first_implemented",
"last_implemented",
]


class JournalSerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
"""Serializer for the journal model."""

url = HyperlinkedIdentityField(view_name="plugins-api:design_builder-api:journal-detail")
design_instance = NestedDesignInstanceSerializer()
job_result = NestedJobResultSerializer()

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

model = Journal
fields = ["id", "url", "design_instance", "job_result"]


class JournalEntrySerializer(NautobotModelSerializer, TaggedModelSerializerMixin):
"""Serializer for the journal entry model."""

url = HyperlinkedIdentityField(view_name="plugins-api:design_builder-api:journalentry-detail")
journal = NestedJournalSerializer()
_design_object_type = ContentTypeField(queryset=ContentType.objects.all(), label="design_object_type")
design_object = SerializerMethodField(read_only=True)

class Meta:
"""Serializer options for the journal entry model."""

model = JournalEntry
fields = ["id", "url", "journal", "_design_object_type", "design_object", "changes", "full_control"]

@extend_schema_field(DictField())
def get_design_object(self, obj):
serializer = get_serializer_for_model(obj.design_object, prefix="Nested")
context = {"request": self.context["request"]}
return serializer(obj.design_object, context=context).data
17 changes: 17 additions & 0 deletions design_builder/api/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""API URLs for design builder."""
from nautobot.core.api import OrderedDefaultRouter
from design_builder.api.views import (
DesignAPIViewSet,
DesignInstanceAPIViewSet,
JournalAPIViewSet,
JournalEntryAPIViewSet,
)

router = OrderedDefaultRouter()

router.register("designs", DesignAPIViewSet)
router.register("design-instances", DesignInstanceAPIViewSet)
router.register("journals", JournalAPIViewSet)
router.register("journal-entries", JournalEntryAPIViewSet)

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

from design_builder.api.serializers import (
DesignSerializer,
DesignInstanceSerializer,
JournalSerializer,
JournalEntrySerializer,
)
from design_builder.filters import DesignFilterSet, DesignInstanceFilterSet, JournalFilterSet, JournalEntryFilterSet
from design_builder.models import Design, DesignInstance, Journal, JournalEntry


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

queryset = Design.objects.all()
serializer_class = DesignSerializer
filterset_class = DesignFilterSet


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

queryset = DesignInstance.objects.all()
serializer_class = DesignInstanceSerializer
filterset_class = DesignInstanceFilterSet


class JournalAPIViewSet(NautobotModelViewSet):
"""API views for the journal model."""

queryset = Journal.objects.all()
serializer_class = JournalSerializer
filterset_class = JournalFilterSet


class JournalEntryAPIViewSet(NautobotModelViewSet):
"""API views for the journal entry model."""

queryset = JournalEntry.objects.all()
serializer_class = JournalEntrySerializer
filterset_class = JournalEntryFilterSet
71 changes: 71 additions & 0 deletions design_builder/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""Filters for the design builder app."""
from nautobot.apps.filters import NautobotFilterSet, NaturalKeyOrPKMultipleChoiceFilter
from nautobot.extras.models import Job, JobResult

from design_builder.models import Design, DesignInstance, Journal, JournalEntry


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"]


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

design = NaturalKeyOrPKMultipleChoiceFilter(
queryset=Design.objects.all(),
label="Design (ID or slug)",
)

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

model = DesignInstance
fields = ["id", "design", "name", "owner", "first_implemented", "last_implemented"]


class JournalFilterSet(NautobotFilterSet):
"""Filter set for the journal model."""

design_instance = NaturalKeyOrPKMultipleChoiceFilter(
queryset=DesignInstance.objects.all(),
label="Design Instance (ID)",
)

job_result = NaturalKeyOrPKMultipleChoiceFilter(
queryset=JobResult.objects.all(),
label="Job Result (ID)",
)

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

model = Journal
fields = ["id", "design_instance", "job_result"]


class JournalEntryFilterSet(NautobotFilterSet):
"""Filter set for the journal entrymodel."""

journal = NaturalKeyOrPKMultipleChoiceFilter(
queryset=Journal.objects.all(),
label="Journal (ID)",
)

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

model = JournalEntry
# TODO: Support design_object somehow?
fields = ["id", "journal", "changes", "full_control"]
49 changes: 49 additions & 0 deletions design_builder/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""Forms for the design builder app."""
from django.forms import NullBooleanField
from nautobot.extras.forms import NautobotFilterForm
from nautobot.extras.models import Job, JobResult
from nautobot.utilities.forms import TagFilterField, DynamicModelChoiceField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES

from design_builder.models import Design, DesignInstance, Journal, JournalEntry


class DesignFilterForm(NautobotFilterForm):
"""Filter form for the design model."""

model = Design

job = DynamicModelChoiceField(queryset=Job.objects.all())
tag = TagFilterField(model)


class DesignInstanceFilterForm(NautobotFilterForm):
"""Filter form for the design instance model."""

model = DesignInstance

design = DynamicModelChoiceField(queryset=Design.objects.all())
tag = TagFilterField(model)


class JournalFilterForm(NautobotFilterForm):
"""Filter form for the journal model."""

model = Journal

design_instance = DynamicModelChoiceField(queryset=DesignInstance.objects.all())
job_result = DynamicModelChoiceField(queryset=JobResult.objects.all())
tag = TagFilterField(model)


class JournalEntryFilterForm(NautobotFilterForm):
"""Filter form for the journal entry model."""

model = JournalEntry

journal = DynamicModelChoiceField(queryset=Journal.objects.all())
full_control = NullBooleanField(
required=False,
label="Does the design have full control over the object?",
widget=StaticSelect2(choices=BOOLEAN_WITH_BLANK_CHOICES),
)
tag = TagFilterField(model)
10 changes: 7 additions & 3 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-08 13:34

import django.core.serializers.json
from django.db import migrations, models
Expand Down Expand Up @@ -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(blank=True, max_length=100, null=True)),
("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
18 changes: 13 additions & 5 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,9 +115,9 @@ 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)
owner = models.CharField(max_length=100, blank=True, null=True)
first_implemented = models.DateTimeField(blank=True, null=True)
last_implemented = models.DateTimeField(blank=True, null=True)

Expand All @@ -141,8 +141,8 @@ def clean(self):
enforce_managed_fields(self, ["design"], message="is a field that cannot be changed")

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

def __str__(self):
"""Stringify instance."""
Expand All @@ -166,6 +166,10 @@ class Journal(PrimaryModel):
design_instance = models.ForeignKey(to=DesignInstance, on_delete=models.CASCADE, editable=False)
job_result = models.ForeignKey(to=JobResult, on_delete=models.PROTECT, editable=False)

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

@property
def user_input(self):
"""Get the user input provided when the job was run.
Expand Down Expand Up @@ -208,3 +212,7 @@ class JournalEntry(PrimaryModel):
design_object = ct_fields.GenericForeignKey(ct_field="_design_object_type", fk_field="_design_object_id")
changes = models.JSONField(encoder=NautobotKombuJSONEncoder, editable=False, null=True, blank=True)
full_control = models.BooleanField(editable=False)

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