Files
djangoadmin-wiki/DjangoAdmin_Django_EleVue/zh/volume4/business/city.md
T
2026-09-12 23:12:15 +08:00

244 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
:::tip 城市管理
城市管理模块用于管理行政区划数据,支持省/市/区/街道/社区五级行政区划。通过 `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_code` 和 `pid` 两个字段形成五级行政区划树形结构:
```
中国 (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)
```
:::tip 行政区划与用户关联
用户表通过 `province_code`、`city_code`、`district_code`、`street_code` 四个字段关联城市表的 `area_code`,`city_info` 字段存储拼接后的城市名称(如"广东省深圳市南山区")。
:::
## API 接口
| 接口 | 方法 | 权限节点 | 说明 |
|------|------|---------|------|
| `/city/list` | GET | `sys:city:list` | 城市列表(支持按 pid 和 name 筛选) |
| `/city/detail/<int:city_id>` | GET | `sys:city:detail` | 城市详情 |
| `/city/add` | POST | `sys:city:add` | 新增城市 |
| `/city/update` | POST | `sys:city:update` | 编辑城市 |
| `/city/delete/<int:city_ids>` | POST | `sys:city:delete` | 删除城市(支持批量) |
## 列表查询
```
GET /city/list?pid=0&name=深圳
```
### 查询参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `pid` | int | 上级城市 ID(默认 0,查顶级) |
| `name` | str | 城市名称(模糊查询,不区分大小写) |
### 响应示例
```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}条数据")
```
:::warning 删除规则
城市采用**自下而上**的删除策略:必须先删除最底层的社区/街道,再删除区县,最后删除城市和省份。这保证了数据的完整性。
:::
## 前端级联选择
前端使用 `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
```
:::tip 数据来源
行政区划数据来源于国家统计局公开数据,覆盖全国省/市/区/街道/社区五级行政区划。数据量约 70 万条记录。
:::
## 总结
城市管理模块具备以下特点:
```
1. 五级行政区划:省/市/区/街道/社区,通过 parent_code 形成树形结构
2. 丰富字段:含经纬度、拼音、邮编等信息
3. 用户关联:用户表通过四级编码关联城市表
4. 级联选择:前端通过 el-cascader 组件实现级联选择
5. 删除校验:删除前检查子级,保证数据完整性
6. 批量导入:内置全国行政区划数据
```