django-rest-framework - Django Rest Framework是否可以通过外键使用geometry_field从查询中序列化geojson?
问题描述
我有两种模型:一种用于维护点网格,另一种用于保存地理空间数据
from django.contrib.gis.db import models as geomodels
from django.db import models
class Grid(geomodels.Model):
geom = geomodels.PointField(srid=4326, unique=True)
elevation = geomodels.FloatField(blank=True, null=True)
class SpData(models.Model):
mtime = models.DateTimeField()
grid = models.ForeignKey(Grid)
measure = models.DecimalField(
max_digits=6,
decimal_places=1,
blank=True,
null=True,
)
使用 django rest 框架,可以很容易地为具有几何字段作为我的网格模型的模型获取 geojson 文件,并且以下视图提供了一个不错的 geojson,其中 geom 作为几何字段并在属性中具有“高程”。
from django.http import HttpResponse
from django.core.serializers import serialize
from rest_framework.views import APIView
from .models import Grid
class GridView(APIView):
def get(self, request):
result = serialize(
"geojson",
Grid.objects.all(),
geometry_field="geom",
srid=4326,
fields=(
"elevation",
),
)
return HttpResponse(result)
现在我想使用同一个网格来显示链接到我的网格模型的 SpData 记录以显示“测量”值,但我没有成功向序列化程序指示我想使用网格表的几何形状。我认为这样优雅的代码可以工作:
class SpDataView(APIView):
def get(self, request):
geodata = SpData.objects.filter(mtime=today())
fields = ("measure", "grid",)
result = serialize(
"geojson",
geodata,
geometry_field="grid.geom",
srid=4326,
fields=fields,
use_natural_foreign_keys=True,
)
return HttpResponse(result)
但事实并非如此。因此,我尝试向我的 SpData 模型添加一个属性字段,但它也不起作用:
class SpData(models.Model):
mtime = models.DateTimeField()
grid = models.ForeignKey(Grid)
measure = models.DecimalField(
max_digits=6,
decimal_places=1,
blank=True,
null=True,
)
@property
def geom(self):
return self.grid.geom
class SpDataView(APIView):
def get(self, request):
geodata = SpData.objects.filter(mtime=today())
fields = ("measure", "geom",)
result = serialize(
"geojson",
geodata,
geometry_field="geom",
srid=4326,
fields=fields,
)
return HttpResponse(result)
我尝试使用原始 SQL 查询,但序列化程序仅考虑已完成原始 sql 的模型的字段。
strsql ='SELECT sp.pk, sp.measure, g.geom '
strsql += ' FROM SpData AS sp'
strsql += ' INNER JOIN Grid AS g'
strsql += ' ON sp.grid = g.pk'
strsql += ' WHERE sp.mtime = today()'
geodata = SpData.objects.raw(strsql)
fields = ("measure", "geom",)
result = serialize(
"geojson",
geodata,
geometry_field="geom",
srid=4326,
fields=fields,
)
return HttpResponse(result)
但是我生成的geojson中的几何等于null。因此,还原 SQL 将提供几何,但不会将字段“测量”添加到属性中:
strsql ='SELECT g.pk, sp.measure, g.geom '
strsql += ' FROM Grid AS g'
strsql += ' INNER JOIN SpData AS sp'
strsql += ' ON sp.grid = g.pk'
strsql += ' WHERE sp.mtime = today()'
geodata = Grid.objects.raw(strsql)
我错过了什么?这超出了当前 geodjango 序列化程序的范围吗?
解决方案
感谢相关列中出现的新链接,我明白了我的假设出了什么问题,最后我找到了两个解决方案:第一个创建专用序列化程序,第二个使用 sqlview 作为模型。
A)创建一个专用的序列化程序,重新定义 get_properties 方法以用所需的措施填充它们:
from rest_framework_gis.serializers import GeoFeatureModelSerializer
from django.db import connection
def dictfetchall(cursor):
"""
When using a django.db.connection for raw sql,
returns all rows from a cursor as a dict
"""
columns = [col[0] for col in cursor.description]
return [
dict(zip(columns, row))
for row in cursor.fetchall()
]
class SpDataSerializer(GeoFeatureModelSerializer):
"""
Geojson Serializer for SpData
"""
def get_properties(self, instance, fields):
"""
Retrieve the measures linked to each grid point
"""
strsql ='SELECT sp.measure '
strsql += ' FROM SpData AS sp'
strsql += ' WHERE sp.grid_id = %s'
strsql += ' AND sp.mtime = today()'
with connection.cursor() as cursor:
cursor.execute(strsql, [instance.id])
properties = dictfetchall(cursor)
return properties[0]
class Meta:
model = Grid
geo_field = "geom"
fields = ("id", "elevation")
然后是一个使用它来生成geojson的视图:
from rest_framework.generics import ListAPIView
class SpDataView(ListAPIView):
serializer_class = SpatialWalSerializer
def get_queryset(self):
# get the grid
queryset = Grid.objects.all()
return queryset
这个优雅的解决方案的限制是完成的查询量(网格模型中选择的每个几何图形)以及使用时间。
B)为了获得性能,我已经实现了 GridView 解决方案(请参阅我的第一个问题),但将其应用于基于https://blog.rescale.com/using-database-views-in-启发的数据库 VIEW 的模型django-orm/:
BEGIN
;
DROP VIEW IF EXISTS app_viewspdata
;
CREATE OR REPLACE VIEW app_viewspdata AS
SELECT sp.id
, g.geom
, g.elevation
, sp.mtime
, sp.measure
FROM app_spdata AS sp
INNER JOIN app_grid AS g
ON g.id = sp.grid_id
;
COMMIT
;
然后我创建了一个新模型:
class ViewSpData(geomodels.Model):
geom = geomodels.PointField(srid=4326, unique=True)
elevation = geomodels.FloatField(blank=True, null=True)
mtime = models.DateTimeField()
measure = models.DecimalField(
max_digits=6,
decimal_places=1,
blank=True,
null=True,
)
class Meta:
managed = False
db_table = 'app_viewspdata'
该模型可用于生成这样的geojson:
from django.http import HttpResponse
from django.core.serializers import serialize
from rest_framework.views import APIView
from .models import ViewSpData
class ViewSpDataView(APIView):
def get(self, request):
result = serialize(
"geojson",
ViewSpData.objects.filter(mtime=today()),
geometry_field="geom",
srid=4326,
fields=(
"measure",
"elevation",
),
)
return HttpResponse(result)
而已。
推荐阅读
- jquery - jquery (.on) 不适用于 primefaces .ui-fileupload-cancel,为什么?
- django - Django 模型表单上的 Request.user
- android - 使用 Android Management API 将设备配置为专用设备不起作用
- css - 使用带有拉伸元素的 flex wrap 作为行
- amazon-web-services - AWS Cognito Authentication Pre-Authentication 触发 Lambda 函数;不存在的电子邮件地址
- angular - Chrome 控制台 - Uncaught (in promise) TypeError: this.engines is not iterable
- java - 无法在 Android Studio 中重新创建导航抽屉活动
- javascript - 每次执行函数并且我的反应状态发生变化时过滤一个数组
- flutter - 如何获得可迭代
- 使用包contacts_services在Flutter中电话值?
- javascript - 如何为 Vue 组件实现策略模式