Django Rest Framework возможно ли сериализовать геойсон из запроса, используя geometry_field через foreignkey? - PullRequest
0 голосов
/ 14 декабря 2018

У меня есть две модели: одна для сохранения сетки точек, а вторая для сохранения геопространственных данных

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 очень легко получить файл геоджон для модели с геометриейполе в качестве моей модели Grid, и следующий вид предоставляет хороший геоджон с геом в качестве поля геометрии и «возвышением» в свойствах.

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 для отображения значения 'measure', но мне не удается указать сериализатору, что я хочу использовать геометрию таблицы 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)

Но геометрия в моем полученном геойсоне равна нулю.Таким образом, обращение к SQL предоставит геометрию, но не добавит поле 'measure' в свойства:

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 голосов
/ 19 декабря 2018

Благодаря новым ссылкам, появившимся в соответствующем столбце, я понял, что не так с моими предположениями, и, наконец, я нашел 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")

Затем представление, которое использует его для генерации геоджона:

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'

Эта модель может быть использована для генерации геоджона следующим образом:

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)

Вот и все.

...