跳转到主要内容

Django Rest Framework的地理扩展

项目描述

django-rest-framework-gis

Build Status Coverage Status Dependency monitoring PyPI version PyPI downloads Black

Django Rest Framework 的地理信息扩展 - 邮件列表

从 PyPI 安装最新稳定版

pip install djangorestframework-gis

安装开发版

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

配置

settings.INSTALLED_APPS 中添加 rest_framework_gis,在 rest_framework 之后

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

与 DRF、Django 和 Python 的兼容性

DRF-gis 版本

DRF 版本

Django 版本

Python 版本

1.1.x

3.123.15

3.2, 4.2 至 5.1

3.83.12

1.0.x

3.103.13

2.2 至 4.0

3.63.9

0.18.x

3.103.13

2.2 至 4.0

3.63.9

0.17.x

3.103.12

2.2 至 3.1

3.63.8

0.16.x

3.10

2.2 至 3.1

3.63.8

0.15.x

3.10

1.11, 2.2 至 3.0

3.53.8

0.14.x

3.33.9

1.112.1

3.43.7

0.13.x

3.33.8

1.112.0

2.73.6

0.12.x

3.13.7

1.112.0

2.73.6

0.11.x

3.13.6

1.71.11

2.73.6

0.10.x

3.13.3

1.71.9

2.73.5

0.9.6

3.13.2

1.51.8

2.63.5

0.9.5

3.13.2

1.51.8

2.63.4

0.9.4

3.13.2

1.51.8

2.63.4

0.9.3

3.1

1.51.8

2.63.4

0.9.2

3.1

1.51.8

2.63.4

0.9.1

3.1

1.51.8

2.63.4

0.9

3.1

1.51.8

2.6, 2.7, 3.3, 3.4

0.9

3.1

1.51.8

2.6, 2.7, 3.3, 3.4

0.9

3.1

1.51.8

2.6, 2.7, 3.3, 3.4

0.8.2

3.0.43.1.1

1.51.8

2.6, 2.7, 3.3, 3.4

0.8.1

3.0.43.1.1

1.51.8

2.6, 2.7, 3.3, 3.4

0.8

3.0.4

1.51.7

2.6, 2.7, 3.3, 3.4

0.7

2.4.3

1.51.7

2.6, 2.7, 3.3, 3.4

0.6

2.4.3

1.51.7

2.6, 2.7, 3.3, 3.4

0.5

2.3.142.4.2

1.51.7

2.6, 2.7, 3.3, 3.4

0.4

2.3.142.4.2

1.51.7

2.6, 2.7, 3.3, 3.4

0.3

2.3.142.4.2

1.5, 1.6

2.6, 2.7

0.2

2.2.22.3.13

1.5, 1.6

2.6, 2.7

字段

GeometryField

提供 GeometryField,它是 Django Rest Framework (以下简称 DRF) 的子类 WritableField。该字段处理 GeoDjango 几何字段,为 GeoJSON 输入/输出提供自定义的 to_nativefrom_native 方法。

此字段接受三个可选参数

  • precision:通过 Python 内置的 round() 函数(文档)传递坐标,将值四舍五入到提供的精度级别。例如,一个经纬度为 [51.0486, -114.0708] 的点通过 GeometryField(precision=2) 传递,将返回一个经纬度为 [51.05, -114.07] 的点。

  • remove_duplicates:从线和多边形几何中删除重复的坐标。当与 precision 参数一起使用时,这特别有用,因为坐标的重复概率会随着坐标精度的降低而增加。

  • auto_bbox:如果为 True,GeoJSON 对象将包含一个 边界框,即包围几何形状的最小可能矩形。

注意:虽然 precisionremove_duplicates 设计用于减小 API 响应的字节大小,但它们也会增加渲染响应所需的处理时间。对于小的 GeoJSON 响应,这可能是微不足道的,但对于大型响应可能会成为问题。

自 0.9.3 以来新增:无需在序列化器中显式定义此字段,它将在 rest_framework_gis.apps.AppConfig.ready() 初始化期间自动映射。

GeometrySerializerMethodField

提供了一种名为 GeometrySerializerMethodField 的字段,它是 DRF SerializerMethodField 的子类,用于处理通过序列化方法计算并用作 geo_field 的值。请参见下面的示例。

序列化器

GeoModelSerializer (已废弃)

已废弃,将在 1.0 版本中删除:从 0.9.3 版本开始,使用此序列化器不再需要,如果将 rest_framework_gis 添加到 settings.INSTALLED_APPS,则序列化将直接与 DRF 一起工作。请参考 问题 #156

提供了一种名为 GeoModelSerializer 的序列化器,它是 DRF ModelSerializer 的子类。此序列化器更新 field_mapping 字典,包括 GeoDjango 几何字段的映射到上面的 GeometryField

例如,以下模型

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()

默认情况下,DRF ModelSerializer ver < 0.9.3 将输出

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

相比之下,GeoModelSerializer 将输出

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

注意:对于 ver>=0.9.3:如果;

  • rest_framework_gis 设置在 settings.INSTALLED_APPS

  • 序列化器中的字段明确设置为 GeometryField,则 DRF 模型序列化器将给出与上面相同的输出。

GeoFeatureModelSerializer

GeoFeatureModelSerializerrest_framework.ModelSerializer 的子类,它将以与 GeoJSON 兼容的格式输出数据。使用上面的示例,GeoFeatureModelSerializer 将输出

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

如果您正在序列化对象列表,GeoFeatureModelSerializer 将创建一个 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"
        }
    }
}
指定几何字段:“geo_field”

GeoFeatureModelSerializer 需要您定义一个 geo_field 以将其序列化为“几何”。例如

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')

如果您的模型没有几何字段,可以将 geo_field 设置为 None,并将生成一个空几何。

将 GeometrySerializerMethodField 用作“geo_field”

geo_field 也可以是 GeometrySerializerMethodField 的实例。在这种情况下,您可以在序列化过程中计算其值。例如

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'

geo_field 的序列化器也可以返回 None 值,这将转换为 geojson 几何字段的 null 值。

指定 ID:“id_field”

模型的 主键(通常是“id”属性)自动用作每个 GeoJSON Feature 对象id 字段。

默认行为遵循 GeoJSON RFC,但可以通过将 id_field 设置为 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')

id_field 也可以设置为使用模型中的某个其他唯一字段,例如: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')
边界框:“auto_bbox”和“bbox_geo_field”

GeoJSON 规范允许一个特征包含一个 特征的边界框GeoFeatureModelSerializer 允许两种不同的方式来填充此属性。第一种是使用 geo_field 来计算特征的边界框。这仅允许 REST 客户端进行读取访问,可以使用 auto_bbox 实现。示例

from rest_framework_gis.serializers import GeoFeatureModelSerializer

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

第二种方法使用bbox_geo_field来指定模型的一个额外的GeometryField,该字段将用于计算边界框。这允许边界框与要素几何形状的精确范围不同。此外,这还使得REST客户端可以读写访问。客户端发送的边界框将被保存为多边形。示例

from rest_framework_gis.serializers import GeoFeatureModelSerializer

class LocationSerializer(GeoFeatureModelSerializer):

    class Meta:
        model = BoxedLocation
        geo_field = 'geometry'
        bbox_geo_field = 'bbox_geometry'
自定义GeoJSON属性源

在GeoJSON中,每个要素都可以有一个包含要素属性的properties成员。默认情况下,此字段填充了来自您的Django模型的属性,不包括id、几何和边界框字段。您可以覆盖此行为并实现一个自定义的properties成员源。

以下示例展示了如何使用PostgreSQL HStore字段作为properties成员的源

# 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

当序列化器渲染GeoJSON时,它会为数据库中的每个对象调用get_properties方法。此函数应返回一个包含要素属性的字典。在HStore字段的情况下,此函数很容易实现。

反之亦然也是必要的:将GeoJSON格式的结构映射到模型的属性。此任务由unformat_geojson完成。它应返回一个字典,其中包含您的模型属性作为键,以及从GeoJSON要素数据中检索到的对应值。

分页

我们提供了一个GeoJsonPagination类。

GeoJsonPagination

基于rest_framework.pagination.PageNumberPagination

代码示例

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

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

示例结果响应(仅截取一个元素,而不是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"
            }
        }
    ]
}

过滤器

注意:此功能已测试到django-filter 1.0。

我们提供了一个用于与django_filter一起使用的GeometryFilter字段以及GeoFilterSet。您只需在查询字符串中提供GEOSGeometry支持的文本类型之一。默认情况下,这包括WKT、HEXEWKB、WKB(在缓冲区中)和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

然后我们可以在URL中使用GeoJSON进行过滤,并将执行一个__contains几何查找,例如:/region/?contains_geom={ "type": "Point", "coordinates": [ -123.26436996459961, 44.564178042345375 ] }

GeoFilterSet

GeoFilterSet提供了一个与django_filter兼容的FilterSet,该FilterSet将自动为GeometryFields创建GeometryFilters

InBBoxFilter

提供了一个InBBoxFilter,它是DRF BaseFilterBackend的子类。将查询集过滤为仅包含特定边界框内的实例。

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

然后我们可以在URL中使用边界框格式(最小经度,最小纬度,最大经度,最大纬度)进行过滤,并可以搜索边界框内的实例,例如:/location/?in_bbox=-90,29,-89,35

默认情况下,InBBoxFilter仅返回完全位于指定边界框内的实例。要包含与边界框重叠的实例,请在您的视图中包含bbox_filter_include_overlapping = True

注意:如果您使用其他过滤器,您还需要在视图中包含您的其他过滤器后端。例如

filter_backends = (InBBoxFilter, DjangoFilterBackend,)

TMSTileFilter

提供了一个TMSTileFilter,它是InBBoxFilter的子类。它将查询集过滤为仅包含由TMS瓦片地址定义的边界框内的实例。

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

然后我们可以在URL中进行过滤,使用TMS瓦片地址的缩放/x/y格式,例如:/location/?tile=8/100/200,这相当于对边界框(-39.37500,-71.07406,-37.96875,-70.61261)进行过滤。

有关配置选项的更多信息,请参阅InBBoxFilter。

请注意,瓦片地址从上左角开始,而不是某些实现中使用的下左角原点。

DistanceToPointFilter

提供了一个DistanceToPointFilter,它是DRF BaseFilterBackend的子类。它将查询集过滤为仅包含在给定点一定距离内的实例。

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,)

然后我们可以在URL中进行过滤,使用以(lon, lat)格式表示的距离和点。距离可以用米或度表示。

例如:/location/?dist=4000&point=-122.4862,37.7694&format=json,这相当于在点(-122.4862, 37.7694)周围4000米内进行过滤。

默认情况下,DistanceToPointFilter会将URL中的‘distance’直接传递给数据库进行搜索。效果取决于使用的数据库srid。如果地理数据以米为索引(srid 3875,即900913),则可以直接传递米距离,无需转换。对于如srid 4326的经纬度数据库,它以度为索引,‘distance’将被解释为度。将标志‘distance_filter_convert_meters’设置为‘True’以将输入距离从米转换为度。这种转换是近似的,且在纬度>60度时的误差大于25%。

DistanceToPointOrderingFilter

提供了一个DistanceToPointOrderingFilter适用于Django >= 3.0,它是DistanceToPointFilter的子类。根据到给定点的距离对查询集进行排序,从最近的点到最远的点。

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,)

然后我们可以通过在URL中传递以(lon, lat)格式表示的点来对结果进行排序。

例如:/location/?point=-122.4862,37.7694&format=json将根据点(-122.4862, 37.7694)的距离对结果进行排序。

我们还可以通过传递order=desc来反转结果的顺序:/location/?point=-122.4862,37.7694&order=desc&format=json

Schema生成

注意:仅当DRF >= 3.12时才支持Schema生成。

最简单的方法是,将DEFAULT_SCHEMA_CLASS更改为rest_framework_gis.schema.GeoFeatureAutoSchema

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

如果您不想更改默认的schema生成类

  • 您可以将此类作为参数传递给get_schema_view函数[Ref]

  • 您可以将此类作为参数传递给generateschema命令[Ref]

运行测试

必需的设置

您需要GeoDjango支持的其中一个空间数据库服务器,并为测试创建一个数据库。

以下可用于PostgreSQL

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

您可能需要根据您的数据库配置调整DB设置。您可以将文件local_settings.example.py复制到local_settings.py,并更改其中的DATABASES和/或INSTALLED_APPS指令。

这应该允许您运行测试。

作为参考,以下步骤将为贡献项目设置开发环境

  • 创建一个名为“django_restframework_gis”的空间数据库

  • 创建local_settings.py,例如:cp local_settings.example.py local_settings.py

  • 根据您的数据库设置,调整DATABASES配置指令

  • 取消注释INSTALLED_APPS

  • 运行python manage.py syncdb

  • 运行python manage.py collectstatic

  • 运行python manage.py runserver

使用tox

运行测试的推荐方法是使用tox,可以使用pip install tox进行安装。

您可以使用tox -l列出可用的环境,然后例如使用以下命令以Python 3.6和Django 1.11运行所有测试

tox -e py36-django111

默认情况下,使用Django的测试运行器,但tox的envlist有一个变体可以使用pytest(使用-pytest后缀)。

您可以将可选参数传递给测试运行器,如下所示

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

手动运行测试

如果您想手动/不使用tox运行测试,请参考tox.ini文件以获取参考/帮助。

要在docker中运行测试,请使用

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

运行QA检查

安装测试需求

pip install -r requirements-test.txt

使用以下链接根据我们的编码风格约定重新格式化代码https://openwisp.io/docs/developer/contributing.html#coding-style-conventions

openwisp-qa-format

使用以下命令运行QA检查

./run-qa-checks

在docker测试中,QA检查会自动执行。

贡献

  1. 加入Django REST Framework GIS邮件列表并宣布您的意图

  2. 遵循Python代码PEP8风格指南

  3. 叉取此仓库

  4. 编写代码

  5. 为您的代码编写测试

  6. 确保所有测试通过

  7. 确保测试覆盖率不低于90%

  8. 记录您的更改

  9. 发送拉取请求

项目详情


下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分布

djangorestframework_gis-1.1.tar.gz (49.6 kB 查看散列)

上传时间

构建分布

djangorestframework_gis-1.1-py2.py3-none-any.whl (21.8 kB 查看散列)

上传时间 Python 2 Python 3

由以下支持