You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django REST Framework中实现关联与聚合查询

Django按城市聚合噪音数据API实现方案

1. 优化模型关联(可选但推荐)

给NoiseData的外键添加related_name,让反向关联查询更直观:

class NoiseData(models.Model):
    location = models.CharField(max_length=100)
    # 添加related_name,方便通过City模型直接关联NoiseData
    city_id = models.ForeignKey(City, on_delete=models.CASCADE, related_name='noise_records')
    noise_type = models.CharField(max_length=30)

2. 用ORM实现聚合查询

通过Django ORM一次性完成所有统计计算,减少数据库请求:

适用于PostgreSQL(支持JSON聚合函数)

from django.db.models import Count, F, Case, When, Value, Subquery, OuterRef
from django.db.models.functions import Func
from django.db import models

# 子查询:按城市分组统计各噪音类型的数量
noise_type_subquery = NoiseData.objects.filter(
    city_id=OuterRef('pk')
).values('noise_type').annotate(
    type_count=Count('id')
).values_list('noise_type', 'type_count')

# 主查询:聚合城市的所有统计数据
city_stats = City.objects.annotate(
    # 统计当前城市的噪音数据总数
    count=Count('noise_records'),
    # 计算数量/城市面积的归一化值,处理面积为0的情况
    count_area_normalized=Case(
        When(area=0, then=Value(0)),
        default=F('count') / F('area'),
        output_field=models.FloatField()
    ),
    # 生成噪音类型统计的JSON对象
    noise_type_metadata=Func(
        Subquery(noise_type_subquery.values('noise_type')),
        Subquery(noise_type_subquery.values('type_count')),
        function='JSON_OBJECT_AGG',
        output_field=models.JSONField()
    )
).values('id', 'name', 'count', 'count_area_normalized', 'noise_type_metadata')

适用于SQLite(无JSON聚合函数)

from django.db.models import Count, F, Case, When, Value, Subquery, OuterRef

# 子查询返回噪音类型统计列表
noise_type_subquery = NoiseData.objects.filter(
    city_id=OuterRef('pk')
).values('noise_type').annotate(
    count=Count('id')
).values('noise_type', 'count')

city_stats = City.objects.annotate(
    count=Count('noise_records'),
    count_area_normalized=Case(
        When(area=0, then=Value(0)),
        default=F('count') / F('area'),
        output_field=models.FloatField()
    ),
    noise_type_metadata=Subquery(noise_type_subquery)
).values('id', 'name', 'count', 'count_area_normalized', 'noise_type_metadata')

3. 序列化器处理数据格式

用Django REST Framework序列化器统一输出格式,处理SQLite下的列表转字典逻辑:

from rest_framework import serializers

class CityNoiseStatsSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField()
    count = serializers.IntegerField()
    count_area_normalized = serializers.FloatField()
    noise_type_metadata = serializers.JSONField()

    def to_representation(self, instance):
        data = super().to_representation(instance)
        # 把列表格式的噪音统计转成键值对JSON
        if isinstance(data['noise_type_metadata'], list):
            data['noise_type_metadata'] = {item['noise_type']: item['count'] for item in data['noise_type_metadata']}
        return data

4. 编写API视图

创建视图返回聚合后的JSON数据:

from rest_framework.views import APIView
from rest_framework.response import Response

class CityNoiseAggregateAPI(APIView):
    def get(self, request):
        # 执行对应数据库类型的ORM查询(示例用PostgreSQL版本)
        noise_type_subquery = NoiseData.objects.filter(
            city_id=OuterRef('pk')
        ).values('noise_type').annotate(
            type_count=Count('id')
        ).values_list('noise_type', 'type_count')

        city_stats = City.objects.annotate(
            count=Count('noise_records'),
            count_area_normalized=Case(
                When(area=0, then=Value(0)),
                default=F('count') / F('area'),
                output_field=models.FloatField()
            ),
            noise_type_metadata=Func(
                Subquery(noise_type_subquery.values('noise_type')),
                Subquery(noise_type_subquery.values('type_count')),
                function='JSON_OBJECT_AGG',
                output_field=models.JSONField()
            )
        ).values('id', 'name', 'count', 'count_area_normalized', 'noise_type_metadata')

        serializer = CityNoiseStatsSerializer(city_stats, many=True)
        return Response(serializer.data)

配置URL

在项目urls.py中添加API路由:

from django.urls import path
from .views import CityNoiseAggregateAPI

urlpatterns = [
    path('city-noise-stats/', CityNoiseAggregateAPI.as_view(), name='city-noise-stats'),
]

内容的提问来源于stack exchange,提问作者SSJJ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 20:18:20