- NetBox
4.5.x–4.6.x - Python
3.12to<3.15
pip install netbox-attachmentsIn NetBox configuration.py:
PLUGINS = ["netbox_attachments"]mkdir -p /opt/netbox/netbox/media/netbox-attachments
chown netbox /opt/netbox/netbox/media/netbox-attachmentspython3 manage.py migrate netbox_attachmentsRestart NetBox application services so plugin hooks and template extensions are loaded.
- Open NetBox UI and confirm plugin menu entries are available.
- Verify API endpoint
/api/plugins/netbox-attachments/netbox-attachments/responds. - Verify API endpoint
/api/plugins/netbox-attachments/netbox-attachment-assignments/responds.
Upgrading to NetBox 4.6 with existing attachments can fail in plugin migration
0007_alter_netboxattachment_object_type with:
psycopg.errors.ForeignKeyViolation: ... violates foreign key constraint
"netbox_attachments_n_object_type_id_..._fk_core_obje"
DETAIL: Key (object_type_id)=(...) is not present in table "core_objecttype".
Why it happens. NetBox 4.6 turned core.ObjectType into a concrete model with its own
core_objecttype table, which is populated by a post_migrate signal that fires only at the end
of a completed migrate run. Migration 0007 re-points the attachment foreign key at
core.objecttype, and PostgreSQL validates that constraint immediately — while core_objecttype
is still empty — so the run aborts before the population signal is ever reached. Fresh installs are
unaffected (no attachment rows to validate).
Recommended upgrade procedure:
-
Comment the plugin out of
PLUGINSinconfiguration.py. -
Run
python3 manage.py migrate. This completes the core upgrade; the end-of-runpost_migratesignal populatescore_objecttypefor all installed models. -
Re-enable the plugin in
PLUGINS. -
Back-fill any object types whose model is no longer installed (a removed plugin, a renamed model) —
post_migrateskips those, but the command recreates them directly fromdjango_content_type:python3 manage.py fix_attachment_object_types # add --dry-run to preview first -
Run
python3 manage.py migrateto apply the plugin migrations.
The fix_attachment_object_types command is idempotent, safe to run repeatedly, and a no-op on
NetBox versions older than 4.6.
If you hit this on a plugin version that does not yet ship the command, run the equivalent directly
in nbshell (python3 manage.py nbshell) after step 3, before the final migrate:
Scope: this snippet assumes the schema left by a failed
0007migration — i.e. the plugin at migration0006, where object-type references live solely innetbox_attachments_netboxattachment.object_type_id(content_type_idwas renamed away in0006, and the assignment table is not created until0008). If your database is in any other state, use thefix_attachment_object_typescommand instead, which auto-detects the source table/column.
from django.db import connection
if "core_objecttype" in connection.introspection.table_names():
with connection.cursor() as cursor:
cursor.execute(
"""
INSERT INTO core_objecttype (contenttype_ptr_id, public, features)
SELECT id, false, '{}'::varchar[]
FROM django_content_type ct
WHERE id IN (
SELECT DISTINCT object_type_id
FROM netbox_attachments_netboxattachment
WHERE object_type_id IS NOT NULL
)
AND NOT EXISTS (
SELECT 1 FROM core_objecttype ot WHERE ot.contenttype_ptr_id = ct.id
)
"""
)
print("created", cursor.rowcount, "core_objecttype row(s)")The post_migrate signal refines the public/features values of the inserted rows on the next
migrate.