Netbox Entity Relationship Diagrams Deep Dive


When you’re working with a relational database, you’ll need to create some kind of Entity Relationship Diagram (ERD) at some point. ERDs are helpful in visualizing the relationships between database tables and can be a helpful tool for designing or reverse-engineering databases.
This project creates a new tab on all netbox model detail views.

When selected, this produces the Entity Relationship Diagram for that model and its related models.

Heres how I did it.
Dependencies
Using Netbox 3.4.4, I added these dependencies using the poetry tool. https://python-poetry.org/docs/cli/#add
python = "^3.8 graphviz = "^0.20.1" django-extensions = "^3.2.1" pydot = "^1.4.2" pygraphviz = "^1.10""
__init__
The required django_extensions package can now be configured in the plugin’s __init__ file.
... django_apps = ["django_extensions"] ...
Template
Nothing special here but note the word safe. This is required to render the xml file. If omitted, only the string is shown.
{% extends 'base/layout.html' % {% block header %}Entity Relationship Diagram
{% endblock header %} {% block content %}{{ xml_str | safe }}{% endblock %} }
View
Here’s the imports.
import django.app import pygraphviz from django.apps import apps from django.contrib.auth.mixins import PermissionRequiredMixin from django_extensions.management.modelviz import ModelGraph, generate_dot, os, loader from django.shortcuts import render from django.views.generic import View from re import search from urllib.parse import urlparse from utilities.views import ViewTab from netbox.registry import registrys
The ERD view is created for numerous (most) netbox models. Each need to be registered with netbox. I looped models and further looked for the User facing ones. I then made the registration.
for model in django.apps.apps.get_models() """Register Models.""" if model._meta.app_label in ["circuits", "wireless", "ipam", "tenancy", "dcim", "virtualization"]: app_label = model._meta.app_label model_name = model._meta.model_name if model_name not in registry["views"][app_label]: registry["views"][app_label][model_name] = [] registry["views"][app_label][model_name].append( { "name": "erd", "view": "netbox_erd.views.EntityRelationshipDiagramView", "path": "../../../plugins/erd", "kwargs": {}, } ):
Here’s the class for the view. The tab is defined here, with the permissions, as per this:
https://docs.netbox.dev/en/stable/plugins/development/views/
class EntityRelationshipDiagramView(PermissionRequiredMixin, View) """View for Entity Relationship Diagram.""" tab = ViewTab(label="Entity Relationship Diagram", permission="netbox_erd.erd") permission_required = "netbox_erd.erd" queryset = None template_name = "netbox_erd/erd.html" def get(self, request): """Display Entity Relationship Diagram.""":
First the Model that the User was previously viewing is found. The app_label and model_name are recovered from the http referer in the request.
# Get the Model that the User was previously viewin referer = request.META.get("HTTP_REFERER") app_label = urlparse(referer).path.split("/")[-4] model_name = urlparse(referer).path.split("/")[-3].replace("-", "").capitalize() if search("ses$", model_name): model_name = urlparse(referer).path.split("/")[-3][:-2].replace("-", "").capitalize() else: model_name = urlparse(referer).path.split("/")[-3][:-1].replace("-", "").capitalize()g
Next a list of related Models to include in the graph is created. This gives the users last viewed model and its related models, that is the relationships from the models Foreign Keys.
# Create a list of related Models to include in the graph include_models = [] include_models.append(model_name) model = apps.get_model(app_label=app_label, model_name=model_name) for relation in model._meta.related_objects: include_models.append(relation.related_model.__name__.capitalize())h
The modelviz ModelGraph is created. This is the minimal required with default values. Here’s the link:
https://github.com/django-extensions/django-extensions/blob/9cc676b98596fc7f716be752bb89f943ea81f234/django_extensions/management/modelviz.py#L73
# Create the Model Graph args = [] cli_options = {} options = { "pygraphviz": True, "all_applications": True, "arrow_shape": "normal", "include_models": include_models, } graph_models = ModelGraph(args, cli_options=cli_options, **options)h
dot file creation
graph_models.generate_graph_data( graph_data = graph_models.get_graph_data(as_json=False) theme = "django2018" template_name = os.path.join("django_extensions", "graph_models", theme, "digraph.dot") template = loader.get_template(template_name) dotdata = generate_dot(graph_data, template=template))
Finally, the xml containing SVG is created for use in the template.
# Create the xml for display graph = pygraphviz.AGraph(dotdata) graph.layout(prog="dot") xml_bytes = graph.draw(format="svg") xml_str = xml_bytes.decode() return render( request, self.template_name, { "xml_str": xml_str, }, )
That’s it.