diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0f6491c..31a1215f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,11 +12,11 @@ on: jobs: build: name: Python==${{ matrix.env.python }} | ${{ matrix.env.TOXENV }} - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 services: postgres: - image: postgis/postgis:15-3.4-alpine + image: postgis/postgis:17-3.5-alpine env: POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres @@ -29,20 +29,47 @@ jobs: fail-fast: false matrix: env: + # DRF 3.14 - python: '3.9' TOXENV: py39-django42-djangorestframework314 - python: '3.10' TOXENV: py310-django42-djangorestframework314 - python: '3.11' TOXENV: py311-django42-djangorestframework314 + # DRF 3.15 + - python: '3.10' + TOXENV: py310-django50-djangorestframework315 - python: '3.11' TOXENV: py311-django50-djangorestframework315 - python: '3.12' TOXENV: py312-django50-djangorestframework315 + - python: '3.13' + TOXENV: py313-django50-djangorestframework315 + - python: '3.10' + TOXENV: py310-django51-djangorestframework315 - python: '3.11' TOXENV: py311-django51-djangorestframework315 - python: '3.12' TOXENV: py312-django51-djangorestframework315 + # DRF 3.16 + - python: '3.11' + TOXENV: py311-django50-djangorestframework316 + - python: '3.12' + TOXENV: py312-django50-djangorestframework316 + - python: '3.11' + TOXENV: py311-django51-djangorestframework316 + - python: '3.12' + TOXENV: py312-django51-djangorestframework316 + - python: '3.13' + TOXENV: py313-django51-djangorestframework315 + - python: '3.10' + TOXENV: py310-django52-djangorestframework315 + - python: '3.11' + TOXENV: py311-django52-djangorestframework315 + - python: '3.12' + TOXENV: py312-django52-djangorestframework315 + - python: '3.13' + TOXENV: py313-django52-djangorestframework315 steps: - uses: actions/checkout@v4 with: diff --git a/.gitignore b/.gitignore index 894d4cf4..c04ff5ad 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ dist/ build/ tests/static/ *local_settings.py -.coverage +.coverage* *~ ._* *.DS_Store diff --git a/CHANGES.rst b/CHANGES.rst index 68fc1f1d..6b2de388 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,8 @@ Changelog Version 1.2.0 [Unreleased] -------------------------- -Work in progress. +- Added Django ``5.2`` to automated testing build. +- Added Python ``3.13`` to automated testing build. Version 1.1.0 [2024-08-17] -------------------------- diff --git a/README.rst b/README.rst index fc6af5d2..5527f869 100644 --- a/README.rst +++ b/README.rst @@ -41,6 +41,7 @@ Compatibility with DRF, Django and Python ======================== ============================ ==================== ================================== DRF-gis version DRF version Django version Python version +**1.2.x** **3.12** up to **3.15** **4.2 to 5.2** **3.9** to **3.13** **1.1.x** **3.12** up to **3.15** **3.2, 4.2 to 5.1** **3.8** to **3.12** **1.0.x** **3.10** up to **3.13** **2.2 to 4.0** **3.6** to **3.9** **0.18.x** **3.10** up to **3.13** **2.2 to 4.0** **3.6** to **3.9** diff --git a/rest_framework_gis/__init__.py b/rest_framework_gis/__init__.py index c8e813a3..54283c5d 100644 --- a/rest_framework_gis/__init__.py +++ b/rest_framework_gis/__init__.py @@ -1,14 +1,14 @@ -VERSION = (1, 2, 0, 'alpha') +VERSION = (1, 2, 0, "alpha") __version__ = VERSION # alias def get_version(): - version = '%s.%s.%s' % (VERSION[0], VERSION[1], VERSION[2]) - if VERSION[3] != 'final': + version = "%s.%s.%s" % (VERSION[0], VERSION[1], VERSION[2]) + if VERSION[3] != "final": first_letter = VERSION[3][0:1] try: suffix = VERSION[4] except IndexError: suffix = 0 - version = '%s%s%s' % (version, first_letter, suffix) + version = "%s%s%s" % (version, first_letter, suffix) return version diff --git a/rest_framework_gis/apps.py b/rest_framework_gis/apps.py index 1519ddd2..15800122 100644 --- a/rest_framework_gis/apps.py +++ b/rest_framework_gis/apps.py @@ -2,7 +2,7 @@ class AppConfig(BaseConfig): - name = 'rest_framework_gis' + name = "rest_framework_gis" def ready(self): """ diff --git a/rest_framework_gis/fields.py b/rest_framework_gis/fields.py index bb0faa65..1870de59 100644 --- a/rest_framework_gis/fields.py +++ b/rest_framework_gis/fields.py @@ -7,7 +7,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework.fields import Field, SerializerMethodField -__all__ = ['GeometryField', 'GeometrySerializerMethodField'] +__all__ = ["GeometryField", "GeometrySerializerMethodField"] class GeometryField(Field): @@ -15,7 +15,7 @@ class GeometryField(Field): A field to handle GeoDjango Geometry fields """ - type_name = 'GeometryField' + type_name = "GeometryField" def __init__( self, precision=None, remove_duplicates=False, auto_bbox=False, **kwargs @@ -27,7 +27,7 @@ def __init__( self.auto_bbox = auto_bbox self.remove_dupes = remove_duplicates super().__init__(**kwargs) - self.style.setdefault('base_template', 'textarea.html') + self.style.setdefault("base_template", "textarea.html") def to_representation(self, value): if isinstance(value, dict) or value is None: @@ -37,26 +37,26 @@ def to_representation(self, value): geojson = GeoJsonDict(value.geojson) # in this case we're dealing with an empty point else: - geojson = GeoJsonDict({'type': value.geom_type, 'coordinates': []}) - if geojson['type'] == 'GeometryCollection': - geometries = geojson.get('geometries') + geojson = GeoJsonDict({"type": value.geom_type, "coordinates": []}) + if geojson["type"] == "GeometryCollection": + geometries = geojson.get("geometries") else: geometries = [geojson] for geometry in geometries: if self.precision is not None: - geometry['coordinates'] = self._recursive_round( - geometry['coordinates'], self.precision + geometry["coordinates"] = self._recursive_round( + geometry["coordinates"], self.precision ) if self.remove_dupes: - geometry['coordinates'] = self._rm_redundant_points( - geometry['coordinates'], geometry['type'] + geometry["coordinates"] = self._rm_redundant_points( + geometry["coordinates"], geometry["type"] ) if self.auto_bbox: geojson["bbox"] = value.extent return geojson def to_internal_value(self, value): - if value == '' or value is None: + if value == "" or value is None: return value if isinstance(value, GEOSGeometry): # value already has the correct representation @@ -68,15 +68,15 @@ def to_internal_value(self, value): except GEOSException: raise ValidationError( _( - 'Invalid format: string or unicode input unrecognized as GeoJSON, WKT EWKT or HEXEWKB.' + "Invalid format: string or unicode input unrecognized as GeoJSON, WKT EWKT or HEXEWKB." ) ) except (ValueError, TypeError, GDALException) as e: - raise ValidationError(_(f'Unable to convert to python object: {str(e)}')) + raise ValidationError(_(f"Unable to convert to python object: {str(e)}")) def validate_empty_values(self, data): - if data == '': - self.fail('required') + if data == "": + self.fail("required") return super().validate_empty_values(data) def _recursive_round(self, value, precision): @@ -85,7 +85,7 @@ def _recursive_round(self, value, precision): value: number or nested array of numbers precision: integer valueue of number of decimals to keep """ - if hasattr(value, '__iter__'): + if hasattr(value, "__iter__"): return tuple(self._recursive_round(v, precision) for v in value) return round(value, precision) @@ -96,8 +96,8 @@ def _rm_redundant_points(self, geometry, geo_type): geo_type: GeoJSON type attribute for provided geometry, used to determine structure of provided `geometry` argument """ - if geo_type in ('MultiPoint', 'LineString'): - close = geo_type == 'LineString' + if geo_type in ("MultiPoint", "LineString"): + close = geo_type == "LineString" output = [] for coord in geometry: coord = tuple(coord) @@ -106,10 +106,10 @@ def _rm_redundant_points(self, geometry, geo_type): if close and len(output) == 1: output.append(output[0]) return tuple(output) - if geo_type in ('MultiLineString', 'Polygon'): - return [self._rm_redundant_points(c, 'LineString') for c in geometry] - if geo_type == 'MultiPolygon': - return [self._rm_redundant_points(c, 'Polygon') for c in geometry] + if geo_type in ("MultiLineString", "Polygon"): + return [self._rm_redundant_points(c, "LineString") for c in geometry] + if geo_type == "MultiPolygon": + return [self._rm_redundant_points(c, "Polygon") for c in geometry] return geometry diff --git a/rest_framework_gis/filters.py b/rest_framework_gis/filters.py index 34311588..0bdb5d3b 100644 --- a/rest_framework_gis/filters.py +++ b/rest_framework_gis/filters.py @@ -24,18 +24,18 @@ __all__ = [ - 'InBBoxFilter', - 'InBBOXFilter', - 'GeometryFilter', - 'GeoFilterSet', - 'TMSTileFilter', - 'DistanceToPointFilter', - 'DistanceToPointOrderingFilter', + "InBBoxFilter", + "InBBOXFilter", + "GeometryFilter", + "GeoFilterSet", + "TMSTileFilter", + "DistanceToPointFilter", + "DistanceToPointOrderingFilter", ] class InBBoxFilter(BaseFilterBackend): - bbox_param = 'in_bbox' # The URL query parameter which contains the bbox. + bbox_param = "in_bbox" # The URL query parameter which contains the bbox. def get_filter_bbox(self, request): bbox_string = request.query_params.get(self.bbox_param, None) @@ -43,22 +43,22 @@ def get_filter_bbox(self, request): return None try: - p1x, p1y, p2x, p2y = (float(n) for n in bbox_string.split(',')) + p1x, p1y, p2x, p2y = (float(n) for n in bbox_string.split(",")) except ValueError: raise ParseError( - f'Invalid bbox string supplied for parameter {self.bbox_param}' + f"Invalid bbox string supplied for parameter {self.bbox_param}" ) x = Polygon.from_bbox((p1x, p1y, p2x, p2y)) return x def filter_queryset(self, request, queryset, view): - filter_field = getattr(view, 'bbox_filter_field', None) - include_overlapping = getattr(view, 'bbox_filter_include_overlapping', False) + filter_field = getattr(view, "bbox_filter_field", None) + include_overlapping = getattr(view, "bbox_filter_include_overlapping", False) if include_overlapping: - geoDjango_filter = 'bboverlaps' + geoDjango_filter = "bboverlaps" else: - geoDjango_filter = 'contained' + geoDjango_filter = "contained" if not filter_field: return queryset @@ -66,7 +66,7 @@ def filter_queryset(self, request, queryset, view): bbox = self.get_filter_bbox(request) if not bbox: return queryset - return queryset.filter(Q(**{f'{filter_field}__{geoDjango_filter}': bbox})) + return queryset.filter(Q(**{f"{filter_field}__{geoDjango_filter}": bbox})) def get_schema_operation_parameters(self, view): return [ @@ -96,13 +96,13 @@ class GeometryFilter(django_filters.Filter): field_class = forms.GeometryField def __init__(self, *args, **kwargs): - kwargs.setdefault('widget', forms.TextInput) + kwargs.setdefault("widget", forms.TextInput) super().__init__(*args, **kwargs) class GeoFilterSet(django_filters.FilterSet): GEOFILTER_FOR_DBFIELD_DEFAULTS = { - models.GeometryField: {'filterset_class': GeometryFilter}, + models.GeometryField: {"filterset_class": GeometryFilter}, } def __new__(cls, *args, **kwargs): @@ -116,7 +116,7 @@ def __new__(cls, *args, **kwargs): class TMSTileFilter(InBBoxFilter): - tile_param = 'tile' # The URL query parameter which contains the tile address + tile_param = "tile" # The URL query parameter which contains the tile address def get_filter_bbox(self, request): tile_string = request.query_params.get(self.tile_param, None) @@ -124,10 +124,10 @@ def get_filter_bbox(self, request): return None try: - z, x, y = (int(n) for n in tile_string.split('/')) + z, x, y = (int(n) for n in tile_string.split("/")) except ValueError: raise ParseError( - f'Invalid tile string supplied for parameter {self.tile_param}' + f"Invalid tile string supplied for parameter {self.tile_param}" ) bbox = Polygon.from_bbox(tile_edges(x, y, z)) @@ -146,8 +146,8 @@ def get_schema_operation_parameters(self, view): class DistanceToPointFilter(BaseFilterBackend): - dist_param = 'dist' - point_param = 'point' # The URL query parameter which contains the + dist_param = "dist" + point_param = "point" # The URL query parameter which contains the def get_filter_point(self, request, **kwargs): point_string = request.query_params.get(self.point_param, None) @@ -155,10 +155,10 @@ def get_filter_point(self, request, **kwargs): return None try: - (x, y) = (float(n) for n in point_string.split(',')) + (x, y) = (float(n) for n in point_string.split(",")) except ValueError: raise ParseError( - 'Invalid geometry string supplied for parameter {}'.format( + "Invalid geometry string supplied for parameter {}".format( self.point_param ) ) @@ -195,9 +195,9 @@ def dist_to_deg(self, distance, latitude): return distance / (earthRadius * latitudeCorrection) * rad2deg def filter_queryset(self, request, queryset, view): - filter_field = getattr(view, 'distance_filter_field', None) - convert_distance_input = getattr(view, 'distance_filter_convert_meters', False) - geoDjango_filter = 'dwithin' # use dwithin for points + filter_field = getattr(view, "distance_filter_field", None) + convert_distance_input = getattr(view, "distance_filter_convert_meters", False) + geoDjango_filter = "dwithin" # use dwithin for points if not filter_field: return queryset @@ -212,7 +212,7 @@ def filter_queryset(self, request, queryset, view): dist = float(dist_string) except ValueError: raise ParseError( - 'Invalid distance string supplied for parameter {}'.format( + "Invalid distance string supplied for parameter {}".format( self.dist_param ) ) @@ -222,7 +222,7 @@ def filter_queryset(self, request, queryset, view): dist = self.dist_to_deg(dist, point[1]) return queryset.filter( - Q(**{f'{filter_field}__{geoDjango_filter}': (point, dist)}) + Q(**{f"{filter_field}__{geoDjango_filter}": (point, dist)}) ) def get_schema_operation_parameters(self, view): @@ -256,10 +256,10 @@ def get_schema_operation_parameters(self, view): class DistanceToPointOrderingFilter(DistanceToPointFilter): srid = 4326 - order_param = 'order' + order_param = "order" def filter_queryset(self, request, queryset, view): - filter_field = getattr(view, 'distance_ordering_filter_field', None) + filter_field = getattr(view, "distance_ordering_filter_field", None) if not filter_field: return queryset @@ -269,7 +269,7 @@ def filter_queryset(self, request, queryset, view): return queryset order = request.query_params.get(self.order_param) - if order == 'desc': + if order == "desc": return queryset.order_by(-GeometryDistance(filter_field, point)) else: return queryset.order_by(GeometryDistance(filter_field, point)) diff --git a/rest_framework_gis/pagination.py b/rest_framework_gis/pagination.py index 519e28b4..17cecb6d 100644 --- a/rest_framework_gis/pagination.py +++ b/rest_framework_gis/pagination.py @@ -9,17 +9,17 @@ class GeoJsonPagination(pagination.PageNumberPagination): A geoJSON implementation of a pagination serializer. """ - page_size_query_param = 'page_size' + page_size_query_param = "page_size" def get_paginated_response(self, data): return Response( OrderedDict( [ - ('type', 'FeatureCollection'), - ('count', self.page.paginator.count), - ('next', self.get_next_link()), - ('previous', self.get_previous_link()), - ('features', data['features']), + ("type", "FeatureCollection"), + ("count", self.page.paginator.count), + ("next", self.get_next_link()), + ("previous", self.get_previous_link()), + ("features", data["features"]), ] ) ) diff --git a/rest_framework_gis/schema.py b/rest_framework_gis/schema.py index 18a6956e..4dd730e4 100644 --- a/rest_framework_gis/schema.py +++ b/rest_framework_gis/schema.py @@ -53,15 +53,15 @@ class GeoFeatureAutoSchema(AutoSchema): } GEO_FIELD_TO_SCHEMA[models.GeometryField] = { - 'type': {'type': 'string'}, - 'coordinates': { - 'oneOf': [ # If you have custom subclass of GeometryField, Override `oneOf` property. + "type": {"type": "string"}, + "coordinates": { + "oneOf": [ # If you have custom subclass of GeometryField, Override `oneOf` property. GEO_FIELD_TO_SCHEMA[models.PointField], GEO_FIELD_TO_SCHEMA[models.LineStringField], GEO_FIELD_TO_SCHEMA[models.PolygonField], ], - 'example': GEO_FIELD_TO_SCHEMA[models.PolygonField]['coordinates'][ - 'example' + "example": GEO_FIELD_TO_SCHEMA[models.PolygonField]["coordinates"][ + "example" ], }, } diff --git a/rest_framework_gis/serializers.py b/rest_framework_gis/serializers.py index 3663196c..11d3f388 100644 --- a/rest_framework_gis/serializers.py +++ b/rest_framework_gis/serializers.py @@ -44,7 +44,7 @@ class GeoFeatureModelSerializer(ModelSerializer): @classmethod def many_init(cls, *args, **kwargs): child_serializer = cls(*args, **kwargs) - list_kwargs = {'child': child_serializer} + list_kwargs = {"child": child_serializer} list_kwargs.update( { key: value @@ -52,27 +52,27 @@ def many_init(cls, *args, **kwargs): if key in LIST_SERIALIZER_KWARGS } ) - meta = getattr(cls, 'Meta', None) + meta = getattr(cls, "Meta", None) list_serializer_class = getattr( - meta, 'list_serializer_class', GeoFeatureModelListSerializer + meta, "list_serializer_class", GeoFeatureModelListSerializer ) return list_serializer_class(*args, **list_kwargs) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - meta = getattr(self, 'Meta') + meta = getattr(self, "Meta") default_id_field = None primary_key = self.Meta.model._meta.pk.name # use primary key as id_field when possible if ( - not hasattr(meta, 'fields') - or meta.fields == '__all__' + not hasattr(meta, "fields") + or meta.fields == "__all__" or primary_key in meta.fields ): default_id_field = primary_key - meta.id_field = getattr(meta, 'id_field', default_id_field) + meta.id_field = getattr(meta, "id_field", default_id_field) - if not hasattr(meta, 'geo_field'): + if not hasattr(meta, "geo_field"): raise ImproperlyConfigured( "You must define a 'geo_field'. " "Set it to None if there is no geometry." @@ -80,12 +80,12 @@ def __init__(self, *args, **kwargs): def check_excludes(field_name, field_role): """make sure the field is not excluded""" - if hasattr(meta, 'exclude') and field_name in meta.exclude: + if hasattr(meta, "exclude") and field_name in meta.exclude: raise ImproperlyConfigured(f"You cannot exclude your '{field_role}'.") def add_to_fields(field_name): """Make sure the field is included in the fields""" - if hasattr(meta, 'fields') and meta.fields != '__all__': + if hasattr(meta, "fields") and meta.fields != "__all__": if field_name not in meta.fields: if type(meta.fields) is tuple: additional_fields = (field_name,) @@ -93,17 +93,17 @@ def add_to_fields(field_name): additional_fields = [field_name] meta.fields += additional_fields - check_excludes(meta.geo_field, 'geo_field') + check_excludes(meta.geo_field, "geo_field") if meta.geo_field is not None: add_to_fields(meta.geo_field) - meta.bbox_geo_field = getattr(meta, 'bbox_geo_field', None) + meta.bbox_geo_field = getattr(meta, "bbox_geo_field", None) if meta.bbox_geo_field: - check_excludes(meta.bbox_geo_field, 'bbox_geo_field') + check_excludes(meta.bbox_geo_field, "bbox_geo_field") add_to_fields(meta.bbox_geo_field) - meta.auto_bbox = getattr(meta, 'auto_bbox', False) + meta.auto_bbox = getattr(meta, "auto_bbox", False) if meta.bbox_geo_field and meta.auto_bbox: raise ImproperlyConfigured( "You must eiher define a 'bbox_geo_field' or " @@ -150,7 +150,7 @@ def to_representation(self, instance): elif self.Meta.bbox_geo_field: field = self.fields[self.Meta.bbox_geo_field] value = field.get_attribute(instance) - feature["bbox"] = value.extent if hasattr(value, 'extent') else None + feature["bbox"] = value.extent if hasattr(value, "extent") else None processed_fields.add(self.Meta.bbox_geo_field) # the list of fields that will be processed by get_properties @@ -197,7 +197,7 @@ def to_internal_value(self, data): """ Override the parent method to first remove the GeoJSON formatting """ - if 'properties' in data: + if "properties" in data: data = self.unformat_geojson(data) return super().to_internal_value(data) @@ -217,13 +217,13 @@ def unformat_geojson(self, feature): """ attrs = feature["properties"] - if 'geometry' in feature and self.Meta.geo_field: - attrs[self.Meta.geo_field] = feature['geometry'] + if "geometry" in feature and self.Meta.geo_field: + attrs[self.Meta.geo_field] = feature["geometry"] - if self.Meta.id_field and 'id' in feature: - attrs[self.Meta.id_field] = feature['id'] + if self.Meta.id_field and "id" in feature: + attrs[self.Meta.id_field] = feature["id"] - if self.Meta.bbox_geo_field and 'bbox' in feature: - attrs[self.Meta.bbox_geo_field] = Polygon.from_bbox(feature['bbox']) + if self.Meta.bbox_geo_field and "bbox" in feature: + attrs[self.Meta.bbox_geo_field] = Polygon.from_bbox(feature["bbox"]) return attrs diff --git a/setup.py b/setup.py index 743705da..a81fd103 100644 --- a/setup.py +++ b/setup.py @@ -8,48 +8,52 @@ here = os.path.abspath(os.path.dirname(__file__)) # Get the long description from the README file -with open(os.path.join(here, 'README.rst'), encoding='utf-8') as f: +with open(os.path.join(here, "README.rst"), encoding="utf-8") as f: long_description = f.read() setup( - name='djangorestframework-gis', + name="djangorestframework-gis", version=get_version(), - license='BSD', - author='Douglas Meehan', - author_email='django-rest-framework-gis@googlegroups.com', - description='Geographic add-ons for Django Rest Framework', + license="BSD", + author="Douglas Meehan", + author_email="django-rest-framework-gis@googlegroups.com", + description="Geographic add-ons for Django Rest Framework", long_description=long_description, - url='https://github.com/openwisp/django-rest-framework-gis', - download_url='https://github.com/openwisp/django-rest-framework-gis/releases', - platforms=['Platform Indipendent'], - keywords=['django', 'rest-framework', 'gis', 'geojson'], - packages=find_packages(exclude=['tests', 'tests.*']), - install_requires=['djangorestframework>=3.12,<3.16', 'django-filter>=23.5,<26.0'], + url="https://github.com/openwisp/django-rest-framework-gis", + download_url="https://github.com/openwisp/django-rest-framework-gis/releases", + platforms=["Platform Indipendent"], + keywords=["django", "rest-framework", "gis", "geojson"], + packages=find_packages(exclude=["tests", "tests.*"]), + install_requires=[ + "django>=4.2", + "djangorestframework>=3.12,<3.17", + "django-filter>=23.5,<26.0", + ], classifiers=[ - 'Development Status :: 4 - Beta', - 'Environment :: Web Environment', - 'Topic :: Internet :: WWW/HTTP', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Framework :: Django', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - 'Programming Language :: Python :: 3 :: Only', + "Development Status :: 5 - Production/Stable", + "Environment :: Web Environment", + "Topic :: Internet :: WWW/HTTP", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Framework :: Django", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", ], project_urls={ - 'Bug Reports': 'https://github.com/openwisp/django-rest-framework-gis/issues', - 'Continuous Integration': ( - 'https://github.com/openwisp/django-rest-framework-gis/actions?' - 'query=workflow%3A%22Django+Rest+Framework+Gis+CI+Build%22' + "Bug Reports": "https://github.com/openwisp/django-rest-framework-gis/issues", + "Continuous Integration": ( + "https://github.com/openwisp/django-rest-framework-gis/actions/workflows/ci.yml" ), - 'Mailing List': 'https://groups.google.com/forum/#!forum/django-rest-framework-gis', - 'Code Coverage': 'https://coveralls.io/github/openwisp/django-rest-framework-gis', - 'Source Code': 'https://github.com/openwisp/django-rest-framework-gis', + "Mailing List": "https://groups.google.com/forum/#!forum/django-rest-framework-gis", + "Code Coverage": "https://coveralls.io/github/openwisp/django-rest-framework-gis", + "Source Code": "https://github.com/openwisp/django-rest-framework-gis", }, ) diff --git a/tests/django_restframework_gis_tests/admin.py b/tests/django_restframework_gis_tests/admin.py index 8a7b5756..f8f32949 100644 --- a/tests/django_restframework_gis_tests/admin.py +++ b/tests/django_restframework_gis_tests/admin.py @@ -6,4 +6,4 @@ @admin.register(Location) class LocationAdmin(GeoModelAdmin): - list_display = ('name', 'geometry') + list_display = ("name", "geometry") diff --git a/tests/django_restframework_gis_tests/migrations/0001_initial.py b/tests/django_restframework_gis_tests/migrations/0001_initial.py index 95c34b4c..1810b453 100644 --- a/tests/django_restframework_gis_tests/migrations/0001_initial.py +++ b/tests/django_restframework_gis_tests/migrations/0001_initial.py @@ -11,77 +11,77 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='BoxedLocation', + name="BoxedLocation", fields=[ ( - 'id', + "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, - verbose_name='ID', + verbose_name="ID", ), ), - ('name', models.CharField(max_length=32)), - ('slug', models.SlugField(blank=True, max_length=128, unique=True)), - ('timestamp', models.DateTimeField(blank=True, null=True)), + ("name", models.CharField(max_length=32)), + ("slug", models.SlugField(blank=True, max_length=128, unique=True)), + ("timestamp", models.DateTimeField(blank=True, null=True)), ( - 'geometry', + "geometry", django.contrib.gis.db.models.fields.GeometryField(srid=4326), ), ( - 'bbox_geometry', + "bbox_geometry", django.contrib.gis.db.models.fields.PolygonField(srid=4326), ), ], - options={'abstract': False}, + options={"abstract": False}, ), migrations.CreateModel( - name='LocatedFile', + name="LocatedFile", fields=[ ( - 'id', + "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, - verbose_name='ID', + verbose_name="ID", ), ), - ('name', models.CharField(max_length=32)), - ('slug', models.SlugField(blank=True, max_length=128, unique=True)), - ('timestamp', models.DateTimeField(blank=True, null=True)), + ("name", models.CharField(max_length=32)), + ("slug", models.SlugField(blank=True, max_length=128, unique=True)), + ("timestamp", models.DateTimeField(blank=True, null=True)), ( - 'geometry', + "geometry", django.contrib.gis.db.models.fields.GeometryField(srid=4326), ), ( - 'file', - models.FileField(blank=True, null=True, upload_to='located_files'), + "file", + models.FileField(blank=True, null=True, upload_to="located_files"), ), ], - options={'abstract': False}, + options={"abstract": False}, ), migrations.CreateModel( - name='Location', + name="Location", fields=[ ( - 'id', + "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, - verbose_name='ID', + verbose_name="ID", ), ), - ('name', models.CharField(max_length=32)), - ('slug', models.SlugField(blank=True, max_length=128, unique=True)), - ('timestamp', models.DateTimeField(blank=True, null=True)), + ("name", models.CharField(max_length=32)), + ("slug", models.SlugField(blank=True, max_length=128, unique=True)), + ("timestamp", models.DateTimeField(blank=True, null=True)), ( - 'geometry', + "geometry", django.contrib.gis.db.models.fields.GeometryField(srid=4326), ), ], - options={'abstract': False}, + options={"abstract": False}, ), ] diff --git a/tests/django_restframework_gis_tests/migrations/0002_nullable.py b/tests/django_restframework_gis_tests/migrations/0002_nullable.py index 06059ef5..e46be60a 100644 --- a/tests/django_restframework_gis_tests/migrations/0002_nullable.py +++ b/tests/django_restframework_gis_tests/migrations/0002_nullable.py @@ -6,32 +6,32 @@ class Migration(migrations.Migration): dependencies = [ - ('django_restframework_gis_tests', '0001_initial'), + ("django_restframework_gis_tests", "0001_initial"), ] operations = [ migrations.CreateModel( - name='Nullable', + name="Nullable", fields=[ ( - 'id', + "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, - verbose_name='ID', + verbose_name="ID", ), ), - ('name', models.CharField(max_length=32)), - ('slug', models.SlugField(blank=True, max_length=128, unique=True)), - ('timestamp', models.DateTimeField(blank=True, null=True)), + ("name", models.CharField(max_length=32)), + ("slug", models.SlugField(blank=True, max_length=128, unique=True)), + ("timestamp", models.DateTimeField(blank=True, null=True)), ( - 'geometry', + "geometry", django.contrib.gis.db.models.fields.GeometryField( blank=True, null=True, srid=4326 ), ), ], - options={'abstract': False}, + options={"abstract": False}, ), ] diff --git a/tests/django_restframework_gis_tests/migrations/0004_auto_20240228_2357.py b/tests/django_restframework_gis_tests/migrations/0004_auto_20240228_2357.py index 2d5888a8..cc7e4f7d 100644 --- a/tests/django_restframework_gis_tests/migrations/0004_auto_20240228_2357.py +++ b/tests/django_restframework_gis_tests/migrations/0004_auto_20240228_2357.py @@ -6,23 +6,23 @@ class Migration(migrations.Migration): dependencies = [ - ('django_restframework_gis_tests', '0003_schema_models'), + ("django_restframework_gis_tests", "0003_schema_models"), ] operations = [ migrations.AlterField( - model_name='boxedlocation', - name='geometry', + model_name="boxedlocation", + name="geometry", field=django.contrib.gis.db.models.fields.GeometryField(srid=4326), ), migrations.AlterField( - model_name='locatedfile', - name='geometry', + model_name="locatedfile", + name="geometry", field=django.contrib.gis.db.models.fields.GeometryField(srid=4326), ), migrations.AlterField( - model_name='location', - name='geometry', + model_name="location", + name="geometry", field=django.contrib.gis.db.models.fields.GeometryField(srid=4326), ), ] diff --git a/tests/django_restframework_gis_tests/models.py b/tests/django_restframework_gis_tests/models.py index e9c044af..df53af6e 100644 --- a/tests/django_restframework_gis_tests/models.py +++ b/tests/django_restframework_gis_tests/models.py @@ -2,17 +2,17 @@ from django.utils.text import slugify __all__ = [ - 'Location', - 'LocatedFile', - 'BoxedLocation', - 'GeometryModel', - 'Nullable', - 'PointModel', - 'LineStringModel', - 'PolygonModel', - 'MultiPolygonModel', - 'MultiLineStringModel', - 'MultiPointModel', + "Location", + "LocatedFile", + "BoxedLocation", + "GeometryModel", + "Nullable", + "PointModel", + "LineStringModel", + "PolygonModel", + "MultiPolygonModel", + "MultiLineStringModel", + "MultiPointModel", ] @@ -28,7 +28,7 @@ def __str__(self): return self.name def _generate_slug(self): - if self.slug == '' or self.slug is None: + if self.slug == "" or self.slug is None: try: name = str(self.name) except NameError: @@ -55,7 +55,7 @@ class Location(BaseModelGeometry): class LocatedFile(BaseModelGeometry): - file = models.FileField(upload_to='located_files', blank=True, null=True) + file = models.FileField(upload_to="located_files", blank=True, null=True) class BoxedLocation(BaseModelGeometry): diff --git a/tests/django_restframework_gis_tests/serializers.py b/tests/django_restframework_gis_tests/serializers.py index 6d4f7874..fe3a8d7a 100644 --- a/tests/django_restframework_gis_tests/serializers.py +++ b/tests/django_restframework_gis_tests/serializers.py @@ -18,43 +18,43 @@ ) __all__ = [ - 'LocationGeoSerializer', - 'PaginatedLocationGeoSerializer', - 'LocationGeoFeatureSerializer', - 'LocationGeoFeatureSlugSerializer', - 'LocationGeoFeatureFalseIdSerializer', - 'LocationGeoFeatureNoIdSerializer', - 'LocatedFileGeoFeatureSerializer', - 'BoxedLocationGeoFeatureSerializer', - 'LocationGeoFeatureBboxSerializer', - 'LocationGeoFeatureMethodSerializer', - 'NoneGeoFeatureMethodSerializer', - 'PointSerializer', - 'ChildPointSerializer', - 'ListChildPointSerializer', - 'LineStringSerializer', - 'PolygonSerializer', - 'MultiPolygonSerializer', - 'MultiLineStringSerializer', - 'MultiPointSerializer', - 'GeometrySerializerMethodFieldSerializer', - 'GeometrySerializer', - 'BoxedLocationGeoFeatureWithBBoxGeoFieldSerializer', + "LocationGeoSerializer", + "PaginatedLocationGeoSerializer", + "LocationGeoFeatureSerializer", + "LocationGeoFeatureSlugSerializer", + "LocationGeoFeatureFalseIdSerializer", + "LocationGeoFeatureNoIdSerializer", + "LocatedFileGeoFeatureSerializer", + "BoxedLocationGeoFeatureSerializer", + "LocationGeoFeatureBboxSerializer", + "LocationGeoFeatureMethodSerializer", + "NoneGeoFeatureMethodSerializer", + "PointSerializer", + "ChildPointSerializer", + "ListChildPointSerializer", + "LineStringSerializer", + "PolygonSerializer", + "MultiPolygonSerializer", + "MultiLineStringSerializer", + "MultiPointSerializer", + "GeometrySerializerMethodFieldSerializer", + "GeometrySerializer", + "BoxedLocationGeoFeatureWithBBoxGeoFieldSerializer", ] class LocationGeoSerializer(serializers.ModelSerializer): """location geo serializer""" - details = serializers.HyperlinkedIdentityField(view_name='api_location_details') + details = serializers.HyperlinkedIdentityField(view_name="api_location_details") class Meta: model = Location - fields = '__all__' + fields = "__all__" class PaginatedLocationGeoSerializer(pagination.PageNumberPagination): - page_size_query_param = 'limit' + page_size_query_param = "limit" page_size = 40 max_page_size = 10000 @@ -63,17 +63,17 @@ class LocationGeoFeatureSerializer(gis_serializers.GeoFeatureModelSerializer): """location geo serializer""" details = serializers.HyperlinkedIdentityField( - view_name='api_geojson_location_details' + view_name="api_geojson_location_details" ) fancy_name = serializers.SerializerMethodField() def get_fancy_name(self, obj): - return 'Kool %s' % obj.name + return "Kool %s" % obj.name class Meta: model = Location - geo_field = 'geometry' - fields = '__all__' + geo_field = "geometry" + fields = "__all__" class LocationGeoFeatureSlugSerializer(LocationGeoFeatureSerializer): @@ -81,9 +81,9 @@ class LocationGeoFeatureSlugSerializer(LocationGeoFeatureSerializer): class Meta: model = Location - geo_field = 'geometry' - id_field = 'slug' - fields = ('name', 'slug', 'timestamp') + geo_field = "geometry" + id_field = "slug" + fields = ("name", "slug", "timestamp") class LocationGeoFeatureFalseIdSerializer(LocationGeoFeatureSerializer): @@ -91,9 +91,9 @@ class LocationGeoFeatureFalseIdSerializer(LocationGeoFeatureSerializer): class Meta: model = Location - geo_field = 'geometry' + geo_field = "geometry" id_field = False - fields = '__all__' + fields = "__all__" class LocationGeoFeatureNoIdSerializer(LocationGeoFeatureSerializer): @@ -104,8 +104,8 @@ class LocationGeoFeatureNoIdSerializer(LocationGeoFeatureSerializer): class Meta: model = Location - geo_field = 'geometry' - fields = ('name',) + geo_field = "geometry" + fields = ("name",) class LocationGeoFeatureWritableIdSerializer(LocationGeoFeatureSerializer): @@ -113,8 +113,8 @@ class LocationGeoFeatureWritableIdSerializer(LocationGeoFeatureSerializer): class Meta: model = Location - geo_field = 'geometry' - fields = ('id', 'name', 'timestamp') + geo_field = "geometry" + fields = ("id", "name", "timestamp") id = serializers.CharField() @@ -123,17 +123,17 @@ class LocatedFileGeoFeatureSerializer(gis_serializers.GeoFeatureModelSerializer) """located file geo serializer""" details = serializers.HyperlinkedIdentityField( - view_name='api_geojson_located_file_details' + view_name="api_geojson_located_file_details" ) fancy_name = serializers.SerializerMethodField() file = serializers.FileField(allow_empty_file=True) def get_fancy_name(self, obj): - return 'Nice %s' % obj.name + return "Nice %s" % obj.name class Meta: model = Location - geo_field = 'geometry' + geo_field = "geometry" exclude = [] @@ -141,20 +141,20 @@ class BoxedLocationGeoFeatureSerializer(gis_serializers.GeoFeatureModelSerialize """location geo serializer""" details = serializers.HyperlinkedIdentityField( - view_name='api_geojson_boxedlocation_details' + view_name="api_geojson_boxedlocation_details" ) class Meta: model = BoxedLocation - geo_field = 'geometry' - bbox_geo_field = 'bbox_geometry' - fields = ['name', 'details', 'id', 'timestamp'] + geo_field = "geometry" + bbox_geo_field = "bbox_geometry" + fields = ["name", "details", "id", "timestamp"] class LocationGeoFeatureBboxSerializer(gis_serializers.GeoFeatureModelSerializer): class Meta: model = Location - geo_field = 'geometry' + geo_field = "geometry" auto_bbox = True exclude = [] @@ -163,14 +163,14 @@ class LocationGeoFeatureMethodSerializer(gis_serializers.GeoFeatureModelSerializ new_geometry = gis_serializers.GeometrySerializerMethodField() def get_new_geometry(self, obj): - if obj.name.startswith('hidden'): + if obj.name.startswith("hidden"): return Point(0.0, 0.0) else: return obj.geometry class Meta: model = Location - geo_field = 'new_geometry' + geo_field = "new_geometry" exclude = [] @@ -182,22 +182,22 @@ def get_new_geometry(self, obj): class Meta: model = Location - geo_field = 'new_geometry' - fields = ['name', 'slug', 'id'] + geo_field = "new_geometry" + fields = ["name", "slug", "id"] class NoGeoFeatureMethodSerializer(gis_serializers.GeoFeatureModelSerializer): class Meta: model = Nullable geo_field = None - fields = ['name', 'slug', 'id'] + fields = ["name", "slug", "id"] class PointSerializer(gis_serializers.GeoFeatureModelSerializer): class Meta: model = PointModel - geo_field = 'location' - fields = '__all__' + geo_field = "location" + fields = "__all__" class ChildPointSerializer(serializers.Serializer): @@ -211,50 +211,50 @@ class ListChildPointSerializer(serializers.Serializer): class LineStringSerializer(gis_serializers.GeoFeatureModelSerializer): class Meta: model = LineStringModel - geo_field = 'points' - fields = '__all__' + geo_field = "points" + fields = "__all__" class PolygonSerializer(gis_serializers.GeoFeatureModelSerializer): class Meta: model = PolygonModel - geo_field = 'polygon' - fields = '__all__' + geo_field = "polygon" + fields = "__all__" class PolygonModelSerializer(serializers.ModelSerializer): class Meta: model = PolygonModel - fields = '__all__' + fields = "__all__" class MultiPolygonSerializer(gis_serializers.GeoFeatureModelSerializer): class Meta: model = MultiPolygonModel - geo_field = 'polygon' - fields = '__all__' + geo_field = "polygon" + fields = "__all__" class MultiLineStringSerializer(gis_serializers.GeoFeatureModelSerializer): class Meta: model = MultiLineStringModel - geo_field = 'points' - fields = '__all__' + geo_field = "points" + fields = "__all__" class MultiPointSerializer(gis_serializers.GeoFeatureModelSerializer): class Meta: model = MultiPointModel - geo_field = 'points' - fields = '__all__' + geo_field = "points" + fields = "__all__" auto_bbox = True class GeometrySerializer(gis_serializers.GeoFeatureModelSerializer): class Meta: model = GeometryModel - geo_field = 'points' - fields = '__all__' + geo_field = "points" + fields = "__all__" auto_bbox = True @@ -267,8 +267,8 @@ def get_other_point(obj): class Meta: model = Location - geo_field = 'other_point' - fields = '__all__' + geo_field = "other_point" + fields = "__all__" class BoxedLocationGeoFeatureWithBBoxGeoFieldSerializer( diff --git a/tests/django_restframework_gis_tests/test_bbox.py b/tests/django_restframework_gis_tests/test_bbox.py index 50a3c456..c399ab73 100644 --- a/tests/django_restframework_gis_tests/test_bbox.py +++ b/tests/django_restframework_gis_tests/test_bbox.py @@ -15,43 +15,43 @@ class TestRestFrameworkGisBBox(TestCase): """ def setUp(self): - self.geojson_boxedlocation_list_url = reverse('api_geojson_boxedlocation_list') - self.geojson_location_bbox_list_url = reverse('api_geojson_location_bbox_list') + self.geojson_boxedlocation_list_url = reverse("api_geojson_boxedlocation_list") + self.geojson_location_bbox_list_url = reverse("api_geojson_location_bbox_list") def _create_locations(self): self.bl1 = BoxedLocation.objects.create( id=1, - name='l1', - slug='l1', - geometry='POINT (13.007 42.423)', - bbox_geometry='POLYGON((12.997 42.413,12.997 42.433,13.017 42.433,13.017 42.413,12.997 42.413))', + name="l1", + slug="l1", + geometry="POINT (13.007 42.423)", + bbox_geometry="POLYGON((12.997 42.413,12.997 42.433,13.017 42.433,13.017 42.413,12.997 42.413))", ) self.bl2 = BoxedLocation.objects.create( id=2, - name='l2', - slug='l2', - geometry='POINT (12.007 43.423)', - bbox_geometry='POLYGON((11.997 43.413,11.997 43.433,12.017 43.433,12.017 43.413,11.997 43.413))', + name="l2", + slug="l2", + geometry="POINT (12.007 43.423)", + bbox_geometry="POLYGON((11.997 43.413,11.997 43.433,12.017 43.433,12.017 43.413,11.997 43.413))", ) self.l1 = Location.objects.create( id=1, - name='l1', - slug='l1', - geometry='POLYGON((12.997 42.413,12.997 42.433,13.017 42.433,13.017 42.413,12.997 42.413))', + name="l1", + slug="l1", + geometry="POLYGON((12.997 42.413,12.997 42.433,13.017 42.433,13.017 42.413,12.997 42.413))", ) def test_list(self): self._create_locations() response = self.client.get(self.geojson_boxedlocation_list_url) self.assertEqual(response.status_code, 200) - self.assertEqual(len(response.data['features']), 2) - for feature in response.data['features']: - self.assertIn('bbox', feature) - fid = feature['id'] + self.assertEqual(len(response.data["features"]), 2) + for feature in response.data["features"]: + self.assertIn("bbox", feature) + fid = feature["id"] if fid == 1: - self.assertEqual(feature['bbox'], self.bl1.bbox_geometry.extent) + self.assertEqual(feature["bbox"], self.bl1.bbox_geometry.extent) elif fid == 2: - self.assertEqual(feature['bbox'], self.bl2.bbox_geometry.extent) + self.assertEqual(feature["bbox"], self.bl2.bbox_geometry.extent) else: self.fail(f"Unexpected id: {fid}") BoxedLocation.objects.all().delete() @@ -66,7 +66,7 @@ def test_post_location_list_geojson(self): response = self.client.post( self.geojson_boxedlocation_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) self.assertEqual(response.status_code, 201) self.assertEqual(BoxedLocation.objects.count(), 1) @@ -79,8 +79,8 @@ def test_get_autogenerated_location_bbox_geojson(self): self._create_locations() response = self.client.get(self.geojson_location_bbox_list_url) self.assertEqual(response.status_code, 200) - self.assertEqual(len(response.data['features']), 1) - self.assertEqual(response.data['features'][0]['bbox'], self.l1.geometry.extent) + self.assertEqual(len(response.data["features"]), 1) + self.assertEqual(response.data["features"][0]["bbox"], self.l1.geometry.extent) def test_bbox_improperly_configured(self): self._create_locations() @@ -88,8 +88,8 @@ def test_bbox_improperly_configured(self): class LocationGeoFeatureSerializer(gis_serializers.GeoFeatureModelSerializer): class Meta: model = Location - geo_field = 'geometry' - bbox_geo_field = 'geometry' + geo_field = "geometry" + bbox_geo_field = "geometry" auto_bbox = True with self.assertRaises(ImproperlyConfigured): diff --git a/tests/django_restframework_gis_tests/test_fields.py b/tests/django_restframework_gis_tests/test_fields.py index b081529d..78b4ed07 100644 --- a/tests/django_restframework_gis_tests/test_fields.py +++ b/tests/django_restframework_gis_tests/test_fields.py @@ -148,7 +148,7 @@ def test_precision_Point(self): data = Serializer(model).data self.assertEqual( self.normalize(data), - {'geometry': {"type": "Point", "coordinates": [-105.02, 39.57]}}, + {"geometry": {"type": "Point", "coordinates": [-105.02, 39.57]}}, ) def test_precision_MultiPoint(self): @@ -158,7 +158,7 @@ def test_precision_MultiPoint(self): self.assertEqual( self.normalize(data), { - 'geometry': { + "geometry": { "type": "MultiPoint", "coordinates": [ [-105.02, 39.57], @@ -177,7 +177,7 @@ def test_precision_LineString(self): self.assertEqual( self.normalize(data), { - 'geometry': { + "geometry": { "type": "LineString", "coordinates": [ [-101.74, 39.32], @@ -197,7 +197,7 @@ def test_precision_MultiLineString(self): self.assertEqual( self.normalize(data), { - 'geometry': { + "geometry": { "type": "MultiLineString", "coordinates": [ [ @@ -229,7 +229,7 @@ def test_precision_Polygon(self): self.assertEqual( self.normalize(data), { - 'geometry': { + "geometry": { "type": "Polygon", "coordinates": [ [ @@ -263,7 +263,7 @@ def test_precision_MultiPolygon(self): self.assertEqual( self.normalize(data), { - 'geometry': { + "geometry": { "type": "MultiPolygon", "coordinates": [ [ @@ -467,7 +467,7 @@ def test_rm_redundant_MultiPolygon(self): self.assertEqual( self.normalize(data), { - 'geometry': { + "geometry": { "type": "MultiPolygon", "coordinates": [ [ @@ -521,7 +521,7 @@ def test_rm_redundant_MultiPolygon_single_polygon(self): self.assertEqual( self.normalize(data), { - 'geometry': { + "geometry": { "type": "MultiPolygon", "coordinates": [ [ @@ -596,7 +596,7 @@ def test_boundingbox_Point(self): self.assertEqual( self.normalize(data), { - 'geometry': { + "geometry": { "type": "Point", "bbox": [-105.0162, 39.5742, -105.0162, 39.5742], "coordinates": [-105.0162, 39.5742], diff --git a/tests/django_restframework_gis_tests/test_filters.py b/tests/django_restframework_gis_tests/test_filters.py index 2f1630af..4175c5cf 100644 --- a/tests/django_restframework_gis_tests/test_filters.py +++ b/tests/django_restframework_gis_tests/test_filters.py @@ -15,8 +15,8 @@ ) has_spatialite = ( - settings.DATABASES['default']['ENGINE'] - == 'django.contrib.gis.db.backends.spatialite' + settings.DATABASES["default"]["ENGINE"] + == "django.contrib.gis.db.backends.spatialite" ) @@ -27,28 +27,28 @@ class TestRestFrameworkGisFilters(TestCase): def setUp(self): self.location_contained_in_bbox_list_url = reverse( - 'api_geojson_location_list_contained_in_bbox_filter' + "api_geojson_location_list_contained_in_bbox_filter" ) self.location_overlaps_bbox_list_url = reverse( - 'api_geojson_location_list_overlaps_bbox_filter' + "api_geojson_location_list_overlaps_bbox_filter" ) self.location_contained_in_tile_list_url = reverse( - 'api_geojson_location_list_contained_in_tile_filter' + "api_geojson_location_list_contained_in_tile_filter" ) self.location_overlaps_tile_list_url = reverse( - 'api_geojson_location_list_overlaps_tile_filter' + "api_geojson_location_list_overlaps_tile_filter" ) self.location_within_distance_of_point_list_url = reverse( - 'api_geojson_location_list_within_distance_of_point_filter' + "api_geojson_location_list_within_distance_of_point_filter" ) self.location_within_degrees_of_point_list_url = reverse( - 'api_geojson_location_list_within_degrees_of_point_filter' + "api_geojson_location_list_within_degrees_of_point_filter" ) self.geojson_contained_in_geometry = reverse( - 'api_geojson_contained_in_geometry' + "api_geojson_contained_in_geometry" ) self.location_order_distance_to_point = reverse( - 'api_geojson_location_order_distance_to_point_list_filter' + "api_geojson_location_order_distance_to_point_list_filter" ) treasure_island_geojson = """{ @@ -150,28 +150,28 @@ def test_inBBOXFilter_filtering(self): xmax = 10 ymax = 10 - url_params = '?in_bbox=%d,%d,%d,%d&format=json' % (xmin, ymin, xmax, ymax) + url_params = "?in_bbox=%d,%d,%d,%d&format=json" % (xmin, ymin, xmax, ymax) # Square with bottom left at (1,1), top right at (9,9) isContained = Location() - isContained.name = 'isContained' + isContained.name = "isContained" isContained.geometry = Polygon(((1, 1), (9, 1), (9, 9), (1, 9), (1, 1))) isContained.save() isEqualToBounds = Location() - isEqualToBounds.name = 'isEqualToBounds' + isEqualToBounds.name = "isEqualToBounds" isEqualToBounds.geometry = Polygon(((0, 0), (10, 0), (10, 10), (0, 10), (0, 0))) isEqualToBounds.save() # Rectangle with bottom left at (-1,1), top right at (5,5) overlaps = Location() - overlaps.name = 'overlaps' + overlaps.name = "overlaps" overlaps.geometry = Polygon(((-1, 1), (5, 1), (5, 5), (-1, 5), (-1, 1))) overlaps.save() # Rectangle with bottom left at (-3,-3), top right at (-1,2) nonIntersecting = Location() - nonIntersecting.name = 'nonIntersecting' + nonIntersecting.name = "nonIntersecting" nonIntersecting.geometry = Polygon( ((-3, -3), (-1, -3), (-1, 2), (-3, 2), (-3, -3)) ) @@ -181,23 +181,23 @@ def test_inBBOXFilter_filtering(self): response = self.client.get( self.location_contained_in_bbox_list_url + url_params ) - self.assertEqual(len(response.data['features']), 2) - for result in response.data['features']: + self.assertEqual(len(response.data["features"]), 2) + for result in response.data["features"]: self.assertEqual( - result['properties']['name'] in ('isContained', 'isEqualToBounds'), True + result["properties"]["name"] in ("isContained", "isEqualToBounds"), True ) # Make sure we get overlapping results for the view which allows bounding box overlaps. response = self.client.get(self.location_overlaps_bbox_list_url + url_params) - self.assertEqual(len(response.data['features']), 3) - for result in response.data['features']: + self.assertEqual(len(response.data["features"]), 3) + for result in response.data["features"]: self.assertEqual( - result['properties']['name'] - in ('isContained', 'isEqualToBounds', 'overlaps'), + result["properties"]["name"] + in ("isContained", "isEqualToBounds", "overlaps"), True, ) - @skipIf(has_spatialite, 'Skipped test for spatialite backend: not accurate enough') + @skipIf(has_spatialite, "Skipped test for spatialite backend: not accurate enough") def test_TileFilter_filtering(self): """ Checks that the TMSTileFilter returns only objects strictly contained @@ -210,16 +210,16 @@ def test_TileFilter_filtering(self): x = 1 y = 0 - url_params = '?tile=%d/%d/%d&format=json' % (z, x, y) + url_params = "?tile=%d/%d/%d&format=json" % (z, x, y) # Square with bottom left at (1,1), top right at (9,9) isContained = Location() - isContained.name = 'isContained' + isContained.name = "isContained" isContained.geometry = Polygon(((1, 1), (9, 1), (9, 9), (1, 9), (1, 1))) isContained.save() isEqualToBounds = Location() - isEqualToBounds.name = 'isEqualToBounds' + isEqualToBounds.name = "isEqualToBounds" isEqualToBounds.geometry = Polygon( ((0, 0), (0, 85.05113), (180, 85.05113), (180, 0), (0, 0)) ) @@ -227,13 +227,13 @@ def test_TileFilter_filtering(self): # Rectangle with bottom left at (-1,1), top right at (5,5) overlaps = Location() - overlaps.name = 'overlaps' + overlaps.name = "overlaps" overlaps.geometry = Polygon(((-1, 1), (5, 1), (5, 5), (-1, 5), (-1, 1))) overlaps.save() # Rectangle with bottom left at (-3,-3), top right at (-1,2) nonIntersecting = Location() - nonIntersecting.name = 'nonIntersecting' + nonIntersecting.name = "nonIntersecting" nonIntersecting.geometry = Polygon( ((-3, -3), (-1, -3), (-1, 2), (-3, 2), (-3, -3)) ) @@ -243,19 +243,19 @@ def test_TileFilter_filtering(self): response = self.client.get( self.location_contained_in_tile_list_url + url_params ) - self.assertEqual(len(response.data['features']), 2) - for result in response.data['features']: + self.assertEqual(len(response.data["features"]), 2) + for result in response.data["features"]: self.assertEqual( - result['properties']['name'] in ('isContained', 'isEqualToBounds'), True + result["properties"]["name"] in ("isContained", "isEqualToBounds"), True ) # Make sure we get overlapping results for the view which allows bounding box overlaps. response = self.client.get(self.location_overlaps_tile_list_url + url_params) - self.assertEqual(len(response.data['features']), 3) - for result in response.data['features']: + self.assertEqual(len(response.data["features"]), 3) + for result in response.data["features"]: self.assertEqual( - result['properties']['name'] - in ('isContained', 'isEqualToBounds', 'overlaps'), + result["properties"]["name"] + in ("isContained", "isEqualToBounds", "overlaps"), True, ) @@ -273,13 +273,13 @@ def test_DistanceToPointFilter_filtering(self): distance = 5000 # meters point_on_alcatraz = [-122.4222, 37.82667] - url_params = f'?dist={distance:0.4f}&point=hello&format=json' + url_params = f"?dist={distance:0.4f}&point=hello&format=json" response = self.client.get( - f'{self.location_within_distance_of_point_list_url}{url_params}' + f"{self.location_within_distance_of_point_list_url}{url_params}" ) self.assertEqual(response.status_code, 400) - url_params = '?dist={:0.4f}&point={:0.4f},{:0.4f}&format=json'.format( + url_params = "?dist={:0.4f}&point={:0.4f},{:0.4f}&format=json".format( distance, point_on_alcatraz[0], point_on_alcatraz[1], @@ -298,31 +298,31 @@ def test_DistanceToPointFilter_filtering(self): # Make sure we only get back the ones within the distance response = self.client.get( - f'{self.location_within_distance_of_point_list_url}{url_params}' + f"{self.location_within_distance_of_point_list_url}{url_params}" ) - self.assertEqual(len(response.data['features']), 1) - for result in response.data['features']: - self.assertEqual(result['properties']['name'], treasure_island.name) + self.assertEqual(len(response.data["features"]), 1) + for result in response.data["features"]: + self.assertEqual(result["properties"]["name"], treasure_island.name) # Make sure we get back all the ones within the distance distance = 7000 - url_params = '?dist={:0.4f}&point={:0.4f},{:0.4f}&format=json'.format( + url_params = "?dist={:0.4f}&point={:0.4f},{:0.4f}&format=json".format( distance, point_on_alcatraz[0], point_on_alcatraz[1], ) response = self.client.get( - f'{self.location_within_distance_of_point_list_url}{url_params}' + f"{self.location_within_distance_of_point_list_url}{url_params}" ) - self.assertEqual(len(response.data['features']), 2) - for result in response.data['features']: + self.assertEqual(len(response.data["features"]), 2) + for result in response.data["features"]: self.assertIn( - result['properties']['name'], (ggpark.name, treasure_island.name) + result["properties"]["name"], (ggpark.name, treasure_island.name) ) # Make sure we only get back the ones within the distance degrees = 0.05 - url_params = '?dist={:0.4f}&point={:0.4f},{:0.4f}&format=json'.format( + url_params = "?dist={:0.4f}&point={:0.4f},{:0.4f}&format=json".format( degrees, point_on_alcatraz[0], point_on_alcatraz[1], @@ -330,9 +330,9 @@ def test_DistanceToPointFilter_filtering(self): response = self.client.get( self.location_within_degrees_of_point_list_url + url_params ) - self.assertEqual(len(response.data['features']), 1) - for result in response.data['features']: - self.assertEqual(result['properties']['name'], treasure_island.name) + self.assertEqual(len(response.data["features"]), 1) + for result in response.data["features"]: + self.assertEqual(result["properties"]["name"], treasure_island.name) @skipIf( has_spatialite, @@ -345,74 +345,74 @@ def test_DistanceToPointOrderingFilter_filtering(self): """ self.assertEqual(Location.objects.count(), 0) - url_params = '?point=hello&format=json' + url_params = "?point=hello&format=json" response = self.client.get( - f'{self.location_order_distance_to_point}{url_params}' + f"{self.location_order_distance_to_point}{url_params}" ) self.assertEqual(response.status_code, 400) Location.objects.create( - name='Houston', geometry='SRID=4326;POINT (-95.363151 29.763374)' + name="Houston", geometry="SRID=4326;POINT (-95.363151 29.763374)" ) Location.objects.create( - name='Dallas', geometry='SRID=4326;POINT (-96.801611 32.782057)' + name="Dallas", geometry="SRID=4326;POINT (-96.801611 32.782057)" ) Location.objects.create( - name='Oklahoma City', geometry='SRID=4326;POINT (-97.521157 34.464642)' + name="Oklahoma City", geometry="SRID=4326;POINT (-97.521157 34.464642)" ) Location.objects.create( - name='Wellington', geometry='SRID=4326;POINT (174.783117 -41.315268)' + name="Wellington", geometry="SRID=4326;POINT (174.783117 -41.315268)" ) Location.objects.create( - name='Pueblo', geometry='SRID=4326;POINT (-104.609252 38.255001)' + name="Pueblo", geometry="SRID=4326;POINT (-104.609252 38.255001)" ) Location.objects.create( - name='Lawrence', geometry='SRID=4326;POINT (-95.235060 38.971823)' + name="Lawrence", geometry="SRID=4326;POINT (-95.235060 38.971823)" ) Location.objects.create( - name='Chicago', geometry='SRID=4326;POINT (-87.650175 41.850385)' + name="Chicago", geometry="SRID=4326;POINT (-87.650175 41.850385)" ) Location.objects.create( - name='Victoria', geometry='SRID=4326;POINT (-123.305196 48.462611)' + name="Victoria", geometry="SRID=4326;POINT (-123.305196 48.462611)" ) point = [-90, 40] - url_params = '?point=%i,%i&format=json' % (point[0], point[1]) + url_params = "?point=%i,%i&format=json" % (point[0], point[1]) response = self.client.get( - f'{self.location_order_distance_to_point}{url_params}' + f"{self.location_order_distance_to_point}{url_params}" ) - self.assertEqual(len(response.data['features']), 8) + self.assertEqual(len(response.data["features"]), 8) self.assertEqual( - [city['properties']['name'] for city in response.data['features']], + [city["properties"]["name"] for city in response.data["features"]], [ - 'Chicago', - 'Lawrence', - 'Oklahoma City', - 'Dallas', - 'Houston', - 'Pueblo', - 'Victoria', - 'Wellington', + "Chicago", + "Lawrence", + "Oklahoma City", + "Dallas", + "Houston", + "Pueblo", + "Victoria", + "Wellington", ], ) - url_params = '?point=%i,%i&order=desc&format=json' % (point[0], point[1]) + url_params = "?point=%i,%i&order=desc&format=json" % (point[0], point[1]) response = self.client.get( - f'{self.location_order_distance_to_point}{url_params}' + f"{self.location_order_distance_to_point}{url_params}" ) - self.assertEqual(len(response.data['features']), 8) + self.assertEqual(len(response.data["features"]), 8) self.assertEqual( - [city['properties']['name'] for city in response.data['features']], + [city["properties"]["name"] for city in response.data["features"]], [ - 'Wellington', - 'Victoria', - 'Pueblo', - 'Houston', - 'Dallas', - 'Oklahoma City', - 'Lawrence', - 'Chicago', + "Wellington", + "Victoria", + "Pueblo", + "Houston", + "Dallas", + "Oklahoma City", + "Lawrence", + "Chicago", ], ) @@ -446,113 +446,113 @@ def test_GeometryField_filtering(self): url_params = f"?contains_properly={quoted_param}" - response = self.client.get(f'{self.geojson_contained_in_geometry}{url_params}') + response = self.client.get(f"{self.geojson_contained_in_geometry}{url_params}") self.assertEqual(len(response.data), 1) - geometry_response = GEOSGeometry(json.dumps(response.data[0]['geometry'])) + geometry_response = GEOSGeometry(json.dumps(response.data[0]["geometry"])) self.assertTrue(geometry_response.equals_exact(self.ggpark_geom)) - self.assertEqual(response.data[0]['name'], ggpark.name) + self.assertEqual(response.data[0]["name"], ggpark.name) # try without any param, should return both response = self.client.get(self.geojson_contained_in_geometry) self.assertEqual(len(response.data), 2) def test_inBBOXFilter_filtering_none(self): - url_params = '?in_bbox=&format=json' + url_params = "?in_bbox=&format=json" response = self.client.get( self.location_contained_in_bbox_list_url + url_params ) self.assertDictEqual( - response.data, {'type': 'FeatureCollection', 'features': []} + response.data, {"type": "FeatureCollection", "features": []} ) def test_inBBOXFilter_ValueError(self): - url_params = '?in_bbox=0&format=json' + url_params = "?in_bbox=0&format=json" response = self.client.get( self.location_contained_in_bbox_list_url + url_params ) self.assertEqual( - response.data['detail'], - 'Invalid bbox string supplied for parameter in_bbox', + response.data["detail"], + "Invalid bbox string supplied for parameter in_bbox", ) def test_inBBOXFilter_filter_field_none(self): original_value = GeojsonLocationContainedInBBoxList.bbox_filter_field GeojsonLocationContainedInBBoxList.bbox_filter_field = None - url_params = '?in_bbox=0,0,0,0&format=json' + url_params = "?in_bbox=0,0,0,0&format=json" response = self.client.get( self.location_contained_in_bbox_list_url + url_params ) self.assertDictEqual( - response.data, {'type': 'FeatureCollection', 'features': []} + response.data, {"type": "FeatureCollection", "features": []} ) GeojsonLocationContainedInBBoxList.bbox_filter_field = original_value def test_TileFilter_filtering_none(self): - url_params = '?tile=&format=json' + url_params = "?tile=&format=json" response = self.client.get( self.location_contained_in_tile_list_url + url_params ) - self.assertEqual(response.data, {'type': 'FeatureCollection', 'features': []}) + self.assertEqual(response.data, {"type": "FeatureCollection", "features": []}) def test_TileFilter_ValueError(self): - url_params = '?tile=1/0&format=json' + url_params = "?tile=1/0&format=json" response = self.client.get( self.location_contained_in_tile_list_url + url_params ) self.assertEqual( - response.data['detail'], 'Invalid tile string supplied for parameter tile' + response.data["detail"], "Invalid tile string supplied for parameter tile" ) def test_DistanceToPointFilter_filtering_none(self): - url_params = '?dist=5000&point=&format=json' + url_params = "?dist=5000&point=&format=json" response = self.client.get( - f'{self.location_within_distance_of_point_list_url}{url_params}' + f"{self.location_within_distance_of_point_list_url}{url_params}" ) self.assertDictEqual( - response.data, {'type': 'FeatureCollection', 'features': []} + response.data, {"type": "FeatureCollection", "features": []} ) def test_DistanceToPointFilter_filter_field_none(self): original_value = GeojsonLocationWithinDistanceOfPointList.distance_filter_field GeojsonLocationWithinDistanceOfPointList.distance_filter_field = None - url_params = '?dist=5000&point=&format=json' + url_params = "?dist=5000&point=&format=json" response = self.client.get( - f'{self.location_within_distance_of_point_list_url}{url_params}' + f"{self.location_within_distance_of_point_list_url}{url_params}" ) self.assertDictEqual( - response.data, {'type': 'FeatureCollection', 'features': []} + response.data, {"type": "FeatureCollection", "features": []} ) GeojsonLocationWithinDistanceOfPointList.distance_filter_field = original_value def test_DistanceToPointFilter_ValueError_point(self): - url_params = '?dist=500.0&point=hello&format=json' + url_params = "?dist=500.0&point=hello&format=json" response = self.client.get( - f'{self.location_within_distance_of_point_list_url}{url_params}' + f"{self.location_within_distance_of_point_list_url}{url_params}" ) self.assertEqual( - response.data['detail'], - 'Invalid geometry string supplied for parameter point', + response.data["detail"], + "Invalid geometry string supplied for parameter point", ) def test_DistanceToPointFilter_ValueError_distance(self): - url_params = '?dist=wrong&point=12.0,42.0&format=json' + url_params = "?dist=wrong&point=12.0,42.0&format=json" response = self.client.get( - f'{self.location_within_distance_of_point_list_url}{url_params}' + f"{self.location_within_distance_of_point_list_url}{url_params}" ) self.assertEqual( - response.data['detail'], - 'Invalid distance string supplied for parameter dist', + response.data["detail"], + "Invalid distance string supplied for parameter dist", ) def test_DistanceToPointOrderingFilter_filtering_none(self): - url_params = '?point=&format=json' + url_params = "?point=&format=json" response = self.client.get( - f'{self.location_order_distance_to_point}{url_params}' + f"{self.location_order_distance_to_point}{url_params}" ) self.assertDictEqual( - response.data, {'type': 'FeatureCollection', 'features': []} + response.data, {"type": "FeatureCollection", "features": []} ) def test_DistanceToPointOrderingFilter_ordering_filter_field_none(self): @@ -560,12 +560,12 @@ def test_DistanceToPointOrderingFilter_ordering_filter_field_none(self): GeojsonLocationOrderDistanceToPointList.distance_ordering_filter_field ) GeojsonLocationOrderDistanceToPointList.distance_ordering_filter_field = None - url_params = '?point=&format=json' + url_params = "?point=&format=json" response = self.client.get( - f'{self.location_order_distance_to_point}{url_params}' + f"{self.location_order_distance_to_point}{url_params}" ) self.assertDictEqual( - response.data, {'type': 'FeatureCollection', 'features': []} + response.data, {"type": "FeatureCollection", "features": []} ) GeojsonLocationOrderDistanceToPointList.distance_ordering_filter_field = ( original_value diff --git a/tests/django_restframework_gis_tests/test_performance.py b/tests/django_restframework_gis_tests/test_performance.py index 2dad1808..8b3045ba 100644 --- a/tests/django_restframework_gis_tests/test_performance.py +++ b/tests/django_restframework_gis_tests/test_performance.py @@ -7,7 +7,7 @@ # django test --keepdb django_restframework_gis_tests.test_performance # or by setting ``settings.TEST_PERFORMANCE`` to ``True`` if ( - 'django_restframework_gis_tests.test_performance' in sys.argv + "django_restframework_gis_tests.test_performance" in sys.argv or settings.TEST_PERFORMANCE ): from contexttimer import Timer @@ -24,9 +24,9 @@ class TestRestFrameworkGisPerformance(TestCase): def _create_data(self): """creates a bunch of gis models instances""" locations = [] - name = 'l{0}' - slug = 'l{0}' - wkt = 'POINT (13.{0}125000020002 42.{0}565179379999)' + name = "l{0}" + slug = "l{0}" + wkt = "POINT (13.{0}125000020002 42.{0}565179379999)" for n in range(1, self.NUMBER_OF_LOCATIONS): locations.append( Location( @@ -39,8 +39,8 @@ def test_geojson_performance(self): class PerfSerializer(gis_serializers.GeoFeatureModelSerializer): class Meta: model = Location - geo_field = 'geometry' - fields = '__all__' + geo_field = "geometry" + fields = "__all__" # create data self._create_data() @@ -49,7 +49,7 @@ class Meta: with Timer() as t: JSONRenderer().render(serializer.data) # print results - msg = 'GeoJSON rendering of {} objects ' 'completed in {}'.format( + msg = "GeoJSON rendering of {} objects " "completed in {}".format( self.NUMBER_OF_LOCATIONS, t.elapsed ) - print(f'\n\033[95m{msg}\033[0m') + print(f"\n\033[95m{msg}\033[0m") diff --git a/tests/django_restframework_gis_tests/test_schema_generation.py b/tests/django_restframework_gis_tests/test_schema_generation.py index 73409a84..39727b08 100644 --- a/tests/django_restframework_gis_tests/test_schema_generation.py +++ b/tests/django_restframework_gis_tests/test_schema_generation.py @@ -409,7 +409,7 @@ class GeometrySerializerFieldView(RetrieveAPIView): content = inspector.map_serializer(serializer) geometry_schema = content["properties"]["geometry"] geometry_schema.pop("type", None) - geometry_schema['properties'].pop("coordinates", None) + geometry_schema["properties"].pop("coordinates", None) self.assertEqual(geometry_schema, {"properties": {"type": {"type": "string"}}}) def check_bbox_schema(self): @@ -510,8 +510,8 @@ def test_geo_json_pagination_schema(self): }, }, } - if parse_version(DRF_VERSION) >= parse_version('3.15'): - expected_schema['required'] = ['count', 'results'] + if parse_version(DRF_VERSION) >= parse_version("3.15"): + expected_schema["required"] = ["count", "results"] self.assertDictEqual(generated_schema, expected_schema) @@ -617,34 +617,34 @@ def test_geometry_field(self): { "type": "object", "properties": { - "id": {'type': 'integer', 'readOnly': True}, - 'polygon': { - 'properties': { - 'coordinates': { - 'example': [ + "id": {"type": "integer", "readOnly": True}, + "polygon": { + "properties": { + "coordinates": { + "example": [ [0.0, 0.0], [0.0, 50.0], [50.0, 50.0], [50.0, 0.0], [0.0, 0.0], ], - 'items': { - 'example': [[22.4707, 70.0577], [12.9721, 77.5933]], - 'items': { - 'example': [12.9721, 77.5933], - 'items': {'format': 'float', 'type': 'number'}, - 'maxItems': 3, - 'minItems': 2, - 'type': 'array', + "items": { + "example": [[22.4707, 70.0577], [12.9721, 77.5933]], + "items": { + "example": [12.9721, 77.5933], + "items": {"format": "float", "type": "number"}, + "maxItems": 3, + "minItems": 2, + "type": "array", }, - 'minItems': 4, - 'type': 'array', + "minItems": 4, + "type": "array", }, - 'type': 'array', + "type": "array", }, - 'type': {'enum': ['Polygon'], 'type': 'string'}, + "type": {"enum": ["Polygon"], "type": "string"}, }, - 'type': 'object', + "type": "object", }, "random_field1": {"type": "string", "maxLength": 32}, "random_field2": { diff --git a/tests/django_restframework_gis_tests/tests.py b/tests/django_restframework_gis_tests/tests.py index 4a3a05e9..6007302d 100644 --- a/tests/django_restframework_gis_tests/tests.py +++ b/tests/django_restframework_gis_tests/tests.py @@ -20,14 +20,14 @@ class TestRestFrameworkGis(TestCase): def setUp(self): - self.location_list_url = reverse('api_location_list') - self.geojson_location_list_url = reverse('api_geojson_location_list') + self.location_list_url = reverse("api_location_list") + self.geojson_location_list_url = reverse("api_geojson_location_list") self.geos_error_message = ( - 'Invalid format: string or unicode input unrecognized as GeoJSON,' - ' WKT EWKT or HEXEWKB.' + "Invalid format: string or unicode input unrecognized as GeoJSON," + " WKT EWKT or HEXEWKB." ) self.gdal_error_message = ( - 'Unable to convert to python object:' + "Unable to convert to python object:" ' Invalid geometry pointer returned from "OGR_G_CreateGeometryFromJson".' ) self.value_error_message = ( @@ -41,15 +41,15 @@ def setUp(self): def _create_locations(self): self.l1 = Location.objects.create( id=1, - name='l1', - slug='l1', - geometry='POINT (13.0078125000020002 42.4234565179379999)', + name="l1", + slug="l1", + geometry="POINT (13.0078125000020002 42.4234565179379999)", ) self.l2 = Location.objects.create( id=2, - name='l2', - slug='l2', - geometry='POINT (12.0078125000020002 43.4234565179379999)', + name="l2", + slug="l2", + geometry="POINT (12.0078125000020002 43.4234565179379999)", ) def test_get_location_list(self): @@ -70,7 +70,7 @@ def test_post_location_list_geojson(self): response = self.client.post( self.location_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) @@ -84,7 +84,7 @@ def test_post_location_list_geojson(self): response = self.client.post( self.location_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 2) @@ -128,19 +128,19 @@ def test_post_HTML_browsable_api(self): ), } response = self.client.post( - self.location_list_url, data, headers={"accept": 'text/html'} + self.location_list_url, data, headers={"accept": "text/html"} ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) location = Location.objects.all()[0] - self.assertEqual(location.name, 'geojson input test2') - self.assertEqual(location.slug, 'prova') + self.assertEqual(location.name, "geojson input test2") + self.assertEqual(location.slug, "prova") def test_post_location_list_WKT(self): self.assertEqual(Location.objects.count(), 0) data = { - 'name': 'WKT input test', - 'geometry': 'POINT (12.492324113849 41.890307434153)', + "name": "WKT input test", + "geometry": "POINT (12.492324113849 41.890307434153)", } response = self.client.post(self.location_list_url, data) self.assertEqual(response.status_code, 201) @@ -149,15 +149,15 @@ def test_post_location_list_WKT(self): def test_post_location_list_EWKT(self): self.assertEqual(Location.objects.count(), 0) data = { - 'name': 'EWKT input test', - 'geometry': 'SRID=28992;POINT(221160 600204)', + "name": "EWKT input test", + "geometry": "SRID=28992;POINT(221160 600204)", } response = self.client.post(self.location_list_url, data) expected_coords = (6.381495826183805, 53.384066927384985) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) for lat, lon in zip( - Location.objects.get(name='EWKT input test').geometry.coords, + Location.objects.get(name="EWKT input test").geometry.coords, expected_coords, ): self.assertAlmostEqual(lat, lon, places=5) @@ -165,65 +165,65 @@ def test_post_location_list_EWKT(self): def test_post_location_list_WKT_as_json(self): self.assertEqual(Location.objects.count(), 0) data = { - 'name': 'WKT input test', - 'geometry': 'POINT (12.492324113849 41.890307434153)', + "name": "WKT input test", + "geometry": "POINT (12.492324113849 41.890307434153)", } response = self.client.post( self.location_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) def test_post_location_list_empty_geometry(self): - data = {'name': 'empty input test'} + data = {"name": "empty input test"} response = self.client.post(self.location_list_url, data) - self.assertEqual(response.data['geometry'][0], 'This field is required.') - data = {'name': 'empty input test', 'geometry': ''} + self.assertEqual(response.data["geometry"][0], "This field is required.") + data = {"name": "empty input test", "geometry": ""} response = self.client.post(self.location_list_url, data) - self.assertEqual(response.data['geometry'][0], 'This field is required.') - data = {'name': 'empty input test'} + self.assertEqual(response.data["geometry"][0], "This field is required.") + data = {"name": "empty input test"} response = self.client.post( self.location_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) - self.assertEqual(response.data['geometry'][0], 'This field is required.') - data = {'name': 'empty input test', 'geometry': ''} + self.assertEqual(response.data["geometry"][0], "This field is required.") + data = {"name": "empty input test", "geometry": ""} response = self.client.post( self.location_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) - self.assertEqual(response.data['geometry'][0], 'This field is required.') + self.assertEqual(response.data["geometry"][0], "This field is required.") def test_post_location_list_invalid_WKT(self): - data = {'name': 'WKT wrong input test', 'geometry': 'I AM OBVIOUSLY WRONG'} + data = {"name": "WKT wrong input test", "geometry": "I AM OBVIOUSLY WRONG"} response = self.client.post( self.location_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) self.assertEqual(response.status_code, 400) self.assertEqual(Location.objects.count(), 0) - self.assertEqual(response.data['geometry'][0], self.value_error_message) + self.assertEqual(response.data["geometry"][0], self.value_error_message) # repeat as multipart form data response = self.client.post(self.location_list_url, data) - self.assertEqual(response.data['geometry'][0], self.value_error_message) + self.assertEqual(response.data["geometry"][0], self.value_error_message) data = { - 'name': 'I AM MODERATELY WRONG', - 'geometry': 'POINT (12.492324113849, 41.890307434153)', + "name": "I AM MODERATELY WRONG", + "geometry": "POINT (12.492324113849, 41.890307434153)", } response = self.client.post( self.location_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) - self.assertEqual(response.data['geometry'][0], self.geos_error_message) + self.assertEqual(response.data["geometry"][0], self.geos_error_message) # repeat as multipart form data response = self.client.post(self.location_list_url, data) - self.assertEqual(response.data['geometry'][0], self.geos_error_message) + self.assertEqual(response.data["geometry"][0], self.geos_error_message) def test_post_location_list_invalid_geojson(self): data = { @@ -236,79 +236,79 @@ def test_post_location_list_invalid_geojson(self): response = self.client.post( self.location_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) - self.assertEqual(response.data['geometry'][0], self.gdal_error_message) - data = {"name": "very wrong", "geometry": ['a', 'b', 'c']} + self.assertEqual(response.data["geometry"][0], self.gdal_error_message) + data = {"name": "very wrong", "geometry": ["a", "b", "c"]} response = self.client.post( self.location_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) - self.assertEqual(response.data['geometry'][0][0:65], self.type_error_message) + self.assertEqual(response.data["geometry"][0][0:65], self.type_error_message) data = {"name": "very wrong", "geometry": False} response = self.client.post( self.location_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) - self.assertEqual(response.data['geometry'][0][0:65], self.type_error_message) + self.assertEqual(response.data["geometry"][0][0:65], self.type_error_message) data = {"name": "very wrong", "geometry": {"value": {"nested": ["yo"]}}} response = self.client.post( self.location_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) - self.assertEqual(response.data['geometry'][0], self.gdal_error_message) + self.assertEqual(response.data["geometry"][0], self.gdal_error_message) def test_geojson_format(self): """test geojson format""" location = Location.objects.create( - name='geojson test', geometry='POINT (135.0 45.0)' + name="geojson test", geometry="POINT (135.0 45.0)" ) - url = reverse('api_geojson_location_details', args=[location.id]) + url = reverse("api_geojson_location_details", args=[location.id]) expected = { - 'id': location.id, - 'type': 'Feature', - 'properties': { - 'details': "http://testserver/geojson/%s/" % location.id, - 'name': 'geojson test', - 'fancy_name': 'Kool geojson test', - 'timestamp': None, - 'slug': 'geojson-test', + "id": location.id, + "type": "Feature", + "properties": { + "details": "http://testserver/geojson/%s/" % location.id, + "name": "geojson test", + "fancy_name": "Kool geojson test", + "timestamp": None, + "slug": "geojson-test", }, - 'geometry': {'type': 'Point', 'coordinates': [135.0, 45.0]}, + "geometry": {"type": "Point", "coordinates": [135.0, 45.0]}, } response = self.client.get(url) self.assertCountEqual(json.dumps(response.data), json.dumps(expected)) - response = self.client.get(url, headers={"accept": 'text/html'}) + response = self.client.get(url, headers={"accept": "text/html"}) self.assertContains(response, "Kool geojson test") def test_geojson_id_attribute(self): location = Location.objects.create( - name='geojson test', geometry='POINT (10.1 10.1)' + name="geojson test", geometry="POINT (10.1 10.1)" ) - url = reverse('api_geojson_location_details', args=[location.id]) + url = reverse("api_geojson_location_details", args=[location.id]) response = self.client.get(url) - self.assertEqual(response.data['id'], location.id) + self.assertEqual(response.data["id"], location.id) def test_geojson_id_attribute_slug(self): location = Location.objects.create( - name='geojson test', geometry='POINT (10.1 10.1)' + name="geojson test", geometry="POINT (10.1 10.1)" ) - url = reverse('api_geojson_location_slug_details', args=[location.slug]) + url = reverse("api_geojson_location_slug_details", args=[location.slug]) response = self.client.get(url) - self.assertEqual(response.data['id'], location.slug) + self.assertEqual(response.data["id"], location.slug) def test_geojson_false_id_attribute_slug(self): location = Location.objects.create( - name='falseid test', geometry='POINT (10.1 10.1)' + name="falseid test", geometry="POINT (10.1 10.1)" ) - url = reverse('api_geojson_location_falseid_details', args=[location.id]) + url = reverse("api_geojson_location_falseid_details", args=[location.id]) response = self.client.get(url) - self.assertEqual(response.data['properties']['name'], 'falseid test') + self.assertEqual(response.data["properties"]["name"], "falseid test") with self.assertRaises(KeyError): - response.data['id'] + response.data["id"] def test_post_geojson_id_attribute(self): self.assertEqual(Location.objects.count(), 0) @@ -318,11 +318,11 @@ def test_post_geojson_id_attribute(self): "properties": {"name": "point"}, "geometry": {"type": "Point", "coordinates": [10.1, 10.1]}, } - url = reverse('api_geojson_location_writable_id_list') + url = reverse("api_geojson_location_writable_id_list") response = self.client.post( url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) @@ -330,21 +330,21 @@ def test_post_geojson_id_attribute(self): def test_geojson_no_id_attribute_slug(self): location = Location.objects.create( - name='noid test', geometry='POINT (10.1 10.1)' + name="noid test", geometry="POINT (10.1 10.1)" ) - url = reverse('api_geojson_location_noid_details', args=[location.id]) + url = reverse("api_geojson_location_noid_details", args=[location.id]) response = self.client.get(url) - self.assertEqual(response.data['properties']['name'], 'noid test') + self.assertEqual(response.data["properties"]["name"], "noid test") with self.assertRaises(KeyError): - response.data['id'] + response.data["id"] def test_geojson_filefield_attribute(self): located_file = LocatedFile.objects.create( - name='geojson filefield test', geometry='POINT (10.1 10.1)' + name="geojson filefield test", geometry="POINT (10.1 10.1)" ) - url = reverse('api_geojson_located_file_details', args=[located_file.id]) + url = reverse("api_geojson_located_file_details", args=[located_file.id]) response = self.client.get(url) - self.assertEqual(response.data['properties']['file'], None) + self.assertEqual(response.data["properties"]["file"], None) def test_post_geojson_location_list(self): self.assertEqual(Location.objects.count(), 0) @@ -356,21 +356,21 @@ def test_post_geojson_location_list(self): response = self.client.post( self.geojson_location_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) url = reverse( - 'api_geojson_location_details', - args=[Location.objects.order_by('-id')[0].id], + "api_geojson_location_details", + args=[Location.objects.order_by("-id")[0].id], ) response = self.client.get(url) - self.assertEqual(response.data['properties']['name'], "point?") - self.assertEqual(response.data['geometry']['type'], "Point") + self.assertEqual(response.data["properties"]["name"], "point?") + self.assertEqual(response.data["geometry"]["type"], "Point") self.assertEqual( - json.dumps(response.data['geometry']['coordinates']), "[10.1, 10.1]" + json.dumps(response.data["geometry"]["coordinates"]), "[10.1, 10.1]" ) - self.assertNotEqual(response.data['properties']['details'], "ignore this") + self.assertNotEqual(response.data["properties"]["details"], "ignore this") def test_post_geojson_location_list_HTML(self): self.assertEqual(Location.objects.count(), 0) @@ -382,22 +382,22 @@ def test_post_geojson_location_list_HTML(self): response = self.client.post( self.geojson_location_list_url, data=json.dumps(data), - content_type='application/json', - headers={"accept": 'text/html'}, + content_type="application/json", + headers={"accept": "text/html"}, ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) url = reverse( - 'api_geojson_location_details', - args=[Location.objects.order_by('-id')[0].id], + "api_geojson_location_details", + args=[Location.objects.order_by("-id")[0].id], ) response = self.client.get(url) - self.assertEqual(response.data['properties']['name'], "point?") - self.assertEqual(response.data['geometry']['type'], "Point") + self.assertEqual(response.data["properties"]["name"], "point?") + self.assertEqual(response.data["geometry"]["type"], "Point") self.assertEqual( - json.dumps(response.data['geometry']['coordinates']), "[10.1, 10.1]" + json.dumps(response.data["geometry"]["coordinates"]), "[10.1, 10.1]" ) - self.assertNotEqual(response.data['properties']['details'], "ignore this") + self.assertNotEqual(response.data["properties"]["details"], "ignore this") def test_post_invalid_geojson_location_list(self): data = { @@ -408,11 +408,11 @@ def test_post_invalid_geojson_location_list(self): response = self.client.post( self.geojson_location_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) self.assertEqual(response.status_code, 400) self.assertEqual(Location.objects.count(), 0) - self.assertEqual(response.data['name'][0], "This field is required.") + self.assertEqual(response.data["name"][0], "This field is required.") data = { "type": "Feature", "properties": {"name": "point?"}, @@ -421,11 +421,11 @@ def test_post_invalid_geojson_location_list(self): response = self.client.post( self.geojson_location_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) self.assertEqual(response.status_code, 400) self.assertEqual(Location.objects.count(), 0) - self.assertEqual(response.data['geometry'][0], self.gdal_error_message) + self.assertEqual(response.data["geometry"][0], self.gdal_error_message) def test_post_geojson_location_list_WKT(self): self.assertEqual(Location.objects.count(), 0) @@ -437,19 +437,19 @@ def test_post_geojson_location_list_WKT(self): response = self.client.post( self.geojson_location_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) url = reverse( - 'api_geojson_location_details', - args=[Location.objects.order_by('-id')[0].id], + "api_geojson_location_details", + args=[Location.objects.order_by("-id")[0].id], ) response = self.client.get(url) - self.assertEqual(response.data['properties']['name'], "point?") - self.assertEqual(response.data['geometry']['type'], "Point") + self.assertEqual(response.data["properties"]["name"], "point?") + self.assertEqual(response.data["geometry"]["type"], "Point") self.assertEqual( - json.dumps(response.data['geometry']['coordinates']), "[10.1, 10.1]" + json.dumps(response.data["geometry"]["coordinates"]), "[10.1, 10.1]" ) def test_geofeatured_model_serializer_compatible_with_geomodel_serializer(self): @@ -466,7 +466,7 @@ def test_geofeatured_model_serializer_compatible_with_geomodel_serializer(self): response = self.client.post( self.geojson_location_list_url, data=json.dumps(data), - content_type='application/json', + content_type="application/json", ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) @@ -483,19 +483,19 @@ def test_geofeatured_model_post_as_multipartformdata(self): response = self.client.post(self.location_list_url, data) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) - self.assertEqual(response.data['geometry']['type'], "Point") + self.assertEqual(response.data["geometry"]["type"], "Point") def test_HTML_browsable_geojson_location_list(self): response = self.client.get( - self.geojson_location_list_url, headers={"accept": 'text/html'} + self.geojson_location_list_url, headers={"accept": "text/html"} ) self.assertEqual(response.status_code, 200) self._create_locations() response = self.client.get( - self.geojson_location_list_url, headers={"accept": 'text/html'} + self.geojson_location_list_url, headers={"accept": "text/html"} ) - self.assertContains(response, 'l1') - self.assertContains(response, 'l2') + self.assertContains(response, "l1") + self.assertContains(response, "l2") def test_post_geojson_location_list_HTML_web_form(self): self.assertEqual(Location.objects.count(), 0) @@ -504,7 +504,7 @@ def test_post_geojson_location_list_HTML_web_form(self): "geometry": json.dumps({"type": "Point", "coordinates": [10.1, 10.1]}), } response = self.client.post( - self.geojson_location_list_url, data, headers={"accept": 'text/html'} + self.geojson_location_list_url, data, headers={"accept": "text/html"} ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) @@ -516,7 +516,7 @@ def test_post_geojson_location_list_HTML_web_form_WKT(self): self.assertEqual(Location.objects.count(), 0) data = {"name": "HTML test WKT", "geometry": "POINT (10.1 10.1)"} response = self.client.post( - self.geojson_location_list_url, data, headers={"accept": 'text/html'} + self.geojson_location_list_url, data, headers={"accept": "text/html"} ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) @@ -526,7 +526,7 @@ def test_post_geojson_location_list_HTML_web_form_WKT(self): def test_geojson_HTML_widget_value(self): self._create_locations() response = self.client.get( - self.geojson_location_list_url, headers={"accept": 'text/html'} + self.geojson_location_list_url, headers={"accept": "text/html"} ) self.assertContains(response, '