Extending the App¶
This page documents the extension points — how to add new fields, new models, new helpers, or new report views without breaking existing behavior.
Adding a new field to Contract¶
- Edit
src/nautobot_contract_models/models/contract.pyand add the field - Generate a migration with
nautobot-server makemigrations nautobot_contract_models(or hand-write it if you hit the UID-mismatch issue) - Add the field to
forms/contract.py(ContractForm.Meta.fields) so the form picks it up - If the field uses choices, define a
ChoiceSetinchoices.pyand reference it - Add a column to
tables/contract.pyif it should appear in the list view - Add it to
filters/contract.pyMeta.fieldsso the FilterSet picks it up (this also makes it available via the REST API) - The detail view uses
ObjectFieldsPanel(fields="__all__")so the field appears automatically - The CSV import surface is auto-generated from the serializer — no extra wiring needed
- Add a test exercising any new behavior
Adding a new model¶
- Create
src/nautobot_contract_models/models/<name>.pysubclassingBaseModel(telemetry / write-once data) orPrimaryModel(UI-managed with ChangeLog / Tags / Relationships) - Add
@extras_features(...)for the features the model needs (graphql,webhooks,custom_fields, etc.) - Add it to
models/__init__.py's import list and__all__ - Generate the migration
- If the model needs a UI, add a viewset to
views/<name>.py— subclassNautobotUIViewSetfor full CRUD orTemplateViewfor non-CRUD reports - Wire URLs in
urls.py— userouter.register(...)for CRUD orpath(...)under a sibling prefix likereports/ - Add a serializer + filter set + API viewset (the read-only
CostSnapshotsetup is a good reference) - Add to the navigation menu in
navigation.py
Adding a new helper¶
The plugin separates helpers by concern:
cost.py— anything about money (burn rate, renewal forecast, snapshots)priority.py— action-priority rubric (URGENT / WARNING / INFO)helpers.py— cross-cutting queries (transitive coverage, etc.)
Add to whichever file matches the concern; create a new file if the concern is genuinely new. Tests live in the matching tests/test_<name>.py file.
Adding a new dashboard panel¶
- Write the data callable in
homepage.py— receivesrequest, returns a value (or dict for multiple values) - Create the template under
templates/nautobot_contract_models/inc/<name>_panel.html - Append a
HomePagePanel(...)to thelayouttuple inhomepage.py - Set
weightto position the panel — lower number = higher in the column
The Cost Summary panel is a good reference for panels with multiple sub-values; the Coverage Gaps panel for simple single-value panels.
Adding a new report page (non-CRUD view)¶
- Create
views/<name>.pywith aTemplateViewsubclass guarded byPermissionRequiredMixin - Create the template under
templates/nautobot_contract_models/<name>.html— extendbase.htmland use{% load static %}+{% static '<path>' %}for any static assets - Add a CSS file under
static/nautobot_contract_models/<name>.cssif styling is needed - Wire the URL in
urls.pyunder thereports/prefix (NOT undercontracts/— the router's UUID-detail pattern collides) - Add a
NavMenuItemto the "Reports" group innavigation.py - Run
collectstaticafter adding CSS
The Renewal Calendar (views/calendar.py), Action Required (views/action_required.py), and Cost History (views/cost_history.py) are three reference implementations.
Adding a new Job¶
- Edit
jobs.py— subclassJob, define vars, implementrun(...) - Add the class to
register_jobs(...)at the bottom of the file - Restart the worker (
make restart-workerordocker compose restart nautobot-worker) — the registry only refreshes on worker startup - Visit
/jobs/?grouping=Contracts— newly-discovered Jobs are disabled by default; toggle Enabled
Test the Job by mocking self.logger and calling .run(...) directly — tests/test_jobs.py has examples. Don't bother running it through run_job_for_testing unless you need the JobResult / scheduling integration.
Where things live (cheat sheet)¶
| File | Purpose |
|---|---|
models/*.py |
Django ORM models |
migrations/*.py |
Schema migrations (auto-generated where possible) |
choices.py |
ChoiceSet enumerations |
forms/*.py |
NautobotModelForm + filter forms |
tables/*.py |
django_tables2 list-view tables |
filters/*.py |
django-filter filtersets (used by both UI list views AND REST API) |
views/*.py |
UI viewsets (CRUD via NautobotUIViewSet) and report views (TemplateView) |
api/serializers.py |
DRF serializers |
api/views.py |
API viewsets (NautobotModelViewSet for CRUD, mixin composition for read-only) |
api/urls.py |
API URL registration |
urls.py |
UI URL registration (router + manual path() for reports) |
navigation.py |
Left sidebar menu |
homepage.py |
Home dashboard panels |
cost.py |
Cost / burn / renewal / snapshot helpers |
priority.py |
Action-priority rubric |
helpers.py |
Transitive coverage + other cross-cutting queries |
jobs.py |
Background Jobs |
templates/nautobot_contract_models/*.html |
Page templates |
templates/nautobot_contract_models/inc/*.html |
Panel includes |
static/nautobot_contract_models/*.css |
Stylesheets |
tests/*.py |
Integration tests (Django test runner) |