Skip to content

v2026.5.11

Released 2026-05-11. Coexistence fix for nautobot-app-device-lifecycle.

What changed

Both Contract.status and Invoice.status now declare an explicit related_name so Django's reverse-accessor names don't collide with the device-lifecycle plugin when both apps are installed in the same Nautobot instance.

Why

nautobot-app-device-lifecycle defines a ContractLCM model with a StatusField. Nautobot's default StatusField.related_name produces the same reverse accessor (Status.contracts) for both Contract and ContractLCM, which Django's system check rejects:

nautobot_contract_models.Contract.status: (fields.E304) Reverse accessor
  'Status.contracts' for 'nautobot_contract_models.Contract.status' clashes
  with reverse accessor for 'nautobot_device_lifecycle_mgmt.ContractLCM.status'.

Operators with both plugins installed couldn't boot Nautobot. This release unblocks them.

How

Two AlterField operations in migration 0009_alter_status_related_name:

  • Contract.statusrelated_name="contract_models_contracts"
  • Invoice.statusrelated_name="contract_models_invoices" (defensive; DLC has no Invoice equivalent today)

The migration is a Python-level rename only — no SQL runs, no column changes. Applies instantly.

Upgrade path

pip install --upgrade nautobot-contract-models==2026.5.11
nautobot-server migrate
sudo systemctl restart nautobot nautobot-worker

If you already have both apps installed and were stuck at SystemCheckError on startup, this release fixes it. nautobot-server check should report clean afterward.

Breaking changes

None at the data layer — recurring_cost, billing_period, all SLA fields, and every other field on Contract / Invoice are unchanged.

Code-level: If your custom code queries Status.contracts.all() or filters with status__contracts__..., you'll need to update to Status.contract_models_contracts.all() / status__contract_models_contracts__.... This is rare — most code accesses the FK forward (contract.status), which is unchanged.

Out of scope (deferred)

This release does NOT add a unified analytics view across both apps. Operators with both installed see two separate "Contracts" surfaces in nav; our Cost Summary / Renewal Calendar / Action Required panels show only our Contract rows; DLC's ContractLCM rows are invisible to those analytics.

Building the bridge — read-only union of DLC contracts into our analytics, plus a one-way migration Job — is feasible but a feature add rather than a blocker fix. Open an issue if you want it prioritized.

Tests

86 passing (82 from 2026.5.9 plus 4 new test_collision.py cases pinning the related_name configuration).