Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
release type: minor
---

`BoolFilterLookup`, `IDFilterLookup`, `IntFilterLookup`, `FloatFilterLookup` and
`DecimalFilterLookup` no longer require a type parameter, matching
`StrFilterLookup`/`DateFilterLookup`/`TimeFilterLookup`/`DatetimeFilterLookup`
(#891, #910). This completes the same fix for every remaining entry in
`type_filter_map` that was still generic.

```python
@strawberry_django.filter_type(models.Project)
class ProjectFilter:
is_active: strawberry_django.BoolFilterLookup | None
id: strawberry_django.IDFilterLookup | None
priority: strawberry_django.IntFilterLookup | None
weight: strawberry_django.FloatFilterLookup | None
budget: strawberry_django.DecimalFilterLookup | None
```

Migrating:

- Drop the type argument from `BaseFilterLookup[bool]`, `ComparisonFilterLookup[int]`,
etc. when referencing the now-concrete classes directly. The bare lookup now works;
the bracket form still resolves to the same class but emits a `DeprecationWarning`.
- Generated GraphQL input names lose their type prefix, e.g. `IntComparisonFilterLookup`
becomes `IntFilterLookup`, `BoolBaseFilterLookup` becomes `BoolFilterLookup`, and
`IDBaseFilterLookup` becomes `IDFilterLookup`. Clients referencing these inputs by
name need to update.

Building on many models with many filterable fields previously re-specialized these
generic lookups independently at every usage site, which is a meaningful share of
schema build time for large schemas (see #519). Making them concrete removes that
redundant generic re-specialization for the most common filter field types.
10 changes: 10 additions & 0 deletions strawberry_django/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@
from .fields.filter_order import filter_field, order_field
from .fields.filter_types import (
BaseFilterLookup,
BoolFilterLookup,
ComparisonFilterLookup,
DateFilterLookup,
DatetimeFilterLookup,
DecimalFilterLookup,
FilterLookup,
FloatFilterLookup,
IDFilterLookup,
IntFilterLookup,
RangeLookup,
StrFilterLookup,
TimeFilterLookup,
Expand Down Expand Up @@ -37,13 +42,18 @@

__all__ = [
"BaseFilterLookup",
"BoolFilterLookup",
"ComparisonFilterLookup",
"DateFilterLookup",
"DatetimeFilterLookup",
"DecimalFilterLookup",
"DjangoFileType",
"DjangoImageType",
"DjangoModelType",
"FilterLookup",
"FloatFilterLookup",
"IDFilterLookup",
"IntFilterLookup",
"ListInput",
"ManyToManyInput",
"ManyToOneInput",
Expand Down
45 changes: 40 additions & 5 deletions strawberry_django/fields/filter_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,15 +186,50 @@ def __class_getitem__(cls, item: Any) -> type:
return cls


@strawberry.input
class BoolFilterLookup(BaseFilterLookup[bool]):
def __class_getitem__(cls, item: Any) -> type:
_warn_concrete_lookup(cls)
return cls


@strawberry.input
class IDFilterLookup(BaseFilterLookup[strawberry.ID]):
def __class_getitem__(cls, item: Any) -> type:
_warn_concrete_lookup(cls)
return cls


@strawberry.input
class IntFilterLookup(ComparisonFilterLookup[int]):
def __class_getitem__(cls, item: Any) -> type:
_warn_concrete_lookup(cls)
return cls


@strawberry.input
class FloatFilterLookup(ComparisonFilterLookup[float]):
def __class_getitem__(cls, item: Any) -> type:
_warn_concrete_lookup(cls)
return cls


@strawberry.input
class DecimalFilterLookup(ComparisonFilterLookup[decimal.Decimal]):
def __class_getitem__(cls, item: Any) -> type:
_warn_concrete_lookup(cls)
return cls


type_filter_map = {
strawberry.ID: BaseFilterLookup,
bool: BaseFilterLookup,
strawberry.ID: IDFilterLookup,
bool: BoolFilterLookup,
datetime.date: DateFilterLookup,
datetime.datetime: DatetimeFilterLookup,
datetime.time: TimeFilterLookup,
decimal.Decimal: ComparisonFilterLookup,
float: ComparisonFilterLookup,
int: ComparisonFilterLookup,
decimal.Decimal: DecimalFilterLookup,
float: FloatFilterLookup,
int: IntFilterLookup,
str: StrFilterLookup,
uuid.UUID: StrFilterLookup,
}
89 changes: 87 additions & 2 deletions tests/filters/test_filters_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,9 +385,9 @@ def custom_filter2(
for f in get_object_definition(FruitOrder, strict=True).fields
if f.name not in {"NOT", "AND", "OR", "DISTINCT"}
] == [
("id", StrawberryDjangoField, "BaseFilterLookup", None),
("id", StrawberryDjangoField, "IDFilterLookup", None),
("name", StrawberryDjangoField, "StrFilterLookup", None),
("sweetness", StrawberryDjangoField, "ComparisonFilterLookup", None),
("sweetness", StrawberryDjangoField, "IntFilterLookup", None),
(
"custom_filter",
FilterOrderField,
Expand Down Expand Up @@ -775,6 +775,91 @@ class Query:
assert "input DatetimeFilterLookup {" in schema.as_str()


def test_bool_filter_lookup_without_type_parameter():
@strawberry_django.filters.filter_type(models.Fruit)
class FruitFilter:
sweet: strawberry_django.BoolFilterLookup | None

@strawberry_django.type(models.Fruit, filters=FruitFilter)
class FruitType:
name: auto

@strawberry.type
class Query:
fruits: list[FruitType] = strawberry_django.field()

schema = strawberry.Schema(query=Query)
assert "input BoolFilterLookup {" in schema.as_str()


def test_id_filter_lookup_without_type_parameter():
@strawberry_django.filters.filter_type(models.Fruit)
class FruitFilter:
id: strawberry_django.IDFilterLookup | None

@strawberry_django.type(models.Fruit, filters=FruitFilter)
class FruitType:
name: auto

@strawberry.type
class Query:
fruits: list[FruitType] = strawberry_django.field()

schema = strawberry.Schema(query=Query)
assert "input IDFilterLookup {" in schema.as_str()


def test_int_filter_lookup_without_type_parameter():
@strawberry_django.filters.filter_type(models.Fruit)
class FruitFilter:
sweetness: strawberry_django.IntFilterLookup | None

@strawberry_django.type(models.Fruit, filters=FruitFilter)
class FruitType:
name: auto

@strawberry.type
class Query:
fruits: list[FruitType] = strawberry_django.field()

schema = strawberry.Schema(query=Query)
assert "input IntFilterLookup {" in schema.as_str()


def test_float_filter_lookup_without_type_parameter():
@strawberry_django.filters.filter_type(models.Fruit)
class FruitFilter:
weight: strawberry_django.FloatFilterLookup | None

@strawberry_django.type(models.Fruit, filters=FruitFilter)
class FruitType:
name: auto

@strawberry.type
class Query:
fruits: list[FruitType] = strawberry_django.field()

schema = strawberry.Schema(query=Query)
assert "input FloatFilterLookup {" in schema.as_str()


def test_decimal_filter_lookup_without_type_parameter():
@strawberry_django.filters.filter_type(models.Fruit)
class FruitFilter:
price: strawberry_django.DecimalFilterLookup | None

@strawberry_django.type(models.Fruit, filters=FruitFilter)
class FruitType:
name: auto

@strawberry.type
class Query:
fruits: list[FruitType] = strawberry_django.field()

schema = strawberry.Schema(query=Query)
assert "input DecimalFilterLookup {" in schema.as_str()


def test_process_filters_with_some_global_id_in_lookup():
@strawberry_django.filters.filter_type(models.Fruit)
class Filter:
Expand Down
Loading