Only display the saved filter dropdown in table controls when a filter
form is present and includes a filter_id field. This prevents rendering
an empty or non-functional dropdown when saved filters are unavailable.
Render navigation menu buttons with the secondary ghost style when their
color is unset or set to the default choice.
This fixes plugin menu buttons, which default to "default" rather than
None, while preserving explicitly configured button colors.
Replace the grouped button wrapper with a semantic
`dropdown-item-buttons` container and render sidebar action buttons with
the `btn-ghost` style while preserving existing color support.
Scope dropdown item link styles to direct child anchors so nested action
buttons keep their intended styling, and reveal the buttons on hover,
active, and focus-within states.
Move the device/VM lookup ahead of is_primary/is_oob handling and only
process those flags when a parent device or VM exists.
This avoids dereferencing None for explicit falsy CSV values such as
"false", which are not covered by the column-absent checks in
clean_is_primary() and clean_is_oob(). This also keeps the behavior
aligned with MACAddressImportForm.
Reject duplicate ScriptModule uploads before writing to storage to
prevent failed uploads from corrupting existing files. Add existence
check in cleanup path to avoid deleting files referenced by concurrent
uploads that won the race.
Fixes#22543
Introduce Breakout1C8Px8C1PCableProfile to map a single 8-position
connector to eight single-position connectors. Add profile choice,
registration, and bidirectional link peer tests for the new breakout.
Fixes#22279
Add post_save signal handler to update cached virtual_chassis field for
member Devices when a VirtualChassis is renamed. Skip updates for
creates, raw saves, or targeted saves excluding the name field.
Fixes stale search cache entries after VirtualChassis name changes.
Fixes#22489
RestrictedQuerySet.restrict() and IsSuperuser short-circuited on is_superuser
without checking is_active, so a deactivated superuser was granted the full
superuser bypass. restrict() in particular fails open, returning the
unrestricted queryset. Both now also require is_active, matching the existing
guard in ObjectPermissionMixin.has_perm.
Introduce GraphQLSchemaCoverageTestCase to verify every model-backed
GraphQL type exposed as a root query field is covered by a test. Add
type_class and graphql_test_exempt attributes to GraphQLTestCase for
explicit type declaration and coverage exclusion. Include
graphql_object_permission_assertions flag to gate permission checks for
types not enforcing object permissions.
Fixes#22089
- Add _validate_json_path(): each __-separated path segment must match
[A-Za-z0-9_][A-Za-z0-9_-]* (allows leading underscores per Jeremy's
suggestion; ORM operator names like 'date'/'regex' are valid JSON keys
and are not blocked — the trailing __ JSONFilter appends makes them
key traversal steps, not ORM transforms)
- Add JSONStringLookup: explicit string-filter type for JSONLookup.
regex/i_regex are included (they offer no additional oracle power
beyond starts_with, which is also present, per Jeremy's observation)
- JSONFilter.filter() validates self.path and returns empty Q() on
invalid input rather than passing untrusted user input to the ORM
- 19 unit tests for path validation and JSONStringLookup field presence
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Update strawberry-graphql-django to 0.86.1 and remove redundant type
parameters from StrFilterLookup, DateFilterLookup, TimeFilterLookup, and
DatetimeFilterLookup annotations across model-backed GraphQL filters.
Add NetBox-local JSON date, time, and datetime lookup input types to
preserve the previous string-backed JSON filter schema without relying
on deprecated upstream generic lookup annotations. These local types
keep the legacy GraphQL type names and date/time sub-lookup fields
intact.
Fixes#22353
The method wrote uploaded files to disk via a raw open(), but no code
path reached it. Its only subclass, ScriptFileForm, overrode save() to
write through django-storages and explicitly skipped the base via
super(ManagedFileForm, self).save(). With the override gone, that call
simplifies back to a plain super().save(). A leftover from #18680, which
moved both upload paths onto django-storages but left the form-level
write in place.
Change `get_schema_extensions()` to return extension factories instead
of instances. This defers extension initialization and prevents stale
references to settings captured at import time.
Lambdas capture settings values when extensions are constructed, and
tests now instantiate extensions from factories to verify configuration.
Fixes#22451