diff --git a/netbox/core/graphql/mixins.py b/netbox/core/graphql/mixins.py index 6c9042313..bbcf1954c 100644 --- a/netbox/core/graphql/mixins.py +++ b/netbox/core/graphql/mixins.py @@ -3,12 +3,12 @@ from typing import Annotated, List, TYPE_CHECKING import strawberry import strawberry_django from django.contrib.contenttypes.models import ContentType +from strawberry.types import Info from core.models import ObjectChange if TYPE_CHECKING: - from core.graphql.types import DataFileType, DataSourceType - from netbox.core.graphql.types import ObjectChangeType + from core.graphql.types import DataFileType, DataSourceType, ObjectChangeType __all__ = ( 'ChangelogMixin', @@ -20,7 +20,7 @@ __all__ = ( class ChangelogMixin: @strawberry_django.field - def changelog(self, info) -> List[Annotated["ObjectChangeType", strawberry.lazy('.types')]]: # noqa: F821 + def changelog(self, info: Info) -> List[Annotated['ObjectChangeType', strawberry.lazy('.types')]]: # noqa: F821 content_type = ContentType.objects.get_for_model(self) object_changes = ObjectChange.objects.filter( changed_object_type=content_type, @@ -31,5 +31,5 @@ class ChangelogMixin: @strawberry.type class SyncedDataMixin: - data_source: Annotated["DataSourceType", strawberry.lazy('core.graphql.types')] | None - data_file: Annotated["DataFileType", strawberry.lazy('core.graphql.types')] | None + data_source: Annotated['DataSourceType', strawberry.lazy('core.graphql.types')] | None + data_file: Annotated['DataFileType', strawberry.lazy('core.graphql.types')] | None diff --git a/netbox/dcim/graphql/gfk_mixins.py b/netbox/dcim/graphql/gfk_mixins.py index 86ca88774..4d0948207 100644 --- a/netbox/dcim/graphql/gfk_mixins.py +++ b/netbox/dcim/graphql/gfk_mixins.py @@ -1,3 +1,5 @@ +from strawberry.types import Info + from circuits.graphql.types import CircuitTerminationType, ProviderNetworkType from circuits.models import CircuitTermination, ProviderNetwork from dcim.graphql.types import ( @@ -49,7 +51,7 @@ class InventoryItemTemplateComponentType: ) @classmethod - def resolve_type(cls, instance, info): + def resolve_type(cls, instance, info: Info): if type(instance) is ConsolePortTemplate: return ConsolePortTemplateType if type(instance) is ConsoleServerPortTemplate: @@ -79,7 +81,7 @@ class InventoryItemComponentType: ) @classmethod - def resolve_type(cls, instance, info): + def resolve_type(cls, instance, info: Info): if type(instance) is ConsolePort: return ConsolePortType if type(instance) is ConsoleServerPort: @@ -112,7 +114,7 @@ class ConnectedEndpointType: ) @classmethod - def resolve_type(cls, instance, info): + def resolve_type(cls, instance, info: Info): if type(instance) is CircuitTermination: return CircuitTerminationType if type(instance) is ConsolePortType: diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index 476c698b1..dbdfae11d 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -196,7 +196,7 @@ class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): verbose_name=_('Type') ) u_height = columns.TemplateColumn( - accessor=tables.A('device_type.u_height'), + accessor=tables.A('device_type__u_height'), verbose_name=_('U Height'), template_code='{{ value|floatformat }}' ) diff --git a/netbox/extras/graphql/mixins.py b/netbox/extras/graphql/mixins.py index 542bbcc85..881a53aa3 100644 --- a/netbox/extras/graphql/mixins.py +++ b/netbox/extras/graphql/mixins.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING, Annotated, List import strawberry import strawberry_django +from strawberry.types import Info __all__ = ( 'ConfigContextMixin', @@ -37,7 +38,7 @@ class CustomFieldsMixin: class ImageAttachmentsMixin: @strawberry_django.field - def image_attachments(self, info) -> List[Annotated["ImageAttachmentType", strawberry.lazy('.types')]]: + def image_attachments(self, info: Info) -> List[Annotated['ImageAttachmentType', strawberry.lazy('.types')]]: return self.images.restrict(info.context.request.user, 'view') @@ -45,17 +46,17 @@ class ImageAttachmentsMixin: class JournalEntriesMixin: @strawberry_django.field - def journal_entries(self, info) -> List[Annotated["JournalEntryType", strawberry.lazy('.types')]]: + def journal_entries(self, info: Info) -> List[Annotated['JournalEntryType', strawberry.lazy('.types')]]: return self.journal_entries.all() @strawberry.type class TagsMixin: - tags: List[Annotated["TagType", strawberry.lazy('.types')]] + tags: List[Annotated['TagType', strawberry.lazy('.types')]] @strawberry.type class ContactsMixin: - contacts: List[Annotated["ContactAssignmentType", strawberry.lazy('tenancy.graphql.types')]] + contacts: List[Annotated['ContactAssignmentType', strawberry.lazy('tenancy.graphql.types')]] diff --git a/netbox/extras/querysets.py b/netbox/extras/querysets.py index ee2afed4c..e3f6a7dcf 100644 --- a/netbox/extras/querysets.py +++ b/netbox/extras/querysets.py @@ -90,7 +90,7 @@ class ConfigContextModelQuerySet(RestrictedQuerySet): ConfigContext.objects.filter( self._get_config_context_filters() ).annotate( - _data=EmptyGroupByJSONBAgg('data', ordering=['weight', 'name']) + _data=EmptyGroupByJSONBAgg('data', order_by=['weight', 'name']) ).values("_data").order_by() ) ) diff --git a/netbox/netbox/graphql/filter_lookups.py b/netbox/netbox/graphql/filter_lookups.py index ef28d5731..ec498222d 100644 --- a/netbox/netbox/graphql/filter_lookups.py +++ b/netbox/netbox/graphql/filter_lookups.py @@ -7,6 +7,7 @@ from django.core.exceptions import FieldDoesNotExist from django.db.models import Q, QuerySet from django.db.models.fields.related import ForeignKey, ManyToManyField, ManyToManyRel, ManyToOneRel from strawberry import ID +from strawberry.directive import DirectiveValue from strawberry.types import Info from strawberry_django import ( ComparisonFilterLookup, @@ -68,7 +69,7 @@ class IntegerLookup: return None @strawberry_django.filter_field - def filter(self, info: Info, queryset: QuerySet, prefix: str = '') -> Tuple[QuerySet, Q]: + def filter(self, info: Info, queryset: QuerySet, prefix: DirectiveValue[str] = '') -> Tuple[QuerySet, Q]: filters = self.get_filter() if not filters: @@ -91,7 +92,7 @@ class FloatLookup: return None @strawberry_django.filter_field - def filter(self, info: Info, queryset: QuerySet, prefix: str = '') -> Tuple[QuerySet, Q]: + def filter(self, info: Info, queryset: QuerySet, prefix: DirectiveValue[str] = '') -> Tuple[QuerySet, Q]: filters = self.get_filter() if not filters: @@ -110,7 +111,7 @@ class JSONFilter: lookup: JSONLookup @strawberry_django.filter_field - def filter(self, info: Info, queryset: QuerySet, prefix: str = '') -> Tuple[QuerySet, Q]: + def filter(self, info: Info, queryset: QuerySet, prefix: DirectiveValue[str] = '') -> Tuple[QuerySet, Q]: filters = self.lookup.get_filter() if not filters: @@ -137,7 +138,7 @@ class TreeNodeFilter: match_type: TreeNodeMatch @strawberry_django.filter_field - def filter(self, info: Info, queryset: QuerySet, prefix: str = '') -> Tuple[QuerySet, Q]: + def filter(self, info: Info, queryset: QuerySet, prefix: DirectiveValue[str] = '') -> Tuple[QuerySet, Q]: model_field_name = prefix.removesuffix('__').removesuffix('_id') model_field = None try: diff --git a/netbox/netbox/graphql/types.py b/netbox/netbox/graphql/types.py index 653462630..bdc38b349 100644 --- a/netbox/netbox/graphql/types.py +++ b/netbox/netbox/graphql/types.py @@ -1,5 +1,6 @@ import strawberry import strawberry_django +from strawberry.types import Info from django.contrib.contenttypes.models import ContentType from core.graphql.mixins import ChangelogMixin @@ -26,7 +27,7 @@ class BaseObjectType: """ @classmethod - def get_queryset(cls, queryset, info, **kwargs): + def get_queryset(cls, queryset, info: Info, **kwargs): # Enforce object permissions on the queryset if hasattr(queryset, 'restrict'): return queryset.restrict(info.context.request.user, 'view') diff --git a/netbox/tenancy/forms/bulk_import.py b/netbox/tenancy/forms/bulk_import.py index 8234513ae..5861c976b 100644 --- a/netbox/tenancy/forms/bulk_import.py +++ b/netbox/tenancy/forms/bulk_import.py @@ -1,3 +1,4 @@ +from django import forms from django.contrib.contenttypes.models import ContentType from django.utils.translation import gettext_lazy as _ @@ -25,7 +26,7 @@ class TenantGroupImportForm(NetBoxModelImportForm): queryset=TenantGroup.objects.all(), required=False, to_field_name='name', - help_text=_('Parent group') + help_text=_('Parent group'), ) slug = SlugField() @@ -41,7 +42,7 @@ class TenantImportForm(NetBoxModelImportForm): queryset=TenantGroup.objects.all(), required=False, to_field_name='name', - help_text=_('Assigned group') + help_text=_('Assigned group'), ) class Meta: @@ -59,7 +60,7 @@ class ContactGroupImportForm(NetBoxModelImportForm): queryset=ContactGroup.objects.all(), required=False, to_field_name='name', - help_text=_('Parent group') + help_text=_('Parent group'), ) slug = SlugField() @@ -81,7 +82,12 @@ class ContactImportForm(NetBoxModelImportForm): queryset=ContactGroup.objects.all(), required=False, to_field_name='name', - help_text=_('Group names separated by commas, encased with double quotes (e.g. "Group 1,Group 2")') + help_text=_('Group names separated by commas, encased with double quotes (e.g. "Group 1,Group 2")'), + ) + link = forms.URLField( + label=_('Link'), + assume_scheme='https', + required=False, ) class Meta: diff --git a/netbox/tenancy/forms/model_forms.py b/netbox/tenancy/forms/model_forms.py index 6ef9d8560..5b1bd7339 100644 --- a/netbox/tenancy/forms/model_forms.py +++ b/netbox/tenancy/forms/model_forms.py @@ -100,6 +100,11 @@ class ContactForm(NetBoxModelForm): queryset=ContactGroup.objects.all(), required=False ) + link = forms.URLField( + label=_('Link'), + assume_scheme='https', + required=False, + ) comments = CommentField() fieldsets = (