RawSq的queryset字段的Django sql名称

2024-06-06 22:30:38 发布

您现在位置:Python中文网/ 问答频道 /正文

我需要生成一个相当大的GeoJSON数据集。将queryset序列化为geojson是可行的,但是速度非常慢。对我来说大约30秒。使用PostGIS ST_AsGeoJSON({geometry_field})::json,然后从该查询集创建数据结构需要几秒钟,从而产生完全相同的结果。你知道吗

因此,要在代码中使用这个方法,我有一个函数geojson_queryset(queryset, geom_field, pk_field, other_fields)(下面引用全文)。我最初写道:

 features_queryset = queryset.extra(
     select={'geojson_queryset_result': f'ST_AsGeoJSON({geometry_field})::json'})\
     .values(*all_fields)

但在读取QuerySet API doc for extra()时,该方法已被弃用,应该编写为:

features_queryset = queryset.annotate(
    geojson_queryset_result=RawSQL(f'ST_AsGeoJSON({geometry_field})::json', [])
).values(*all_fields)

这里我不关心SQL注入,因为这些字段完全不受用户输入的影响,我们只有一个geom、一个simplified_geomlocation。你知道吗

但问题是,当queryset执行连接时,名称“geom”可能变得模棱两可。Area.objects.filter(foo=1)很好,但是Area.objects.filter(zone__foo=1)如果分区也有geom字段,则会失败。你知道吗

在RawSQL中使用参数是行不通的,因为它们是用于值的,而不是用于列名,所以它们被引用了。你知道吗

所以我的问题是如何将geom转换成正确的sql列表达式,如a0.geom?你知道吗

完整代码: sql\注入\几何\正则表达式=重新编译(r“[^a-zA-Z0-9]”)

def geojson_queryset(queryset, geometry_field, pk_field='id', fields=[]):
    """
    This method is fast way to serialize to GeoJSON a queryset. The regular serializer is extremely slow and will need
    to be parsed from text to return in a Response object. This method is roughly 20x faster.
    :param queryset: queryset to return data from
    :param geometry_field: the field to be serialized to geojson
    :param pk_field: GeoJSON requires a primary key value, defaults to 'id'
    :param fields: List of fields to include in the result
    :return: a GeoJSON FeatureCollection with the requested data.
    """
    all_fields = [pk_field, *fields, 'geojson_queryset_result']
    # Try to avoid SQL injection in the geom_field name
    if sql_injection_geom_regex.match(geometry_field):
        raise ValueError("invalid geom field name")

    features_queryset = queryset.annotate(
        geojson_queryset_result=RawSQL(f'ST_AsGeoJSON({geometry_field})::json', [])
    ).values(*all_fields)

    # features_queryset = queryset.extra(
    #     select={'geojson_queryset_result': f'ST_AsGeoJSON({geometry_field})::json'})\
    #     .values(*all_fields)

    features = []
    for row in features_queryset:
        features.append({
            'type': 'Feature',
            'id': row['id'],
            'geometry': row['geojson_queryset_result'],
            'properties': {field: row[field] for field in fields}
        })

    return {
        'type': 'FeatureCollection',
        'crs': {
            'type': 'name',
            'properties': {
                'name': 'EPSG:4326'
            }
        },
        'features': features
    }

Tags: toinjsonfieldfieldsgeojsonresultall