Skip to content

Commit 66e7bcc

Browse files
authored
Merge pull request #332 from adamchainz/update_tested_versions
[change] Update tested versions
2 parents df8e8b5 + 59dfd89 commit 66e7bcc

29 files changed

+762
-738
lines changed

.github/workflows/ci.yml

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ on:
1212
jobs:
1313
build:
1414
name: Python==${{ matrix.env.python }} | ${{ matrix.env.TOXENV }}
15-
runs-on: ubuntu-20.04
15+
runs-on: ubuntu-24.04
1616

1717
services:
1818
postgres:
19-
image: postgis/postgis:15-3.4-alpine
19+
image: postgis/postgis:17-3.5-alpine
2020
env:
2121
POSTGRES_PASSWORD: postgres
2222
POSTGRES_USER: postgres
@@ -37,10 +37,16 @@ jobs:
3737
- python: '3.11'
3838
TOXENV: py311-django42-djangorestframework314
3939
# DRF 3.15
40+
- python: '3.10'
41+
TOXENV: py310-django50-djangorestframework315
4042
- python: '3.11'
4143
TOXENV: py311-django50-djangorestframework315
4244
- python: '3.12'
4345
TOXENV: py312-django50-djangorestframework315
46+
- python: '3.13'
47+
TOXENV: py313-django50-djangorestframework315
48+
- python: '3.10'
49+
TOXENV: py310-django51-djangorestframework315
4450
- python: '3.11'
4551
TOXENV: py311-django51-djangorestframework315
4652
- python: '3.12'
@@ -54,6 +60,16 @@ jobs:
5460
TOXENV: py311-django51-djangorestframework316
5561
- python: '3.12'
5662
TOXENV: py312-django51-djangorestframework316
63+
- python: '3.13'
64+
TOXENV: py313-django51-djangorestframework315
65+
- python: '3.10'
66+
TOXENV: py310-django52-djangorestframework315
67+
- python: '3.11'
68+
TOXENV: py311-django52-djangorestframework315
69+
- python: '3.12'
70+
TOXENV: py312-django52-djangorestframework315
71+
- python: '3.13'
72+
TOXENV: py313-django52-djangorestframework315
5773
steps:
5874
- uses: actions/checkout@v4
5975
with:

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ dist/
55
build/
66
tests/static/
77
*local_settings.py
8-
.coverage
8+
.coverage*
99
*~
1010
._*
1111
*.DS_Store

CHANGES.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ Changelog
44
Version 1.2.0 [Unreleased]
55
--------------------------
66

7-
Work in progress.
7+
- Added Django ``5.2`` to automated testing build.
8+
- Added Python ``3.13`` to automated testing build.
89

910
Version 1.1.0 [2024-08-17]
1011
--------------------------

README.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Compatibility with DRF, Django and Python
4141

4242
======================== ============================ ==================== ==================================
4343
DRF-gis version DRF version Django version Python version
44+
**1.2.x** **3.12** up to **3.15** **4.2 to 5.2** **3.9** to **3.13**
4445
**1.1.x** **3.12** up to **3.15** **3.2, 4.2 to 5.1** **3.8** to **3.12**
4546
**1.0.x** **3.10** up to **3.13** **2.2 to 4.0** **3.6** to **3.9**
4647
**0.18.x** **3.10** up to **3.13** **2.2 to 4.0** **3.6** to **3.9**

rest_framework_gis/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
VERSION = (1, 2, 0, 'alpha')
1+
VERSION = (1, 2, 0, "alpha")
22
__version__ = VERSION # alias
33

44

55
def get_version():
6-
version = '%s.%s.%s' % (VERSION[0], VERSION[1], VERSION[2])
7-
if VERSION[3] != 'final':
6+
version = "%s.%s.%s" % (VERSION[0], VERSION[1], VERSION[2])
7+
if VERSION[3] != "final":
88
first_letter = VERSION[3][0:1]
99
try:
1010
suffix = VERSION[4]
1111
except IndexError:
1212
suffix = 0
13-
version = '%s%s%s' % (version, first_letter, suffix)
13+
version = "%s%s%s" % (version, first_letter, suffix)
1414
return version

rest_framework_gis/apps.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33

44
class AppConfig(BaseConfig):
5-
name = 'rest_framework_gis'
5+
name = "rest_framework_gis"
66

77
def ready(self):
88
"""

rest_framework_gis/fields.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
from django.utils.translation import gettext_lazy as _
88
from rest_framework.fields import Field, SerializerMethodField
99

10-
__all__ = ['GeometryField', 'GeometrySerializerMethodField']
10+
__all__ = ["GeometryField", "GeometrySerializerMethodField"]
1111

1212

1313
class GeometryField(Field):
1414
"""
1515
A field to handle GeoDjango Geometry fields
1616
"""
1717

18-
type_name = 'GeometryField'
18+
type_name = "GeometryField"
1919

2020
def __init__(
2121
self, precision=None, remove_duplicates=False, auto_bbox=False, **kwargs
@@ -27,7 +27,7 @@ def __init__(
2727
self.auto_bbox = auto_bbox
2828
self.remove_dupes = remove_duplicates
2929
super().__init__(**kwargs)
30-
self.style.setdefault('base_template', 'textarea.html')
30+
self.style.setdefault("base_template", "textarea.html")
3131

3232
def to_representation(self, value):
3333
if isinstance(value, dict) or value is None:
@@ -37,26 +37,26 @@ def to_representation(self, value):
3737
geojson = GeoJsonDict(value.geojson)
3838
# in this case we're dealing with an empty point
3939
else:
40-
geojson = GeoJsonDict({'type': value.geom_type, 'coordinates': []})
41-
if geojson['type'] == 'GeometryCollection':
42-
geometries = geojson.get('geometries')
40+
geojson = GeoJsonDict({"type": value.geom_type, "coordinates": []})
41+
if geojson["type"] == "GeometryCollection":
42+
geometries = geojson.get("geometries")
4343
else:
4444
geometries = [geojson]
4545
for geometry in geometries:
4646
if self.precision is not None:
47-
geometry['coordinates'] = self._recursive_round(
48-
geometry['coordinates'], self.precision
47+
geometry["coordinates"] = self._recursive_round(
48+
geometry["coordinates"], self.precision
4949
)
5050
if self.remove_dupes:
51-
geometry['coordinates'] = self._rm_redundant_points(
52-
geometry['coordinates'], geometry['type']
51+
geometry["coordinates"] = self._rm_redundant_points(
52+
geometry["coordinates"], geometry["type"]
5353
)
5454
if self.auto_bbox:
5555
geojson["bbox"] = value.extent
5656
return geojson
5757

5858
def to_internal_value(self, value):
59-
if value == '' or value is None:
59+
if value == "" or value is None:
6060
return value
6161
if isinstance(value, GEOSGeometry):
6262
# value already has the correct representation
@@ -68,15 +68,15 @@ def to_internal_value(self, value):
6868
except GEOSException:
6969
raise ValidationError(
7070
_(
71-
'Invalid format: string or unicode input unrecognized as GeoJSON, WKT EWKT or HEXEWKB.'
71+
"Invalid format: string or unicode input unrecognized as GeoJSON, WKT EWKT or HEXEWKB."
7272
)
7373
)
7474
except (ValueError, TypeError, GDALException) as e:
75-
raise ValidationError(_(f'Unable to convert to python object: {str(e)}'))
75+
raise ValidationError(_(f"Unable to convert to python object: {str(e)}"))
7676

7777
def validate_empty_values(self, data):
78-
if data == '':
79-
self.fail('required')
78+
if data == "":
79+
self.fail("required")
8080
return super().validate_empty_values(data)
8181

8282
def _recursive_round(self, value, precision):
@@ -85,7 +85,7 @@ def _recursive_round(self, value, precision):
8585
value: number or nested array of numbers
8686
precision: integer valueue of number of decimals to keep
8787
"""
88-
if hasattr(value, '__iter__'):
88+
if hasattr(value, "__iter__"):
8989
return tuple(self._recursive_round(v, precision) for v in value)
9090
return round(value, precision)
9191

@@ -96,8 +96,8 @@ def _rm_redundant_points(self, geometry, geo_type):
9696
geo_type: GeoJSON type attribute for provided geometry, used to
9797
determine structure of provided `geometry` argument
9898
"""
99-
if geo_type in ('MultiPoint', 'LineString'):
100-
close = geo_type == 'LineString'
99+
if geo_type in ("MultiPoint", "LineString"):
100+
close = geo_type == "LineString"
101101
output = []
102102
for coord in geometry:
103103
coord = tuple(coord)
@@ -106,10 +106,10 @@ def _rm_redundant_points(self, geometry, geo_type):
106106
if close and len(output) == 1:
107107
output.append(output[0])
108108
return tuple(output)
109-
if geo_type in ('MultiLineString', 'Polygon'):
110-
return [self._rm_redundant_points(c, 'LineString') for c in geometry]
111-
if geo_type == 'MultiPolygon':
112-
return [self._rm_redundant_points(c, 'Polygon') for c in geometry]
109+
if geo_type in ("MultiLineString", "Polygon"):
110+
return [self._rm_redundant_points(c, "LineString") for c in geometry]
111+
if geo_type == "MultiPolygon":
112+
return [self._rm_redundant_points(c, "Polygon") for c in geometry]
113113
return geometry
114114

115115

rest_framework_gis/filters.py

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,49 +24,49 @@
2424

2525

2626
__all__ = [
27-
'InBBoxFilter',
28-
'InBBOXFilter',
29-
'GeometryFilter',
30-
'GeoFilterSet',
31-
'TMSTileFilter',
32-
'DistanceToPointFilter',
33-
'DistanceToPointOrderingFilter',
27+
"InBBoxFilter",
28+
"InBBOXFilter",
29+
"GeometryFilter",
30+
"GeoFilterSet",
31+
"TMSTileFilter",
32+
"DistanceToPointFilter",
33+
"DistanceToPointOrderingFilter",
3434
]
3535

3636

3737
class InBBoxFilter(BaseFilterBackend):
38-
bbox_param = 'in_bbox' # The URL query parameter which contains the bbox.
38+
bbox_param = "in_bbox" # The URL query parameter which contains the bbox.
3939

4040
def get_filter_bbox(self, request):
4141
bbox_string = request.query_params.get(self.bbox_param, None)
4242
if not bbox_string:
4343
return None
4444

4545
try:
46-
p1x, p1y, p2x, p2y = (float(n) for n in bbox_string.split(','))
46+
p1x, p1y, p2x, p2y = (float(n) for n in bbox_string.split(","))
4747
except ValueError:
4848
raise ParseError(
49-
f'Invalid bbox string supplied for parameter {self.bbox_param}'
49+
f"Invalid bbox string supplied for parameter {self.bbox_param}"
5050
)
5151

5252
x = Polygon.from_bbox((p1x, p1y, p2x, p2y))
5353
return x
5454

5555
def filter_queryset(self, request, queryset, view):
56-
filter_field = getattr(view, 'bbox_filter_field', None)
57-
include_overlapping = getattr(view, 'bbox_filter_include_overlapping', False)
56+
filter_field = getattr(view, "bbox_filter_field", None)
57+
include_overlapping = getattr(view, "bbox_filter_include_overlapping", False)
5858
if include_overlapping:
59-
geoDjango_filter = 'bboverlaps'
59+
geoDjango_filter = "bboverlaps"
6060
else:
61-
geoDjango_filter = 'contained'
61+
geoDjango_filter = "contained"
6262

6363
if not filter_field:
6464
return queryset
6565

6666
bbox = self.get_filter_bbox(request)
6767
if not bbox:
6868
return queryset
69-
return queryset.filter(Q(**{f'{filter_field}__{geoDjango_filter}': bbox}))
69+
return queryset.filter(Q(**{f"{filter_field}__{geoDjango_filter}": bbox}))
7070

7171
def get_schema_operation_parameters(self, view):
7272
return [
@@ -96,13 +96,13 @@ class GeometryFilter(django_filters.Filter):
9696
field_class = forms.GeometryField
9797

9898
def __init__(self, *args, **kwargs):
99-
kwargs.setdefault('widget', forms.TextInput)
99+
kwargs.setdefault("widget", forms.TextInput)
100100
super().__init__(*args, **kwargs)
101101

102102

103103
class GeoFilterSet(django_filters.FilterSet):
104104
GEOFILTER_FOR_DBFIELD_DEFAULTS = {
105-
models.GeometryField: {'filterset_class': GeometryFilter},
105+
models.GeometryField: {"filterset_class": GeometryFilter},
106106
}
107107

108108
def __new__(cls, *args, **kwargs):
@@ -116,18 +116,18 @@ def __new__(cls, *args, **kwargs):
116116

117117

118118
class TMSTileFilter(InBBoxFilter):
119-
tile_param = 'tile' # The URL query parameter which contains the tile address
119+
tile_param = "tile" # The URL query parameter which contains the tile address
120120

121121
def get_filter_bbox(self, request):
122122
tile_string = request.query_params.get(self.tile_param, None)
123123
if not tile_string:
124124
return None
125125

126126
try:
127-
z, x, y = (int(n) for n in tile_string.split('/'))
127+
z, x, y = (int(n) for n in tile_string.split("/"))
128128
except ValueError:
129129
raise ParseError(
130-
f'Invalid tile string supplied for parameter {self.tile_param}'
130+
f"Invalid tile string supplied for parameter {self.tile_param}"
131131
)
132132

133133
bbox = Polygon.from_bbox(tile_edges(x, y, z))
@@ -146,19 +146,19 @@ def get_schema_operation_parameters(self, view):
146146

147147

148148
class DistanceToPointFilter(BaseFilterBackend):
149-
dist_param = 'dist'
150-
point_param = 'point' # The URL query parameter which contains the
149+
dist_param = "dist"
150+
point_param = "point" # The URL query parameter which contains the
151151

152152
def get_filter_point(self, request, **kwargs):
153153
point_string = request.query_params.get(self.point_param, None)
154154
if not point_string:
155155
return None
156156

157157
try:
158-
(x, y) = (float(n) for n in point_string.split(','))
158+
(x, y) = (float(n) for n in point_string.split(","))
159159
except ValueError:
160160
raise ParseError(
161-
'Invalid geometry string supplied for parameter {}'.format(
161+
"Invalid geometry string supplied for parameter {}".format(
162162
self.point_param
163163
)
164164
)
@@ -195,9 +195,9 @@ def dist_to_deg(self, distance, latitude):
195195
return distance / (earthRadius * latitudeCorrection) * rad2deg
196196

197197
def filter_queryset(self, request, queryset, view):
198-
filter_field = getattr(view, 'distance_filter_field', None)
199-
convert_distance_input = getattr(view, 'distance_filter_convert_meters', False)
200-
geoDjango_filter = 'dwithin' # use dwithin for points
198+
filter_field = getattr(view, "distance_filter_field", None)
199+
convert_distance_input = getattr(view, "distance_filter_convert_meters", False)
200+
geoDjango_filter = "dwithin" # use dwithin for points
201201

202202
if not filter_field:
203203
return queryset
@@ -212,7 +212,7 @@ def filter_queryset(self, request, queryset, view):
212212
dist = float(dist_string)
213213
except ValueError:
214214
raise ParseError(
215-
'Invalid distance string supplied for parameter {}'.format(
215+
"Invalid distance string supplied for parameter {}".format(
216216
self.dist_param
217217
)
218218
)
@@ -222,7 +222,7 @@ def filter_queryset(self, request, queryset, view):
222222
dist = self.dist_to_deg(dist, point[1])
223223

224224
return queryset.filter(
225-
Q(**{f'{filter_field}__{geoDjango_filter}': (point, dist)})
225+
Q(**{f"{filter_field}__{geoDjango_filter}": (point, dist)})
226226
)
227227

228228
def get_schema_operation_parameters(self, view):
@@ -256,10 +256,10 @@ def get_schema_operation_parameters(self, view):
256256

257257
class DistanceToPointOrderingFilter(DistanceToPointFilter):
258258
srid = 4326
259-
order_param = 'order'
259+
order_param = "order"
260260

261261
def filter_queryset(self, request, queryset, view):
262-
filter_field = getattr(view, 'distance_ordering_filter_field', None)
262+
filter_field = getattr(view, "distance_ordering_filter_field", None)
263263

264264
if not filter_field:
265265
return queryset
@@ -269,7 +269,7 @@ def filter_queryset(self, request, queryset, view):
269269
return queryset
270270

271271
order = request.query_params.get(self.order_param)
272-
if order == 'desc':
272+
if order == "desc":
273273
return queryset.order_by(-GeometryDistance(filter_field, point))
274274
else:
275275
return queryset.order_by(GeometryDistance(filter_field, point))

0 commit comments

Comments
 (0)