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()`, `bulk_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
ObjectBulkCreateViewMixin | bulk_create
ObjectBulkUpdateViewMixin | bulk_update

For example, for a DetailView template for `YourAppModel`, the template name will be `yourapp/yourappmodel_retrieve.html`, for a BulkCreateView template for `yourappmodel`, the template name will be `yourapp/yourappmodel_bulk_create.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)