Skip to content

NautobotUIViewSet

Added in version 1.4.0

New in Nautobot 1.4 is the debut of NautobotUIViewSet: A powerful app development tool that can save app developer hundreds of lines of code compared to using legacy generic.views. Using it to gain access to default functionalities previous provided by generic.views such as create(), update(), partial_update(), bulk_update(), destroy(), bulk_destroy(), retrieve() and list() actions.

Note that this ViewSet is catered specifically to the UI, not the API.

Concrete examples on how to use NautobotUIViewSet resides in nautobot.circuits.views.

Below we provide an example on how to use NautobotUIViewSet on a theoretical app model.

from nautobot.apps.views import NautobotUIViewSet
from yourapp import filters, forms, models, tables
from yourapp.api import serializers

class YourAppModelUIViewSet(NautobotUIViewSet):
    bulk_update_form_class = forms.YourAppModelBulkEditForm
    filterset_class = filters.YourAppModelFilterSet
    filterset_form_class = forms.YourAppModelFilterForm
    form_class = forms.YourAppModelForm
    queryset = models.YourAppModel.objects.all()
    serializer_class = serializers.YourAppModelSerializer
    table_class = tables.YourAppModelTable

Setting ViewSet Attributes

One caveat of using the NautobotUIViewSet is that the queryset, serializer_class and table_class attribute of the YourAppModelUIViewSet has to be set before most of the NautobotUIViewSet functionalities will become available.

By default the URL patterns generated by a NautobotUIViewSet are based on the model's pk (/model-name/<pk>/ for the detail view, /model-name/<pk>/edit/ for the edit view, etc.). if you need to use a different field to look up an object, just override the default lookup_field in your ViewSet attributes:

from nautobot.apps.views import NautobotUIViewSet

class YourAppModelUIViewSet(NautobotUIViewSet):
    ...
    lookup_field = "slug"
    ...
Changed in version 2.0.0

The default lookup_field for NautobotUIViewSet has been changed from "slug" to "pk".

Note

Using a field other than the default pk or the alternative field slug (as shown in the example above), may result in certain pieces of the UI not displaying (for example, the edit and delete buttons on the object detail view). This is due to the URL expecting a named key of slug or pk, rather than id.

View Template Context

Templates can benefit from a very rich context passed down from the views and renderer, including forms, tables, as well as any other information that may be helpful for rendering templates. The keys it provides are as follows:

  • content_type: The ContentType object for the associated model
  • filter_form: The FilterForm object for the associated model
  • form: A Form object for the associated model if relevant (None for list and detail/retrieve views)
  • object: An instance of the associated mode if available (None for list and bulk operation views)
  • permissions: Summary of user permissions for the given model
  • return_url: The relevant return URL
  • table: A Table object for the associated model if relevant (None for detail/retrieve and update views)
  • table_config_form: A TableConfigForm object for the associated table, providing the ability to customize the table
  • verbose_name: The singular form of the model's name
  • verbose_name_plural: The plural form of the model's name

An example from editing a Provider object:

{
    'content_type': <ContentType: circuits | provider>,
    'filter_form': <ProviderFilterForm bound=True, valid=Unknown, fields=(location;q;asn;tag)>,
    'form': <ProviderForm bound=False, valid=Unknown, fields=(name;asn;account;portal_url;noc_contact;admin_contact;comments;tags;object_note)>,
    'object': <Provider: NautobotProvider>,
    'permissions': {'add': True, 'change': True, 'delete': True, 'view': True},
    'return_url': '/circuits/providers/nautobotprovider',
    'table': None,
    'table_config_form': None,
    'verbose_name': 'provider',
    'verbose_name_plural': 'providers'
}

Other context keys may be available for certain views:

  • editing: Provided for create and update views to help the template determine if this is a new or existing object
  • action_buttons: Provided for the list view for the top of table buttons (such as "Add" and "Export")

You may see other context keys as well, but any not documented above should not be relied upon as they may be removed in a future release. Some examples of those are:

  • obj: Please use object instead
  • obj_type: Please use verbose_name instead
  • obj_type_plural: Please use verbose_name_plural instead
Removed in version 2.0.0

The changelog_url context key was removed. Use object.get_changelog_url instead.

Excluding ViewMixins from NautobotUIViewSet

For app models that do not require certain views, simply inherit directly from the ViewMixin classes available in nautobot.apps.views instead of NautobotUIViewSet.

Concrete examples for excluding ViewMixins, checkout CircuitTerminationUIViewSet and CircuitTypeUIViewSet in nautobot.circuits.views.

## An app model viewset that does not support bulk views and operations
import nautobot.apps.views

class YourAppModelUIViewSet(
    nautobot.apps.views.ObjectListViewMixin,
    nautobot.apps.views.ObjectDetailViewMixin,
    nautobot.apps.views.ObjectEditViewMixin,
    nautobot.apps.views.ObjectDestroyViewMixin,
):

    filterset_class = YourAppModelFilterSet
    filterset_form_class = YourAppModelFilterForm
    form_class = YourAppModelForm
    queryset = YourAppModel.objects.all()
    serializer_class = serializers.YourAppModelSerializer
    table_class = YourAppModelTable
    # You do not need to specify attributes that are not needed.

Excluding unwanted urls from NautobotUIViewSetRouter is done for you at the ViewSet level. If you do not inherit the unwanted ViewMixins, the corresponding route from the router will not be published.

# urls.py
# All the urls correspond to BulkViewMixins will not be published when you register your ViewSet with the router.
router.register("yourappmodel", views.YourAppModelUIViewSet)

Template Naming for NautobotUIViewSet

Template naming is very intuitive in NautobotUIViewSet. In templates/yourapp folder, name your templates following the convention {model_name}_{action}.html.

ViewMixins action
ObjectListViewMixin list
ObjectDetailViewMixin retrieve
ObjectEditViewMixin create/update
ObjectDestroyViewMixin destroy
ObjectBulkDestroyViewMixin bulk_destroy
ObjectBulkUpdateViewMixin bulk_update
Removed in version 2.2.0

ObjectBulkCreateViewMixin is deprecated as its functionality has been replaced by a system Job. It will be removed from the code base entirely in Nautobot 3.0.

For example, for a DetailView template for YourAppModel, the template name will be yourapp/yourappmodel_retrieve.html, for a BulkUpdateView template for yourappmodel, the template name will be yourapp/yourappmodel_bulk_update.html and etc.

If you do not provide your own templates in the yourapp/templates/yourapp folder, NautobotUIViewSet will fall back to generic/object_{self.action}.html.

Since in many cases the create and update templates for a model will be identical, you are not required to create both. If you provide a {app_label}/{model_opts.model_name}_create.html file but not a {app_label}/{model_opts.model_name}_update.html file, then when you update an object, it will fall back to {app_label}/{model_opts.model_name}_create.html and vice versa.

Adding Custom Views To NautobotUIViewSet & NautobotUIViewSetRouter

Changed in version 1.6.0

Via PR #4045, notes and changelog views provided by mixins have now been moved to this pattern.

Django REST Framework provides the ability to decorate a method on a ViewSet with @action(detail=True) to add the method as a view to the ViewSetRouter. This method must return a fully rendered HTML view.

Below is an example of adding a custom action view to an App's ViewSet. A few considerations to keep in mind:

  • The method name is the action in Django REST Framework terms.
  • The action will be used for template lookup
  • The action will be used for URL naming and construction (plugins:<app>:<model>_<action>, /plugins/<app>/<model>/<uuid>/<action>/).
  • The action will be used as a custom permission that users must have (<app>.<action>_<model>)

In the below example:

  • The expected template must be named yourapp/yourappmodel_customview.html
  • The reversible URL name will be plugins:yourapp:yourappmodel_customview
  • The URL pattern will be /plugins/yourapp/yourappmodel/<uuid>/customview/
  • Users will need yourapp.customview_yourappmodel permission to access this view.
from nautobot.apps.views import NautobotUIViewSet
from rest_framework.decorators import action
from rest_framework.response import Response

from yourapp import filters, forms, models, tables
from yourapp.api import serializers

class YourAppModelUIViewSet(NautobotUIViewSet):
    bulk_update_form_class = forms.YourAppModelBulkEditForm
    filterset_class = filters.YourAppModelFilterSet
    filterset_form_class = forms.YourAppModelFilterForm
    form_class = forms.YourAppModelForm
    queryset = models.YourAppModel.objects.all()
    serializer_class = serializers.YourAppModelSerializer
    table_class = tables.YourAppModelTable

    @action(detail=True)
    def customview(self, request, *args, **kwargs):
        """Context passed to template for rendering.

        Expected URL pattern will be `/plugins/yourapp/yourappmodel/<uuid>/customview/`
        """
        context = {
            "some_key": "some value",
        }
        return Response(context)