Coder Social home page Coder Social logo

openwisp / django-rest-framework-gis Goto Github PK

View Code? Open in Web Editor NEW
1.1K 42.0 200.0 583 KB

Geographic add-ons for Django REST Framework. Maintained by the OpenWISP Project.

Home Page: http://openwisp.org

License: MIT License

Python 99.51% Dockerfile 0.44% Shell 0.05%
django django-rest-framework gis python geojson hacktoberfest

django-rest-framework-gis's Introduction

django-rest-framework-gis

Build Status Coverage Status Dependency monitoring PyPI version PyPI downloads Black

Geographic add-ons for Django Rest Framework - Mailing List.

Install last stable version from pypi

pip install djangorestframework-gis

Install development version

pip install https://github.com/openwisp/django-rest-framework-gis/tarball/master

Setup

Add rest_framework_gis in settings.INSTALLED_APPS, after rest_framework:

INSTALLED_APPS = [
    # ...
    'rest_framework',
    'rest_framework_gis',
    # ...
]

Compatibility with DRF, Django and Python

DRF-gis version DRF version Django version Python version
1.0.x 3.10 up to 3.13 2.2 to 4.0 3.6 to 3.9
0.18.x 3.10 up to 3.13 2.2 to 4.0 3.6 to 3.9
0.17.x 3.10 up to 3.12 2.2 to 3.1 3.6 to 3.8
0.16.x 3.10 2.2 to 3.1 3.6 to 3.8
0.15.x 3.10 1.11, 2.2 to 3.0 3.5 to 3.8
0.14.x 3.3 to 3.9 1.11 to 2.1 3.4 to 3.7
0.13.x 3.3 to 3.8 1.11 to 2.0 2.7 to 3.6
0.12.x 3.1 to 3.7 1.11 to 2.0 2.7 to 3.6
0.11.x 3.1 to 3.6 1.7 to 1.11 2.7 to 3.6
0.10.x 3.1 to 3.3 1.7 to 1.9 2.7 to 3.5
0.9.6 3.1 to 3.2 1.5 to 1.8 2.6 to 3.5
0.9.5 3.1 to 3.2 1.5 to 1.8 2.6 to 3.4
0.9.4 3.1 to 3.2 1.5 to 1.8 2.6 to 3.4
0.9.3 3.1 1.5 to 1.8 2.6 to 3.4
0.9.2 3.1 1.5 to 1.8 2.6 to 3.4
0.9.1 3.1 1.5 to 1.8 2.6 to 3.4
0.9 3.1 1.5 to 1.8 2.6, 2.7, 3.3, 3.4
0.9 3.1 1.5 to 1.8 2.6, 2.7, 3.3, 3.4
0.9 3.1 1.5 to 1.8 2.6, 2.7, 3.3, 3.4
0.8.2 3.0.4 to 3.1.1 1.5 to 1.8 2.6, 2.7, 3.3, 3.4
0.8.1 3.0.4 to 3.1.1 1.5 to 1.8 2.6, 2.7, 3.3, 3.4
0.8 3.0.4 1.5 to 1.7 2.6, 2.7, 3.3, 3.4
0.7 2.4.3 1.5 to 1.7 2.6, 2.7, 3.3, 3.4
0.6 2.4.3 1.5 to 1.7 2.6, 2.7, 3.3, 3.4
0.5 from 2.3.14 to 2.4.2 1.5 to 1.7 2.6, 2.7, 3.3, 3.4
0.4 from 2.3.14 to 2.4.2 1.5 to 1.7 2.6, 2.7, 3.3, 3.4
0.3 from 2.3.14 to 2.4.2 1.5, 1.6 2.6, 2.7
0.2 from 2.2.2 to 2.3.13 1.5, 1.6 2.6, 2.7

Fields

GeometryField

Provides a GeometryField, which is a subclass of Django Rest Framework (from now on DRF) WritableField. This field handles GeoDjango geometry fields, providing custom to_native and from_native methods for GeoJSON input/output.

This field takes three optional arguments:

  • precision: Passes coordinates through Python's builtin round() function (docs), rounding values to the provided level of precision. E.g. A Point with lat/lng of [51.0486, -114.0708] passed through a GeometryField(precision=2) would return a Point with a lat/lng of [51.05, -114.07].
  • remove_duplicates: Remove sequential duplicate coordinates from line and polygon geometries. This is particularly useful when used with the precision argument, as the likelihood of duplicate coordinates increase as precision of coordinates are reduced.
  • auto_bbox: If True, the GeoJSON object will include a bounding box, which is the smallest possible rectangle enclosing the geometry.

Note: While precision and remove_duplicates are designed to reduce the byte size of the API response, they will also increase the processing time required to render the response. This will likely be negligible for small GeoJSON responses but may become an issue for large responses.

New in 0.9.3: there is no need to define this field explicitly in your serializer, it's mapped automatically during initialization in rest_framework_gis.apps.AppConfig.ready().

GeometrySerializerMethodField

Provides a GeometrySerializerMethodField, which is a subclass of DRF SerializerMethodField and handles values which are computed with a serializer method and are used as a geo_field. See example below.

Serializers

GeoModelSerializer (DEPRECATED)

Deprecated, will be removed in 1.0: Using this serializer is not needed anymore since 0.9.3, if you add rest_framework_gis in settings.INSTALLED_APPS the serialization will work out of the box with DRF. Refer Issue #156.

Provides a GeoModelSerializer, which is a subclass of DRF ModelSerializer. This serializer updates the field_mapping dictionary to include field mapping of GeoDjango geometry fields to the above GeometryField.

For example, the following model:

class Location(models.Model):
    """
    A model which holds information about a particular location
    """
    address = models.CharField(max_length=255)
    city = models.CharField(max_length=100)
    state = models.CharField(max_length=100)
    point = models.PointField()

By default, the DRF ModelSerializer ver < 0.9.3 will output:

{
    "id": 1,
    "address": "742 Evergreen Terrace",
    "city":  "Springfield",
    "state": "Oregon",
    "point": "POINT(-123.0208 44.0464)"
}

In contrast, the GeoModelSerializer will output:

{
    "id": 1,
    "address": "742 Evergreen Terrace",
    "city":  "Springfield",
    "state": "Oregon",
    "point": {
        "type": "Point",
        "coordinates": [-123.0208, 44.0464],
    }
}

Note: For ver>=0.9.3: The DRF model serializer will give the same output as above, if;

  • rest_framework_gis is set in settings.INSTALLED_APPS or
  • the field in the serializer is set explicitly as GeometryField.

GeoFeatureModelSerializer

GeoFeatureModelSerializer is a subclass of rest_framework.ModelSerializer which will output data in a format that is GeoJSON compatible. Using the above example, the GeoFeatureModelSerializer will output:

 {
    "id": 1,
    "type": "Feature",
    "geometry": {
        "type": "Point",
        "coordinates": [-123.0208, 44.0464],
    },
    "properties": {
        "address": "742 Evergreen Terrace",
        "city":  "Springfield",
        "state": "Oregon"
    }
}

If you are serializing an object list, GeoFeatureModelSerializer will create a FeatureCollection:

{
    "type": "FeatureCollection",
    "features": [
    {
        "id": 1
        "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": [-123.0208, 44.0464],
        },
        "properties": {
            "address": "742 Evergreen Terrace",
            "city":  "Springfield",
            "state": "Oregon",
        }
    }
    {
        "id": 2,
        "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": [-123.0208, 44.0489],
        },
        "properties": {
            "address": "744 Evergreen Terrace",
            "city":  "Springfield",
            "state": "Oregon"
        }
    }
}
Specifying the geometry field: "geo_field"

GeoFeatureModelSerializer requires you to define a geo_field to be serialized as the "geometry". For example:

from rest_framework_gis.serializers import GeoFeatureModelSerializer

class LocationSerializer(GeoFeatureModelSerializer):
    """ A class to serialize locations as GeoJSON compatible data """

    class Meta:
        model = Location
        geo_field = "point"

        # you can also explicitly declare which fields you want to include
        # as with a ModelSerializer.
        fields = ('id', 'address', 'city', 'state')

If your model is geometry-less, you can set geo_field to None and a null geometry will be produced.

Using GeometrySerializerMethodField as "geo_field"

geo_field may also be an instance of GeometrySerializerMethodField. In this case you can compute its value during serialization. For example:

from django.contrib.gis.geos import Point
from rest_framework_gis.serializers import GeoFeatureModelSerializer, GeometrySerializerMethodField

class LocationSerializer(GeoFeatureModelSerializer):
    """ A class to serialize locations as GeoJSON compatible data """

    # a field which contains a geometry value and can be used as geo_field
    other_point = GeometrySerializerMethodField()

    def get_other_point(self, obj):
        return Point(obj.point.lat / 2, obj.point.lon / 2)

    class Meta:
        model = Location
        geo_field = 'other_point'

Serializer for geo_field may also return None value, which will translate to null value for geojson geometry field.

Specifying the ID: "id_field"

The primary key of the model (usually the "id" attribute) is automatically used as the id field of each GeoJSON Feature Object.

The default behaviour follows the GeoJSON RFC, but it can be disabled by setting id_field to False:

from rest_framework_gis.serializers import GeoFeatureModelSerializer

class LocationSerializer(GeoFeatureModelSerializer):

    class Meta:
        model = Location
        geo_field = "point"
        id_field = False
        fields = ('id', 'address', 'city', 'state')

The id_field can also be set to use some other unique field in your model, eg: slug:

from rest_framework_gis.serializers import GeoFeatureModelSerializer

class LocationSerializer(GeoFeatureModelSerializer):

    class Meta:
        model = Location
        geo_field = 'point'
        id_field = 'slug'
        fields = ('slug', 'address', 'city', 'state')
Bounding Box: "auto_bbox" and "bbox_geo_field"

The GeoJSON specification allows a feature to contain a boundingbox of a feature. GeoFeatureModelSerializer allows two different ways to fill this property. The first is using the geo_field to calculate the bounding box of a feature. This only allows read access for a REST client and can be achieved using auto_bbox. Example:

from rest_framework_gis.serializers import GeoFeatureModelSerializer

class LocationSerializer(GeoFeatureModelSerializer):
    class Meta:
        model = Location
        geo_field = 'geometry'
        auto_bbox = True

The second approach uses the bbox_geo_field to specify an additional GeometryField of the model which will be used to calculate the bounding box. This allows boundingboxes differ from the exact extent of a features geometry. Additionally this enables read and write access for the REST client. Bounding boxes send from the client will be saved as Polygons. Example:

from rest_framework_gis.serializers import GeoFeatureModelSerializer

class LocationSerializer(GeoFeatureModelSerializer):

    class Meta:
        model = BoxedLocation
        geo_field = 'geometry'
        bbox_geo_field = 'bbox_geometry'
Custom GeoJSON properties source

In GeoJSON each feature can have a properties member containing the attributes of the feature. By default this field is filled with the attributes from your Django model, excluding the id, geometry and bounding box fields. It's possible to override this behaviour and implement a custom source for the properties member.

The following example shows how to use a PostgreSQL HStore field as a source for the properties member:

# models.py
class Link(models.Model):
    """
    Metadata is stored in a PostgreSQL HStore field, which allows us to
    store arbitrary key-value pairs with a link record.
    """
    metadata = HStoreField(blank=True, null=True, default=dict)
    geo = models.LineStringField()
    objects = models.GeoManager()

# serializers.py
class NetworkGeoSerializer(GeoFeatureModelSerializer):
    class Meta:
        model = models.Link
        geo_field = 'geo'
        auto_bbox = True

    def get_properties(self, instance, fields):
        # This is a PostgreSQL HStore field, which django maps to a dict
        return instance.metadata

    def unformat_geojson(self, feature):
        attrs = {
            self.Meta.geo_field: feature["geometry"],
            "metadata": feature["properties"]
        }

        if self.Meta.bbox_geo_field and "bbox" in feature:
            attrs[self.Meta.bbox_geo_field] = Polygon.from_bbox(feature["bbox"])

        return attrs

When the serializer renders GeoJSON, it calls the method get_properties for each object in the database. This function should return a dictionary containing the attributes for the feature. In the case of a HStore field, this function is easily implemented.

The reverse is also required: mapping a GeoJSON formatted structure to attributes of your model. This task is done by unformat_geojson. It should return a dictionary with your model attributes as keys, and the corresponding values retrieved from the GeoJSON feature data.

Pagination

We provide a GeoJsonPagination class.

GeoJsonPagination

Based on rest_framework.pagination.PageNumberPagination.

Code example:

from rest_framework_gis.pagination import GeoJsonPagination
# --- other omitted imports --- #

class GeojsonLocationList(generics.ListCreateAPIView):
    # -- other omitted view attributes --- #
    pagination_class = GeoJsonPagination

Example result response (cut to one element only instead of 10):

{
    "type": "FeatureCollection",
    "count": 25,
    "next": "http://localhost:8000/geojson/?page=2",
    "previous": null,
    "features": [
        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [
                    42.0,
                    50.0
                ]
            },
            "properties": {
                "name": "test"
            }
        }
    ]
}

Filters

note: this feature has been tested up to django-filter 1.0.

We provide a GeometryFilter field as well as a GeoFilterSet for usage with django_filter. You simply provide, in the query string, one of the textual types supported by GEOSGeometry. By default, this includes WKT, HEXEWKB, WKB (in a buffer), and GeoJSON.

GeometryFilter

from rest_framework_gis.filterset import GeoFilterSet
from rest_framework_gis.filters import GeometryFilter
from django_filters import filters

class RegionFilter(GeoFilterSet):
    slug = filters.CharFilter(name='slug', lookup_expr='istartswith')
    contains_geom = GeometryFilter(name='geom', lookup_expr='contains')

    class Meta:
        model = Region

We can then filter in the URL, using GeoJSON, and we will perform a __contains geometry lookup, e.g. /region/?contains_geom={ "type": "Point", "coordinates": [ -123.26436996459961, 44.564178042345375 ] }.

GeoFilterSet

The GeoFilterSet provides a django_filter compatible FilterSet that will automatically create GeometryFilters for GeometryFields.

InBBoxFilter

Provides a InBBoxFilter, which is a subclass of DRF BaseFilterBackend. Filters a queryset to only those instances within a certain bounding box.

views.py:

from rest_framework_gis.filters import InBBoxFilter

class LocationList(ListAPIView):

    queryset = models.Location.objects.all()
    serializer_class = serializers.LocationSerializer
    bbox_filter_field = 'point'
    filter_backends = (InBBoxFilter,)
    bbox_filter_include_overlapping = True # Optional

We can then filter in the URL, using Bounding Box format (min Lon, min Lat, max Lon, max Lat), and we can search for instances within the bounding box, e.g.: /location/?in_bbox=-90,29,-89,35.

By default, InBBoxFilter will only return those instances entirely within the stated bounding box. To include those instances which overlap the bounding box, include bbox_filter_include_overlapping = True in your view.

Note that if you are using other filters, you'll want to include your other filter backend in your view. For example:

filter_backends = (InBBoxFilter, DjangoFilterBackend,)

TMSTileFilter

Provides a TMSTileFilter, which is a subclass of InBBoxFilter. Filters a queryset to only those instances within a bounding box defined by a TMS tile address.

views.py:

from rest_framework_gis.filters import TMSTileFilter

class LocationList(ListAPIView):

    queryset = models.Location.objects.all()
    serializer_class = serializers.LocationSerializer
    bbox_filter_field = 'point'
    filter_backends = (TMSTileFilter,)
    bbox_filter_include_overlapping = True # Optional

We can then filter in the URL, using TMS tile addresses in the zoom/x/y format, eg:. /location/?tile=8/100/200 which is equivalent to filtering on the bbox (-39.37500,-71.07406,-37.96875,-70.61261).

For more information on configuration options see InBBoxFilter.

Note that the tile address start in the upper left, not the lower left origin used by some implementations.

DistanceToPointFilter

Provides a DistanceToPointFilter, which is a subclass of DRF BaseFilterBackend. Filters a queryset to only those instances within a certain distance of a given point.

views.py:

from rest_framework_gis.filters import DistanceToPointFilter

class LocationList(ListAPIView):

    queryset = models.Location.objects.all()
    serializer_class = serializers.LocationSerializer
    distance_filter_field = 'geometry'
    filter_backends = (DistanceToPointFilter,)

We can then filter in the URL, using a distance and a point in (lon, lat) format. The distance can be given in meters or in degrees.

eg:. /location/?dist=4000&point=-122.4862,37.7694&format=json which is equivalent to filtering within 4000 meters of the point (-122.4862, 37.7694).

By default, DistanceToPointFilter will pass the 'distance' in the URL directly to the database for the search. The effect depends on the srid of the database in use. If geo data is indexed in meters (srid 3875, aka 900913), a distance in meters can be passed in directly without conversion. For lat-lon databases such as srid 4326, which is indexed in degrees, the 'distance' will be interpreted as degrees. Set the flag, 'distance_filter_convert_meters' to 'True' in order to convert an input distance in meters to degrees. This conversion is approximate, and the errors at latitudes > 60 degrees are > 25%.

DistanceToPointOrderingFilter

Provides a DistanceToPointOrderingFilter, available on Django >= 3.0, which is a subclass of DistanceToPointFilter. Orders a queryset by distance to a given point, from the nearest to the most distant point.

views.py:

from rest_framework_gis.filters import DistanceToPointOrderingFilter

class LocationList(ListAPIView):

    queryset = models.Location.objects.all()
    serializer_class = serializers.LocationSerializer
    distance_ordering_filter_field = 'geometry'
    filter_backends = (DistanceToPointOrderingFilter,)

We can then order the results by passing a point in (lon, lat) format in the URL.

eg:. /location/?point=-122.4862,37.7694&format=json will order the results by the distance to the point (-122.4862, 37.7694).

We can also reverse the order of the results by passing order=desc: /location/?point=-122.4862,37.7694&order=desc&format=json

Schema Generation

Note: Schema generation support is available only for DRF >= 3.12.

Simplest Approach would be, change DEFAULT_SCHEMA_CLASS to rest_framework_gis.schema.GeoFeatureAutoSchema:

REST_FRAMEWORK = {
    ...
    'DEFAULT_SCHEMA_CLASS': 'rest_framework_gis.schema.GeoFeatureAutoSchema',
    ...
}

If you do not want to change default schema generator class:

  • You can pass this class as an argument to get_schema_view function [Ref].
  • You can pass this class as an argument to the generateschema command [Ref].

Running the tests

Required setup

You need one of the Spatial Database servers supported by GeoDjango, and create a database for the tests.

The following can be used with PostgreSQL:

createdb django_restframework_gis
psql -U postgres -d django_restframework_gis -c "CREATE EXTENSION postgis"

You might need to tweak the DB settings according to your DB configuration. You can copy the file local_settings.example.py to local_settings.py and change the DATABASES and/or INSTALLED_APPS directives there.

This should allow you to run the tests already.

For reference, the following steps will setup a development environment for contributing to the project:

  • create a spatial database named "django_restframework_gis"
  • create local_settings.py, eg: cp local_settings.example.py local_settings.py
  • tweak the DATABASES configuration directive according to your DB settings
  • uncomment INSTALLED_APPS
  • run python manage.py syncdb
  • run python manage.py collectstatic
  • run python manage.py runserver

Using tox

The recommended way to run the tests is by using tox, which can be installed using pip install tox.

You can use tox -l to list the available environments, and then e.g. use the following to run all tests with Python 3.6 and Django 1.11:

tox -e py36-django111

By default Django's test runner is used, but there is a variation of tox's envlist to use pytest (using the -pytest suffix).

You can pass optional arguments to the test runner like this:

tox -e py36-django111-pytest -- -k test_foo

Running tests manually

Please refer to the tox.ini file for reference/help in case you want to run tests manually / without tox.

To run tests in docker use

docker-compose build
docker-compose run --rm test

Running QA-checks

Install the test requirements:

pip install -r requirements-test.txt

Reformat the code according to our coding style conventions with:

openwisp-qa-format

Run the QA checks by using

./run-qa-checks

In docker testing, QA checks are executed automatically.

Contributing

  1. Join the Django REST Framework GIS Mailing List and announce your intentions
  2. Follow the PEP8 Style Guide for Python Code
  3. Fork this repo
  4. Write code
  5. Write tests for your code
  6. Ensure all tests pass
  7. Ensure test coverage is not under 90%
  8. Document your changes
  9. Send pull request

django-rest-framework-gis's People

Contributors

alexdebrie avatar auvipy avatar ayzhu avatar blueyed avatar codingjoe avatar devkapilbansal avatar dhaval-mehta avatar dmeehan avatar erictheise avatar fingel avatar fladi avatar franciscouzo avatar gbip avatar gregseb avatar imomaliev avatar jarus avatar jessecrocker avatar mikeedwards avatar nemesifier avatar nmandery avatar nparley avatar pauloxnet avatar philipn avatar pkolios avatar rpkilby avatar shanto avatar strang1ato avatar suz avatar ticosax avatar willemarcel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-rest-framework-gis's Issues

Installation fails

Running pip install -U djangorestframework-gis, I get:

Downloading/unpacking djangorestframework-gis from https://pypi.python.org/packages/3.4/d/djangorestframework-gis/djangorestframework-gis-0.8.0.tar.gz#md5=11a3a53b010685e2a74a6ab92a23f1ad
  Running setup.py egg_info for package djangorestframework-gis
    Traceback (most recent call last):
      File "<string>", line 14, in <module>
      File "/vagrant/envs/updatedrf/build/djangorestframework-gis/setup.py", line 43, in <module>
        install_requires=get_install_requires(),
      File "/vagrant/envs/updatedrf/build/djangorestframework-gis/setup.py", line 13, in get_install_requires
        for line in open('requirements.txt').readlines():
    IOError: [Errno 2] No such file or directory: 'requirements.txt'
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):

  File "<string>", line 14, in <module>

  File "/vagrant/envs/updatedrf/build/djangorestframework-gis/setup.py", line 43, in <module>

    install_requires=get_install_requires(),

  File "/vagrant/envs/updatedrf/build/djangorestframework-gis/setup.py", line 13, in get_install_requires

    for line in open('requirements.txt').readlines():

IOError: [Errno 2] No such file or directory: 'requirements.txt'

----------------------------------------
Command python setup.py egg_info failed with error code 1 in /vagrant/envs/updatedrf/build/djangorestframework-gis
Storing complete log in /home/vagrant/.pip/pip.log

Renders

I'm wondering if anyone has similar issues and how you might handle them. I currently have an api that provides a feature collection of polygons that represent cemeteries. For example:

http://127.0.0.1:8000/api/v1/cemeteries

I have someone using this api that wants to get the data and do some things in javascript but the feature collection makes that painful for some people that are using the api that may not be as familiar with geodata.

I'm considering creating a new non-spatial api endpoint for the same data but it feels like I should avoid doing this as I would need to create two endpoints for every geojson dataset I'm sending out.

I'd like to have one endpoint that is by default non-spatial but returns a feature collection if specified in the request. Perhaps something like:

http://127.0.0.1:8000/api/v1/cemeteries?format=geojson

Which leads me to think that I need to create a custom renderer? Any thoughts on the proper way to do something like this and continue to use drf-gis would be very helpful.

GEOSException: GeoJSON output only supported when GDAL is installed.

  File "/var/www/django/django/django/apps/api/serializers.py", line 248, in get_geolocations
    return [ GeoLocationSerializer(obj.geolocation).data ]
  File "/var/www/django/venv/local/lib/python2.7/site-packages/rest_framework/serializers.py", line 466, in data
    ret = super(Serializer, self).data
  File "/var/www/django/venv/local/lib/python2.7/site-packages/rest_framework/serializers.py", line 213, in data
    self._data = self.to_representation(self.instance)
  File "/var/www/django/venv/local/lib/python2.7/site-packages/rest_framework_gis/serializers.py", line 104, in to_representation
    value = field.to_representation(value)
  File "/var/www/django/venv/local/lib/python2.7/site-packages/rest_framework_gis/fields.py", line 31, in to_representation
    return json.loads(GEOSGeometry(value).geojson)
  File "/var/www/django/venv/local/lib/python2.7/site-packages/django/contrib/gis/geos/geometry.py", line 422, in json
    raise GEOSException('GeoJSON output only supported when GDAL is installed.')
GEOSException: GeoJSON output only supported when GDAL is installed.

Any ideas?

Output properly formatted GeoJson

We need to properly format the outputted GeoJson so that it can be consumed by client-side mapping software (Leaflet, OpenLayers, etc)

So, for example, lets look at the following Location model:

class Location(models.Model):
    """
    A model which holds information about a particular location
   """
   address = models.Charfield(max_length=255)
   city = models.CharField(max_length=100)
   state = models.CharField(max_length=100)
   point = models.PointField()

By default, DRF will format a json response as (for example):

{
     "id": 1, 
     "address": "742 Evergreen Terrace", 
      "city":  "Springfield", 
      "state": "Oregon",
      "point": "POINT(-123.0208 44.0464)" 
}

With a new GeoDjango enabled GeometryField as an add on, it will output "point" as a GeoJson:

{
     "id": 1, 
     "address": "742 Evergreen Terrace", 
      "city":  "Springfield", 
      "state": "Oregon",
      "point": {
            "type": "Point",
            "coordinates": [-123.0208, 44.0464],
       },
}

But, we now need to format the entire json response as GeoJson, for example:

{ 
    "type": "Feature",
     "geometry": {
           "type": "Point",
           "coordinates": [-123.0208, 44.0464],
     },
     "properties": {
         "id": 1, 
         "address": "742 Evergreen Terrace", 
         "city":  "Springfield", 
         "state": "Oregon",
     }
}

Need to investigate the best place in DRF to override for this behavior.

For reference: http://geojson.org/geojson-spec.html

DecimalField receiving django.contrib.gis.db.models.query.GeoQuerySet

Libs:

django==1.8.2
djangorestframework==3.1.3
djangorestframework-gis==0.9.1

settings:

'COERCE_DECIMAL_TO_STRING': True

Model:

class A(models.Model):
    aaa = models.PolygonField(null=True, blank=True, srid=4326)

Serializer:

class Aserial(GeoModelSerializer):
    class Meta:
        model = A
        fields = ('aaa',)

And serializer.data returns:

  File "/Users/paulocheque/Programming/leaf-toptal/Leaf-Management-Software/env1.8/lib/python3.4/site-packages/rest_framework/serializers.py", line 622, in data
    ret = super(ListSerializer, self).data
  File "/Users/paulocheque/Programming/leaf-toptal/Leaf-Management-Software/env1.8/lib/python3.4/site-packages/rest_framework/serializers.py", line 217, in data
    self._data = self.to_representation(self.instance)
  File "/Users/paulocheque/Programming/leaf-toptal/Leaf-Management-Software/env1.8/lib/python3.4/site-packages/rest_framework/serializers.py", line 572, in to_representation
    self.child.to_representation(item) for item in iterable
  File "/Users/paulocheque/Programming/leaf-toptal/Leaf-Management-Software/env1.8/lib/python3.4/site-packages/rest_framework/serializers.py", line 572, in <listcomp>
    self.child.to_representation(item) for item in iterable
  File "/Users/paulocheque/Programming/leaf-toptal/Leaf-Management-Software/env1.8/lib/python3.4/site-packages/rest_framework/serializers.py", line 439, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "/Users/paulocheque/Programming/leaf-toptal/Leaf-Management-Software/env1.8/lib/python3.4/site-packages/rest_framework/serializers.py", line 439, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "/Users/paulocheque/Programming/leaf-toptal/Leaf-Management-Software/env1.8/lib/python3.4/site-packages/rest_framework/fields.py", line 863, in to_representation
    value = decimal.Decimal(six.text_type(value).strip())
decimal.InvalidOperation: [<class 'decimal.ConversionSyntax'>]

I printed the value and its type and the GeometryField(allow_null=True, required=False) is receiving a django.contrib.gis.db.models.query.GeoQuerySet

Not used metadata write_only_fields on create field

I have geo field in write_only_fields metadata, but this filed present in view response

Test Model:

class Profile(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=50)
    position = models.PointField(null=True, blank=True, geography=True)

Test Serializer:

class ProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = Profile
        fields = ('id', 'name', 'position', )
        write_only_fields = ('position', )

Server response:
GET /profile

[
    {
        "id": 4,
        "name": "Test",
        "position": {
            "type": "Point",
            "coordinates": [
                37.44638510000001,
                55.72377399999998
            ]
        }
    }
]

Expected response without "position" field

Allow tuple for geo_field property

I've been asked to create a Django app where users can manage their existing library of KML documents along with other GIS formats. Unfortunately, it seems that about half of the documents have 2D geometry while the rest are 3D. This causes issues when saving the geometry model because GeoDjango's GeometryField has a default dim value of 2, which results in errors when uploading a 3D file.

My conundrum is similar to the question here at GIS Stackexchange, just inverted. However, I would not want to not force a 2D transform, as the height values are a big part of the data.

A short term solution is to include two geometry fields within the model, one 2D and the other 3D. Depending on the dimension, the geometry will be saved to the appropriate field in the model. In order to use this within the serializer, a GeometrySerializerMethodField needs to be used, much like

class SegmentSerializer(gis_serializer.GeoFeatureModelSerializer):
    trail = TrailSerializer()
    geometry = gis_serializer.GeometrySerializerMethodField()

    def get_geometry(self, obj):
        print(obj)
        if obj.two_d_geom is None:
            return obj.three_d_geom
        else:
            return obj.two_d_geom

    class Meta:
        model = TrailSegment
        geo_field = 'geometry'

If geo_field were to accept a tuple as a value, then the above could be functionally identical to

class SegmentSerializer(gis_serializer.GeoFeatureModelSerializer):
    trail = TrailSerializer()

    class Meta:
        model = TrailSegment
        geo_field = ('two_d_geom', 'three_d_geom', )

This would also benefit devs who find themselves doing far too many joins that impact performance and use denormalization as a solution.

Tests fail for Django 1.9

======================================================================
ERROR: django_restframework_gis_tests.test_filters (unittest.loader.ModuleImportFailure)
----------------------------------------------------------------------
ImportError: Failed to import test module: django_restframework_gis_tests.test_filters
Traceback (most recent call last):
  File "/usr/lib/python2.7/unittest/loader.py", line 254, in _find_tests
    module = self._get_module_from_name(name)
  File "/usr/lib/python2.7/unittest/loader.py", line 232, in _get_module_from_name
    __import__(name)
  File "tests/django_restframework_gis_tests/test_filters.py", line 6, in <module>
    from django.utils.unittest import skipIf
ImportError: No module named unittest


----------------------------------------------------------------------

tests fail

I: pybuild base:170: python2.7 ./runtests.py
............................F..........s.s.s.........
======================================================================
FAIL: test_post_location_list_EWKT (django_restframework_gis_tests.tests.TestRestFrameworkGis)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/django_restframework_gis_tests/tests.py", line 137, in test_post_location_list_EWKT
    expected_coords
AssertionError: Tuples differ: (6.381496, 53.384067) != (6.381495826183805, 53.3840669...

First differing element 0:
6.381496
6.38149582618

- (6.381496, 53.384067)
+ (6.381495826183805, 53.384066927384985)

----------------------------------------------------------------------
Ran 53 tests in 0.355s

FAILED (failures=1, skipped=3)
Creating test database for alias 'default'...
Destroying test database for alias 'default'...

Version 0.9.6 does not support DRF 3.3.X.

According to the compatibility list on pypi, version 0.9.6 supports DRF 3.1.X to 3.3.X. However, this does not match with requirements.txt:

b6549cb

What's the reason that this commit was reverted?

Geometry field rendered with error

I have a method in a model that returns a LineString that contains the coordinates of two points that are ForeignKey of my model. See it in https://github.com/ibamacsr/routes_registry_api/blob/c3f05d1fe75d03fe4c73fb980f6452f92374c25a/routes_registry_api/routes/models.py#L75

I wrote a serializer using GeoFeatureModelSerializer, passing this method as a field https://github.com/ibamacsr/routes_registry_api/blob/c3f05d1fe75d03fe4c73fb980f6452f92374c25a/routes_registry_api/routes/serializers.py#L75

However the geometry field is rendered wrongly in the API:

{
    "type": "Feature",
    "geometry": [
        [
            -51.06870710141181,
            0.050141347646707
        ],
        [
            -67.89624167465826,
            -9.86850298262667
        ]
    ],
    "properties": {

using restframework 2.4.3 and restframework-gis 0.7

How to enable required=False for GeometryField?

Hi,

I want to be able to update the location of an user without having the model itself managed by GeoManager, so I've created a ono to one relation with a geo model.
For the sake of simplicity I want to update the location of the user in the same api call where I'm updating user details. I've added GeometryField on the User ModelSerializer with params required=False and allow_null=True, but still on POST/PATCH/PUT I have the "This field is required." for the geo field. How I can literally disable this?

Regards,
Dacian

pip install doesn't install latest

Running pip install djangorestframework-gis installs 0.7.

Running pip install djangorestframework-gis==0.8 returns

Downloading/unpacking djangorestframework-gis==0.8
  Could not find a version that satisfies the requirement djangorestframework-gis==0.8 (from versions: )
No distributions matching the version for djangorestframework-gis==0.8
Storing complete log in /home/vagrant/.pip/pip.log

Downloading the egg from pypi and installing it from the directory works, however.

ID attribute missing

In the GeoJSON spec is written:

"If a feature has a commonly used identifier, that identifier should be included as a member of the feature object with the name "id"."
http://www.geojson.org/geojson-spec.html#feature-objects

Which means having something like:

{
    "type": "Feature",
    "id": 23,
    "properties": {
        "prop0": "value0",
        "prop1": 0.0
    }
    "geometry": {
        "type": "LineString",
        "coordinates": [
            [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]
        ]
    }
}

Note the "id" attribute.

I propose to automatically list the primary key of a model as the id attribute with the possibility to specify a different field, similarly as "geo_field". We could call name the meta property "id_field".

I propose to do this before resolving issue #12

Order By Distance?

How to order by distance?

My API view is:

class PeopleViewSet(
    RPACheckMixin,
    viewsets.mixins.ListModelMixin,
    viewsets.mixins.RetrieveModelMixin,
    viewsets.GenericViewSet):

    queryset = AppUser.objects.all()
    serializer_class = PublicProfileSerializer
    permission_classes = (IsAuthenticated, IsUserVerified, IsUserSignedPrivacyAgreement, )
    filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter, DistanceToPointFilter, )
    distance_filter_field = 'location'
    distance_filter_convert_meters = True # by default we use srid=4326 (so in degrees) therefore we should convert meters to degrees

    filter_fields = ('gender', )
    ordering_fields = ('date_joined', 'last_login', 'first_name', 'last_name', 'gender')
    ordering = ('date_joined',)
    search_fields = ('first_name', 'last_name', 'email', 'username')

    def get_queryset(self):
        # remove yourself and all people who didn't update their location during last 5 minutes
        return AppUser.objects.\
            filter(location_updated__gte=timezone.now()-timedelta(minutes=5)).\
            exclude(id=self.request.user.id)

Exception hides dependency isssue

Hi,

On my first attempt to setup django-rest-framework-gis, I stumble upon an exception thrown here:

https://github.com/djangonauts/django-rest-framework-gis/blob/master/rest_framework_gis/fields.py#L44

So I put together a small example:

>>> import json
>>> t = json.dumps({ "type":  "Point", "coordinates":[45.563237,-73.582419]})
>>> t
'{"type": "Point", "coordinates": [45.563237, -73.582419]}'
>>> GEOSGeometry(t)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/srv/app/.virtualenvs/env/local/lib/python2.7/site-packages/django/contrib/gis/geos/geometry.py", line 64, in __init__
    raise ValueError('Initializing geometry from JSON input requires GDAL.')

which shows that the underlying issue was related to a missing library (python-gdal).

I think it's Ok to return a ValidationError but I think it might make sense to also add the message from the captured exception so users know what went wrong.

What do you think?

support rest framework web form

I noticed that if I use the GeoFeatureModelSerializer the nice web form provided by django rest framework stops working.

I tried this and it kinda works:
nemesifier@93b50c7

But i'm not very satisfied with the final result, because if I leave some required fields blank I do not get the correct validation error message.

Any suggestion? @tomchristie & @dmeehan

can't save value of point field through PATCH request

PATCH /request
{
    "geometry": {
    "type": "Point",
    "coordinates": [125.6, 10.1]
    },
    "type": "location"    
}

tried all different combinations, no luck. Ideal would be to do something like this

PATCH /request
{
    "location": [125.6, 10.1]
}

my serializer

class UserSerializer(serializers.ModelSerializer):
    rpa_signed = DateTimeFieldWihTZ(read_only=True)
    verified = DateTimeFieldWihTZ(read_only=True)


    class Meta:
        model = User
        geo_field = 'location'
        fields = tuple(User.REQUIRED_FIELDS) + (
            User._meta.pk.name,
            User.USERNAME_FIELD,
        ) + User.CUSTOM_FIELDS
        read_only_fields = (
            User.USERNAME_FIELD,
        ) + User.READ_ONLY_FIELDS

my model

class AppUser(AbstractUser):
    """
    Custom user model for the application
    blank=True means Empty values allowed from the backend, if blank=False, then the field will be required in backend
    """

    _SEX = (
        ('M', 'Male'),
        ('F', 'Female'),
    )

    _PACKAGES = (
        ('A', 'Annual'),
        ('M', 'Monthly'),
    )

    # define a validator for the phone
    _pregex   = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: "
                                                                 "'+999999999'. Up to 15 digits allowed.")
    phone     = models.CharField(validators=[_pregex, validate_phone_number], max_length=16, blank=False, null=False) # validations should be a list
    gender    = models.CharField(max_length=1,  blank=False, null=True, choices=_SEX)
    birthday  = models.DateField(blank=False,   null=True)
    package   = models.CharField(max_length=1,  blank=True, null=True, choices=_PACKAGES) # stripe payment plans (annuals or monthly)
    phonecode = models.CharField(max_length=40, verbose_name="Phone Code", blank=True, null=True, db_index=True) # verification code over SMS?
    emailcode = models.CharField(max_length=40, verbose_name="Email Code", blank=True, null=True, db_index=True)  # verification code over email?
    avatar    = models.ImageField(upload_to=upload_user_media_to, editable=True, null=True, blank=True)
    verified  = models.DateTimeField(null=True, blank=True)
    signature = models.CharField(max_length=255, verbose_name="Privacy Agreement Signature", blank=True, null=True, editable=False)

    # right of privacy agreement PDF file
    rpa = models.FileField(upload_to=upload_user_rights_of_privacy_agreement_to, editable=False, null=True, blank=True)
    rpa_signed   = models.DateTimeField(null=True, blank=True, editable=False)
    rpa_revision = models.IntegerField(null=True, blank=True, editable=False)
    rpa_content  = models.ForeignKey(Content, verbose_name="RPA Source File",  null=True, blank=True, editable=False, db_index=True)

    # GEOFencing
    location = gis_models.PointField(default='POINT(0.0 0.0)', blank=True)
    location_updated = models.DateTimeField(null=True, blank=True, editable=False)

    REQUIRED_FIELDS = User.REQUIRED_FIELDS + ['first_name', 'last_name',  'phone', 'gender', 'birthday']
    CUSTOM_FIELDS = ('phone', 'gender', 'birthday', 'package', 'avatar', 'verified', 'rpa', 'rpa_signed', 'rpa_revision', 'location', 'location_updated')
    # for serializer hack
    READ_ONLY_FIELDS = ('avatar', 'package', 'rpa', 'rpa_signed', 'rpa_revision', 'location_updated')

I tried to add location = GeometryField() inside serializer, but that didn't help. So how do I use patch and persist the value in DB?

Drop support for old django versions

Django Rest Framework 3.3 dropped support for django versions older than 1.7, which also means dropping support for python 2.6.

django-rest-framework-gis will likely do the same in version 0.10.

Pickle Errors with GeoJsonDict

v0.9.3 (specifically a3ddd3d) introduced a regression when trying to cache responses since GeoJsonDictcannot be pickled. More precisely, a GeoJsonDict cannot be restored once pickled. The following example will raise TypeError: __init__() missing 1 required positional argument: 'geometry' on the final pickle.loads:

import pickle
from django.contrib.gis.geos import GEOSGeometry
from rest_framework_gis.fields import GeoJsonDict

geometry = GEOSGeometry('POINT (30 10)')
result = GeoJsonDict(geometry)
dump = pickle.dumps(result)
restored = pickle.loads(dump)

I was using Django's UpdateCacheMiddleware/FetchFromCacheMiddleware when I ran into this issue but the same would be true using the low-level cache.

DRF 2.4.3 breaks drf-GIS

As the title says.
Specifically, when using getJSON, leaflet.js throws an error of invalid json.
Everything works fine when going back to DRF 2.4.2.

Custom source for feature metadata

My current use case:

  • Model with geometry and variable metadata
  • Metadata is stored in PostgreSQL HStore field
  • I want to render a GeoJSON layer using the HStore field as source for the feature "properties"

Looking at the source code for GeoFeatureModelSerializer (https://github.com/djangonauts/django-rest-framework-gis/blob/master/rest_framework_gis/serializers.py#L96), I can see that there is no possibility yet to override the source for feature properties.

I have the following idea as a possible solution:

  • Add a method get_feature_properties to GeoFeatureModelSerializer, which accepts the current instance as parameter, and returns a dict containing the properties for the current feature.
  • By default returns the instance fields with current checks for id/bbox/geo field in place.
  • Subclasses can override this function (and thus return metadata from for example the HStore field).

Any suggestions? I will open a pull request shortly.

fields.py Geometryfield unicode broken for Python 3.0 ... 3.2

Hi, we got an suprise while some of us work on Python 3.4 and we deployed to a virtuenv with Python 3.2 that breaks with:

Exception Type: SyntaxError at /api/v1/accesspoint/192.168.1.239
Exception Value: invalid syntax (fields.py, line 46)

At line 46 we got explicit unicode casting u' '
Corresponding to pep 414 this syntax is reintroduced at Python 3.3 but was removed since 3.0. Thus it will currently break if you use just python 3.2

This was tested at

Django==1.7.7
djangorestframework==3.1.1
djangorestframework-gis==0.8.1

Hope this will help to fix it :-)

Fields deserialized to GeoJSON dicts ??

GeometryField is currently returning geojson from its to_internal_value. Is this the desired behavior? Wouldn't make more sense to return a GEOSGeometry or a value usable in geo lookups?

This seems to be crashing one of our serializers with a unique_together composed of a PointField. DRF is attempts to validate uniqueness with an .existst() and the JSON is not valid value.

Error when model is rendered in drf html

When im logged and drf tries to render the html form to make POST, drf-gis gets broken in serializers.py in from_native, line 115 with argument of type 'NoneType' is not iterable.

SkipField No exception message supplied

Here is my model:

class Location(models.Model):
    venue = models.ForeignKey(Venue, related_name="location")
    address = models.TextField(blank=True)
    city = models.CharField(max_length=100, blank=True)
    postal_code = models.CharField(max_length=100, blank=True)
    country = models.CharField(max_length=100, blank=True)
    point = models.PointField(null=True)

Here is my serializer:

class LocationSerializer(GeoFeatureModelSerializer):
    class Meta:
        model = Location
        geo_field = "point"
        fields = ("id",
                  "address",
                  "city",
                  "postal_code",
                  "country")

Django Version: 1.8.4
Exception Type: SkipField
Exception Location: /usr/local/lib/python2.7/dist-packages/rest_framework/fields.py in get_attribute, line 408

Any help?

Thanks

cannot import name ModelSerializerOptions

If you install the package using pip install djangorestframework-gis you get an error cannot import name ModelSerializerOptions.

This seems to be related to a change made in in this commit: b93c9a6 in the serializers.py file.

Downloading the project and using python setup.py install resolves the issue.

TEMPLATE_STRING_IF_INVALID issue

originally sent by @pglotov on #21

To replicate it with the DRFgis testsuite:

  1. add
    TEMPLATE_STRING_IF_INVALID = 'INVALID_TEMPLATE: %s END_INVALID_TEMPLATE'
    to tests/settings.py

  2. put a break point in tests.py:
    def test_HTML_browsable_geojson_location_list(self):
    response = self.client.get(self.geojson_location_list_url, HTTP_ACCEPT='text/html')
    import pdb; pdb.set_trace()

  3. run ./runtests.py, when it hits break point do
    str(response)
    and look for INVALID_TEMPLATE

Set geo_field to a geo_field from a linked model

Given the following models, is there a way to serialize MainModel as Geojson by specifying the geo_field attribute from the linked GeoModel ?

class GeoModel(models.Model):
    point = models.PointField()
    objects = models.GeoManager()

class MainModel(models.Model):
    geo_model = models.ForeignKey(GeoModel)

Willing to help contributing if some insight about implementation could be shared. Thanks

additional setting for djangorestframework-gis in settings.py

I implemented the djangorestframework-gis in one of my project. But while accessing the admin panel of django. It gave me the following error templatedoesnotexist gis/admin/openlayers.html . So I first came to see the documentation of djangorestframework-gis but it didn't solved my problem .Then I googled to resolve the error and I found one useful answer at Stack Overflow that solved my problem.
Here is the link to it: http://stackoverflow.com/questions/25220540/django-templatedoesnotexist-gis-admin-openlayers-html.

So could we please update the readme at the repository, to make it more better.

EWKT SRID is not respected in GeometryField

Hello,

I was observing strange behaviour when posting EWKT strings as geometry values. It looks like the SRID in the EWKT is not respected on save.

For example, my model has a geometry field with srid=4326 en I post this EWKT 'SRID=28992;POINT(221160 600204)', the field gets posted without errors but when I request the data, I see that the coordinates are 221160, 600204 instead of the expected WGS84 latlon coordinates.

I checked the concerning code:
https://github.com/djangonauts/django-rest-framework-gis/blob/master/rest_framework_gis/fields.py#L30-L41

and found that this line returns a property (ie geojson) of a GEOSGeometry instead of an instance of a GEOSGeometry resulting in a loss of the SRID, resulting in a wrong (ie. no) conversion:
https://github.com/djangonauts/django-rest-framework-gis/blob/master/rest_framework_gis/fields.py#L39

My guess would be that this PR fixes the problem:
#75

0.9.3 GeoFeatureModelSerializer not backward compatible unless changing INSTALLED_APPS

Hello,
Using the GeoFeatureModelSerializer, I not able to get a proper geojson as the example.

I use :
django==1.8.3
djangorestframework==3.1.3
djangorestframework-gis==0.9.3

My model:

class Address(gis_models.Model):
   """Address model"""

   address = models.CharField(_('title'), max_length=64, blank=True, null=True)
   point = gis_models.PointField(blank=True, null=True)
   objects = gis_models.GeoManager()

My Serializer

class AddressSerializer(serializers.GeoFeatureModelSerializer):
   """
   Serializing all the addresses
   """

   class Meta:
      model = Address
      fields = ('id', 'address')
      geo_field = 'point'

My view

class AddressDetail(RetrieveUpdateDestroyAPIView):
   """
   Retrieve, update or delete an address instance.
   """

   queryset = Address.objects.all()
   serializer_class = AddressSerializer

Here is the wrong json:

{
  "id": 9,
  "type": "Feature",
  "geometry": "SRID=4326;POINT (14.4923241138489995 41.8903074341530015)",
  "properties": {
    "address": "Via Laietana, 54, 08003 Barcelona, Barcelona, Spain"
  }
}

The solution was to add rest_framework_gis in INSTALLED_APPS, and it solved the problem.

SkipField Exception when serializing list

Given the following model

class TrailSegment(models.Model):
    '''A trail is made of 1 to n paths, whose geometry is recorded here'''

    trail = models.ForeignKey(Trail, on_delete=models.CASCADE)
    properties = models.CharField(max_length=1024)
    state = models.CharField(max_length=200)

    two_d_geom = models.GeometryField(dim=3, null=Trail, default=None)
    three_d_geom = models.GeometryField(dim=3, null=Trail, default=None)
    objects = models.GeoManager()

and its associated serializer

from rest_framework_gis import serializers as gis_serializer

class SegmentSerializer(gis_serializer.GeoFeatureModelSerializer):
    trail = TrailSerializer()

    class Meta:
        model = TrailSegment
        geo_field = 'three_d_geom'
        #fields = ('properties', 'state', 'trail')
        exclude = ('two_d_geom', )

Doing the following in a Django shell results in a SkipField exception being thrown when serializing a list:

In [1]: from app.trails.serializers import SegmentSerializer, TrailSerializer
In [2]: from app.trails.models import Trail, TrailSegment
In [3]: trail = Trail.objects.first()
In [4]: segments = TrailSegment.objects.all().filter(trail = trail)
In [5]: serialized_segments = SegmentSerializer(segments).data

The exception from the shell is

    ---------------------------------------------------------------------------
SkipField                                 Traceback (most recent call last)
<ipython-input-5-7981db9b6b94> in <module>()
----> 1 serialized_segments = SegmentSerializer(segments).data

/Users/dd/.virtualenvs/dev.trailtracker27.com/lib/python2.7/site-packages/rest_framework/serializers.pyc in data(self)
    501     @property
    502     def data(self):
--> 503         ret = super(Serializer, self).data
    504         return ReturnDict(ret, serializer=self)
    505

/Users/dd/.virtualenvs/dev.trailtracker27.com/lib/python2.7/site-packages/rest_framework/serializers.pyc in data(self)
    237         if not hasattr(self, '_data'):
    238             if self.instance is not None and not getattr(self, '_errors', None):
--> 239                 self._data = self.to_representation(self.instance)
    240             elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):
    241                 self._data = self.to_representation(self.validated_data)

/Users/dd/.virtualenvs/dev.trailtracker27.com/lib/python2.7/site-packages/rest_framework_gis/serializers.pyc in to_representation(self, instance)
    103         if self.Meta.id_field:
    104             field = self.fields[self.Meta.id_field]
--> 105             value = field.get_attribute(instance)
    106             feature["id"] = field.to_representation(value)
    107             fields.remove(field)

/Users/dd/.virtualenvs/dev.trailtracker27.com/lib/python2.7/site-packages/rest_framework/fields.pyc in get_attribute(self, instance)
    406         except (KeyError, AttributeError) as exc:
    407             if not self.required and self.default is empty:
--> 408                 raise SkipField()
    409             msg = (
    410                 'Got {exc_type} when attempting to get a value for field '

SkipField:

However, if I do

serialized = SegmentSerializer(segments[0]).data

it returns the expected output of

In [17]: serialized
Out[17]: {'geometry': GeoJsonDict([('type', u'LineString'), ('coordinates', ((-70.28106362981032, 43.72027255750996, 0.0), (-70.28102415320637, 43.72024866593903, 0.0), (-70.28100459902832, 43.72022743482305, 0.0), (-70.28099649739845, 43.72018180012078, 0.0), (-70.28099016885537, 43.72015219882969, 0.0), (-70.28099027591736, 43.72014710918082, 0.0), (-70.28098126248, 43.72011660536592, 0.0), (-70.28098975144866, 43.72010650081797, 0.0), (-70.28101690111572, 43.72007413103765, 0.0), (-70.28102129749787, 43.72006604693912, 0.0), (-70.28104659807954, 43.72000778037587, 0.0), (-70.28104677648884, 43.72000179259669, 0.0), (-70.28103314480208, 43.71995316596087, 0.0), (-70.28099852661745, 43.71991178240064, 0.0)))]), 'type': 'Feature', 'id': 22, 'properties': OrderedDict([('trail', OrderedDict([('trail_name', u'Universal Presumpscot'), ('active', True), ('date_uploaded', u'2016-01-29T18:25:28.364904Z'), ('owner', OrderedDict([('id', 1), ('email', u'[email protected]'), ('username', u'admin'), ('first_name', u''), ('last_name', u''), ('created_at', u'2016-01-15T13:47:02.735044Z'), ('updated_at', u'2016-01-15T13:47:02.735214Z')]))])), ('properties', u'{"styleHash": "-2c8a94e3", "styleUrl": "#msn_ylw-pushpin110", "name": "Hope Connector"}'), ('state', u'ACTIVE')])}

Rest Framework 3.3.2
Rest Framework GIS 0.10.1.FINAL
Django 1.8
Python 2.7.11 (because of several other libraries that have not been ported to 3.4+)

0.9.6 complains about drf 3.3.2 being too new.

Edit: Ah I see that there were closed tickets about this. Wasn't expecting them to be closed so I apologize for not looking there first. Should the documentation be updated to reflect this non-support?

It seems that I can't use drf-gis 0.9.6 with drf 3.3 despite what the documentation says.

user@computer:~/repos/ansible_vm_prod$ sudo pip install djangorestframework-gis==0.9.6
Collecting djangorestframework-gis==0.9.6
  Downloading djangorestframework_gis-0.9.6-py2.py3-none-any.whl
Collecting djangorestframework<3.3,>=3.0.4 (from djangorestframework-gis==0.9.6)
  Downloading djangorestframework-3.2.5-py2.py3-none-any.whl (542kB)
    100% |████████████████████████████████| 544kB 106kB/s 
Installing collected packages: djangorestframework, djangorestframework-gis
  Found existing installation: djangorestframework 3.3.2
    Uninstalling djangorestframework-3.3.2:
      Successfully uninstalled djangorestframework-3.3.2
Successfully installed djangorestframework-3.2.5 djangorestframework-gis-0.9.6

Am I misunderstanding how pip should handle this? I don't expect it to downgrade an existing package if it's supposed to be supported.

KeyError raised when id field not in `fields`

I had a serializer like:

class MyGeoSerializer(GeoFeatureModelSerializer):
    class Meta:
        model = CurrentDataSet
        geo_field = 'geom'
        fields = ('abbrev', 'name', 'area_sq_mi', 'states',
                     'current', 'past')

In version 0.9.4, the id field in the serialized JSON was null, but that was ok.

Now after upgrading to 0.9.5, a KeyError is thrown at https://github.com/djangonauts/django-rest-framework-gis/blame/master/rest_framework_gis/serializers.py#L97 with partial traceback:

  File "/Users/anna/.virtualenvs/watttime-grid-api/lib/python2.7/site-packages/rest_framework_gis/serializers.py", line 97, in to_representation
    field = self.fields[self.Meta.id_field]
  File "/Users/anna/.virtualenvs/watttime-grid-api/lib/python2.7/site-packages/rest_framework/utils/serializer_helpers.py", line 136, in __getitem__
    return self.fields[key]
KeyError: u'id'

Adding id to the fields tuple fixes the error.

I see two possible fixes:

  • backwards-compatible behavior would catch the KeyError and set the id field to None
  • possibly more intuitive behavior would be to preemptively add id to the list of fields if it's not present, I guess during init?

Until there's a fix, it would help to add docs to clarify that it's required to include id in the list of fields.

Version 0.8 with GeoFeatureModelSerializer and null DateTimeField causes Error

I'm pretty new to this project, but am impressed. Thanks for your work on this. I've just tested out the recent v0.8 with DRF 3.0.4 on an existing project, and have issues with a DateTimeField that has a null value while using the GeoFeatureModelSerializer.

Interestingly, using the GeoModelSerializer instead does not cause an error.

When using GeoFeatureModelSerializer with null DateTimeField, I get the following:

'NoneType' object has no attribute 'isoformat'
Exception Location: /<path>/lib/python2.7/site-packages/rest_framework/fields.py in to_representation, line 869
Python Executable:  /<path>/bin/python
Python Version: 2.7.8

Relates to #30

Version 0.8 with GeoFeatureModelSerializer duplicates properties

Should the key-value pairs that appear within the properties object also appear outside of properties? Judging from the readme, I figured not. Also the geometric object is shown both within geometry and within the original field (here rect).

It also appears that the field name, here rect, is not nested in the geometry object per the docs' example of point.

Here's what I'm getting with v0.8 and DRF 3.0.4:

{
    "id": 5, 
    "type": "Feature", 
    "geometry": {
        "type": "Polygon", 
        "coordinates": [
            [
                [
                    0.0, 
                    0.0
                ], 
                [
                    0.0, 
                    1.0
                ], 
                [
                    1.0, 
                    1.0
                ], 
                [
                    0.0, 
                    0.0
                ]
            ]
        ]
    }, 
    "properties": {
        "url": "http://127.0.0.1:8000/api/poly/5/", 
        "account": 1, 
        "name": "Foo", 
        "active_start": "2015-02-06T19:39:29.155867Z"
    }, 
    "url": "http://127.0.0.1:8000/api/poly/5/", 
    "account": 1, 
    "name": "Foo", 
    "active_start": "2015-02-06T19:39:29.155867Z", 
    "rect": {
        "type": "Polygon", 
        "coordinates": [
            [
                [
                    0.0, 
                    0.0
                ], 
                [
                    0.0, 
                    1.0
                ], 
                [
                    1.0, 
                    1.0
                ], 
                [
                    0.0, 
                    0.0
                ]
            ]
        ]
    }
}

Relates to #30

Question: can I access request.user in serializer validate_field() function?

I am trying to validate a field and need to get access to authorized user to validate for duplicate rows in database. If I don't catch it in serializer it produces IntegrityError (duplicate) while calling save() method and throws an exception, returning 500 internal server error response. Can I catch it internally somehow?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.