让我们尝试使用Django Rest Framework Gis
首先
这篇文章是MIERUNE Advent Calendar 2021的第21天(虽然年已经过去了…哭)。上一次我们讨论了GeoDjango!,所以这一次我们来尝试一下django-rest-framework-gis。由于上一篇文章有点沉重,这次我会写得轻松一些。
在实际业务中,Django通常被用作API服务器,而在处理GIS数据时,经常会引入这个模块。
Django Rest Framework GIS是什么?
在实现Django的REST API时,可以使用一个第三方库叫做Django REST framework(DRF)。而为DRF添加了地理空间功能的库称为django-rest-framework-gis。
源代码在这里。
前提 tí)
本文是关于利用Django官方文档的GeoDjango教程中的源代码,并尝试添加DRF和django-rest-framework-gis来测试其功能。
目标
-
- DRFはある程度触ったことがある
- django-rest-framework-gisははじめて
版本
python_version = "3.8"
django = "==3.2.6"
psycopg2-binary = "==2.9.1"
Docker的版本
$ docker version --format '{{.Server.Version}}'
20.10.11
源代码/环境
我将以下执行过的源代码贴在下面。我在上一篇文章中介绍的存储库中,添加了包含django-rest-framework-gis的REST API功能。
https://github.com/selfsryo/GeoDjangoOfficialTutorial
我将使用在Docker上构建的Django + PostGIS环境。
以下是这样的构成。
GeoDjangoOfficialTutorial
├── .gitignore
├── docker-compose.yml
├── geodjango
│ ├── .env.sample
│ ├── Dockerfile
│ ├── Pipfile
│ ├── Pipfile.lock
│ ├── entrypoint.sh
│ ├── geodjango
│ │ ├── __init__.py
│ │ ├── asgi.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ ├── manage.py
│ └── world
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── data
│ │ ├── Readme.txt
│ │ ├── TM_WORLD_BORDERS-0.3.dbf
│ │ ├── TM_WORLD_BORDERS-0.3.prj
│ │ ├── TM_WORLD_BORDERS-0.3.shp
│ │ └── TM_WORLD_BORDERS-0.3.shx
│ ├── load.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── serializerss.py
│ ├── tests.py
│ └── views.py
└── postgres
├── .env.db.sample
├── Dockerfile
└── sql
└── init.sql
我们将使用上一篇文章中准备的Shapefile来处理数据。
让我们试一试
准备
在Pipenv环境中安装DRF、django-rest-framework-gis和django-filter。
$ cd .../GeoDjangoOfficialTutorial/geodjango
$ pipenv install djangorestframework djangorestframework-gis django-filter
然后,删除容器一次。
$ cd ..
$ docker compose down -v
在settings.py的INSTALLED_APP中添加’django.contrib.gis’和刚才创建的world。此外,由于我在使用Docker,所以我将一些值作为环境变量从geodjango/.env文件中读取。DATABASES的设置值也连接到了Docker的PostGIS。基于以上考虑,settings.py如下所示。
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.environ.get("SECRET_KEY")
DEBUG = int(os.environ.get("DEBUG", default=0))
ALLOWED_HOSTS = []
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.gis',
# 追加
'django_filters',
'rest_framework',
'rest_framework_gis',
# 作成したアプリケーション
'world',
]
序列化器
然后创建serializers.py。将其创建在应用程序的根目录下的world/serializers.py的层级。
world
├── data
│ ├── Readme.txt
│ ├── TM_WORLD_BORDERS-0.3.dbf
│ ├── TM_WORLD_BORDERS-0.3.prj
│ ├── TM_WORLD_BORDERS-0.3.shp
│ └── TM_WORLD_BORDERS-0.3.shx
│
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── models.py
├── serializerss.py
├── tests.py
└── views.py
这个内容如下。这次我们使用的是GeoFeatureModelSerializer这个类。(官方的README在这里)
from rest_framework_gis.serializers import GeoFeatureModelSerializer
from world.models import WorldBorder
class WorldBorderSerializer(GeoFeatureModelSerializer):
class Meta:
model = WorldBorder
geo_field = 'mpoly'
auto_bbox = True
fields = ('__all__')
GeoFeatureModelSerializer是ModelSerializer的子类,但具有以下特点。
geo_fieldの定義が必要
GeometrySerializerMethodFieldとして地理空間的な処理を行った上で指定することも可能
GeoJSON形式でレスポンスを返す
id_fieldにてidを含むか含まないか、および別フィールドを指定可能(デフォルトはpk)
auto_bboxにてgeo_field(およびbbox_geo_field)から計算されたbboxの値をレスポンスに含むか含まないか指定可能
GeoJSONのpropertiesはカスタム可能
这次我们尝试将auto_bbox设置为True。
查看
然后,views.py如下所示。视图使用了DRF标准的ModelViewSet。
from rest_framework import viewsets
from rest_framework_gis.filters import DistanceToPointFilter, InBBoxFilter
from rest_framework_gis.pagination import GeoJsonPagination
from world.serializers import WorldBorderSerializer
from world.models import WorldBorder
class MyPagination(GeoJsonPagination):
page_size_query_param = 'page_size'
class WorldBorderViewSet(viewsets.ModelViewSet):
queryset = WorldBorder.objects.all()
serializer_class = WorldBorderSerializer
pagination_class = MyPagination
filter_backends = (DistanceToPointFilter, InBBoxFilter)
distance_filter_field = 'mpoly'
bbox_filter_field = 'mpoly'
bbox_filter_include_overlapping = True
以下是django-rest-framework-gis的特点:
pagination_classにGeoJsonPaginationを指定
DRFのページネーション(PageNumberPaginationなど)とはレスポンスの形式が一部異なり、GeoJSON形式になる
フィルタリングのクラスを指定できる
DistanceToPointFilterの場合、指定した距離に含まれるインスタンスを返す
distance_filter_fieldで基準となるフィールドの指定が必要
InBBoxFilterの場合、指定されたbbox内に完全に含まれるインスタンスを返す
bbox_filter_fieldで基準となるフィールドの指定が必要
bbox_filter_include_overlapping = Trueでbbox内に完全に含まれなくとも重なっていればそのインスタンスを返す
链接
然后更新urls.py文件。这次我们将按照以下方式进行更新。
from django.contrib import admin
from django.urls import include, path
from rest_framework import routers
from world.views import WorldBorderViewSet
router = routers.DefaultRouter()
router.register('border', WorldBorderViewSet)
urlpatterns = [
path('admin/', admin.site.urls),
path('world/', include(router.urls)),
]
可浏览的API
因为设置已经完成,所以我们将通过DRF的BrawsableAPI确认。我们将启动Docker容器。
$ docker compose up --build -d
然后进行数据插入。执行上次文章中创建的脚本。
$ docker container exec -it geodjango python3 manage.py shell -c "from world import load; load.run()"
我会在浏览器中确认一下。我将尝试一个单独的GET请求。我将通过浏览器访问以下内容。
通过更改访问的终端节点,我们能够对获取到的地物进行过滤。
最后。
虽然内容基本,但能系统地接触django-rest-framework-gis,这对我的学习很有帮助。