如何用设计模式优化Python/Django文件上传至PostgreSQL的脚本
解决多格式文件导入PostgreSQL的冗余与维护问题
核心问题拆解
你的脚本冗余、难维护,本质是不同文件格式的解析逻辑耦合在一起,新增/修改字段时要在多处修改代码,违反了单一职责和开闭原则。
推荐的设计模式及落地方式
1. 策略模式(Strategy Pattern)
把每种文件格式的解析逻辑封装成独立的策略类,主流程只负责调度,不关心具体解析细节。
- 定义统一的解析接口,比如
FileParser,包含parse方法返回标准化的数据结构(字典列表) - 为CSV、Excel、JSON、GeoJSON分别实现对应的解析类:
CsvParser、ExcelParser、JsonParser、GeoJsonParser - 用映射字典或简单工厂类根据文件类型匹配对应的解析策略
示例代码:
from abc import ABC, abstractmethod from django.db.models import Model # 统一解析接口 class FileParser(ABC): @abstractmethod def parse(self, file_path: str) -> list[dict]: """解析文件,返回标准化字典列表""" pass # CSV解析策略 import csv class CsvParser(FileParser): def parse(self, file_path: str) -> list[dict]: data = [] with open(file_path, 'r', encoding='utf-8') as f: reader = csv.DictReader(f) for row in reader: # 可在此添加字段清洗、映射逻辑 data.append(row) return data # Excel解析策略(基于openpyxl) from openpyxl import load_workbook class ExcelParser(FileParser): def parse(self, file_path: str) -> list[dict]: data = [] wb = load_workbook(file_path) ws = wb.active headers = [cell.value for cell in ws[1]] for row in ws.iter_rows(min_row=2, values_only=True): data.append(dict(zip(headers, row))) return data # 策略调度器(简单工厂实现) class ParserDispatcher: def __init__(self): self.parsers = { 'csv': CsvParser(), 'xlsx': ExcelParser(), 'json': JsonParser(), 'geojson': GeoJsonParser() } def get_parser(self, file_ext: str) -> FileParser: return self.parsers.get(file_ext.lower()) # 主导入流程 def import_to_db(file_path: str, model: Model): ext = file_path.split('.')[-1] dispatcher = ParserDispatcher() parser = dispatcher.get_parser(ext) if not parser: raise ValueError(f"不支持的文件格式: {ext}") data = parser.parse(file_path) # Django批量插入数据库 instances = [model(**item) for item in data] model.objects.bulk_create(instances)
2. 适配器模式(Adapter Pattern)
如果不同格式的字段名与数据库模型字段不匹配,用适配器统一做字段映射,避免在解析类里硬编码映射规则。
示例:
class GeoJsonAdapter: @staticmethod def adapt(feature: dict) -> dict: """将GeoJSON的properties字段映射到模型字段""" props = feature.get('properties', {}) return { 'db_city': props.get('city_name'), 'db_population': props.get('pop'), # 其他字段映射 } # 在GeoJsonParser中调用适配器 class GeoJsonParser(FileParser): def parse(self, file_path: str) -> list[dict]: import json with open(file_path, 'r') as f: geojson_data = json.load(f) return [GeoJsonAdapter.adapt(feature) for feature in geojson_data.get('features', [])]
额外维护优化建议
- 把字段映射规则放到配置文件(JSON/YAML)中,修改字段时仅需调整配置,无需改动代码
- 用
pydantic为每种解析输出添加数据校验,提前拦截错误数据 - 抽象数据库通用操作逻辑(如批量插入/更新),避免重复编写数据库交互代码
内容的提问来源于stack exchange,提问作者aba2s
相关产品推荐
相关产品推荐

