diff --git a/.travis.yml b/.travis.yml index 14eaa327..a2d9986a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,8 @@ before_install: - sudo add-apt-repository ppa:ubuntugis/ubuntugis-unstable -y - sudo apt-get update -q - sudo apt-get install binutils libproj-dev gdal-bin -y - + - pip install -U pip setuptools wheel + - pip install -U -r requirements-test.txt install: - pip install tox - pip install docutils pygments # for setup.py check -r -s @@ -39,6 +40,7 @@ install: before_script: - createdb django_restframework_gis - psql -U postgres -d django_restframework_gis -c "CREATE EXTENSION postgis;" + - ./run-qa-checks script: - tox -e travis diff --git a/LICENSE b/LICENSE index cfff5c92..638618c7 100644 --- a/LICENSE +++ b/LICENSE @@ -16,4 +16,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +THE SOFTWARE. diff --git a/README.rst b/README.rst index 30587130..5a25cb1e 100644 --- a/README.rst +++ b/README.rst @@ -689,6 +689,17 @@ To run tests in docker use docker-compose build docker-compose run --rm test +Running QA-checks +================= + +You can run qa-checks by using + +.. code-block:: shell + + ./run-qa-checks + + +In docker testing, QA checks are executed automatically. Contributing ------------ diff --git a/docker-compose.yml b/docker-compose.yml index 96595838..1d020f42 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,7 +17,7 @@ services: - ./tests:/project/tests - ./README.rst:/project/README.rst - ./setup.py:/project/setup.py - command: sh -c "pip install -e . && python ./tests/manage.py test tests/django_restframework_gis_tests" + command: sh -c "pip install -e . && python ./tests/manage.py test tests/django_restframework_gis_tests && ./run-qa-checks" postgres: image: mdillon/postgis:10-alpine diff --git a/requirements-test.txt b/requirements-test.txt index a0b7a2f0..e6caa773 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,4 +1,5 @@ psycopg2 -coveralls django-filter>=2.0 contexttimer +# QA checks +openwisp-utils[qa]~=0.6 diff --git a/rest_framework_gis/apps.py b/rest_framework_gis/apps.py index 8ae6249f..fe2076fe 100644 --- a/rest_framework_gis/apps.py +++ b/rest_framework_gis/apps.py @@ -10,6 +10,7 @@ def ready(self): """ from django.contrib.gis.db import models from rest_framework.serializers import ModelSerializer + from .fields import GeometryField try: @@ -20,13 +21,15 @@ def ready(self): field_mapping = ModelSerializer.serializer_field_mapping # map GeoDjango fields to drf-gis GeometryField - field_mapping.update({ - models.GeometryField: GeometryField, - models.PointField: GeometryField, - models.LineStringField: GeometryField, - models.PolygonField: GeometryField, - models.MultiPointField: GeometryField, - models.MultiLineStringField: GeometryField, - models.MultiPolygonField: GeometryField, - models.GeometryCollectionField: GeometryField - }) + field_mapping.update( + { + models.GeometryField: GeometryField, + models.PointField: GeometryField, + models.LineStringField: GeometryField, + models.PolygonField: GeometryField, + models.MultiPointField: GeometryField, + models.MultiLineStringField: GeometryField, + models.MultiPolygonField: GeometryField, + models.GeometryCollectionField: GeometryField, + } + ) diff --git a/rest_framework_gis/fields.py b/rest_framework_gis/fields.py index 0d0e15f1..1a0be355 100644 --- a/rest_framework_gis/fields.py +++ b/rest_framework_gis/fields.py @@ -1,13 +1,12 @@ import json from collections import OrderedDict -from django.contrib.gis.geos import GEOSGeometry, GEOSException from django.contrib.gis.gdal import GDALException +from django.contrib.gis.geos import GEOSException, GEOSGeometry from django.core.exceptions import ValidationError from django.utils.translation import ugettext_lazy as _ from rest_framework.fields import Field, SerializerMethodField - __all__ = ['GeometryField', 'GeometrySerializerMethodField'] @@ -15,6 +14,7 @@ class GeometryField(Field): """ A field to handle GeoDjango Geometry fields """ + type_name = 'GeometryField' def __init__(self, precision=None, remove_duplicates=False, **kwargs): @@ -31,10 +31,7 @@ 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': [] - }) + geojson = GeoJsonDict({'type': value.geom_type, 'coordinates': []}) if geojson['type'] == 'GeometryCollection': geometries = geojson.get('geometries') else: @@ -42,10 +39,12 @@ def to_representation(self, value): for geometry in geometries: if self.precision is not None: geometry['coordinates'] = self._recursive_round( - geometry['coordinates'], self.precision) + geometry['coordinates'], self.precision + ) if self.remove_dupes: geometry['coordinates'] = self._rm_redundant_points( - geometry['coordinates'], geometry['type']) + geometry['coordinates'], geometry['type'] + ) return geojson def to_internal_value(self, value): @@ -59,9 +58,15 @@ def to_internal_value(self, value): try: return GEOSGeometry(value) except (GEOSException): - raise ValidationError(_('Invalid format: string or unicode input unrecognized as GeoJSON, WKT EWKT or HEXEWKB.')) + raise ValidationError( + _( + 'Invalid format: string or unicode input unrecognized as GeoJSON, WKT EWKT or HEXEWKB.' + ) + ) except (ValueError, TypeError, GDALException) as e: - raise ValidationError(_('Unable to convert to python object: {}'.format(str(e)))) + raise ValidationError( + _('Unable to convert to python object: {}'.format(str(e))) + ) def validate_empty_values(self, data): if data == '': @@ -86,7 +91,7 @@ def _rm_redundant_points(self, geometry, geo_type): determine structure of provided `geometry` argument """ if geo_type in ('MultiPoint', 'LineString'): - close = (geo_type == 'LineString') + close = geo_type == 'LineString' output = [] for coord in geometry: coord = tuple(coord) @@ -96,8 +101,7 @@ def _rm_redundant_points(self, geometry, geo_type): output.append(output[0]) return tuple(output) if geo_type in ('MultiLineString', 'Polygon'): - return [ - self._rm_redundant_points(c, 'LineString') for c in geometry] + 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 @@ -118,6 +122,7 @@ class GeoJsonDict(OrderedDict): Used for serializing GIS values to GeoJSON values TODO: remove this when support for python 2.6/2.7 will be dropped """ + def __init__(self, *args, **kwargs): """ If a string is passed attempt to pass it through json.loads, diff --git a/rest_framework_gis/filters.py b/rest_framework_gis/filters.py index 29abdbe3..9c502518 100644 --- a/rest_framework_gis/filters.py +++ b/rest_framework_gis/filters.py @@ -1,13 +1,12 @@ from math import cos, pi -from django.db.models import Q -from django.core.exceptions import ImproperlyConfigured -from django.contrib.gis.db import models -from django.contrib.gis.geos import Polygon, Point from django.contrib.gis import forms - -from rest_framework.filters import BaseFilterBackend +from django.contrib.gis.db import models +from django.contrib.gis.geos import Point, Polygon +from django.core.exceptions import ImproperlyConfigured +from django.db.models import Q from rest_framework.exceptions import ParseError +from rest_framework.filters import BaseFilterBackend from .tilenames import tile_edges @@ -45,7 +44,7 @@ 'GeoFilterSet', 'TMSTileFilter', 'DistanceToPointFilter', - 'DistanceToPointOrderingFilter' + 'DistanceToPointOrderingFilter', ] @@ -60,7 +59,9 @@ def get_filter_bbox(self, request): try: p1x, p1y, p2x, p2y = (float(n) for n in bbox_string.split(',')) except ValueError: - raise ParseError('Invalid bbox string supplied for parameter {0}'.format(self.bbox_param)) + raise ParseError( + 'Invalid bbox string supplied for parameter {0}'.format(self.bbox_param) + ) x = Polygon.from_bbox((p1x, p1y, p2x, p2y)) return x @@ -80,6 +81,8 @@ def filter_queryset(self, request, queryset, view): if not bbox: return queryset return queryset.filter(Q(**{'%s__%s' % (filter_field, geoDjango_filter): bbox})) + + # backward compatibility InBBOXFilter = InBBoxFilter @@ -94,9 +97,7 @@ def __init__(self, *args, **kwargs): class GeoFilterSet(django_filters.FilterSet): GEOFILTER_FOR_DBFIELD_DEFAULTS = { - models.GeometryField: { - 'filter_class': GeometryFilter - }, + models.GeometryField: {'filter_class': GeometryFilter}, } def __new__(cls, *args, **kwargs): @@ -120,7 +121,9 @@ def get_filter_bbox(self, request): try: z, x, y = (int(n) for n in tile_string.split('/')) except ValueError: - raise ParseError('Invalid tile string supplied for parameter {0}'.format(self.tile_param)) + raise ParseError( + 'Invalid tile string supplied for parameter {0}'.format(self.tile_param) + ) bbox = Polygon.from_bbox(tile_edges(x, y, z)) return bbox @@ -138,7 +141,11 @@ def get_filter_point(self, request, **kwargs): try: (x, y) = (float(n) for n in point_string.split(',')) except ValueError: - raise ParseError('Invalid geometry string supplied for parameter {0}'.format(self.point_param)) + raise ParseError( + 'Invalid geometry string supplied for parameter {0}'.format( + self.point_param + ) + ) p = Point(x, y, **kwargs) return p @@ -169,7 +176,7 @@ def dist_to_deg(self, distance, latitude): rad2deg = 180 / pi earthRadius = 6378160.0 latitudeCorrection = 0.5 * (1 + cos(lat * pi / 180)) - return (distance / (earthRadius * latitudeCorrection) * rad2deg) + return distance / (earthRadius * latitudeCorrection) * rad2deg def filter_queryset(self, request, queryset, view): filter_field = getattr(view, 'distance_filter_field', None) @@ -188,13 +195,19 @@ def filter_queryset(self, request, queryset, view): try: dist = float(dist_string) except ValueError: - raise ParseError('Invalid distance string supplied for parameter {0}'.format(self.dist_param)) + raise ParseError( + 'Invalid distance string supplied for parameter {0}'.format( + self.dist_param + ) + ) - if (convert_distance_input): + if convert_distance_input: # Warning: assumes that the point is (lon,lat) dist = self.dist_to_deg(dist, point[1]) - return queryset.filter(Q(**{'%s__%s' % (filter_field, geoDjango_filter): (point, dist)})) + return queryset.filter( + Q(**{'%s__%s' % (filter_field, geoDjango_filter): (point, dist)}) + ) class DistanceToPointOrderingFilter(DistanceToPointFilter): diff --git a/rest_framework_gis/pagination.py b/rest_framework_gis/pagination.py index 2842b8a7..61d347c4 100644 --- a/rest_framework_gis/pagination.py +++ b/rest_framework_gis/pagination.py @@ -8,13 +8,18 @@ class GeoJsonPagination(pagination.PageNumberPagination): """ A geoJSON implementation of a pagination serializer. """ + 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']) - ])) + return Response( + OrderedDict( + [ + ('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/serializers.py b/rest_framework_gis/serializers.py index cd088181..517666cf 100644 --- a/rest_framework_gis/serializers.py +++ b/rest_framework_gis/serializers.py @@ -1,9 +1,12 @@ from collections import OrderedDict -from django.core.exceptions import ImproperlyConfigured from django.contrib.gis.geos import Polygon - -from rest_framework.serializers import ModelSerializer, ListSerializer, LIST_SERIALIZER_KWARGS +from django.core.exceptions import ImproperlyConfigured +from rest_framework.serializers import ( + LIST_SERIALIZER_KWARGS, + ListSerializer, + ModelSerializer, +) from .fields import GeometryField, GeometrySerializerMethodField # noqa @@ -23,10 +26,15 @@ def to_representation(self, data): """ Add GeoJSON compatible formatting to a serialized queryset list """ - return OrderedDict(( - ("type", "FeatureCollection"), - ("features", super(GeoFeatureModelListSerializer, self).to_representation(data)) - )) + return OrderedDict( + ( + ("type", "FeatureCollection"), + ( + "features", + super(GeoFeatureModelListSerializer, self).to_representation(data), + ), + ) + ) class GeoFeatureModelSerializer(ModelSerializer): @@ -35,16 +43,24 @@ class GeoFeatureModelSerializer(ModelSerializer): that outputs geojson-ready data as features and feature collections """ + @classmethod def many_init(cls, *args, **kwargs): child_serializer = cls(*args, **kwargs) list_kwargs = {'child': child_serializer} - list_kwargs.update(dict([ - (key, value) for key, value in kwargs.items() - if key in LIST_SERIALIZER_KWARGS - ])) + list_kwargs.update( + dict( + [ + (key, value) + for key, value in kwargs.items() + if key in LIST_SERIALIZER_KWARGS + ] + ) + ) meta = getattr(cls, 'Meta', None) - list_serializer_class = getattr(meta, 'list_serializer_class', GeoFeatureModelListSerializer) + list_serializer_class = getattr( + meta, 'list_serializer_class', GeoFeatureModelListSerializer + ) return list_serializer_class(*args, **list_kwargs) def __init__(self, *args, **kwargs): @@ -53,7 +69,11 @@ def __init__(self, *args, **kwargs): 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__' or primary_key in meta.fields: + if ( + 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) @@ -63,7 +83,9 @@ 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: - raise ImproperlyConfigured("You cannot exclude your '{0}'.".format(field_role)) + raise ImproperlyConfigured( + "You cannot exclude your '{0}'.".format(field_role) + ) def add_to_fields(field_name): """Make sure the field is included in the fields""" @@ -85,8 +107,10 @@ def add_to_fields(field_name): 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 " - "'auto_bbox', but you can not set both") + raise ImproperlyConfigured( + "You must eiher define a 'bbox_geo_field' or " + "'auto_bbox', but you can not set both" + ) def to_representation(self, instance): """ @@ -132,7 +156,8 @@ def to_representation(self, instance): # we will remove fields that have been already processed # to increase performance on large numbers fields = [ - field_value for field_key, field_value in self.fields.items() + field_value + for field_key, field_value in self.fields.items() if field_key not in processed_fields ] diff --git a/rest_framework_gis/tilenames.py b/rest_framework_gis/tilenames.py index a0825ab8..3704e6d8 100644 --- a/rest_framework_gis/tilenames.py +++ b/rest_framework_gis/tilenames.py @@ -8,11 +8,13 @@ # Written by Oliver White, 2007 # This file is public-domain # ------------------------------------------------------- -from math import pi, atan, sinh, degrees, pow as math_pow +from math import atan, degrees, pi +from math import pow as math_pow +from math import sinh def num_tiles(z): - return(math_pow(2, z)) + return math_pow(2, z) def lat_edges(y, z): @@ -22,7 +24,7 @@ def lat_edges(y, z): relY2 = relY1 + unit lat1 = mercator_to_lat(pi * (1 - 2 * relY1)) lat2 = mercator_to_lat(pi * (1 - 2 * relY2)) - return(lat1, lat2) + return (lat1, lat2) def lon_edges(x, z): @@ -30,14 +32,14 @@ def lon_edges(x, z): unit = 360 / n lon1 = -180 + x * unit lon2 = lon1 + unit - return(lon1, lon2) + return (lon1, lon2) def tile_edges(x, y, z): lat1, lat2 = lat_edges(y, z) lon1, lon2 = lon_edges(x, z) - return((lon1, lat2, lon2, lat1)) # w, s, e, n + return (lon1, lat2, lon2, lat1) # w, s, e, n def mercator_to_lat(mercatorY): - return(degrees(atan(sinh(mercatorY)))) + return degrees(atan(sinh(mercatorY))) diff --git a/run-qa-checks b/run-qa-checks new file mode 100755 index 00000000..b5cc6d8c --- /dev/null +++ b/run-qa-checks @@ -0,0 +1,4 @@ +#!/bin/bash +set -e + +openwisp-qa-check --skip-checkmigrations --skip-checkmakemigrations diff --git a/setup.cfg b/setup.cfg index 3c6e79cf..9162d1dc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,18 @@ [bdist_wheel] universal=1 + +[flake8] +exclude = ./tests/*settings*.py, + docs/* +max-line-length = 110 +# W503: line break before or after operator +# W504: line break after or after operator +# W605: invalid escape sequence +ignore = W605, W503, W504 + +[isort] +multi_line_output=3 +use_parentheses=True +include_trailing_comma=True +force_grid_wrap=0 +line_length=88 diff --git a/setup.py b/setup.py index af9cbdf1..c1115e40 100644 --- a/setup.py +++ b/setup.py @@ -36,9 +36,7 @@ platforms=['Platform Indipendent'], keywords=['django', 'rest-framework', 'gis', 'geojson'], packages=find_packages(exclude=['tests', 'tests.*']), - install_requires=[ - 'djangorestframework', - ], + install_requires=['djangorestframework'], classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Web Environment', diff --git a/tests/django_restframework_gis_tests/admin.py b/tests/django_restframework_gis_tests/admin.py index 8784869a..b6622900 100644 --- a/tests/django_restframework_gis_tests/admin.py +++ b/tests/django_restframework_gis_tests/admin.py @@ -1,6 +1,4 @@ from django.contrib import admin -from django.conf import settings - from django.contrib.gis.admin import ModelAdmin as GeoModelAdmin from .models import Location diff --git a/tests/django_restframework_gis_tests/migrations/0001_initial.py b/tests/django_restframework_gis_tests/migrations/0001_initial.py index 6bc4bc3f..7458e470 100644 --- a/tests/django_restframework_gis_tests/migrations/0001_initial.py +++ b/tests/django_restframework_gis_tests/migrations/0001_initial.py @@ -10,49 +10,81 @@ class Migration(migrations.Migration): initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( name='BoxedLocation', fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ( + 'id', + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + 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)), - ('geometry', django.contrib.gis.db.models.fields.GeometryField(srid=4326)), - ('bbox_geometry', django.contrib.gis.db.models.fields.PolygonField(srid=4326)), + ( + 'geometry', + django.contrib.gis.db.models.fields.GeometryField(srid=4326), + ), + ( + 'bbox_geometry', + django.contrib.gis.db.models.fields.PolygonField(srid=4326), + ), ], - options={ - 'abstract': False, - }, + options={'abstract': False}, ), migrations.CreateModel( name='LocatedFile', fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ( + 'id', + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + 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)), - ('geometry', django.contrib.gis.db.models.fields.GeometryField(srid=4326)), - ('file', models.FileField(blank=True, null=True, upload_to='located_files')), + ( + 'geometry', + django.contrib.gis.db.models.fields.GeometryField(srid=4326), + ), + ( + 'file', + models.FileField(blank=True, null=True, upload_to='located_files'), + ), ], - options={ - 'abstract': False, - }, + options={'abstract': False}, ), migrations.CreateModel( name='Location', fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ( + 'id', + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + 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)), - ('geometry', django.contrib.gis.db.models.fields.GeometryField(srid=4326)), + ( + '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 0f32ed2b..5f0d7530 100644 --- a/tests/django_restframework_gis_tests/migrations/0002_nullable.py +++ b/tests/django_restframework_gis_tests/migrations/0002_nullable.py @@ -14,14 +14,25 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Nullable', fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ( + 'id', + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + 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)), - ('geometry', django.contrib.gis.db.models.fields.GeometryField(blank=True, null=True, srid=4326)), + ( + '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/models.py b/tests/django_restframework_gis_tests/models.py index 4b3e9527..a670895b 100644 --- a/tests/django_restframework_gis_tests/models.py +++ b/tests/django_restframework_gis_tests/models.py @@ -1,13 +1,7 @@ from django.contrib.gis.db import models from django.utils.text import slugify - -__all__ = [ - 'Location', - 'LocatedFile', - 'BoxedLocation', - 'Nullable' -] +__all__ = ['Location', 'LocatedFile', 'BoxedLocation', 'Nullable'] class BaseModel(models.Model): diff --git a/tests/django_restframework_gis_tests/serializers.py b/tests/django_restframework_gis_tests/serializers.py index 97695db8..1646652e 100644 --- a/tests/django_restframework_gis_tests/serializers.py +++ b/tests/django_restframework_gis_tests/serializers.py @@ -1,10 +1,9 @@ from django.contrib.gis.geos import Point - from rest_framework import pagination, serializers -from rest_framework_gis import serializers as gis_serializers -from .models import * +from rest_framework_gis import serializers as gis_serializers +from .models import BoxedLocation, Location __all__ = [ 'LocationGeoSerializer', @@ -23,6 +22,7 @@ class LocationGeoSerializer(serializers.ModelSerializer): """ location geo serializer """ + details = serializers.HyperlinkedIdentityField(view_name='api_location_details') class Meta: @@ -38,7 +38,10 @@ class PaginatedLocationGeoSerializer(pagination.PageNumberPagination): class LocationGeoFeatureSerializer(gis_serializers.GeoFeatureModelSerializer): """ location geo serializer """ - details = serializers.HyperlinkedIdentityField(view_name='api_geojson_location_details') + + details = serializers.HyperlinkedIdentityField( + view_name='api_geojson_location_details' + ) fancy_name = serializers.SerializerMethodField() def get_fancy_name(self, obj): @@ -52,6 +55,7 @@ class Meta: class LocationGeoFeatureSlugSerializer(LocationGeoFeatureSerializer): """ use slug as id attribute """ + class Meta: model = Location geo_field = 'geometry' @@ -61,6 +65,7 @@ class Meta: class LocationGeoFeatureFalseIdSerializer(LocationGeoFeatureSerializer): """ id attribute set as False """ + class Meta: model = Location geo_field = 'geometry' @@ -73,6 +78,7 @@ class LocationGeoFeatureNoIdSerializer(LocationGeoFeatureSerializer): id attribute left out, issue #82 see: https://github.com/openwisp/django-rest-framework-gis/issues/82 """ + class Meta: model = Location geo_field = 'geometry' @@ -81,7 +87,10 @@ class Meta: class LocatedFileGeoFeatureSerializer(gis_serializers.GeoFeatureModelSerializer): """ located file geo serializer """ - details = serializers.HyperlinkedIdentityField(view_name='api_geojson_located_file_details') + + details = serializers.HyperlinkedIdentityField( + view_name='api_geojson_located_file_details' + ) fancy_name = serializers.SerializerMethodField() file = serializers.FileField(allow_empty_file=True) @@ -96,7 +105,10 @@ class Meta: class BoxedLocationGeoFeatureSerializer(gis_serializers.GeoFeatureModelSerializer): """ location geo serializer """ - details = serializers.HyperlinkedIdentityField(view_name='api_geojson_boxedlocation_details') + + details = serializers.HyperlinkedIdentityField( + view_name='api_geojson_boxedlocation_details' + ) class Meta: model = BoxedLocation @@ -118,7 +130,7 @@ class LocationGeoFeatureMethodSerializer(gis_serializers.GeoFeatureModelSerializ def get_new_geometry(self, obj): if obj.name.startswith('hidden'): - return Point(0., 0.) + return Point(0.0, 0.0) else: return obj.geometry diff --git a/tests/django_restframework_gis_tests/test_bbox.py b/tests/django_restframework_gis_tests/test_bbox.py index ab048fad..f3affb89 100644 --- a/tests/django_restframework_gis_tests/test_bbox.py +++ b/tests/django_restframework_gis_tests/test_bbox.py @@ -1,38 +1,48 @@ import json from django.test import TestCase + try: from django.urls import reverse except ImportError: from django.core.urlresolvers import reverse + from django.core.exceptions import ImproperlyConfigured from rest_framework_gis import serializers as gis_serializers from .models import BoxedLocation, Location -from .serializers import LocationGeoSerializer class TestRestFrameworkGisBBox(TestCase): """ unit tests for bbox support in restframework_gis """ + 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') 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))' + 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))', ) 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))' + 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))', ) 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))' + 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))', ) def test_list(self): @@ -54,25 +64,21 @@ def test_list(self): def test_post_location_list_geojson(self): self.assertEqual(BoxedLocation.objects.count(), 0) data = { - "properties": { - "name": "geojson input test", - }, - "geometry": { - "type": "Point", - "coordinates": [ - 12.49, - 41.89 - ] - }, - "bbox": [11.0, 40.0, 13.0, 42.0] + "properties": {"name": "geojson input test"}, + "geometry": {"type": "Point", "coordinates": [12.49, 41.89]}, + "bbox": [11.0, 40.0, 13.0, 42.0], } response = self.client.post( - self.geojson_boxedlocation_list_url, data=json.dumps(data), - content_type='application/json' + self.geojson_boxedlocation_list_url, + data=json.dumps(data), + content_type='application/json', ) self.assertEqual(response.status_code, 201) self.assertEqual(BoxedLocation.objects.count(), 1) - self.assertEqual(BoxedLocation.objects.all()[0].bbox_geometry.extent, (11.0, 40.0, 13.0, 42.0)) + self.assertEqual( + BoxedLocation.objects.all()[0].bbox_geometry.extent, + (11.0, 40.0, 13.0, 42.0), + ) def test_get_autogenerated_location_bbox_geojson(self): self._create_locations() diff --git a/tests/django_restframework_gis_tests/test_fields.py b/tests/django_restframework_gis_tests/test_fields.py index 4c5b9435..aeafcf85 100644 --- a/tests/django_restframework_gis_tests/test_fields.py +++ b/tests/django_restframework_gis_tests/test_fields.py @@ -3,13 +3,10 @@ from django.contrib.gis.geos import GEOSGeometry from django.test import TestCase from rest_framework import serializers -from rest_framework_gis import serializers as gis_serializers +from rest_framework_gis import serializers as gis_serializers -Point = { - "type": "Point", - "coordinates": [-105.0162, 39.5742] -} +Point = {"type": "Point", "coordinates": [-105.0162, 39.5742]} MultiPoint = { "type": "MultiPoint", @@ -18,7 +15,7 @@ [-80.6665, 35.0539], [-80.6665, 35.0539], # Dupe [-80.672, 35.049], - ] + ], } LineString = { @@ -28,8 +25,8 @@ [-101.4021, 39.3300], [-101.4038, 39.3300], [-101.4038, 39.3300], # Dupe - [-97.6354, 38.8739] - ] + [-97.6354, 38.8739], + ], } MultiLineString = { @@ -42,7 +39,7 @@ [-105.0215, 39.5771], [-105.0215, 39.5771], # Dupe [-105.0215, 39.5770], - [-105.0215, 39.5767] + [-105.0215, 39.5767], ], [ [-105.0171, 39.5744], @@ -50,9 +47,9 @@ [-105.0166, 39.5743], [-105.0166, 39.5743], # Dupe [-105.0165, 39.5744], - [-105.0159, 39.5742] + [-105.0159, 39.5742], ], - ] + ], } Polygon = { @@ -67,16 +64,16 @@ [-82.5787, 35.9613], # Dupe [-82.5677, 35.9513], [-84.2211, 34.9850], - [-84.3228, 34.9895] + [-84.3228, 34.9895], ], [ [-75.6903, 35.7420], [-75.5914, 35.7420], [-75.5914, 35.7420], # Dupe [-75.7067, 35.7420], - [-75.6903, 35.7420] + [-75.6903, 35.7420], ], - ] + ], } MultiPolygon = { @@ -88,15 +85,15 @@ [-84.3227, 34.9895], [-84.3227, 34.9895], # Dupe [-84.2211, 34.9850], - [-84.3228, 34.9895] + [-84.3228, 34.9895], ], [ [-75.6903, 35.7420], [-75.5913, 35.7420], [-75.5913, 35.7420], # Dupe [-75.5914, 35.7420], - [-75.6903, 35.7420] - ] + [-75.6903, 35.7420], + ], ], [ [ @@ -104,19 +101,15 @@ [-102.0629, 40.9798], [-102.0629, 40.9798], # Dupe [-109.0283, 36.9851], - [-109.0283, 36.9850] + [-109.0283, 36.9850], ], - ] - ] + ], + ], } GeometryCollection = { "type": "GeometryCollection", - "geometries": [ - Point, - Polygon, - LineString, - ] + "geometries": [Point, Polygon, LineString], } @@ -126,12 +119,14 @@ def get_instance(data_dict): class Model(object): def __init__(self, geojson_dict): self.geometry = GEOSGeometry(json.dumps(geojson_dict)) + return Model(data_dict) @staticmethod def create_serializer(**kwargs): class LocationGeoSerializer(serializers.Serializer): geometry = gis_serializers.GeometryField(**kwargs) + return LocationGeoSerializer def normalize(self, data): @@ -147,345 +142,363 @@ def normalize(self, data): class TestPrecision(BaseTestCase): - def test_precision_Point(self): model = self.get_instance(Point) Serializer = self.create_serializer(precision=2) data = Serializer(model).data - self.assertEqual(self.normalize(data), { - 'geometry': { - "type": "Point", - "coordinates": [ - -105.02, - 39.57 - ] - } - }) + self.assertEqual( + self.normalize(data), + {'geometry': {"type": "Point", "coordinates": [-105.02, 39.57]}}, + ) def test_precision_MultiPoint(self): model = self.get_instance(MultiPoint) Serializer = self.create_serializer(precision=2) data = Serializer(model).data - self.assertEqual(self.normalize(data), { - 'geometry': { - "type": "MultiPoint", - "coordinates": [ - [-105.02, 39.57], - [-80.67, 35.05], - [-80.67, 35.05], - [-80.67, 35.05], - ] - } - }) + self.assertEqual( + self.normalize(data), + { + 'geometry': { + "type": "MultiPoint", + "coordinates": [ + [-105.02, 39.57], + [-80.67, 35.05], + [-80.67, 35.05], + [-80.67, 35.05], + ], + } + }, + ) def test_precision_LineString(self): model = self.get_instance(LineString) Serializer = self.create_serializer(precision=2) data = Serializer(model).data - self.assertEqual(self.normalize(data), { - 'geometry': { - "type": "LineString", - "coordinates": [ - [-101.74, 39.32], - [-101.40, 39.33], - [-101.40, 39.33], - [-101.40, 39.33], - [-97.64, 38.87] - ] - } - }) + self.assertEqual( + self.normalize(data), + { + 'geometry': { + "type": "LineString", + "coordinates": [ + [-101.74, 39.32], + [-101.40, 39.33], + [-101.40, 39.33], + [-101.40, 39.33], + [-97.64, 38.87], + ], + } + }, + ) def test_precision_MultiLineString(self): model = self.get_instance(MultiLineString) Serializer = self.create_serializer(precision=2) data = Serializer(model).data - self.assertEqual(self.normalize(data), { - 'geometry': { - "type": "MultiLineString", - "coordinates": [ - [ - [-105.02, 39.58], - [-105.02, 39.58], - [-105.02, 39.58], - [-105.02, 39.58], - [-105.02, 39.58], - [-105.02, 39.58], - [-105.02, 39.58] - ], - [ - [-105.02, 39.57], - [-105.02, 39.57], - [-105.02, 39.57], - [-105.02, 39.57], - [-105.02, 39.57], - [-105.02, 39.57] + self.assertEqual( + self.normalize(data), + { + 'geometry': { + "type": "MultiLineString", + "coordinates": [ + [ + [-105.02, 39.58], + [-105.02, 39.58], + [-105.02, 39.58], + [-105.02, 39.58], + [-105.02, 39.58], + [-105.02, 39.58], + [-105.02, 39.58], + ], + [ + [-105.02, 39.57], + [-105.02, 39.57], + [-105.02, 39.57], + [-105.02, 39.57], + [-105.02, 39.57], + [-105.02, 39.57], + ], ], - ] - } - }) + } + }, + ) def test_precision_Polygon(self): model = self.get_instance(Polygon) Serializer = self.create_serializer(precision=2) data = Serializer(model).data - self.assertEqual(self.normalize(data), { - 'geometry': { - "type": "Polygon", - "coordinates": [ - [ - [-84.32, 34.99], - [-82.61, 36.03], - [-82.61, 35.99], - [-82.61, 35.98], - [-82.58, 35.96], - [-82.58, 35.96], - [-82.57, 35.95], - [-84.22, 34.98], - [-84.32, 34.99] - ], - [ - [-75.69, 35.74], - [-75.59, 35.74], - [-75.59, 35.74], - [-75.71, 35.74], - [-75.69, 35.74] - ] - ] - } - }) - - def test_precision_MultiPolygon(self): - model = self.get_instance(MultiPolygon) - Serializer = self.create_serializer(precision=2) - data = Serializer(model).data - - self.assertEqual(self.normalize(data), { - 'geometry': { - "type": "MultiPolygon", - "coordinates": [ - [ + self.assertEqual( + self.normalize(data), + { + 'geometry': { + "type": "Polygon", + "coordinates": [ [ [-84.32, 34.99], - [-84.32, 34.99], - [-84.32, 34.99], + [-82.61, 36.03], + [-82.61, 35.99], + [-82.61, 35.98], + [-82.58, 35.96], + [-82.58, 35.96], + [-82.57, 35.95], [-84.22, 34.98], - [-84.32, 34.99] + [-84.32, 34.99], ], [ [-75.69, 35.74], [-75.59, 35.74], [-75.59, 35.74], - [-75.59, 35.74], - [-75.69, 35.74] - ], - ], - [ - [ - [-109.03, 36.98], - [-102.06, 40.98], - [-102.06, 40.98], - [-109.03, 36.99], - [-109.03, 36.98] + [-75.71, 35.74], + [-75.69, 35.74], ], ], - ], - } - }) + } + }, + ) - def test_precision_GeometryCollection(self): - model = self.get_instance(GeometryCollection) + def test_precision_MultiPolygon(self): + model = self.get_instance(MultiPolygon) Serializer = self.create_serializer(precision=2) data = Serializer(model).data - self.assertEqual(self.normalize(data), { - "geometry": { - "type": "GeometryCollection", - "geometries": [ - { - "type": "Point", - "coordinates": [-105.02, 39.57] - }, - { - "type": "Polygon", - "coordinates": [ + + self.assertEqual( + self.normalize(data), + { + 'geometry': { + "type": "MultiPolygon", + "coordinates": [ + [ [ [-84.32, 34.99], - [-82.61, 36.03], - [-82.61, 35.99], - [-82.61, 35.98], - [-82.58, 35.96], - [-82.58, 35.96], - [-82.57, 35.95], + [-84.32, 34.99], + [-84.32, 34.99], [-84.22, 34.98], - [-84.32, 34.99] + [-84.32, 34.99], ], [ [-75.69, 35.74], [-75.59, 35.74], [-75.59, 35.74], - [-75.71, 35.74], - [-75.69, 35.74] - ] - ] - }, - { - "type": "LineString", - "coordinates": [ - [-101.74, 39.32], - [-101.4, 39.33], - [-101.4, 39.33], - [-101.4, 39.33], - [-97.64, 38.87] - ] - } - ] - } - }) + [-75.59, 35.74], + [-75.69, 35.74], + ], + ], + [ + [ + [-109.03, 36.98], + [-102.06, 40.98], + [-102.06, 40.98], + [-109.03, 36.99], + [-109.03, 36.98], + ], + ], + ], + } + }, + ) + def test_precision_GeometryCollection(self): + model = self.get_instance(GeometryCollection) + Serializer = self.create_serializer(precision=2) + data = Serializer(model).data + self.assertEqual( + self.normalize(data), + { + "geometry": { + "type": "GeometryCollection", + "geometries": [ + {"type": "Point", "coordinates": [-105.02, 39.57]}, + { + "type": "Polygon", + "coordinates": [ + [ + [-84.32, 34.99], + [-82.61, 36.03], + [-82.61, 35.99], + [-82.61, 35.98], + [-82.58, 35.96], + [-82.58, 35.96], + [-82.57, 35.95], + [-84.22, 34.98], + [-84.32, 34.99], + ], + [ + [-75.69, 35.74], + [-75.59, 35.74], + [-75.59, 35.74], + [-75.71, 35.74], + [-75.69, 35.74], + ], + ], + }, + { + "type": "LineString", + "coordinates": [ + [-101.74, 39.32], + [-101.4, 39.33], + [-101.4, 39.33], + [-101.4, 39.33], + [-97.64, 38.87], + ], + }, + ], + } + }, + ) -class TestRmRedundant(BaseTestCase): +class TestRmRedundant(BaseTestCase): def test_rm_redundant_Point(self): - model = self.get_instance({ - "type": "Point", - "coordinates": [-1.1, -1.1] - }) + model = self.get_instance({"type": "Point", "coordinates": [-1.1, -1.1]}) Serializer = self.create_serializer(remove_duplicates=True) data = Serializer(model).data - self.assertEqual(self.normalize(data), { - "geometry": { - "type": "Point", - "coordinates": [-1.1, -1.1] - } - }) + self.assertEqual( + self.normalize(data), + {"geometry": {"type": "Point", "coordinates": [-1.1, -1.1]}}, + ) def test_rm_redundant_MultiPoint(self): model = self.get_instance(MultiPoint) Serializer = self.create_serializer(remove_duplicates=True) data = Serializer(model).data - self.assertEqual(self.normalize(data), { - "geometry": { - "type": "MultiPoint", - "coordinates": [ - [-105.0162, 39.5742], - [-80.6665, 35.0539], - # [-80.6665, 35.0539], # Dupe - [-80.672, 35.049], - ] - } - }) + self.assertEqual( + self.normalize(data), + { + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [-105.0162, 39.5742], + [-80.6665, 35.0539], + # [-80.6665, 35.0539], # Dupe + [-80.672, 35.049], + ], + } + }, + ) def test_rm_redundant_LineString(self): model = self.get_instance(LineString) Serializer = self.create_serializer(remove_duplicates=True) data = Serializer(model).data - self.assertEqual(self.normalize(data), { - "geometry": { - "type": "LineString", - "coordinates": [ - [-101.7443, 39.3215], - [-101.4021, 39.3300], - [-101.4038, 39.3300], - # [-101.4038, 39.3300], # Dupe - [-97.6354, 38.8739] - ] - } - }) + self.assertEqual( + self.normalize(data), + { + "geometry": { + "type": "LineString", + "coordinates": [ + [-101.7443, 39.3215], + [-101.4021, 39.3300], + [-101.4038, 39.3300], + # [-101.4038, 39.3300], # Dupe + [-97.6354, 38.8739], + ], + } + }, + ) def test_rm_redundant_MultiLineString(self): model = self.get_instance(MultiLineString) Serializer = self.create_serializer(remove_duplicates=True) data = Serializer(model).data - self.assertEqual(self.normalize(data), { - "geometry": { - "type": "MultiLineString", - "coordinates": [ - [ - [-105.0214, 39.5780], - [-105.0215, 39.5778], - [-105.0215, 39.5774], - [-105.0215, 39.5771], - # [-105.0215, 39.5771], # Dupe - [-105.0215, 39.5770], - [-105.0215, 39.5767] - ], - [ - [-105.0171, 39.5744], - [-105.0169, 39.5743], - [-105.0166, 39.5743], - # [-105.0166, 39.5743], # Dupe - [-105.0165, 39.5744], - [-105.0159, 39.5742] + self.assertEqual( + self.normalize(data), + { + "geometry": { + "type": "MultiLineString", + "coordinates": [ + [ + [-105.0214, 39.5780], + [-105.0215, 39.5778], + [-105.0215, 39.5774], + [-105.0215, 39.5771], + # [-105.0215, 39.5771], # Dupe + [-105.0215, 39.5770], + [-105.0215, 39.5767], + ], + [ + [-105.0171, 39.5744], + [-105.0169, 39.5743], + [-105.0166, 39.5743], + # [-105.0166, 39.5743], # Dupe + [-105.0165, 39.5744], + [-105.0159, 39.5742], + ], ], - ] - } - }) + } + }, + ) def test_rm_redundant_Polygon(self): model = self.get_instance(Polygon) Serializer = self.create_serializer(remove_duplicates=True) data = Serializer(model).data - self.assertEqual(self.normalize(data), { - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [-84.3228, 34.9895], - [-82.6062, 36.0335], - [-82.6062, 35.9913], - [-82.6062, 35.9791], - [-82.5787, 35.9613], - # [-82.5787, 35.9613], # Dupe - [-82.5677, 35.9513], - [-84.2211, 34.985], - [-84.3228, 34.9895] - ], - [ - [-75.6903, 35.742], - [-75.5914, 35.742], - # [-75.5914, 35.7420], # Dupe - [-75.7067, 35.742], - [-75.6903, 35.742] + self.assertEqual( + self.normalize(data), + { + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [-84.3228, 34.9895], + [-82.6062, 36.0335], + [-82.6062, 35.9913], + [-82.6062, 35.9791], + [-82.5787, 35.9613], + # [-82.5787, 35.9613], # Dupe + [-82.5677, 35.9513], + [-84.2211, 34.985], + [-84.3228, 34.9895], + ], + [ + [-75.6903, 35.742], + [-75.5914, 35.742], + # [-75.5914, 35.7420], # Dupe + [-75.7067, 35.742], + [-75.6903, 35.742], + ], ], - ] - } - }) + } + }, + ) def test_rm_redundant_MultiPolygon(self): model = self.get_instance(MultiPolygon) Serializer = self.create_serializer(remove_duplicates=True) data = Serializer(model).data - self.assertEqual(self.normalize(data), { - 'geometry': { - "type": "MultiPolygon", - "coordinates": [ - [ + self.assertEqual( + self.normalize(data), + { + 'geometry': { + "type": "MultiPolygon", + "coordinates": [ [ - [-84.3228, 34.9895], - [-84.3227, 34.9895], - # [-84.3227, 34.9895], # Dupe - [-84.2211, 34.9850], - [-84.3228, 34.9895] + [ + [-84.3228, 34.9895], + [-84.3227, 34.9895], + # [-84.3227, 34.9895], # Dupe + [-84.2211, 34.9850], + [-84.3228, 34.9895], + ], + [ + [-75.6903, 35.7420], + [-75.5913, 35.7420], + # [-75.5913, 35.7420], # Dupe + [-75.5914, 35.7420], + [-75.6903, 35.7420], + ], ], [ - [-75.6903, 35.7420], - [-75.5913, 35.7420], - # [-75.5913, 35.7420], # Dupe - [-75.5914, 35.7420], - [-75.6903, 35.7420] - ] - ], - [ - [ - [-109.0283, 36.9850], - [-102.0629, 40.9798], - # [-102.0629, 40.9798], # Dupe - [-109.0283, 36.9851], - [-109.0283, 36.9850] + [ + [-109.0283, 36.9850], + [-102.0629, 40.9798], + # [-102.0629, 40.9798], # Dupe + [-109.0283, 36.9851], + [-109.0283, 36.9850], + ], ], ], - ], - } - }) + } + }, + ) def test_rm_redundant_MultiPolygon_single_polygon(self): MultiPolygon = { @@ -497,76 +510,79 @@ def test_rm_redundant_MultiPolygon_single_polygon(self): [-109.218215942383, 45.0039901733398], [-109.218215942383, 45.0039901733398], [-109.175567626953, 45.0041999816896], - [-109.17227935791, 45.0041122436525] + [-109.17227935791, 45.0041122436525], ] ] - ] + ], } model = self.get_instance(MultiPolygon) Serializer = self.create_serializer(remove_duplicates=True) data = Serializer(model).data - self.assertEqual(self.normalize(data), { - 'geometry': { - "type": "MultiPolygon", - "coordinates": [ - [ + self.assertEqual( + self.normalize(data), + { + 'geometry': { + "type": "MultiPolygon", + "coordinates": [ [ - [-109.17227935791, 45.0041122436525], - [-109.218215942383, 45.0039901733398], - # [-109.218215942383, 45.0039901733398], # Dupe - [-109.175567626953, 45.0041999816896], - [-109.17227935791, 45.0041122436525] + [ + [-109.17227935791, 45.0041122436525], + [-109.218215942383, 45.0039901733398], + # [-109.218215942383, 45.0039901733398], # Dupe + [-109.175567626953, 45.0041999816896], + [-109.17227935791, 45.0041122436525], + ] ] - ] - ] - } - }) + ], + } + }, + ) def test_rm_redundant_GeometryCollection(self): model = self.get_instance(GeometryCollection) Serializer = self.create_serializer(remove_duplicates=True) data = Serializer(model).data - self.assertEqual(self.normalize(data), { - "geometry": { - "type": "GeometryCollection", - "geometries": [ - { - "type": "Point", - "coordinates": [-105.0162, 39.5742] - }, - { - "type": "Polygon", - "coordinates": [ - [ - [-84.3228, 34.9895], - [-82.6062, 36.0335], - [-82.6062, 35.9913], - [-82.6062, 35.9791], - [-82.5787, 35.9613], - # [-82.5787, 35.9613], # Dupe - [-82.5677, 35.9513], - [-84.2211, 34.985], - [-84.3228, 34.9895] + self.assertEqual( + self.normalize(data), + { + "geometry": { + "type": "GeometryCollection", + "geometries": [ + {"type": "Point", "coordinates": [-105.0162, 39.5742]}, + { + "type": "Polygon", + "coordinates": [ + [ + [-84.3228, 34.9895], + [-82.6062, 36.0335], + [-82.6062, 35.9913], + [-82.6062, 35.9791], + [-82.5787, 35.9613], + # [-82.5787, 35.9613], # Dupe + [-82.5677, 35.9513], + [-84.2211, 34.985], + [-84.3228, 34.9895], + ], + [ + [-75.6903, 35.742], + [-75.5914, 35.742], + # [-75.5914, 35.7420], # Dupe + [-75.7067, 35.742], + [-75.6903, 35.742], + ], ], - [ - [-75.6903, 35.742], - [-75.5914, 35.742], - # [-75.5914, 35.7420], # Dupe - [-75.7067, 35.742], - [-75.6903, 35.742] + }, + { + "type": "LineString", + "coordinates": [ + [-101.7443, 39.3215], + [-101.4021, 39.3300], + [-101.4038, 39.3300], + # [-101.4038, 39.3300], # Dupe + [-97.6354, 38.8739], ], - ] - }, - { - "type": "LineString", - "coordinates": [ - [-101.7443, 39.3215], - [-101.4021, 39.3300], - [-101.4038, 39.3300], - # [-101.4038, 39.3300], # Dupe - [-97.6354, 38.8739] - ] - } - ] - } - }) + }, + ], + } + }, + ) diff --git a/tests/django_restframework_gis_tests/test_filters.py b/tests/django_restframework_gis_tests/test_filters.py index d1244700..7b9de58f 100644 --- a/tests/django_restframework_gis_tests/test_filters.py +++ b/tests/django_restframework_gis_tests/test_filters.py @@ -1,11 +1,11 @@ import json import urllib - from unittest import skipIf from django.conf import settings -from django.test import TestCase from django.contrib.gis.geos import GEOSGeometry, Polygon +from django.test import TestCase + try: from django.urls import reverse except ImportError: @@ -15,13 +15,15 @@ from .views import ( GeojsonLocationContainedInBBoxList, GeojsonLocationOrderDistanceToPointList, - GeojsonLocationWithinDistanceOfPointList + GeojsonLocationWithinDistanceOfPointList, ) -has_spatialite = settings.DATABASES['default']['ENGINE'] == 'django.contrib.gis.db.backends.spatialite' +has_spatialite = ( + settings.DATABASES['default']['ENGINE'] + == 'django.contrib.gis.db.backends.spatialite' +) try: - from django.contrib.gis.db.models.functions import GeometryDistance has_geometry_distance = True except ImportError: has_geometry_distance = False @@ -31,15 +33,32 @@ class TestRestFrameworkGisFilters(TestCase): """ unit tests for filters feature in restframework_gis """ + def setUp(self): - self.location_contained_in_bbox_list_url = reverse('api_geojson_location_list_contained_in_bbox_filter') - self.location_overlaps_bbox_list_url = reverse('api_geojson_location_list_overlaps_bbox_filter') - self.location_contained_in_tile_list_url = reverse('api_geojson_location_list_contained_in_tile_filter') - self.location_overlaps_tile_list_url = reverse('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') - self.location_within_degrees_of_point_list_url = reverse('api_geojson_location_list_within_degrees_of_point_filter') - self.geojson_contained_in_geometry = reverse('api_geojson_contained_in_geometry') - self.location_order_distance_to_point = reverse('api_geojson_location_order_distance_to_point_list_filter') + self.location_contained_in_bbox_list_url = reverse( + 'api_geojson_location_list_contained_in_bbox_filter' + ) + self.location_overlaps_bbox_list_url = reverse( + 'api_geojson_location_list_overlaps_bbox_filter' + ) + self.location_contained_in_tile_list_url = reverse( + 'api_geojson_location_list_contained_in_tile_filter' + ) + self.location_overlaps_tile_list_url = reverse( + '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' + ) + self.location_within_degrees_of_point_list_url = reverse( + 'api_geojson_location_list_within_degrees_of_point_filter' + ) + self.geojson_contained_in_geometry = reverse( + 'api_geojson_contained_in_geometry' + ) + self.location_order_distance_to_point = reverse( + 'api_geojson_location_order_distance_to_point_list_filter' + ) treasure_island_geojson = """{ "type": "Polygon", @@ -162,20 +181,30 @@ def test_inBBOXFilter_filtering(self): # Rectangle with bottom left at (-3,-3), top right at (-1,2) nonIntersecting = Location() nonIntersecting.name = 'nonIntersecting' - nonIntersecting.geometry = Polygon(((-3, -3), (-1, -3), (-1, 2), (-3, 2), (-3, -3))) + nonIntersecting.geometry = Polygon( + ((-3, -3), (-1, -3), (-1, 2), (-3, 2), (-3, -3)) + ) nonIntersecting.save() # Make sure we only get back the ones strictly contained in the bounding box - response = self.client.get(self.location_contained_in_bbox_list_url + url_params) + 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(result['properties']['name'] in ('isContained', 'isEqualToBounds'), True) + self.assertEqual( + 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(result['properties']['name'] in ('isContained', 'isEqualToBounds', 'overlaps'), True) + self.assertEqual( + result['properties']['name'] + in ('isContained', 'isEqualToBounds', 'overlaps'), + True, + ) @skipIf(has_spatialite, 'Skipped test for spatialite backend: not accurate enough') def test_TileFilter_filtering(self): @@ -200,7 +229,9 @@ def test_TileFilter_filtering(self): isEqualToBounds = Location() isEqualToBounds.name = 'isEqualToBounds' - isEqualToBounds.geometry = Polygon(((0, 0), (0, 85.05113), (180, 85.05113), (180, 0), (0, 0))) + isEqualToBounds.geometry = Polygon( + ((0, 0), (0, 85.05113), (180, 85.05113), (180, 0), (0, 0)) + ) isEqualToBounds.save() # Rectangle with bottom left at (-1,1), top right at (5,5) @@ -212,22 +243,34 @@ def test_TileFilter_filtering(self): # Rectangle with bottom left at (-3,-3), top right at (-1,2) nonIntersecting = Location() nonIntersecting.name = 'nonIntersecting' - nonIntersecting.geometry = Polygon(((-3, -3), (-1, -3), (-1, 2), (-3, 2), (-3, -3))) + nonIntersecting.geometry = Polygon( + ((-3, -3), (-1, -3), (-1, 2), (-3, 2), (-3, -3)) + ) nonIntersecting.save() # Make sure we only get back the ones strictly contained in the bounding box - response = self.client.get(self.location_contained_in_tile_list_url + url_params) + 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(result['properties']['name'] in ('isContained', 'isEqualToBounds'), True) + self.assertEqual( + 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(result['properties']['name'] in ('isContained', 'isEqualToBounds', 'overlaps'), True) - - @skipIf(has_spatialite, 'Skipped test for spatialite backend: missing feature "dwithin"') + self.assertEqual( + result['properties']['name'] + in ('isContained', 'isEqualToBounds', 'overlaps'), + True, + ) + + @skipIf( + has_spatialite, 'Skipped test for spatialite backend: missing feature "dwithin"' + ) def test_DistanceToPointFilter_filtering(self): """ Checks that the DistanceFilter returns only objects within the given distance of the @@ -240,10 +283,16 @@ def test_DistanceToPointFilter_filtering(self): point_on_alcatraz = [-122.4222, 37.82667] url_params = '?dist=%0.4f&point=hello&format=json' % (distance,) - response = self.client.get('%s%s' % (self.location_within_distance_of_point_list_url, url_params)) + response = self.client.get( + '%s%s' % (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' % (distance, point_on_alcatraz[0], point_on_alcatraz[1]) + url_params = '?dist=%0.4f&point=%0.4f,%0.4f&format=json' % ( + distance, + point_on_alcatraz[0], + point_on_alcatraz[1], + ) treasure_island = Location() treasure_island.name = "Treasure Island" @@ -257,29 +306,51 @@ def test_DistanceToPointFilter_filtering(self): ggpark.save() # Make sure we only get back the ones within the distance - response = self.client.get('%s%s' % (self.location_within_distance_of_point_list_url, url_params)) + response = self.client.get( + '%s%s' % (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) # 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' % (distance, point_on_alcatraz[0], point_on_alcatraz[1]) - response = self.client.get('%s%s' % (self.location_within_distance_of_point_list_url, url_params)) + url_params = '?dist=%0.4f&point=%0.4f,%0.4f&format=json' % ( + distance, + point_on_alcatraz[0], + point_on_alcatraz[1], + ) + response = self.client.get( + '%s%s' % (self.location_within_distance_of_point_list_url, url_params) + ) self.assertEqual(len(response.data['features']), 2) for result in response.data['features']: - self.assertIn(result['properties']['name'], (ggpark.name, treasure_island.name)) + self.assertIn( + result['properties']['name'], (ggpark.name, treasure_island.name) + ) # Make sure we only get back the ones within the distance - degrees = .05 - url_params = '?dist=%0.4f&point=%0.4f,%0.4f&format=json' % (degrees, point_on_alcatraz[0], point_on_alcatraz[1]) - response = self.client.get(self.location_within_degrees_of_point_list_url + url_params) + degrees = 0.05 + url_params = '?dist=%0.4f&point=%0.4f,%0.4f&format=json' % ( + degrees, + point_on_alcatraz[0], + point_on_alcatraz[1], + ) + 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) - @skipIf(has_spatialite, 'Skipped test for spatialite backend: missing feature "GeometryDistance"') - @skipIf(not has_geometry_distance, 'Skipped test for Django < 3.0: missing feature "GeometryDistance"') + @skipIf( + has_spatialite, + 'Skipped test for spatialite backend: missing feature "GeometryDistance"', + ) + @skipIf( + not has_geometry_distance, + 'Skipped test for Django < 3.0: missing feature "GeometryDistance"', + ) def test_DistanceToPointOrderingFilter_filtering(self): """ Checks that the DistanceOrderingFilter returns the objects in the correct order @@ -288,37 +359,80 @@ def test_DistanceToPointOrderingFilter_filtering(self): self.assertEqual(Location.objects.count(), 0) url_params = '?point=hello&format=json' - response = self.client.get('%s%s' % (self.location_order_distance_to_point, url_params)) + response = self.client.get( + '%s%s' % (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)') - Location.objects.create(name='Dallas', geometry='SRID=4326;POINT (-96.801611 32.782057)') - Location.objects.create(name='Oklahoma City', geometry='SRID=4326;POINT (-97.521157 34.464642)') - Location.objects.create(name='Wellington', geometry='SRID=4326;POINT (174.783117 -41.315268)') - Location.objects.create(name='Pueblo', geometry='SRID=4326;POINT (-104.609252 38.255001)') - Location.objects.create(name='Lawrence', geometry='SRID=4326;POINT (-95.235060 38.971823)') - Location.objects.create(name='Chicago', geometry='SRID=4326;POINT (-87.650175 41.850385)') - Location.objects.create(name='Victoria', geometry='SRID=4326;POINT (-123.305196 48.462611)') + Location.objects.create( + name='Houston', geometry='SRID=4326;POINT (-95.363151 29.763374)' + ) + Location.objects.create( + name='Dallas', geometry='SRID=4326;POINT (-96.801611 32.782057)' + ) + Location.objects.create( + name='Oklahoma City', geometry='SRID=4326;POINT (-97.521157 34.464642)' + ) + Location.objects.create( + name='Wellington', geometry='SRID=4326;POINT (174.783117 -41.315268)' + ) + Location.objects.create( + name='Pueblo', geometry='SRID=4326;POINT (-104.609252 38.255001)' + ) + Location.objects.create( + name='Lawrence', geometry='SRID=4326;POINT (-95.235060 38.971823)' + ) + Location.objects.create( + name='Chicago', geometry='SRID=4326;POINT (-87.650175 41.850385)' + ) + Location.objects.create( + name='Victoria', geometry='SRID=4326;POINT (-123.305196 48.462611)' + ) point = [-90, 40] url_params = '?point=%i,%i&format=json' % (point[0], point[1]) - response = self.client.get('%s%s' % (self.location_order_distance_to_point, url_params)) + response = self.client.get( + '%s%s' % (self.location_order_distance_to_point, url_params) + ) self.assertEqual(len(response.data['features']), 8) self.assertEqual( [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]) - response = self.client.get('%s%s' % (self.location_order_distance_to_point, url_params)) + response = self.client.get( + '%s%s' % (self.location_order_distance_to_point, url_params) + ) self.assertEqual(len(response.data['features']), 8) self.assertEqual( [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', + ], ) - @skipIf(has_spatialite, 'Skipped test for spatialite backend: missing feature "contains_properly"') + @skipIf( + has_spatialite, + 'Skipped test for spatialite backend: missing feature "contains_properly"', + ) def test_GeometryField_filtering(self): """Checks that the GeometryField allows sane filtering.""" self.assertEqual(Location.objects.count(), 0) @@ -334,7 +448,9 @@ def test_GeometryField_filtering(self): ggpark.geometry = self.ggpark_geom ggpark.save() - point_inside_ggpark_geojson = """{ "type": "Point", "coordinates": [ -122.49034881591797, 37.76949349270407 ] }""" + point_inside_ggpark_geojson = """ + { "type": "Point", "coordinates": [ -122.49034881591797, 37.76949349270407 ] } + """ try: quoted_param = urllib.quote(point_inside_ggpark_geojson) @@ -343,7 +459,9 @@ def test_GeometryField_filtering(self): url_params = "?contains_properly=%s" % (quoted_param,) - response = self.client.get('{0}{1}'.format(self.geojson_contained_in_geometry, url_params)) + response = self.client.get( + '{0}{1}'.format(self.geojson_contained_in_geometry, url_params) + ) self.assertEqual(len(response.data), 1) geometry_response = GEOSGeometry(json.dumps(response.data[0]['geometry'])) @@ -357,72 +475,129 @@ def test_GeometryField_filtering(self): def test_inBBOXFilter_filtering_none(self): 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 = self.client.get( + self.location_contained_in_bbox_list_url + url_params + ) + self.assertDictEqual( + response.data, {'type': 'FeatureCollection', 'features': []} + ) def test_inBBOXFilter_ValueError(self): 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 = 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', + ) 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' - response = self.client.get(self.location_contained_in_bbox_list_url + url_params) - self.assertDictEqual(response.data, {'type': 'FeatureCollection', 'features': []}) + response = self.client.get( + self.location_contained_in_bbox_list_url + url_params + ) + self.assertDictEqual( + response.data, {'type': 'FeatureCollection', 'features': []} + ) GeojsonLocationContainedInBBoxList.bbox_filter_field = original_value def test_TileFilter_filtering_none(self): url_params = '?tile=&format=json' - response = self.client.get(self.location_contained_in_tile_list_url + url_params) + response = self.client.get( + self.location_contained_in_tile_list_url + url_params + ) self.assertEqual(response.data, {'type': 'FeatureCollection', 'features': []}) def test_TileFilter_ValueError(self): 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 = self.client.get( + self.location_contained_in_tile_list_url + url_params + ) + self.assertEqual( + response.data['detail'], 'Invalid tile string supplied for parameter tile' + ) def test_DistanceToPointFilter_filtering_none(self): url_params = '?dist=5000&point=&format=json' - response = self.client.get('%s%s' % (self.location_within_distance_of_point_list_url, url_params)) - self.assertDictEqual(response.data, {'type': 'FeatureCollection', 'features': []}) + response = self.client.get( + '%s%s' % (self.location_within_distance_of_point_list_url, url_params) + ) + self.assertDictEqual( + 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' - response = self.client.get('%s%s' % (self.location_within_distance_of_point_list_url, url_params)) - self.assertDictEqual(response.data, {'type': 'FeatureCollection', 'features': []}) + response = self.client.get( + '%s%s' % (self.location_within_distance_of_point_list_url, url_params) + ) + self.assertDictEqual( + 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' - response = self.client.get('%s%s' % (self.location_within_distance_of_point_list_url, url_params)) - self.assertEqual(response.data['detail'], 'Invalid geometry string supplied for parameter point') + response = self.client.get( + '%s%s' % (self.location_within_distance_of_point_list_url, url_params) + ) + self.assertEqual( + 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' - response = self.client.get('%s%s' % (self.location_within_distance_of_point_list_url, url_params)) - self.assertEqual(response.data['detail'], 'Invalid distance string supplied for parameter dist') + response = self.client.get( + '%s%s' % (self.location_within_distance_of_point_list_url, url_params) + ) + self.assertEqual( + response.data['detail'], + 'Invalid distance string supplied for parameter dist', + ) - @skipIf(not has_geometry_distance, 'Skipped test for Django < 3.0: missing feature "GeometryDistance"') + @skipIf( + not has_geometry_distance, + 'Skipped test for Django < 3.0: missing feature "GeometryDistance"', + ) def test_DistanceToPointOrderingFilter_filtering_none(self): url_params = '?point=&format=json' - response = self.client.get('%s%s' % (self.location_order_distance_to_point, url_params)) - self.assertDictEqual(response.data, {'type': 'FeatureCollection', 'features': []}) + response = self.client.get( + '%s%s' % (self.location_order_distance_to_point, url_params) + ) + self.assertDictEqual( + response.data, {'type': 'FeatureCollection', 'features': []} + ) - @skipIf(not has_geometry_distance, 'Skipped test for Django < 3.0: missing feature "GeometryDistance"') + @skipIf( + not has_geometry_distance, + 'Skipped test for Django < 3.0: missing feature "GeometryDistance"', + ) def test_DistanceToPointOrderingFilter_ordering_filter_field_none(self): - original_value = GeojsonLocationOrderDistanceToPointList.distance_ordering_filter_field + original_value = ( + GeojsonLocationOrderDistanceToPointList.distance_ordering_filter_field + ) GeojsonLocationOrderDistanceToPointList.distance_ordering_filter_field = None url_params = '?point=&format=json' - response = self.client.get('%s%s' % (self.location_order_distance_to_point, url_params)) - self.assertDictEqual(response.data, {'type': 'FeatureCollection', 'features': []}) - GeojsonLocationOrderDistanceToPointList.distance_ordering_filter_field = original_value + response = self.client.get( + '%s%s' % (self.location_order_distance_to_point, url_params) + ) + self.assertDictEqual( + response.data, {'type': 'FeatureCollection', 'features': []} + ) + GeojsonLocationOrderDistanceToPointList.distance_ordering_filter_field = ( + original_value + ) @skipIf(has_geometry_distance, 'Skipped test for Django >= 3.0') def test_DistanceToPointOrderingFilter_not_available(self): url_params = '?point=12,42&format=json' with self.assertRaises(ValueError): - self.client.get('%s%s' % (self.location_order_distance_to_point, url_params)) + self.client.get( + '%s%s' % (self.location_order_distance_to_point, url_params) + ) diff --git a/tests/django_restframework_gis_tests/test_performance.py b/tests/django_restframework_gis_tests/test_performance.py index b4323d22..3e5cd54f 100644 --- a/tests/django_restframework_gis_tests/test_performance.py +++ b/tests/django_restframework_gis_tests/test_performance.py @@ -1,19 +1,21 @@ import sys + from django.conf import settings # this test must be run explicitly # either by calling: # 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 or settings.TEST_PERFORMANCE: +if ( + 'django_restframework_gis_tests.test_performance' in sys.argv + or settings.TEST_PERFORMANCE +): + from contexttimer import Timer from django.test import TestCase - try: - from django.urls import reverse - except ImportError: - from django.core.urlresolvers import reverse from rest_framework.renderers import JSONRenderer + from rest_framework_gis import serializers as gis_serializers - from contexttimer import Timer + from .models import Location class TestRestFrameworkGisPerformance(TestCase): @@ -26,9 +28,11 @@ def _create_data(self): slug = 'l{0}' wkt = 'POINT (13.{0}125000020002 42.{0}565179379999)' for n in range(1, self.NUMBER_OF_LOCATIONS): - locations.append(Location(name=name.format(n), - slug=slug.format(n), - geometry=wkt.format(n))) + locations.append( + Location( + name=name.format(n), slug=slug.format(n), geometry=wkt.format(n) + ) + ) Location.objects.bulk_create(locations) def test_geojson_performance(self): @@ -37,6 +41,7 @@ class Meta: model = Location geo_field = 'geometry' fields = '__all__' + # create data self._create_data() # initialize serializer @@ -44,6 +49,7 @@ class Meta: with Timer() as t: JSONRenderer().render(serializer.data) # print results - msg = 'GeoJSON rendering of {0} objects '\ - 'completed in {1}'.format(self.NUMBER_OF_LOCATIONS, t.elapsed) + msg = 'GeoJSON rendering of {0} objects ' 'completed in {1}'.format( + self.NUMBER_OF_LOCATIONS, t.elapsed + ) print('\n\033[95m{0}\033[0m'.format(msg)) diff --git a/tests/django_restframework_gis_tests/tests.py b/tests/django_restframework_gis_tests/tests.py index fc1d30db..ff3b0feb 100644 --- a/tests/django_restframework_gis_tests/tests.py +++ b/tests/django_restframework_gis_tests/tests.py @@ -2,28 +2,27 @@ unit tests for restframework_gis """ -import django -import urllib -import sys import json import pickle - +import sys from unittest import skipIf +import django +from django.contrib.gis.geos import GEOSGeometry, Point from django.test import TestCase -from django.contrib.gis.geos import GEOSGeometry, Polygon, Point + try: from django.urls import reverse except ImportError: from django.core.urlresolvers import reverse -from django.core.exceptions import ImproperlyConfigured import rest_framework +from django.core.exceptions import ImproperlyConfigured from rest_framework_gis import serializers as gis_serializers from rest_framework_gis.fields import GeoJsonDict -from .models import Location, LocatedFile, Nullable +from .models import LocatedFile, Location, Nullable from .serializers import LocationGeoSerializer is_pre_drf_39 = not rest_framework.VERSION.startswith('3.9') @@ -33,17 +32,41 @@ 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.geos_error_message = 'Invalid format: string or unicode input unrecognized as GeoJSON, WKT EWKT or HEXEWKB.' - self.gdal_error_message = 'Unable to convert to python object: Invalid geometry pointer returned from "OGR_G_CreateGeometryFromJson".' + self.geos_error_message = ( + 'Invalid format: string or unicode input unrecognized as GeoJSON,' + ' WKT EWKT or HEXEWKB.' + ) + self.gdal_error_message = ( + 'Unable to convert to python object:' + ' Invalid geometry pointer returned from "OGR_G_CreateGeometryFromJson".' + ) if django.VERSION >= (2, 0, 0): - self.value_error_message = "Unable to convert to python object: String input unrecognized as WKT EWKT, and HEXEWKB." + self.value_error_message = ( + "Unable to convert to python object:" + " String input unrecognized as WKT EWKT, and HEXEWKB." + ) else: - self.value_error_message = "Unable to convert to python object: String or unicode input unrecognized as WKT EWKT, and HEXEWKB." - self.type_error_message = "Unable to convert to python object: Improper geometry input type:" + self.value_error_message = ( + "Unable to convert to python object:" + " String or unicode input unrecognized as WKT EWKT, and HEXEWKB." + ) + self.type_error_message = ( + "Unable to convert to python object: Improper geometry input type:" + ) def _create_locations(self): - self.l1 = Location.objects.create(id=1, 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)') + self.l1 = Location.objects.create( + id=1, + 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)', + ) def test_get_location_list(self): response = self.client.get(self.location_list_url) @@ -56,30 +79,29 @@ def test_post_location_list_geojson(self): "geometry": { "type": "GeometryCollection", "geometries": [ - { - "type": "Point", - "coordinates": [ - 12.492324113849, - 41.890307434153 - ] - } - ] - } + {"type": "Point", "coordinates": [12.492324113849, 41.890307434153]} + ], + }, } - response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') + response = self.client.post( + self.location_list_url, + data=json.dumps(data), + content_type='application/json', + ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) data = { "name": "geojson input test2", "geometry": { "type": "Point", - "coordinates": [ - 12.492324113849, - 41.890307434153 - ] - } + "coordinates": [12.492324113849, 41.890307434153], + }, } - response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') + response = self.client.post( + self.location_list_url, + data=json.dumps(data), + content_type='application/json', + ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 2) @@ -88,18 +110,17 @@ def test_post_location_list_geojson_as_multipartformdata(self): self.assertEqual(Location.objects.count(), 0) data = { "name": "geojson input test", - "geometry": json.dumps({ - "type": "GeometryCollection", - "geometries": [ - { - "type": "Point", - "coordinates": [ - 12.492324113849, - 41.890307434153 - ] - } - ] - }) + "geometry": json.dumps( + { + "type": "GeometryCollection", + "geometries": [ + { + "type": "Point", + "coordinates": [12.492324113849, 41.890307434153], + } + ], + } + ), } response = self.client.post(self.location_list_url, data) self.assertEqual(response.status_code, 201) @@ -110,20 +131,21 @@ def test_post_HTML_browsable_api(self): data = { "name": "geojson input test2", "slug": "prova", - "geometry": json.dumps({ - "type": "GeometryCollection", - "geometries": [ - { - "type": "Point", - "coordinates": [ - 12.492324113849, - 41.890307434153 - ] - } - ] - }) + "geometry": json.dumps( + { + "type": "GeometryCollection", + "geometries": [ + { + "type": "Point", + "coordinates": [12.492324113849, 41.890307434153], + } + ], + } + ), } - response = self.client.post(self.location_list_url, data, HTTP_ACCEPT='text/html') + response = self.client.post( + self.location_list_url, data, HTTP_ACCEPT='text/html' + ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) location = Location.objects.all()[0] @@ -134,7 +156,7 @@ def test_post_location_list_WKT(self): self.assertEqual(Location.objects.count(), 0) data = { 'name': 'WKT input test', - 'geometry': 'POINT (12.492324113849 41.890307434153)' + 'geometry': 'POINT (12.492324113849 41.890307434153)', } response = self.client.post(self.location_list_url, data) self.assertEqual(response.status_code, 201) @@ -144,22 +166,29 @@ def test_post_location_list_EWKT(self): self.assertEqual(Location.objects.count(), 0) data = { 'name': 'EWKT input test', - 'geometry': 'SRID=28992;POINT(221160 600204)' + '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 l, e in zip(Location.objects.get(name='EWKT input test').geometry.coords, expected_coords): - self.assertAlmostEqual(l, e, places=5) + for lat, lon in zip( + Location.objects.get(name='EWKT input test').geometry.coords, + expected_coords, + ): + self.assertAlmostEqual(lat, lon, places=5) 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)' + 'geometry': 'POINT (12.492324113849 41.890307434153)', } - response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') + response = self.client.post( + self.location_list_url, + data=json.dumps(data), + content_type='application/json', + ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) @@ -171,18 +200,27 @@ def test_post_location_list_empty_geometry(self): response = self.client.post(self.location_list_url, data) 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') + response = self.client.post( + self.location_list_url, + data=json.dumps(data), + content_type='application/json', + ) 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') + response = self.client.post( + self.location_list_url, + data=json.dumps(data), + content_type='application/json', + ) 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' - } - response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') + 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', + ) self.assertEqual(response.status_code, 400) self.assertEqual(Location.objects.count(), 0) self.assertEqual(response.data['geometry'][0], self.value_error_message) @@ -191,9 +229,13 @@ def test_post_location_list_invalid_WKT(self): self.assertEqual(response.data['geometry'][0], self.value_error_message) data = { 'name': 'I AM MODERATELY WRONG', - 'geometry': 'POINT (12.492324113849, 41.890307434153)' + 'geometry': 'POINT (12.492324113849, 41.890307434153)', } - response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') + response = self.client.post( + self.location_list_url, + data=json.dumps(data), + content_type='application/json', + ) self.assertEqual(response.data['geometry'][0], self.geos_error_message) # repeat as multipart form data response = self.client.post(self.location_list_url, data) @@ -204,38 +246,42 @@ def test_post_location_list_invalid_geojson(self): "name": "quite wrong", "geometry": { "type": "ARRRR", - "dasdas": [ - { - "STtype": "PTUAMAoint", - "NNAare": "rgon" - } - ] - } + "dasdas": [{"STtype": "PTUAMAoint", "NNAare": "rgon"}], + }, } - response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') + response = self.client.post( + self.location_list_url, + data=json.dumps(data), + content_type='application/json', + ) 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') + data = {"name": "very wrong", "geometry": ['a', 'b', 'c']} + response = self.client.post( + self.location_list_url, + data=json.dumps(data), + content_type='application/json', + ) 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') + data = {"name": "very wrong", "geometry": False} + response = self.client.post( + self.location_list_url, + data=json.dumps(data), + content_type='application/json', + ) 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') + data = {"name": "very wrong", "geometry": {"value": {"nested": ["yo"]}}} + response = self.client.post( + self.location_list_url, + data=json.dumps(data), + content_type='application/json', + ) 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)') + location = Location.objects.create( + name='geojson test', geometry='POINT (135.0 45.0)' + ) url = reverse('api_geojson_location_details', args=[location.id]) expected = { 'id': location.id, @@ -247,13 +293,7 @@ def test_geojson_format(self): '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) if sys.version_info > (3, 0, 0): @@ -264,19 +304,25 @@ def test_geojson_format(self): 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)') + location = Location.objects.create( + name='geojson test', geometry='POINT (10.1 10.1)' + ) url = reverse('api_geojson_location_details', args=[location.id]) response = self.client.get(url) 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)') + location = Location.objects.create( + name='geojson test', geometry='POINT (10.1 10.1)' + ) url = reverse('api_geojson_location_slug_details', args=[location.slug]) response = self.client.get(url) 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)') + location = Location.objects.create( + name='falseid test', geometry='POINT (10.1 10.1)' + ) url = reverse('api_geojson_location_falseid_details', args=[location.id]) response = self.client.get(url) self.assertEqual(response.data['properties']['name'], 'falseid test') @@ -284,7 +330,9 @@ def test_geojson_false_id_attribute_slug(self): response.data['id'] def test_geojson_no_id_attribute_slug(self): - location = Location.objects.create(name='noid test', geometry='POINT (10.1 10.1)') + location = Location.objects.create( + name='noid test', geometry='POINT (10.1 10.1)' + ) url = reverse('api_geojson_location_noid_details', args=[location.id]) response = self.client.get(url) self.assertEqual(response.data['properties']['name'], 'noid test') @@ -292,7 +340,9 @@ def test_geojson_no_id_attribute_slug(self): response.data['id'] def test_geojson_filefield_attribute(self): - located_file = LocatedFile.objects.create(name='geojson filefield test', geometry='POINT (10.1 10.1)') + located_file = LocatedFile.objects.create( + name='geojson filefield test', geometry='POINT (10.1 10.1)' + ) url = reverse('api_geojson_located_file_details', args=[located_file.id]) response = self.client.get(url) self.assertEqual(response.data['properties']['file'], None) @@ -301,83 +351,79 @@ def test_post_geojson_location_list(self): self.assertEqual(Location.objects.count(), 0) data = { "type": "Feature", - "properties": { - "name": "point?", - "details": "ignore this" - }, - "geometry": { - "type": "Point", - "coordinates": [ - 10.1, - 10.1 - ] - } + "properties": {"name": "point?", "details": "ignore this"}, + "geometry": {"type": "Point", "coordinates": [10.1, 10.1]}, } - response = self.client.post(self.geojson_location_list_url, data=json.dumps(data), content_type='application/json') + response = self.client.post( + self.geojson_location_list_url, + data=json.dumps(data), + 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]) + url = reverse( + '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(json.dumps(response.data['geometry']['coordinates']), "[10.1, 10.1]") + self.assertEqual( + json.dumps(response.data['geometry']['coordinates']), "[10.1, 10.1]" + ) self.assertNotEqual(response.data['properties']['details'], "ignore this") def test_post_geojson_location_list_HTML(self): self.assertEqual(Location.objects.count(), 0) data = { "type": "Feature", - "properties": { - "name": "point?", - "details": "ignore this" - }, - "geometry": { - "type": "Point", - "coordinates": [ - 10.1, - 10.1 - ] - } + "properties": {"name": "point?", "details": "ignore this"}, + "geometry": {"type": "Point", "coordinates": [10.1, 10.1]}, } - response = self.client.post(self.geojson_location_list_url, data=json.dumps(data), content_type='application/json', HTTP_ACCEPT='text/html') + response = self.client.post( + self.geojson_location_list_url, + data=json.dumps(data), + content_type='application/json', + HTTP_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]) + url = reverse( + '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(json.dumps(response.data['geometry']['coordinates']), "[10.1, 10.1]") + self.assertEqual( + json.dumps(response.data['geometry']['coordinates']), "[10.1, 10.1]" + ) self.assertNotEqual(response.data['properties']['details'], "ignore this") def test_post_invalid_geojson_location_list(self): data = { "type": "Feature", - "properties": { - "details": "ignore this" - }, - "geometry": { - "type": "Point", - "coordinates": [ - 10.1, - 10.1 - ] - } + "properties": {"details": "ignore this"}, + "geometry": {"type": "Point", "coordinates": [10.1, 10.1]}, } - response = self.client.post(self.geojson_location_list_url, data=json.dumps(data), content_type='application/json') + response = self.client.post( + self.geojson_location_list_url, + data=json.dumps(data), + 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.") data = { "type": "Feature", - "properties": { - "name": "point?", - }, - "geometry": { - "type": "Point", - "WRONG": {} - } + "properties": {"name": "point?"}, + "geometry": {"type": "Point", "WRONG": {}}, } - response = self.client.post(self.geojson_location_list_url, data=json.dumps(data), content_type='application/json') + response = self.client.post( + self.geojson_location_list_url, + data=json.dumps(data), + 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) @@ -386,19 +432,26 @@ def test_post_geojson_location_list_WKT(self): self.assertEqual(Location.objects.count(), 0) data = { "type": "Feature", - "properties": { - "name": "point?", - }, - "geometry": "POINT (10.1 10.1)" + "properties": {"name": "point?"}, + "geometry": "POINT (10.1 10.1)", } - response = self.client.post(self.geojson_location_list_url, data=json.dumps(data), content_type='application/json') + response = self.client.post( + self.geojson_location_list_url, + data=json.dumps(data), + 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]) + url = reverse( + '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(json.dumps(response.data['geometry']['coordinates']), "[10.1, 10.1]") + self.assertEqual( + json.dumps(response.data['geometry']['coordinates']), "[10.1, 10.1]" + ) def test_geofeatured_model_serializer_compatible_with_geomodel_serializer(self): self.assertEqual(Location.objects.count(), 0) @@ -407,17 +460,15 @@ def test_geofeatured_model_serializer_compatible_with_geomodel_serializer(self): "geometry": { "type": "GeometryCollection", "geometries": [ - { - "type": "Point", - "coordinates": [ - 12.492324113849, - 41.890307434153 - ] - } - ] - } + {"type": "Point", "coordinates": [12.492324113849, 41.890307434153]} + ], + }, } - response = self.client.post(self.geojson_location_list_url, data=json.dumps(data), content_type='application/json') + response = self.client.post( + self.geojson_location_list_url, + data=json.dumps(data), + content_type='application/json', + ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) @@ -426,13 +477,9 @@ def test_geofeatured_model_post_as_multipartformdata(self): self.assertEqual(Location.objects.count(), 0) data = { "name": "geojson input test", - "geometry": json.dumps({ - "type": "Point", - "coordinates": [ - 12.492324113849, - 41.890307434153 - ] - }) + "geometry": json.dumps( + {"type": "Point", "coordinates": [12.492324113849, 41.890307434153]} + ), } response = self.client.post(self.location_list_url, data) self.assertEqual(response.status_code, 201) @@ -440,10 +487,14 @@ def test_geofeatured_model_post_as_multipartformdata(self): self.assertEqual(response.data['geometry']['type'], "Point") def test_HTML_browsable_geojson_location_list(self): - response = self.client.get(self.geojson_location_list_url, HTTP_ACCEPT='text/html') + response = self.client.get( + self.geojson_location_list_url, HTTP_ACCEPT='text/html' + ) self.assertEqual(response.status_code, 200) self._create_locations() - response = self.client.get(self.geojson_location_list_url, HTTP_ACCEPT='text/html') + response = self.client.get( + self.geojson_location_list_url, HTTP_ACCEPT='text/html' + ) self.assertContains(response, 'l1') self.assertContains(response, 'l2') @@ -451,15 +502,11 @@ def test_post_geojson_location_list_HTML_web_form(self): self.assertEqual(Location.objects.count(), 0) data = { "name": "HTML test", - "geometry": json.dumps({ - "type": "Point", - "coordinates": [ - 10.1, - 10.1 - ] - }) + "geometry": json.dumps({"type": "Point", "coordinates": [10.1, 10.1]}), } - response = self.client.post(self.geojson_location_list_url, data, HTTP_ACCEPT='text/html') + response = self.client.post( + self.geojson_location_list_url, data, HTTP_ACCEPT='text/html' + ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) location = Location.objects.all()[0] @@ -468,11 +515,10 @@ def test_post_geojson_location_list_HTML_web_form(self): 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, HTTP_ACCEPT='text/html') + data = {"name": "HTML test WKT", "geometry": "POINT (10.1 10.1)"} + response = self.client.post( + self.geojson_location_list_url, data, HTTP_ACCEPT='text/html' + ) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) location = Location.objects.all()[0] @@ -481,7 +527,9 @@ def test_post_geojson_location_list_HTML_web_form_WKT(self): @skipIf(is_pre_drf_39, 'Skip this test if DRF < 3.9') def test_geojson_HTML_widget_value(self): self._create_locations() - response = self.client.get(self.geojson_location_list_url, HTTP_ACCEPT='text/html') + response = self.client.get( + self.geojson_location_list_url, HTTP_ACCEPT='text/html' + ) self.assertContains(response, '