Skip to content

Commit

Permalink
feat: deprecate nSomething in favor of using NOT (#381)
Browse files Browse the repository at this point in the history
Fix #361
  • Loading branch information
bellini666 authored Oct 1, 2023
1 parent c732dbe commit f0f8a3c
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 72 deletions.
4 changes: 4 additions & 0 deletions docs/guide/filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ input FruitFilter {
name: String
AND: FruitFilter
OR: FruitFilter
NOT: FruitFilter
}
```

Expand Down Expand Up @@ -129,6 +130,7 @@ input FruitFilter {
name: StrFilterLookup
AND: FruitFilter
OR: FruitFilter
NOT: FruitFilter
}
```

Expand Down Expand Up @@ -165,6 +167,7 @@ input ColorFilter {
name: String
AND: ColorFilter
OR: ColorFilter
NOT: ColorFilter
}

input FruitFilter {
Expand All @@ -173,6 +176,7 @@ input FruitFilter {
color: ColorFilter
AND: FruitFilter
OR: FruitFilter
NOT: FruitFilter
}
```

Expand Down
101 changes: 81 additions & 20 deletions strawberry_django/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from strawberry.type import WithStrawberryObjectDefinition, has_object_definition
from strawberry.types import Info
from strawberry.unset import UnsetType
from typing_extensions import Self, dataclass_transform
from typing_extensions import Self, assert_never, dataclass_transform

from strawberry_django.utils.typing import (
WithStrawberryDjangoObjectDefinition,
Expand All @@ -50,6 +50,11 @@ class DjangoModelFilterInput:
pk: strawberry.ID


_n_deprecation_reason = """\
The "n" prefix is deprecated and will be removed in the future, use `NOT` instead.
"""


@strawberry.input
class FilterLookup(Generic[T]):
exact: Optional[T] = UNSET
Expand All @@ -69,23 +74,74 @@ class FilterLookup(Generic[T]):
is_null: Optional[bool] = UNSET
regex: Optional[str] = UNSET
i_regex: Optional[str] = UNSET
n_exact: Optional[T] = UNSET
n_i_exact: Optional[T] = UNSET
n_contains: Optional[T] = UNSET
n_i_contains: Optional[T] = UNSET
n_in_list: Optional[List[T]] = UNSET
n_gt: Optional[T] = UNSET
n_gte: Optional[T] = UNSET
n_lt: Optional[T] = UNSET
n_lte: Optional[T] = UNSET
n_starts_with: Optional[T] = UNSET
n_i_starts_with: Optional[T] = UNSET
n_ends_with: Optional[T] = UNSET
n_i_ends_with: Optional[T] = UNSET
n_range: Optional[List[T]] = UNSET
n_is_null: Optional[bool] = UNSET
n_regex: Optional[str] = UNSET
n_i_regex: Optional[str] = UNSET
n_exact: Optional[T] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)
n_i_exact: Optional[T] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)
n_contains: Optional[T] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)
n_i_contains: Optional[T] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)
n_in_list: Optional[List[T]] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)
n_gt: Optional[T] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)
n_gte: Optional[T] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)
n_lt: Optional[T] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)
n_lte: Optional[T] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)
n_starts_with: Optional[T] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)
n_i_starts_with: Optional[T] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)
n_ends_with: Optional[T] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)
n_i_ends_with: Optional[T] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)
n_range: Optional[List[T]] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)
n_is_null: Optional[bool] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)
n_regex: Optional[str] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)
n_i_regex: Optional[str] = strawberry.field(
default=UNSET,
deprecation_reason=_n_deprecation_reason,
)


lookup_name_conversion_map = {
Expand Down Expand Up @@ -155,16 +211,21 @@ def build_filter_kwargs(
continue

if django_model:
if field_name in ("AND", "OR"):
if field_name in ("AND", "OR", "NOT"):
if has_object_definition(field_value):
(
subfield_filter_kwargs,
subfield_filter_methods,
) = build_filter_kwargs(field_value, path)
if field_name == "AND":
filter_kwargs &= subfield_filter_kwargs
else:
elif field_name == "OR":
filter_kwargs |= subfield_filter_kwargs
elif field_name == "NOT":
filter_kwargs &= ~subfield_filter_kwargs
else:
assert_never(field_name)

filter_methods.extend(subfield_filter_methods)
continue

Expand Down
1 change: 1 addition & 0 deletions strawberry_django/type.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ def _process_type(
{
"AND": Optional[Self], # type:ignore
"OR": Optional[Self], # type:ignore
"NOT": Optional[Self], # type:ignore
},
)

Expand Down
3 changes: 3 additions & 0 deletions tests/fields/test_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class Query:
field: String
AND: MyTypeFilter
OR: MyTypeFilter
NOT: MyTypeFilter
}
type Query {
Expand Down Expand Up @@ -137,6 +138,7 @@ class Query:
field: String
AND: MyTypeFilter
OR: MyTypeFilter
NOT: MyTypeFilter
}}
"""An object with a Globally Unique ID"""
Expand Down Expand Up @@ -192,6 +194,7 @@ class Query:
field: String
AND: MyTypeFilter
OR: MyTypeFilter
NOT: MyTypeFilter
}}
"""An object with a Globally Unique ID"""
Expand Down
21 changes: 20 additions & 1 deletion tests/filters/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def test_in_list(query, fruits):
]


def test_not(query, fruits):
def test_deprecated_not(query, fruits):
result = query(
"""{ fruits(filters: {
name: { nEndsWith: "berry" }
Expand All @@ -156,6 +156,25 @@ def test_not(query, fruits):
]


def test_not(query, fruits):
result = query("""{
fruits(
filters: {
NOT: {
name: { endsWith: "berry" }
}
}
) {
id
name
}
}""")
assert not result.errors
assert result.data["fruits"] == [
{"id": "3", "name": "banana"},
]


def test_and(query, fruits):
result = query(
"""{ fruits(filters: {
Expand Down
5 changes: 5 additions & 0 deletions tests/filters/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class Filter:
("types", StrawberryOptional(DjangoModelFilterInput)),
("AND", StrawberryOptional(Filter)),
("OR", StrawberryOptional(Filter)),
("NOT", StrawberryOptional(Filter)),
]


Expand All @@ -47,6 +48,7 @@ class Filter:
("types", "DjangoModelFilterInput"),
("AND", "Filter"),
("OR", "Filter"),
("NOT", "Filter"),
]


Expand All @@ -70,6 +72,7 @@ class Filter(Base):
("types", StrawberryOptional(DjangoModelFilterInput)),
("AND", StrawberryOptional(Filter)),
("OR", StrawberryOptional(Filter)),
("NOT", StrawberryOptional(Filter)),
]


Expand All @@ -87,6 +90,7 @@ class Filter:
("color", StrawberryOptional(ColorFilter)),
("AND", StrawberryOptional(Filter)),
("OR", StrawberryOptional(Filter)),
("NOT", StrawberryOptional(Filter)),
]


Expand All @@ -108,4 +112,5 @@ class Filter(Base):
("color", StrawberryOptional(ColorFilter)),
("AND", StrawberryOptional(Filter)),
("OR", StrawberryOptional(Filter)),
("NOT", StrawberryOptional(Filter)),
]
70 changes: 36 additions & 34 deletions tests/projects/snapshots/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -123,23 +123,23 @@ input DateFilterLookup {
isNull: Boolean
regex: String
iRegex: String
nExact: Date
nIExact: Date
nContains: Date
nIContains: Date
nInList: [Date!]
nGt: Date
nGte: Date
nLt: Date
nLte: Date
nStartsWith: Date
nIStartsWith: Date
nEndsWith: Date
nIEndsWith: Date
nRange: [Date!]
nIsNull: Boolean
nRegex: String
nIRegex: String
nExact: Date @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nIExact: Date @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nContains: Date @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nIContains: Date @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nInList: [Date!] @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nGt: Date @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nGte: Date @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nLt: Date @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nLte: Date @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nStartsWith: Date @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nIStartsWith: Date @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nEndsWith: Date @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nIEndsWith: Date @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nRange: [Date!] @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nIsNull: Boolean @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nRegex: String @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nIRegex: String @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
}

"""Date with time (isoformat)"""
Expand Down Expand Up @@ -273,6 +273,7 @@ input MilestoneFilter {
search: String
AND: MilestoneFilter
OR: MilestoneFilter
NOT: MilestoneFilter
}

input MilestoneInput {
Expand Down Expand Up @@ -430,6 +431,7 @@ input ProjectFilter {
dueDate: DateFilterLookup
AND: ProjectFilter
OR: ProjectFilter
NOT: ProjectFilter
}

input ProjectInputPartial {
Expand Down Expand Up @@ -740,23 +742,23 @@ input StrFilterLookup {
isNull: Boolean
regex: String
iRegex: String
nExact: String
nIExact: String
nContains: String
nIContains: String
nInList: [String!]
nGt: String
nGte: String
nLt: String
nLte: String
nStartsWith: String
nIStartsWith: String
nEndsWith: String
nIEndsWith: String
nRange: [String!]
nIsNull: Boolean
nRegex: String
nIRegex: String
nExact: String @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nIExact: String @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nContains: String @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nIContains: String @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nInList: [String!] @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nGt: String @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nGte: String @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nLt: String @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nLte: String @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nStartsWith: String @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nIStartsWith: String @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nEndsWith: String @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nIEndsWith: String @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nRange: [String!] @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nIsNull: Boolean @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nRegex: String @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
nIRegex: String @deprecated(reason: "The \"n\" prefix is deprecated and will be removed in the future, use `NOT` instead.\n")
}

input TagInputPartial {
Expand Down
Loading

0 comments on commit f0f8a3c

Please sign in to comment.