* refactor(forms): Add GenericObjectChoiceField
Replace separate scope_type/scope and parent_object_type/parent field
pairs with unified GenericObjectChoiceField. Introduce
GenericObjectFormMixin to handle GFK descriptor initialization and
assignment.
This removes redundant HTMX/queryset setup logic from ScopedForm,
VLANGroupForm, and ServiceForm by delegating GFK presentation to a
single reusable field and mixin pair. Field query param references now
use `$scope_object_id` instead of `$scope` to match the subwidget name.
Fixes#19821
* fix(forms): Skip validation on HTMX bulk-edit dependent field refresh
Render bulk-edit form unbound when an HTMX request changes a dependent
field (e.g. content type) without clicking Apply. This prevents
validation errors from surfacing before the user submits.
Cache ContentType lookups in GenericObjectChoiceField and sync widget
references before setting queryset to ensure choices land on the
rendered subwidget.
* fix(ipam): Update scope query params for GenericObjectChoiceField
Change available-prefix Add links to use `scope_content_type` and
`scope_object_id` query parameters instead of `scope_type` and `scope`.
This aligns with the GenericObjectChoiceField subwidget naming
introduced in the earlier refactor.
* refactor(models): Simplify GFK handling in clone_fields
Replace `scope_type`/`scope_id` pairs with bare `scope` GFK names in
clone_fields across models. Update CloningMixin to emit GFK subwidget
parameters (`scope_content_type`, `scope_object_id`) directly when a
GenericForeignKey appears in clone_fields.
* Update pre-populated links
---------
Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
Defer CachedValue updates from post_save/post_delete signal handlers to a
SearchCacheJob that runs after the surrounding transaction commits. Coalesce
pending updates per database alias and savepoint scope, preserving rollback
semantics while reducing synchronous write latency.
When no worker is available, or Redis cannot be reached during dispatch, apply
the update inline so existing single-process installs continue to behave
correctly. Replay the originating database alias during deferred reads and
writes so cache updates remain routed to the schema that emitted the signal.
Keep deferral private to CachedValueSearchBackend so custom SEARCH_BACKEND
implementations continue to use the existing synchronous cache/remove contract.
Move the job runner to search/jobs.py and keep the CachedValue-specific update
logic on the backend.
Document the eventual consistency of global search results and add coverage for
coalescing, rollback/savepoint behavior, worker dispatch, inline fallback,
deleted objects, and the custom backend contract.
Adds a nullable DurationField computed as completed - started, set in
Job.terminate(). Exposes it as an orderable table column, detail-panel
attr, REST API field, and UI/API range filters (execution_time__gte /
execution_time__lte).
A plain stored field (not a GeneratedField) keeps the migration
metadata-only, avoiding a full table rewrite on core_job.
* #21025: WIP
* Fixes#22357: Remove unused `local_context_data` field from dcim.Module (#22364)
* Add partial index for checking null CC data
* Ensure the data returned by get_config_context() is safe for mutation
* Implement selective backup queryset annotation to avoid n+1 overhead on cold cache
* Fix migration conflict
* Replace MPTT with Ltree per #21418
Bulk write operations (create/update/delete a JSON list at a model's list
endpoint) can opt into background processing with the ?background=true query
parameter. The request is validated synchronously and, if accepted, an
AsyncAPIJob is enqueued and a 202 Accepted is returned with the job id and
poll URL; the write is performed later by a worker that re-invokes the same
viewset action, so behavior matches the synchronous path (including
all-or-nothing transaction semantics).
- AsyncAPIJob reconstructs the request in the worker, re-applies object
permissions, runs within the request processors (change logging/events),
and captures the action's response into job.data as {status_code, data}.
- Handled rejections are translated to match the synchronous API: APIException
via handle_exception(), and AbortRequest/ProtectedError/RestrictedError via a
new NetBoxModelViewSet.exception_to_response() helper. These terminate the
job as "failed" (reserving "errored" for unexpected crashes).
- Background processing is refused with 503 when no worker is servicing the
queue, and rejected with 400 when combined with an If-Match precondition
(which cannot be honored once execution is deferred).
- Single-object writes, GET requests, and non-list payloads ignore the
parameter and run synchronously.
exception_to_response() intentionally duplicates the translation logic in
dispatch() rather than dispatch() being refactored to call it; consolidating
the two is left as a follow-up to keep this change off the synchronous hot path.
* Address code review feedback (#21992)
- Carry the request's scheme and host into the background worker so absolute
URLs in the captured job result point at the real server instead of a
hardcoded http://localhost/.
- Emit the same protected-delete warning log in exception_to_response() that
dispatch() produces, restoring application-log parity for background failures.
- Drop the inert `_authenticator = None` assignment: setting request.user
already prevents lazy re-authentication via the public API, and nothing on
the worker's action path reads the authenticator.
- Remove the redundant success-path job.save() (JobRunner.handle() ->
terminate() persists job.data) and hoist the AsyncAPIJob import in mixins.py
to module level (no real import cycle through it).
- Add a test asserting result URLs reflect the request host.
* Fix IPv6 host parsing in background API request reconstruction
Parse the carried host with urlsplit (and pass it verbatim as HTTP_HOST)
instead of host.partition(':'), which split bracketed IPv6 hosts like
[::1]:8443 on their inner colons. Extract request construction into
AsyncAPIJob._build_request and add a test asserting the IPv6 host round-trips.
* Address review feedback (#21992)
- Make the bulk mixins safe to use without BackgroundOperationMixin: guard the
_background_requested / _maybe_background_bulk_create calls with a getattr
fallback so BulkUpdateModelMixin/BulkDestroyModelMixin/SequentialBulkCreatesMixin
retain their standalone behavior in custom viewset composition.
- Add a test covering the background ProtectedError/RestrictedError path: a bulk
delete of a protected object records the same 409 the synchronous API returns
(job failed, status_code 409, object preserved), via exception_to_response().
Follow-up to #22363: align the plugin hook names with the already-renamed
JINJA_FILTERS setting (#22288) and with the rest of the codebase's 'Jinja'
spelling convention.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add support for literal `{lat}` and `{lon}` placeholders in `MAPS_URL`
when rendering GPS coordinate links. Existing configurations continue to
work by falling back to appending `lat,lon` when no coordinate placeholders
are present.
Move map URL handling into shared UI helpers so `GPSCoordinatesAttr` and
`AddressAttr` use consistent placeholder detection. When `MAPS_URL` contains
coordinate placeholders, suppress address-based map links to avoid rendering
invalid URLs.
Add tests for placeholder replacement, decimal coordinate values, fallback
behavior, and address link suppression. Also document the address link behavior
in the `MAPS_URL` configuration description.
Adds warning and examples for validating NetBox objects before saving
in Custom Scripts. Direct ORM writes bypass UI/API validation and can
introduce invalid data. Recommends calling `full_clean()` explicitly.
Fixes#22249
Fix miscounting of total_vlan_ids when VLANGroup vid_ranges use
non-canonical bounds (e.g. '[]'). Normalize ranges to '[)' on save and
add migration to recompute existing totals. Prevent division-by-zero in
utilization queries for legacy rows with miscounted totals.
Fixes#22228
Allow IP ranges where start_address equals end_address to model
single-IP pools like DHCP or NAT reservations. Add validation tests,
filterset coverage, and display logic to render both endpoints.
Fixes#21993
Display a warning in the UI whenever a user goes to provision a v1
token (both via the admin token form and the user profile token form).
Update documentation to note that v1 tokens are deprecated and will be
removed in NetBox v5.0.