Skip to content

Deleting a Custom Object with a multiobject field raises ValueError (recurrence of #477 in v0.6.0) #640

Description

@krancslawomir

Plugin Version

v0.6.0

NetBox Version

v4.6.6

Python Version

3.14

Steps to Reproduce

  1. Create a Custom Object Type, e.g. name=internal_service,
    verbose_name="Internal Service", slug=internal-services.
  2. Add a field to it:
    • name: depends_on
    • type: multiobject
    • either self-referencing (related type = the same Custom Object
      Type) or referencing another Custom Object Type — any
      multiobject field reproduces it, self-reference is not required.
  3. Create one instance of this Custom Object Type via the UI or via
    POST /api/plugins/custom-objects/internal-services/.
  4. Navigate to the object's detail page and click "Delete", OR send
    GET /plugins/custom-objects/internal-services/<id>/delete/
    directly (this is the confirmation page — no POST/actual deletion
    is even required to trigger it).
  5. Alternatively: send DELETE /api/plugins/custom-objects/internal-services/<id>/
    via the REST API directly (raw HTTP, no client library).

Expected Behavior

The detail page renders normally and shows the depends_on field
(empty or populated), exactly as the REST API already does for the
same object.

Observed Behavior

Server Error. Full exception:

FieldDoesNotExist

Table<A>Model has no field named 'depends_on'

This happens on a freshly created field that has never been**
**edited, and persists across:

  • restarting the netbox and netbox-worker containers
  • a full docker compose down && docker build --no-cache && docker compose up -d (i.e. a completely fresh Python process, ruling out
    any in-memory cache)

We confirmed directly via ./manage.py nbshell, on a fresh process
right after the rebuild above, that the dynamically generated model
class genuinely lacks the field:

>>> from netbox_custom_objects.models import CustomObjectType
>>> cot = CustomObjectType.objects.get(id=<A>)
>>> model = cot.get_model()
>>> [f.name for f in model._meta.get_fields()]
['id', 'created', 'last_updated', 'owner', 'service_id', 'product', 'status', 'tag', 'info_yetiforce', 'tags', 'bookmarks', 'contacts', 'journal_entries', 'subscriptions', 'tagged_items']
# 'depends_on' is simply absent from this list, despite the field
# existing and being correctly returned by the REST API for the same
# object at the same time.

At container/process startup, the following warnings appear in the
logs, once per Custom Object Type that has such a polymorphic field:

RuntimeWarning: Model 'netbox_custom_objects.through_custom_objects_<N>_depends_on' was already registered. Reloading models is not advised as it can lead to inconsistencies, most notably with related models.
  _apps.register_model(APP_LABEL, through_model)

This suggests the intermediate/through model backing the polymorphic
relation is registered more than once during app startup, and the
duplicate apps.register_model() call (which Django treats as a
no-op for an already-registered model name) silently keeps whichever
class got registered first — which appears to be missing the field
in question.

Empirically confirmed trigger condition

We tested this extensively across 4 different Custom Object Types in
the same NetBox instance, changing only the field configuration:

related_object_types_input length Detail view result
1 (non-polymorphic, single app_label+model) Works correctly, every time
2 Crashes, every time
3 Crashes, every time

This held regardless of:

  • whether one of the 2-3 types was the field's own Custom Object Type
    (self-reference) or not
  • how many times the field had previously been deleted and recreated
    (we confirmed it on fields created for the very first time, never
    touched again, immediately after creation)
  • whether is_cloneable, weight, or other non-structural metadata
    had been set at creation time vs patched afterward

The only reliable fix we found was reducing every such field to
exactly one related type (converting it from
is_polymorphic: true with N types, to a plain non-polymorphic
object/multiobject field with a single app_label+model).

Notes / Root cause hint

This looks related to (but may be a distinct or regressed variant of)
the previously-reported and closed #384 ("Model generation
problem: fields disappear when Custom Object Types reference each
other"), fixed via PR #530 in milestone v0.5.2 for a narrower
repro (two Custom Object Types referencing each other via single-type
object fields, in an NGINX Unit multi-process environment, symptom
being a field intermittently present/absent). Our case:

We were unable to find a way to trigger clear_model_cache() or
force a correct rebuild without reducing the field to a single type;
happy to provide DB schema dumps, the exact API payloads used to
create every field, and/or grant reproduction access if useful for
maintainers.

Workaround found

Model dependencies using multiple separate non-polymorphic fields
(one per related type, e.g. depends_on_service_a,
depends_on_service_b) instead of one polymorphic field with several
related types. Each field individually works reliably; the modeling
goal (an object depending on instances of several different Custom
Object Types) is achieved by having several fields instead of one.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions