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.