-
-
Notifications
You must be signed in to change notification settings - Fork 132
Feature: Add geometry filters #710
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e3ed66b
fb3a065
3fc6b2c
2e3e35b
744c260
a0bfa66
17ce3f8
a097404
95dab27
e68b416
2b1db9c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,19 +2,25 @@ | |
import decimal | ||
import uuid | ||
from typing import ( | ||
TYPE_CHECKING, | ||
Annotated, | ||
Generic, | ||
Optional, | ||
TypeVar, | ||
) | ||
|
||
import strawberry | ||
from django.core.exceptions import ImproperlyConfigured | ||
from django.db.models import Q | ||
from strawberry import UNSET | ||
|
||
from strawberry_django.filters import resolve_value | ||
|
||
from .filter_order import filter_field | ||
|
||
if TYPE_CHECKING: | ||
from .types import Geometry | ||
|
||
T = TypeVar("T") | ||
|
||
_SKIP_MSG = "Filter will be skipped on `null` value" | ||
|
@@ -123,3 +129,59 @@ | |
str: FilterLookup, | ||
uuid.UUID: FilterLookup, | ||
} | ||
|
||
|
||
GeometryFilterLookup = None | ||
Check failure on line 134 in strawberry_django/fields/filter_types.py
|
||
|
||
try: | ||
pass | ||
except ImproperlyConfigured: | ||
# If gdal is not available, skip. | ||
pass | ||
else: | ||
|
||
@strawberry.input | ||
class GeometryFilterLookup(Generic[T]): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (complexity): Consider dynamically generating the geometry fields to reduce code duplication. Consider reducing the repetitive field definitions by generating the geometry fields dynamically. For example, you can define a list of method names and then use a helper to build your fields. This will reduce the nesting and repetitive code without changing functionality. For instance: try:
from django.contrib.gis.geos import GEOSGeometry
except ImproperlyConfigured:
# If gdal is not available, skip.
pass
else:
GEO_FIELD_TYPE = Optional[Annotated["Geometry", strawberry.lazy(".types")]]
geometry_methods = [
"bbcontains", "bboverlaps", "contained", "contains", "contains_properly",
"coveredby", "covers", "crosses", "disjoint", "equals", "exacts",
"intersects", "overlaps", "touches", "within", "left", "right",
"overlaps_left", "overlaps_right", "overlaps_above", "overlaps_below",
"strictly_above", "strictly_below",
]
def _build_geometry_fields():
fields = {name: UNSET for name in geometry_methods}
fields.update({
"isempty": filter_field(description=f"Test whether it's empty. {_SKIP_MSG}"),
"isvalid": filter_field(description=f"Test whether it's valid. {_SKIP_MSG}"),
})
return fields
GeometryFields = _build_geometry_fields()
GeometryFilterLookup = strawberry.input(
type("GeometryFilterLookup", (Generic[T],), GeometryFields)
) This refactoring maintains all functionality while reducing the duplication and nesting in your class definition. |
||
bbcontains: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = UNSET | ||
bboverlaps: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = UNSET | ||
contained: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = UNSET | ||
contains: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = UNSET | ||
contains_properly: Optional[ | ||
Annotated["Geometry", strawberry.lazy(".types")] | ||
] = UNSET | ||
coveredby: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = UNSET | ||
covers: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = UNSET | ||
crosses: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = UNSET | ||
disjoint: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = UNSET | ||
equals: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = UNSET | ||
exacts: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = UNSET | ||
intersects: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = UNSET | ||
isempty: Optional[bool] = filter_field( | ||
description=f"Test whether it's empty. {_SKIP_MSG}" | ||
) | ||
isvalid: Optional[bool] = filter_field( | ||
description=f"Test whether it's valid. {_SKIP_MSG}" | ||
) | ||
overlaps: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = UNSET | ||
touches: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = UNSET | ||
within: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = UNSET | ||
left: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = UNSET | ||
right: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = UNSET | ||
overlaps_left: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = ( | ||
UNSET | ||
) | ||
overlaps_right: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = ( | ||
UNSET | ||
) | ||
overlaps_above: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = ( | ||
UNSET | ||
) | ||
overlaps_below: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = ( | ||
UNSET | ||
) | ||
strictly_above: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = ( | ||
UNSET | ||
) | ||
strictly_below: Optional[Annotated["Geometry", strawberry.lazy(".types")]] = ( | ||
UNSET | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ | |
from strawberry.file_uploads.scalars import Upload | ||
from strawberry.scalars import JSON | ||
from strawberry.types.enum import EnumValueDefinition | ||
from strawberry.types.scalar import ScalarWrapper | ||
from strawberry.utils.str_converters import capitalize_first, to_camel_case | ||
|
||
from strawberry_django import filters | ||
|
@@ -348,7 +349,7 @@ | |
Geometry = strawberry.scalar( | ||
NewType("Geometry", geos.GEOSGeometry), | ||
serialize=lambda v: v.tuple if isinstance(v, geos.GEOSGeometry) else v, # type: ignore | ||
parse_value=lambda v: geos.GeometryCollection, | ||
parse_value=lambda v: geos.GEOSGeometry(v), | ||
description=( | ||
"An arbitrary geographical object. One of Point, " | ||
"LineString, LinearRing, Polygon, MultiPoint, MultiLineString, MultiPolygon." | ||
|
@@ -556,10 +557,23 @@ | |
and (field_type is not bool or not using_old_filters) | ||
): | ||
if using_old_filters: | ||
field_type = filters.FilterLookup[field_type] | ||
field_type = filters.FilterLookup[field_type] # type: ignore | ||
elif type( | ||
field_type | ||
) is ScalarWrapper and field_type._scalar_definition.name in ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: might need a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK. |
||
"Point", | ||
"LineString", | ||
"LinearRing", | ||
"Polygon", | ||
"MultiPoint", | ||
"MultilineString", | ||
"MultiPolygon", | ||
"Geometry", | ||
): | ||
field_type = filter_types.GeometryFilterLookup[field_type] | ||
else: | ||
field_type = filter_types.type_filter_map.get( # type: ignore | ||
field_type, filter_types.FilterLookup | ||
Check failure on line 576 in strawberry_django/fields/types.py
|
||
)[field_type] | ||
|
||
return field_type | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
todo: hrm, I think this is missing something =P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that it was removed by auto fixes from pre-commit.com hooks. Originally there was a code to test whether geodjango is available.
from django.contrib.gis.geos import GEOSGeometry
And I think that it's a better idea to define a configuration variable that indicates geodjango is available or not. But I don't know where it should be located.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found this in tests/models.py file. But I think it should be located in somewhere else.