Skip to content

城市管理

城市管理模块用于管理行政区划数据,支持省/市/区/街道/社区五级行政区划。通过 parent_code 字段实现树形结构。

城市模型

python
# application/city/models.py
from application.models import BaseModel

class City(BaseModel):
    """城市/行政区划模型类"""
    class Meta:
        db_table = 'django_city'

    city_code = models.CharField(max_length=6, verbose_name='城市区号')
    area_code = models.CharField(max_length=20, verbose_name='行政编码')
    parent_code = models.CharField(max_length=20, null=True, blank=True, verbose_name='上级行政编码')
    zip_code = models.CharField(max_length=6, verbose_name='邮政编码')
    level = models.IntegerField(default=0, verbose_name='城市级别:0-省份 1-城市 2-县区 3-街道 4-社区')
    pid = models.IntegerField(default=0, verbose_name='上级城市ID')
    name = models.CharField(max_length=150, verbose_name='城市名称')
    short_name = models.CharField(max_length=150, verbose_name='城市简称')
    full_name = models.CharField(max_length=150, null=True, blank=True, verbose_name='城市全称')
    pinyin = models.CharField(max_length=150, null=True, blank=True, verbose_name='城市拼音')
    lng = models.CharField(max_length=150, null=True, blank=True, verbose_name='城市经度')
    lat = models.CharField(max_length=150, null=True, blank=True, verbose_name='城市纬度')

字段说明

字段说明示例
city_code城市区号0755
area_code行政编码(唯一标识)440305
parent_code上级行政编码440300(深圳市)
zip_code邮政编码518000
level层级:0-省 1-市 2-区 3-街道 4-社区2
pid上级城市 ID(关联本表 id)123
name城市名称南山区
short_name城市简称南山
full_name城市全称广东省深圳市南山区
pinyin城市拼音nanshanqu
lng经度113.9304
lat纬度22.5333

树形结构

城市通过 parent_codepid 两个字段形成五级行政区划树形结构:

中国 (parent_code=null, level=0)
+-- 广东省 (parent_code=100000, level=0)
|   +-- 深圳市 (parent_code=440000, level=1)
|   |   +-- 南山区 (parent_code=440300, level=2)
|   |   |   +-- 粤海街道 (parent_code=440305, level=3)
|   |   +-- 福田区 (parent_code=440300, level=2)
|   +-- 广州市 (parent_code=440000, level=1)
+-- 浙江省 (parent_code=100000, level=0)
    +-- 杭州市 (parent_code=330000, level=1)

行政区划与用户关联

用户表通过 province_codecity_codedistrict_codestreet_code 四个字段关联城市表的 area_codecity_info 字段存储拼接后的城市名称(如"广东省深圳市南山区")。

API 接口

接口方法权限节点说明
/city/listGETsys:city:list城市列表(支持按 pid 和 name 筛选)
/city/detail/<int:city_id>GETsys:city:detail城市详情
/city/addPOSTsys:city:add新增城市
/city/updatePOSTsys:city:update编辑城市
/city/delete/<int:city_ids>POSTsys:city:delete删除城市(支持批量)

列表查询

GET /city/list?pid=0&name=深圳

查询参数

参数类型说明
pidint上级城市 ID(默认 0,查顶级)
namestr城市名称(模糊查询,不区分大小写)

响应示例

json
{
    "code": 0,
    "data": [
        {
            "id": 1,
            "cityCode": "0755",
            "areaCode": "440305",
            "parentCode": "440300",
            "zipCode": "518057",
            "level": 2,
            "pid": 123,
            "name": "南山区",
            "shortName": "南山",
            "fullName": "广东省深圳市南山区",
            "pinyin": "nanshanqu",
            "lng": "113.9304",
            "lat": "22.5333"
        }
    ],
    "msg": "操作成功",
    "ok": true
}

子级城市查询

根据上级城市编码获取子级城市列表,用于前端级联选择器:

python
# application/city/services.py
def get_child_cities(city_code):
    """根据上级城市编码获取子级城市列表"""
    child_list = City.objects.filter(
        is_delete=False,
        parent_code=city_code
    ).values()
    return list(child_list)

添加城市

python
# application/city/services.py
def add_city(request):
    """添加城市"""
    data, error = parse_request_body(request)
    if error:
        return R.failed(msg=error)

    # 表单验证
    form = forms.CityForm(data)
    if not form.is_valid():
        return R.failed(msg=regular.get_err(form))

    cleaned_data = form.cleaned_data
    City.objects.create(
        city_code=cleaned_data.get('cityCode'),
        area_code=cleaned_data.get('areaCode'),
        parent_code=cleaned_data.get('parentCode'),
        zip_code=cleaned_data.get('zipCode'),
        level=int(cleaned_data.get('level')),
        pid=int(cleaned_data.get('pid')),
        name=cleaned_data.get('name'),
        short_name=cleaned_data.get('shortName'),
        lng=cleaned_data.get('lng'),
        lat=cleaned_data.get('lat'),
        create_user=get_username(request),
        update_user=get_username(request)
    )
    return R.ok(msg="创建成功")

删除城市(带子级校验)

删除城市前会检查是否存在子级城市,如果有则阻止删除:

python
def delete_cities(city_ids):
    """删除城市(批量删除)"""
    id_list = [int(id_str.strip()) for id_str in city_ids.split(',') if id_str.strip()]

    # 检查是否有子级城市
    has_children = City.objects.filter(pid__in=id_list, is_delete=False).exists()
    if has_children:
        return R.failed("请先删除子级城市")

    # 批量逻辑删除
    updated_count = City.objects.filter(
        id__in=id_list, is_delete=False
    ).update(is_delete=True)

    return R.ok(msg=f"本次共删除{updated_count}条数据")

删除规则

城市采用自下而上的删除策略:必须先删除最底层的社区/街道,再删除区县,最后删除城市和省份。这保证了数据的完整性。

前端级联选择

前端使用 el-cascader 组件实现省市区三级联动选择:

vue
<el-cascader
    v-model="selectedRegion"
    :props="cascaderProps"
    placeholder="请选择省/市/区"
/>

<script setup>
const cascaderProps = {
    lazy: true,
    lazyLoad: async (node, resolve) => {
        const { level, value } = node;
        const res = await getCityList({ pid: value || 0 });
        const nodes = res.map(item => ({
            value: item.areaCode,
            label: item.name,
            leaf: level >= 2  // 区县级为叶子节点
        }));
        resolve(nodes);
    }
};
</script>

数据导入

行政区划数据通常通过 SQL 文件批量导入,而非手动添加。项目内置了全国行政区划数据:

bash
# 导入行政区划数据(首次部署时执行)
python manage.py migrate

数据来源

行政区划数据来源于国家统计局公开数据,覆盖全国省/市/区/街道/社区五级行政区划。数据量约 70 万条记录。

总结

城市管理模块具备以下特点:

1. 五级行政区划:省/市/区/街道/社区,通过 parent_code 形成树形结构
2. 丰富字段:含经纬度、拼音、邮编等信息
3. 用户关联:用户表通过四级编码关联城市表
4. 级联选择:前端通过 el-cascader 组件实现级联选择
5. 删除校验:删除前检查子级,保证数据完整性
6. 批量导入:内置全国行政区划数据

小蚂蚁云团队 · 提供技术支持