Arthur 1 год назад
Родитель
Сommit
2c9bea9ab9

+ 17 - 300
netbox/circuits/graphql/filters.py

@@ -1,16 +1,8 @@
-from typing import List
-
-import django_filters
 import strawberry
 import strawberry_django
 from circuits import filtersets, models
-from functools import partial, partialmethod, wraps
-from strawberry import auto
-from strawberry_django.filters import FilterLookup
-from tenancy.graphql.filter_mixins import ContactModelFilterMixin, TenancyFilterMixin
-from utilities.filters import *
 
-from netbox.graphql.filter_mixins import NetBoxModelFilterMixin
+from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
 
 __all__ = (
     'CircuitTerminationFilter',
@@ -21,313 +13,38 @@ __all__ = (
     'ProviderNetworkFilter',
 )
 
-# def filter_by_filterset(self, queryset, key, cls, filterset):
-#     breakpoint()
-#     return filterset(data={key: getattr(cls, key)}, queryset=queryset).qs
-
-
-def autotype_decorator(filterset):
-
-    def wrapper(cls):
-        cls.filterset = filterset
-        fields = filterset.get_fields()
-        print(f"fields: {fields}")
-        for fieldname in fields.keys():
-            if fieldname not in cls.__annotations__:
-                cls.__annotations__[fieldname] = auto
-
-        # fields = list(filterset.get_fields().keys())
-        declared_filters = filterset.declared_filters
-        print(f"declared_filters: {declared_filters}")
-        print("")
-        for fieldname, v in declared_filters.items():
-            create_function = False
-            attr_type = None
-            print(f"{fieldname}: {v}")
-
-            if isinstance(v, ContentTypeFilter):
-                print("ContentTypeFilter")
-            elif isinstance(v, MACAddressFilter):
-                print("MACAddressFilter")
-            elif isinstance(v, MultiValueArrayFilter):
-                print("MultiValueArrayFilter")
-            elif isinstance(v, MultiValueCharFilter):
-                print("MultiValueCharFilter")
-            elif isinstance(v, MultiValueDateFilter):
-                print("MultiValueDateFilter")
-            elif isinstance(v, MultiValueDateTimeFilter):
-                print("MultiValueDateTimeFilter")
-            elif isinstance(v, MultiValueDecimalFilter):
-                print("MultiValueDecimalFilter")
-            elif isinstance(v, MultiValueMACAddressFilter):
-                print("MultiValueMACAddressFilter")
-            elif isinstance(v, MultiValueNumberFilter):
-                print("MultiValueNumberFilter")
-            elif isinstance(v, MultiValueTimeFilter):
-                print("MultiValueTimeFilter")
-            elif isinstance(v, MultiValueWWNFilter):
-                print("MultiValueWWNFilter")
-            elif isinstance(v, NullableCharFieldFilter):
-                print("NullableCharFieldFilter")
-            elif isinstance(v, NumericArrayFilter):
-                print("NumericArrayFilter")
-            elif isinstance(v, TreeNodeMultipleChoiceFilter):
-                print("TreeNodeMultipleChoiceFilter")
-
-            elif issubclass(type(v), django_filters.CharFilter):
-                print("CharFilter")
-            elif issubclass(type(v), django_filters.UUIDFilter):
-                print("UUIDFilter")
-            elif issubclass(type(v), django_filters.BooleanFilter):
-                print("BooleanFilter")
-            elif issubclass(type(v), django_filters.ChoiceFilter):
-                print("ChoiceFilter")
-            elif issubclass(type(v), django_filters.TypedChoiceFilter):
-                print("TypedChoiceFilter")
-            elif issubclass(type(v), django_filters.DateFilter):
-                print("DateFilter")
-            elif issubclass(type(v), django_filters.TimeFilter):
-                print("TimeFilter")
-            elif issubclass(type(v), django_filters.DateTimeFilter):
-                print("DateTimeFilter")
-            elif issubclass(type(v), django_filters.IsoDateTimeFilter):
-                print("IsoDateTimeFilter")
-            elif issubclass(type(v), django_filters.DurationFilter):
-                print("DurationFilter")
-            elif issubclass(type(v), django_filters.ModelChoiceFilter):
-                print("ModelChoiceFilter")
-            elif issubclass(type(v), django_filters.ModelMultipleChoiceFilter):
-                create_function = True
-                attr_type = List[str] | None
-                print("ModelMultipleChoiceFilter")
-            elif issubclass(type(v), django_filters.NumberFilter):
-                print("NumberFilter")
-            elif issubclass(type(v), django_filters.NumericRangeFilter):
-                print("NumericRangeFilter")
-            elif issubclass(type(v), django_filters.RangeFilter):
-                print("RangeFilter")
-            elif issubclass(type(v), django_filters.DateRangeFilter):
-                print("DateRangeFilter")
-            elif issubclass(type(v), django_filters.DateFromToRangeFilter):
-                print("DateFromToRangeFilter")
-            elif issubclass(type(v), django_filters.DateTimeFromToRangeFilter):
-                print("DateTimeFromToRangeFilter")
-            elif issubclass(type(v), django_filters.IsoDateTimeFromToRangeFilter):
-                print("IsoDateTimeFromToRangeFilter")
-            elif issubclass(type(v), django_filters.TimeRangeFilter):
-                print("TimeRangeFilter")
-            elif issubclass(type(v), django_filters.AllValuesFilter):
-                print("AllValuesFilter")
-            elif issubclass(type(v), django_filters.AllValuesMultipleFilter):
-                print("AllValuesMultipleFilter")
-            elif issubclass(type(v), django_filters.LookupChoiceFilter):
-                print("LookupChoiceFilter")
-            elif issubclass(type(v), django_filters.BaseInFilter):
-                print("BaseInFilter")
-            elif issubclass(type(v), django_filters.BaseRangeFilter):
-                print("BaseRangeFilter")
-            elif issubclass(type(v), django_filters.OrderingFilter):
-                print("OrderingFilter")
-            elif issubclass(type(v), django_filters.TypedMultipleChoiceFilter):
-                print("TypedMultipleChoiceFilter")
-            elif issubclass(type(v), django_filters.MultipleChoiceFilter):
-                print("MultipleChoiceFilter")
-            else:
-                print("unknown type!")
-
-            if fieldname not in cls.__annotations__ and attr_type:
-                print(f"adding {fieldname} to class")
-                cls.__annotations__[fieldname] = attr_type
-
-            fname = f"filter_{fieldname}"
-            if create_function and not hasattr(cls, fname):
-                print(f"creating function {fname}")
-                filter_by_filterset = getattr(cls, 'filter_by_filterset')
-                setattr(cls, fname, partialmethod(filter_by_filterset, key=fieldname))
-                # setattr(cls, fname, partial(filter_by_filterset, key=fieldname, cls=cls, filterset=filterset))
-
-        print("")
-        return cls
-
-    return wrapper
-
-
-"""
-class autotype_decorator(object):
-    def __init__(self, filterset):
-        self.filterset = filterset
-    def __call__(self, cls):
-        class Wrapped(cls):
-            '''
-            cls.filterset = filterset
-            fields = filterset.get_fields()
-            print(fields)
-            fields = list(filterset.get_fields().keys())
-            declared_filters = filterset.declared_filters
-            print(declared_filters)
-            fields.extend(list(filterset.declared_filters.keys()))
-            for field in fields:
-                print(field)
-
-            '''
-            print(f"cls: {cls}")
-            print(f"self: {self}")
-            vars()['cid'] = strawberry.unset.UnsetType
-            # setattr(cls, 'cid', strawberry.unset.UnsetType)
-            pass
-
-        setattr(Wrapped, 'cid', strawberry.unset.UnsetType)
-        print(f"hasattr: {hasattr(Wrapped, 'cid')}")
-        print(Wrapped)
-        return Wrapped
-"""
-
 
 @strawberry_django.filter(models.CircuitTermination, lookups=True)
-class CircuitTerminationFilter(filtersets.CircuitTerminationFilterSet):
-    id: auto
-    term_side: auto
-    port_speed: auto
-    upstream_speed: auto
-    xconnect_id: auto
-    description: auto
-    cable_end: auto
-    # q: auto
-    circuit_id: auto
-    site_id: auto
-    site: auto
-    # provider_network_id: auto
+@autotype_decorator(filtersets.CircuitTerminationFilterSet)
+class CircuitTerminationFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Circuit, lookups=True)
 @autotype_decorator(filtersets.CircuitFilterSet)
-class CircuitFilter:
-    # class CircuitFilter(NetBoxModelFilterMixin, TenancyFilterMixin, ContactModelFilterMixin):
-
-    def filter_by_filterset(self, queryset, key):
-        return self.filterset(data={key: getattr(self, key)}, queryset=queryset).qs
-
+class CircuitFilter(BaseFilterMixin):
     pass
-    """
-    # vars()['cid'] = strawberry.unset.UnsetType
-    # cid: auto
-    description: auto
-    install_date: auto
-    termination_date: auto
-    commit_rate: auto
-
-    provider_id: List[str] | None
-    provider: List[str] | None
-    provider_account_id: List[str] | None
-    provider_network_id: List[str] | None
-    type_id: List[str] | None
-    type: List[str] | None
-    status: auto
-    region_id: List[str] | None
-    region: List[str] | None
-    site_group_id: List[str] | None
-    site_group: List[str] | None
-    site_id: List[str] | None
-    site: List[str] | None
-
-    def filter_provider_id(self, queryset):
-        return self.filter_by_filterset(queryset, 'provider_id')
-
-    def filter_provider(self, queryset):
-        return self.filter_by_filterset(queryset, 'provider')
-
-    def filter_provider_account_id(self, queryset):
-        return self.filter_by_filterset(queryset, 'provider_account_id')
-
-    def filter_provider_network_id(self, queryset):
-        return self.filter_by_filterset(queryset, 'provider_network_id')
-
-    def filter_type_id(self, queryset):
-        return self.filter_by_filterset(queryset, 'type_id')
-
-    def filter_type(self, queryset):
-        return self.filter_by_filterset(queryset, 'type')
-
-    def filter_region_id(self, queryset):
-        return self.filter_by_filterset(queryset, 'region_id')
-
-    def filter_region(self, queryset):
-        return self.filter_by_filterset(queryset, 'region')
-
-    def filter_site_group_id(self, queryset):
-        return self.filter_by_filterset(queryset, 'site_group_id')
-
-    def filter_site_group(self, queryset):
-        return self.filter_by_filterset(queryset, 'site_group')
-
-    def filter_site_id(self, queryset):
-        return self.filter_by_filterset(queryset, 'site_id')
-
-    def filter_site(self, queryset):
-        return self.filter_by_filterset(queryset, 'site')
-    """
-
-
-# @strawberry_django.filter(models.Circuit, lookups=True)
-# class CircuitFilter(filtersets.CircuitFilterSet):
-#     id: auto
-#     cid: auto
-#     description: auto
-#     install_date: auto
-#     termination_date: auto
-#     commit_rate: auto
-#     provider_id: auto
-#     provider: auto
-#     provider_account_id: auto
-#     # provider_network_id: auto
-#     type_id: auto
-#     type: auto
-#     status: auto
-#     # region_id: auto
-#     # region: auto
-#     # site_group_id: auto
-#     # site_group: auto
-#     # site_id: auto
-#     # site: auto
 
 
 @strawberry_django.filter(models.CircuitType, lookups=True)
-class CircuitTypeFilter(filtersets.CircuitTypeFilterSet):
-    id: auto
-    name: auto
-    slug: auto
-    description: auto
+@autotype_decorator(filtersets.CircuitTypeFilterSet)
+class CircuitTypeFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Provider, lookups=True)
-class ProviderFilter(filtersets.ProviderFilterSet):
-    id: auto
-    name: auto
-    slug: auto
-    # region_id: auto
-    # region: auto
-    # site_group_id: auto
-    # site_group: auto
-    # site_id: auto
-    # site: auto
-    # asn_id: auto
+@autotype_decorator(filtersets.ProviderFilterSet)
+class ProviderFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.ProviderAccount, lookups=True)
-class ProviderAccountFilter(filtersets.ProviderAccountFilterSet):
-    id: auto
-    name: auto
-    account: auto
-    description: auto
-    # provider_id: auto
-    # provider: auto
+@autotype_decorator(filtersets.ProviderAccountFilterSet)
+class ProviderAccountFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.ProviderNetwork, lookups=True)
-class ProviderNetworkFilter(filtersets.ProviderNetworkFilterSet):
-    id: auto
-    name: auto
-    service_id: auto
-    description: auto
-    # provider_id: auto
-    # provider: auto
+@autotype_decorator(filtersets.ProviderNetworkFilterSet)
+class ProviderNetworkFilter(BaseFilterMixin):
+    pass

+ 8 - 5
netbox/core/graphql/filters.py

@@ -1,7 +1,8 @@
 import strawberry
 import strawberry_django
 from core import filtersets, models
-from strawberry import auto
+
+from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
 
 __all__ = (
     'DataFileFilter',
@@ -10,10 +11,12 @@ __all__ = (
 
 
 @strawberry_django.filter(models.DataFile, lookups=True)
-class DataFileFilter(filtersets.DataFileFilterSet):
-    id: auto
+@autotype_decorator(filtersets.DataFileFilterSet)
+class DataFileFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.DataSource, lookups=True)
-class DataSourceFilter(filtersets.DataSourceFilterSet):
-    id: auto
+@autotype_decorator(filtersets.DataSourceFilterSet)
+class DataSourceFilter(BaseFilterMixin):
+    pass

+ 125 - 83
netbox/dcim/graphql/filters.py

@@ -1,7 +1,8 @@
 import strawberry
 import strawberry_django
 from dcim import filtersets, models
-from strawberry import auto
+
+from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
 
 __all__ = (
     'CableFilter',
@@ -49,205 +50,246 @@ __all__ = (
 
 
 @strawberry_django.filter(models.Cable, lookups=True)
-class CableFilter(filtersets.CableFilterSet):
-    id: auto
+@autotype_decorator(filtersets.CableFilterSet)
+class CableFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.CableTermination, lookups=True)
-class CableTerminationFilter(filtersets.CableTerminationFilterSet):
-    id: auto
+@autotype_decorator(filtersets.CableTerminationFilterSet)
+class CableTerminationFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.ConsolePort, lookups=True)
-class ConsolePortFilter(filtersets.ConsolePortFilterSet):
-    id: auto
+@autotype_decorator(filtersets.ConsolePortFilterSet)
+class ConsolePortFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.ConsolePortTemplate, lookups=True)
-class ConsolePortTemplateFilter(filtersets.ConsolePortTemplateFilterSet):
-    id: auto
+@autotype_decorator(filtersets.ConsolePortTemplateFilterSet)
+class ConsolePortTemplateFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.ConsoleServerPort, lookups=True)
-class ConsoleServerPortFilter(filtersets.ConsoleServerPortFilterSet):
-    id: auto
+@autotype_decorator(filtersets.ConsoleServerPortFilterSet)
+class ConsoleServerPortFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.ConsoleServerPortTemplate, lookups=True)
-class ConsoleServerPortTemplateFilter(filtersets.ConsoleServerPortTemplateFilterSet):
-    id: auto
+@autotype_decorator(filtersets.ConsoleServerPortTemplateFilterSet)
+class ConsoleServerPortTemplateFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Device, lookups=True)
-class DeviceFilter(filtersets.DeviceFilterSet):
-    id: auto
+@autotype_decorator(filtersets.DeviceFilterSet)
+class DeviceFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.DeviceBay, lookups=True)
-class DeviceBayFilter(filtersets.DeviceBayFilterSet):
-    id: auto
+@autotype_decorator(filtersets.DeviceBayFilterSet)
+class DeviceBayFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.DeviceBayTemplate, lookups=True)
-class DeviceBayTemplateFilter(filtersets.DeviceBayTemplateFilterSet):
-    id: auto
+@autotype_decorator(filtersets.DeviceBayTemplateFilterSet)
+class DeviceBayTemplateFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.InventoryItemTemplate, lookups=True)
-class InventoryItemTemplateFilter(filtersets.InventoryItemTemplateFilterSet):
-    id: auto
+@autotype_decorator(filtersets.InventoryItemTemplateFilterSet)
+class InventoryItemTemplateFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.DeviceRole, lookups=True)
-class DeviceRoleFilter(filtersets.DeviceRoleFilterSet):
-    id: auto
+@autotype_decorator(filtersets.DeviceRoleFilterSet)
+class DeviceRoleFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.DeviceType, lookups=True)
-class DeviceTypeFilter(filtersets.DeviceTypeFilterSet):
-    id: auto
+@autotype_decorator(filtersets.DeviceTypeFilterSet)
+class DeviceTypeFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.FrontPort, lookups=True)
-class FrontPortFilter(filtersets.FrontPortFilterSet):
-    id: auto
+@autotype_decorator(filtersets.FrontPortFilterSet)
+class FrontPortFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.FrontPortTemplate, lookups=True)
-class FrontPortTemplateFilter(filtersets.FrontPortTemplateFilterSet):
-    id: auto
+@autotype_decorator(filtersets.FrontPortTemplateFilterSet)
+class FrontPortTemplateFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Interface, lookups=True)
-class InterfaceFilter(filtersets.InterfaceFilterSet):
-    id: auto
+@autotype_decorator(filtersets.InterfaceFilterSet)
+class InterfaceFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.InterfaceTemplate, lookups=True)
-class InterfaceTemplateFilter(filtersets.InterfaceTemplateFilterSet):
-    id: auto
+@autotype_decorator(filtersets.InterfaceTemplateFilterSet)
+class InterfaceTemplateFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.InventoryItem, lookups=True)
-class InventoryItemFilter(filtersets.InventoryItemFilterSet):
-    id: auto
+@autotype_decorator(filtersets.InventoryItemFilterSet)
+class InventoryItemFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.InventoryItemRole, lookups=True)
-class InventoryItemRoleFilter(filtersets.InventoryItemRoleFilterSet):
-    id: auto
+@autotype_decorator(filtersets.InventoryItemRoleFilterSet)
+class InventoryItemRoleFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Location, lookups=True)
-class LocationFilter(filtersets.LocationFilterSet):
-    id: auto
+@autotype_decorator(filtersets.LocationFilterSet)
+class LocationFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Manufacturer, lookups=True)
-class ManufacturerFilter(filtersets.ManufacturerFilterSet):
-    id: auto
+@autotype_decorator(filtersets.ManufacturerFilterSet)
+class ManufacturerFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Module, lookups=True)
-class ModuleFilter(filtersets.ModuleFilterSet):
-    id: auto
+@autotype_decorator(filtersets.ModuleFilterSet)
+class ModuleFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.ModuleBay, lookups=True)
-class ModuleBayFilter(filtersets.ModuleBayFilterSet):
-    id: auto
+@autotype_decorator(filtersets.ModuleBayFilterSet)
+class ModuleBayFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.ModuleBayTemplate, lookups=True)
-class ModuleBayTemplateFilter(filtersets.ModuleBayTemplateFilterSet):
-    id: auto
+@autotype_decorator(filtersets.ModuleBayTemplateFilterSet)
+class ModuleBayTemplateFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.ModuleType, lookups=True)
-class ModuleTypeFilter(filtersets.ModuleTypeFilterSet):
-    id: auto
+@autotype_decorator(filtersets.ModuleTypeFilterSet)
+class ModuleTypeFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Platform, lookups=True)
-class PlatformFilter(filtersets.PlatformFilterSet):
-    id: auto
+@autotype_decorator(filtersets.PlatformFilterSet)
+class PlatformFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.PowerFeed, lookups=True)
-class PowerFeedFilter(filtersets.PowerFeedFilterSet):
-    id: auto
+@autotype_decorator(filtersets.PowerFeedFilterSet)
+class PowerFeedFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.PowerOutlet, lookups=True)
-class PowerOutletFilter(filtersets.PowerOutletFilterSet):
-    id: auto
+@autotype_decorator(filtersets.PowerOutletFilterSet)
+class PowerOutletFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.PowerOutletTemplate, lookups=True)
-class PowerOutletTemplateFilter(filtersets.PowerOutletTemplateFilterSet):
-    id: auto
+@autotype_decorator(filtersets.PowerOutletTemplateFilterSet)
+class PowerOutletTemplateFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.PowerPanel, lookups=True)
-class PowerPanelFilter(filtersets.PowerPanelFilterSet):
-    id: auto
+@autotype_decorator(filtersets.PowerPanelFilterSet)
+class PowerPanelFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.PowerPort, lookups=True)
-class PowerPortFilter(filtersets.PowerPortFilterSet):
-    id: auto
+@autotype_decorator(filtersets.PowerPortFilterSet)
+class PowerPortFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.PowerPortTemplate, lookups=True)
-class PowerPortTemplateFilter(filtersets.PowerPortTemplateFilterSet):
-    id: auto
+@autotype_decorator(filtersets.PowerPortTemplateFilterSet)
+class PowerPortTemplateFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Rack, lookups=True)
-class RackFilter(filtersets.RackFilterSet):
-    id: auto
+@autotype_decorator(filtersets.RackFilterSet)
+class RackFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.RackReservation, lookups=True)
-class RackReservationFilter(filtersets.RackReservationFilterSet):
-    id: auto
+@autotype_decorator(filtersets.RackReservationFilterSet)
+class RackReservationFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.RackRole, lookups=True)
-class RackRoleFilter(filtersets.RackRoleFilterSet):
-    id: auto
+@autotype_decorator(filtersets.RackRoleFilterSet)
+class RackRoleFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.RearPort, lookups=True)
-class RearPortFilter(filtersets.RearPortFilterSet):
-    id: auto
+@autotype_decorator(filtersets.RearPortFilterSet)
+class RearPortFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.RearPortTemplate, lookups=True)
-class RearPortTemplateFilter(filtersets.RearPortTemplateFilterSet):
-    id: auto
+@autotype_decorator(filtersets.RearPortTemplateFilterSet)
+class RearPortTemplateFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Region, lookups=True)
-class RegionFilter(filtersets.RegionFilterSet):
-    id: auto
+@autotype_decorator(filtersets.RegionFilterSet)
+class RegionFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Site, lookups=True)
-class SiteFilter(filtersets.SiteFilterSet):
-    id: auto
+@autotype_decorator(filtersets.SiteFilterSet)
+class SiteFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.SiteGroup, lookups=True)
-class SiteGroupFilter(filtersets.SiteGroupFilterSet):
-    id: auto
+@autotype_decorator(filtersets.SiteGroupFilterSet)
+class SiteGroupFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.VirtualChassis, lookups=True)
-class VirtualChassisFilter(filtersets.VirtualChassisFilterSet):
-    id: auto
+@autotype_decorator(filtersets.VirtualChassisFilterSet)
+class VirtualChassisFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.VirtualDeviceContext, lookups=True)
-class VirtualDeviceContextFilter(filtersets.VirtualDeviceContextFilterSet):
-    id: auto
+@autotype_decorator(filtersets.VirtualDeviceContextFilterSet)
+class VirtualDeviceContextFilter(BaseFilterMixin):
+    pass

+ 42 - 99
netbox/extras/graphql/filters.py

@@ -1,7 +1,8 @@
 import strawberry
 import strawberry_django
-from strawberry import auto
-from extras import models, filtersets
+from extras import filtersets, models
+
+from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
 
 __all__ = (
     'ConfigContextFilter',
@@ -21,136 +22,78 @@ __all__ = (
 
 
 @strawberry_django.filter(models.ConfigContext, lookups=True)
-class ConfigContextFilter(filtersets.ConfigContextFilterSet):
-    id: auto
-    name: auto
-    is_active: auto
-    data_synced: auto
+@autotype_decorator(filtersets.ConfigContextFilterSet)
+class ConfigContextFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.ConfigTemplate, lookups=True)
-class ConfigTemplateFilter(filtersets.ConfigTemplateFilterSet):
-    id: auto
-    name: auto
-    description: auto
-    data_synced: auto
+@autotype_decorator(filtersets.ConfigTemplateFilterSet)
+class ConfigTemplateFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.CustomField, lookups=True)
-class CustomFieldFilter(filtersets.CustomFieldFilterSet):
-    id: auto
-    object_types: auto
-    name: auto
-    group_name: auto
-    required: auto
-    search_weight: auto
-    filter_logic: auto
-    weight: auto
-    is_cloneable: auto
-    description: auto
+@autotype_decorator(filtersets.CustomFieldFilterSet)
+class CustomFieldFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.CustomFieldChoiceSet, lookups=True)
-class CustomFieldChoiceSetFilter(filtersets.CustomFieldChoiceSetFilterSet):
-    id: auto
-    name: auto
-    description: auto
-    base_choices: auto
-    order_alphabetically: auto
+@autotype_decorator(filtersets.CustomFieldChoiceSetFilterSet)
+class CustomFieldChoiceSetFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.CustomLink, lookups=True)
-class CustomLinkFilter(filtersets.CustomLinkFilterSet):
-    id: auto
-    object_types: auto
-    name: auto
-    enabled: auto
-    link_text: auto
-    link_url: auto
-    weight: auto
-    group_name: auto
-    new_window: auto
+@autotype_decorator(filtersets.CustomLinkFilterSet)
+class CustomLinkFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.ExportTemplate, lookups=True)
-class ExportTemplateFilter(filtersets.ExportTemplateFilterSet):
-    id: auto
-    object_types: auto
-    name: auto
-    description: auto
-    data_synced: auto
+@autotype_decorator(filtersets.ExportTemplateFilterSet)
+class ExportTemplateFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.ImageAttachment, lookups=True)
-class ImageAttachmentFilter(filtersets.ImageAttachmentFilterSet):
-    id: auto
-    object_type_id: auto
-    object_id: auto
-    name: auto
+@autotype_decorator(filtersets.ImageAttachmentFilterSet)
+class ImageAttachmentFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.JournalEntry, lookups=True)
-class JournalEntryFilter(filtersets.JournalEntryFilterSet):
-    id: auto
-    assigned_object_type_id: auto
-    assigned_object_id: auto
-    created: auto
-    kind: auto
+@autotype_decorator(filtersets.JournalEntryFilterSet)
+class JournalEntryFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.ObjectChange, lookups=True)
-class ObjectChangeFilter(filtersets.ObjectChangeFilterSet):
-    id: auto
-    user: auto
-    user_name: auto
-    request_id: auto
-    action: auto
-    changed_object_type_id: auto
-    changed_object_id: auto
-    object_repr: auto
+@autotype_decorator(filtersets.ObjectChangeFilterSet)
+class ObjectChangeFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.SavedFilter, lookups=True)
-class SavedFilterFilter(filtersets.SavedFilterFilterSet):
-    id: auto
-    object_types: auto
-    name: auto
-    slug: auto
-    description: auto
-    enabled: auto
-    shared: auto
-    weight: auto
+@autotype_decorator(filtersets.SavedFilterFilterSet)
+class SavedFilterFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Tag, lookups=True)
-class TagFilter(filtersets.TagFilterSet):
-    id: auto
-    name: auto
-    slug: auto
-    # color: auto
-    description: auto
-    object_types: auto
+@autotype_decorator(filtersets.TagFilterSet)
+class TagFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Webhook, lookups=True)
-class WebhookFilter(filtersets.WebhookFilterSet):
-    id: auto
-    name: auto
-    payload_url: auto
-    http_method: auto
-    http_content_type: auto
-    secret: auto
-    ssl_verification: auto
-    ca_file_path: auto
+@autotype_decorator(filtersets.WebhookFilterSet)
+class WebhookFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.EventRule, lookups=True)
-class EventRuleFilter(filtersets.EventRuleFilterSet):
-    id: auto
-    name: auto
-    enabled: auto
-    type_create: auto
-    type_update: auto
-    type_delete: auto
-    type_job_start: auto
-    type_job_end: auto
+@autotype_decorator(filtersets.EventRuleFilterSet)
+class EventRuleFilter(BaseFilterMixin):
+    pass

+ 49 - 34
netbox/ipam/graphql/filters.py

@@ -1,7 +1,8 @@
 import strawberry
 import strawberry_django
-from strawberry import auto
-from ipam import models, filtersets
+from ipam import filtersets, models
+
+from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
 
 
 __all__ = (
@@ -25,80 +26,94 @@ __all__ = (
 
 
 @strawberry_django.filter(models.ASN, lookups=True)
-class ASNFilter(filtersets.ASNFilterSet):
-    id: auto
+class ASNFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.ASNRange, lookups=True)
-class ASNRangeFilter(filtersets.ASNRangeFilterSet):
-    id: auto
+class ASNRangeFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Aggregate, lookups=True)
-class AggregateFilter(filtersets.AggregateFilterSet):
-    id: auto
+@autotype_decorator(filtersets.AggregateFilterSet)
+class AggregateFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.FHRPGroup, lookups=True)
-class FHRPGroupFilter(filtersets.FHRPGroupFilterSet):
-    id: auto
+@autotype_decorator(filtersets.FHRPGroupFilterSet)
+class FHRPGroupFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.FHRPGroupAssignment, lookups=True)
-class FHRPGroupAssignmentFilter(filtersets.FHRPGroupAssignmentFilterSet):
-    id: auto
+@autotype_decorator(filtersets.FHRPGroupAssignmentFilterSet)
+class FHRPGroupAssignmentFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.IPAddress, lookups=True)
-class IPAddressFilter(filtersets.IPAddressFilterSet):
-    id: auto
+@autotype_decorator(filtersets.IPAddressFilterSet)
+class IPAddressFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.IPRange, lookups=True)
-class IPRangeFilter(filtersets.IPRangeFilterSet):
-    id: auto
+@autotype_decorator(filtersets.IPRangeFilterSet)
+class IPRangeFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Prefix, lookups=True)
-class PrefixFilter(filtersets.PrefixFilterSet):
-    id: auto
+@autotype_decorator(filtersets.PrefixFilterSet)
+class PrefixFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.RIR, lookups=True)
-class RIRFilter(filtersets.RIRFilterSet):
-    id: auto
+@autotype_decorator(filtersets.RIRFilterSet)
+class RIRFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Role, lookups=True)
-class RoleFilter(filtersets.RoleFilterSet):
-    id: auto
+@autotype_decorator(filtersets.RoleFilterSet)
+class RoleFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.RouteTarget, lookups=True)
-class RouteTargetFilter(filtersets.RouteTargetFilterSet):
-    id: auto
+@autotype_decorator(filtersets.RouteTargetFilterSet)
+class RouteTargetFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.Service, lookups=True)
-class ServiceFilter(filtersets.ServiceFilterSet):
-    id: auto
+@autotype_decorator(filtersets.ServiceFilterSet)
+class ServiceFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.ServiceTemplate, lookups=True)
-class ServiceTemplateFilter(filtersets.ServiceTemplateFilterSet):
-    id: auto
+@autotype_decorator(filtersets.ServiceTemplateFilterSet)
+class ServiceTemplateFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.VLAN, lookups=True)
-class VLANFilter(filtersets.VLANFilterSet):
-    id: auto
+@autotype_decorator(filtersets.VLANFilterSet)
+class VLANFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.VLANGroup, lookups=True)
-class VLANGroupFilter(filtersets.VLANGroupFilterSet):
-    id: auto
+@autotype_decorator(filtersets.VLANGroupFilterSet)
+class VLANGroupFilter(BaseFilterMixin):
+    pass
 
 
 @strawberry_django.filter(models.VRF, lookups=True)
-class VRFFilter(filtersets.VRFFilterSet):
-    id: auto
+@autotype_decorator(filtersets.VRFFilterSet)
+class VRFFilter(BaseFilterMixin):
+    pass

+ 169 - 1
netbox/netbox/graphql/filter_mixins.py

@@ -1,12 +1,180 @@
+from functools import partial, partialmethod, wraps
 from typing import List
+
+import django_filters
 import strawberry
 import strawberry_django
 from strawberry import auto
+from utilities.fields import ColorField
+from utilities.filters import *
+
+
+def autotype_decorator(filterset):
+
+    def wrapper(cls):
+        print(f"cls: {cls}")
+        cls.filterset = filterset
+        fields = filterset.get_fields()
+        model = filterset._meta.model
+        for fieldname in fields.keys():
+            attr_type = auto
+            if fieldname not in cls.__annotations__:
+                field = model._meta.get_field(fieldname)
+                if isinstance(field, ColorField):
+                    attr_type = List[str] | None
+
+                cls.__annotations__[fieldname] = attr_type
+
+        declared_filters = filterset.declared_filters
+        for fieldname, v in declared_filters.items():
+            create_function = False
+            attr_type = None
+
+            # NetBox Filter types - put base classes after derived classes
+            if isinstance(v, ContentTypeFilter):
+                create_function = True
+                attr_type = str | None
+            elif isinstance(v, MACAddressFilter):
+                print(f"{fieldname}: {v}")
+                print("MACAddressFilter")
+            elif isinstance(v, MultiValueArrayFilter):
+                print(f"{fieldname}: {v}")
+                print("MultiValueArrayFilter")
+            elif isinstance(v, MultiValueCharFilter):
+                create_function = True
+                attr_type = List[str] | None
+            elif isinstance(v, MultiValueDateFilter):
+                attr_type = auto
+            elif isinstance(v, MultiValueDateTimeFilter):
+                attr_type = auto
+            elif isinstance(v, MultiValueDecimalFilter):
+                print(f"{fieldname}: {v}")
+                print("MultiValueDecimalFilter")
+            elif isinstance(v, MultiValueMACAddressFilter):
+                create_function = True
+                attr_type = List[str] | None
+            elif isinstance(v, MultiValueNumberFilter):
+                create_function = True
+                attr_type = List[str] | None
+            elif isinstance(v, MultiValueTimeFilter):
+                print(f"{fieldname}: {v}")
+                print("MultiValueTimeFilter")
+            elif isinstance(v, MultiValueWWNFilter):
+                create_function = True
+                attr_type = List[str] | None
+            elif isinstance(v, NullableCharFieldFilter):
+                print(f"{fieldname}: {v}")
+                print("NullableCharFieldFilter")
+            elif isinstance(v, NumericArrayFilter):
+                print(f"{fieldname}: {v}")
+                print("NumericArrayFilter")
+            elif isinstance(v, TreeNodeMultipleChoiceFilter):
+                create_function = True
+                attr_type = List[str] | None
+
+            # From django_filters - ordering of these matters as base classes must
+            # come after derived classes so the base class doesn't get matched first
+            elif issubclass(type(v), django_filters.OrderingFilter):
+                print(f"{fieldname}: {v}")
+                print("OrderingFilter")
+            elif issubclass(type(v), django_filters.BaseRangeFilter):
+                print(f"{fieldname}: {v}")
+                print("BaseRangeFilter")
+            elif issubclass(type(v), django_filters.BaseInFilter):
+                print(f"{fieldname}: {v}")
+                print("BaseInFilter")
+            elif issubclass(type(v), django_filters.LookupChoiceFilter):
+                print(f"{fieldname}: {v}")
+                print("LookupChoiceFilter")
+            elif issubclass(type(v), django_filters.AllValuesMultipleFilter):
+                print(f"{fieldname}: {v}")
+                print("AllValuesMultipleFilter")
+            elif issubclass(type(v), django_filters.AllValuesFilter):
+                print(f"{fieldname}: {v}")
+                print("AllValuesFilter")
+            elif issubclass(type(v), django_filters.TimeRangeFilter):
+                print(f"{fieldname}: {v}")
+                print("TimeRangeFilter")
+            elif issubclass(type(v), django_filters.IsoDateTimeFromToRangeFilter):
+                create_function = True
+                attr_type = str | None
+            elif issubclass(type(v), django_filters.DateTimeFromToRangeFilter):
+                create_function = True
+                attr_type = str | None
+            elif issubclass(type(v), django_filters.DateFromToRangeFilter):
+                create_function = True
+                attr_type = str | None
+            elif issubclass(type(v), django_filters.DateRangeFilter):
+                create_function = True
+                attr_type = str | None
+            elif issubclass(type(v), django_filters.RangeFilter):
+                print(f"{fieldname}: {v}")
+                print("RangeFilter")
+            elif issubclass(type(v), django_filters.NumericRangeFilter):
+                print(f"{fieldname}: {v}")
+                print("NumericRangeFilter")
+            elif issubclass(type(v), django_filters.NumberFilter):
+                print(f"{fieldname}: {v}")
+                print("NumberFilter")
+            elif issubclass(type(v), django_filters.ModelMultipleChoiceFilter):
+                create_function = True
+                attr_type = List[str] | None
+            elif issubclass(type(v), django_filters.ModelChoiceFilter):
+                create_function = True
+                attr_type = str | None
+            elif issubclass(type(v), django_filters.DurationFilter):
+                print(f"{fieldname}: {v}")
+                print("DurationFilter")
+            elif issubclass(type(v), django_filters.IsoDateTimeFilter):
+                print(f"{fieldname}: {v}")
+                print("IsoDateTimeFilter")
+            elif issubclass(type(v), django_filters.DateTimeFilter):
+                attr_type = auto
+            elif issubclass(type(v), django_filters.TimeFilter):
+                attr_type = auto
+            elif issubclass(type(v), django_filters.DateFilter):
+                attr_type = auto
+            elif issubclass(type(v), django_filters.TypedMultipleChoiceFilter):
+                print(f"{fieldname}: {v}")
+                print("TypedMultipleChoiceFilter")
+            elif issubclass(type(v), django_filters.MultipleChoiceFilter):
+                create_function = True
+                attr_type = List[str] | None
+            elif issubclass(type(v), django_filters.TypedChoiceFilter):
+                print(f"{fieldname}: {v}")
+                print("TypedChoiceFilter")
+            elif issubclass(type(v), django_filters.ChoiceFilter):
+                print(f"{fieldname}: {v}")
+                print("ChoiceFilter")
+            elif issubclass(type(v), django_filters.BooleanFilter):
+                create_function = True
+                attr_type = bool | None
+            elif issubclass(type(v), django_filters.UUIDFilter):
+                create_function = True
+                attr_type = str | None
+            elif issubclass(type(v), django_filters.CharFilter):
+                # looks like only used by 'q'
+                create_function = True
+                attr_type = str | None
+            else:
+                print(f"{fieldname}: {v}")
+                print("unknown type!")
+
+            if fieldname not in cls.__annotations__ and attr_type:
+                cls.__annotations__[fieldname] = attr_type
+
+            fname = f"filter_{fieldname}"
+            if create_function and not hasattr(cls, fname):
+                filter_by_filterset = getattr(cls, 'filter_by_filterset')
+                setattr(cls, fname, partialmethod(filter_by_filterset, key=fieldname))
+
+        return cls
+
+    return wrapper
 
 
 @strawberry.input
 class BaseFilterMixin:
-    id: auto
 
     def filter_by_filterset(self, queryset, key):
         return self.filterset(data={key: getattr(self, key)}, queryset=queryset).qs