Django Rest Framework是否可以通过外键使用geometry_field从查询序列化geojson?

时间:2018-12-14 17:14:33

标签: django-rest-framework foreign-keys geojson geodjango

我有两种模型:一种用于维护点网格,第二种用于保存地理空间数据

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记录 我的网格模型显示“度量”值,但我没有成功指出 到我要使用Grid表的几何图形的序列化程序。我想 这样优雅的代码可以工作:

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序列化程序的范围?

1 个答案:

答案 0 :(得分:0)

感谢相关列中显示的新链接,我了解了 我的假设有误,最后我找到了2个解决方案: 第一个创建专用的序列化器, 第二个使用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

此优雅解决方案的局限性在于完成的查询数量 (对于在Grid模型中选择的每个几何图形一个),因此要花费时间。

B)为了获得更高的性能,我已经实现了GridView解决方案  (请参阅我的第一个问题),但将其应用于基于数据库VIEW的模型  被https://blog.rescale.com/using-database-views-in-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)

就是这样。