Skip to content

Populating Extensibility Features

Added in version 1.2.0

In many cases, an app may wish to make use of Nautobot's various extensibility features, such as custom fields or relationships. It can be useful for an app to automatically create a custom field definition or relationship definition as a consequence of being installed and activated, so that everyday usage of the app can rely upon these definitions to be present.

To make this possible, Nautobot provides a custom signal, nautobot_database_ready, that apps can register to listen for. This signal is triggered when nautobot-server migrate or nautobot-server post_upgrade is run after installing an app, and provides an opportunity for the app to make any desired additions to the database at this time.

For example, maybe we want our app to make use of a Relationship allowing each Location to be linked to our Animal model. We would define our callback function that makes sure this Relationship exists, by convention in a signals.py file:

# signals.py

from nautobot.extras.choices import RelationshipTypeChoices

def create_location_to_animal_relationship(sender, apps, **kwargs):
    """Create a Location-to-Animal Relationship if it doesn't already exist."""
    # Use apps.get_model to look up Nautobot core models
    ContentType = apps.get_model("contenttypes", "ContentType")
    Relationship = apps.get_model("extras", "Relationship")
    Location = apps.get_model("dcim", "Location")
    # Use sender.get_model to look up models from this app
    Animal = sender.get_model("Animal")

    # Ensure that the Relationship exists
    Relationship.objects.update_or_create(
        key="location_favorite_animal",
        defaults={
            "label": "Location's Favorite Animal",
            "type": RelationshipTypeChoices.TYPE_ONE_TO_MANY,
            "source_type": ContentType.objects.get_for_model(Animal),
            "source_label": "Locations that love this Animal",
            "destination_type": ContentType.objects.get_for_model(Location),
            "destination_label": "Favorite Animal",
        },
    )

Then, in the NautobotAppConfig ready() function, we connect this callback function to the nautobot_database_ready signal:

# __init__.py

from nautobot.apps import nautobot_database_ready, NautobotAppConfig

from .signals import create_location_to_animal_relationship

class AnimalSoundsConfig(NautobotAppConfig):
    # ...

    def ready(self):
        super().ready()
        nautobot_database_ready.connect(create_location_to_animal_relationship, sender=self)

config = AnimalSoundsConfig

Warning

It is crucial that you add the sender=self parameter to the connect method call - otherwise your signal handler will run as many times as the NautobotAppConfig.ready method is called, which may be a lot of times.

After writing this code, run nautobot-server migrate or nautobot-server post_upgrade, then restart the Nautobot server, and you should see that this custom Relationship has now been automatically created.