Skip to content

Adding Database Models

If your app introduces a new type of object in Nautobot, you'll probably want to create a Django model for it. A model is essentially a Python representation of a database table, with attributes that represent individual columns. Model instances can be created, manipulated, and deleted using queries. Models must be defined within a file named models.py.

It is highly recommended to have app models inherit from at least nautobot.apps.models.BaseModel which provides base functionality and convenience methods common to all models.

For more advanced usage, you may want to instead inherit from one of Nautobot's "generic" models derived from BaseModel -- nautobot.apps.models.OrganizationalModel or nautobot.apps.models.PrimaryModel. The inherent capabilities provided by inheriting from these various parent models differ as follows:

Feature django.db.models.Model BaseModel OrganizationalModel PrimaryModel
UUID primary key
Natural keys
Object permissions
validated_save()
Object Metadata
Change logging
Contacts and Teams
Custom fields
Dynamic Groups
Notes
Relationships
Saved Views
Tags
Added in version 2.2.0 — Support for Contact and Team assignment on all models

Support for Contact and Team assignment to all Nautobot model types was added.

Added in version 2.3.0 — Support for Object Metadata assignment on all models

Support for assigning Object Metadata was added to BaseModel (and therefore also OrganizationalModel and PrimaryModel) subclasses. If a specific model should not support assignment of metadata to its records (for example, a many-to-many "through" table model such as CloudNetworkPrefixAssignment), the model author can define the class attribute is_metadata_associable_model = False to opt it out from this feature.

Changed in version 2.3.0 — Support for Contact and Team assignment on OrganizationalModel and PrimaryModel only

Default support for Contact and Team assignment was removed from django.db.models.Model and BaseModel. The mixin class ContactMixin has been added to be used by BaseModel subclasses that want to be assignable to Contacts and Teams. All subclasses of OrganizationalModel and PrimaryModel include this mixin and therefore default to supporting Contact and Team assignment. Models can opt out of this feature by declaring the class attribute is_contact_associable_model = False.

Added in version 2.3.0 — Support for Dynamic Groups and Saved Views on OrganizationalModel and PrimaryModel

Support for Dynamic Groups and Saved Views was added to OrganizationalModel and PrimaryModel. The mixin classes DynamicGroupsModelMixin and SavedViewMixin (included in both of those base classes) have been added to be used by BaseModel subclasses that want to be assignable to Dynamic Groups and/or to be Saved View capable. Models can opt out of either of these features by declaring is_dynamic_group_associable_model = False and/or is_saved_view_model = False as applicable.

Changed in version 2.3.0 — Replacement of DynamicGroupMixin with DynamicGroupsModelMixin

In previous Nautobot releases, a model could opt in to support of Dynamic Groups by including the DynamicGroupMixin mixin class. This class is now deprecated, and models should use the newly added DynamicGroupsModelMixin mixin class in its place.

Added in version 2.4.41 — Support for sensitive fields on all models

A BaseModel subclass can declare fields holding credentials or comparable secrets in the class attribute sensitive_fields. The ORM then refuses to return those values unless a caller opts in explicitly, and user-authored Jinja2 templates can never read them. Filtering by such a field still works. See Sensitive Model Fields for the opt-in methods and the limitations.

The ORM-side enforcement is governed by STRICT_SENSITIVE_FIELDS, which defaults to False so that existing code reading such a field keeps working, and is expected to default to True in a future major release. Denial of template access is not governed by that setting and always applies. An App that declares sensitive_fields should therefore not assume the ORM enforcement is active on a given deployment, and should move its own reads to the opt-in methods now so that it keeps working when the default changes.

Below is an example models.py file containing a basic model with two character fields:

# models.py
from django.db import models

from nautobot.apps.models import BaseModel


class Animal(BaseModel):
    """Base model for animals."""

    name = models.CharField(max_length=50)
    sound = models.CharField(max_length=50)

    def __str__(self):
        return self.name

    class Meta:
        unique_together = [["name", "sound"]]

Once you have defined the model(s) for your app, you'll need to create the database schema migrations. A migration file is essentially a set of instructions for manipulating the database to support your new model, or to alter existing models.

Creating migrations can be done automatically using the nautobot-server makemigrations <app_name> management command, where <app_name> is the name of the Python package for your app (e.g. nautobot_animal_sounds):

nautobot-server makemigrations nautobot_animal_sounds

Note

An app must be installed before it can be used with Django management commands. If you skipped this step above, run poetry install from the app's root directory.

nautobot-server makemigrations nautobot_animal_sounds

Example output:

Migrations for 'nautobot_animal_sounds':
  /home/bjones/animal_sounds/nautobot_animal_sounds/migrations/0001_initial.py
    - Create model Animal

Next, apply the migration to the database with the nautobot-server migrate <app_name> command:

nautobot-server migrate nautobot_animal_sounds

Example output:

Operations to perform:
  Apply all migrations: nautobot_animal_sounds
Running migrations:
  Applying nautobot_animal_sounds.0001_initial... OK

For more background on schema migrations, see the Django documentation.