转自“今日头条·ROBO热爱”
https://www.toutiao.com/article/7357667557463114280/

对于才从PHP转到Python的新手,首先需要实现的是项目的配置设置,数据库连接,项目的自动加载等。借助GPTs,虽然之前从未用Python写过业务,也能摸着石头过河。

配置文件格式

配置文件格式选用YAML,相比JSON,YAML可读性更好,容易编辑,原生支持注释,非常适合用于项目配置文件。

主要引用的Python包

Peewee,简洁强大的Python ORM工具包
PyMySQL,Python MySQL Client库
PyYAML,YAML配置文件解析

# 使用pip安装
pip install peewee pymysql pyyaml

目录组织结构

简单的目录结构

Root_Directory/
│
└── Config/
        └── database.yaml # 数据库配置
        └── openai.yaml # openai配置
└── Lib/
        └── Config/
                  └── Loader.py # 配置加载器
        └── Python/
                  └── Connector.py # 数据库连接器
└── Model/
       └── Python/
               └── GlobalProduct.py # 数据库模型定义
└── Scripts/
        └── Test.py # 测试脚本

配置文件内容示例

# YAML 用于数据序列化的格式,易于阅读与编写,可添加注释
# 数据相关的配置
# database.yaml

Name: The DataBase config file

# 默认数据库连接,为ERP
Default:
    Host: "127.0.0.1"
    Port: 3306
    User: root
    Password: ******
    Database: "erp"
    Charset: utf8

# erp数据库配置
ERP:
    Host: "127.0.0.1"
    Port: 3306
    User: root
    Password: ******
    Database: "erp"
    Charset: utf8
    
# openai.yaml
API_KEY: "********"

先看测试脚本

测试脚本Test.py,也是我们的入口文件,先做一些初始的配置与设置,以及一个数据库调用。

# 路径:ROOT_PATH/Scripts/Test.py
# 先看测试脚本,相当于PHP的入口文件
# 业务流程,调用逻辑一目了然

# 导入系统操作模块,用于进行系统级别的操作,如读写文件,操作路径等
import sys
# 导入io模块,用于处理流(stream)的核心工具
import io
# 导入os模块,提供了非常丰富的方法用来处理文件和目录
import os
# 导入importlib模块,它提供了丰富的接口来动态操作模块
import importlib
# 导入Path类,用于路径操作的现代化库
from pathlib import Path

# 下面是一些项目初始化与配置,这样的初始化配置代码块对于构建可维护和可扩展的应用很有用,
# 尤其是在多环境(开发、测试、生产)或大型项目中

# 将系统的标准输出的编码设置为utf-8,确保控制台输出可以处理UTF-8字符,
# 特别是在Windows环境下
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

# 获取当前文件(__file__是当前脚本的路径)的父目录的父目录,即项目的根目录
root_dir = Path(__file__).resolve().parents[1]
# 将项目根目录加入到sys.path列表的开头,这样Python就可以导入那些在根目录下的模块或包
sys.path.insert(0, str(root_dir))

# 设置环境变量'ROOT_PATH',将其值设置为项目的根目录,便于在项目中的其他位置引用根目录
os.environ['ROOT_PATH'] = str(root_dir)
# 定义配置文件所在目录,即根目录下的'Config'文件夹
config_dir = root_dir / 'Config'
# 设置环境变量'CONFIG_PATH',将其值设置为配置目录的路径,方便全局访问配置文件目录
os.environ['CONFIG_PATH'] = str(config_dir)

# 数据库访问与测试
# 动态加载位于'Model.Python'包内名为'GlobalProduct'的模型定义文件
global_product = importlib.import_module('Model.Python.GlobalProduct')
# 调用刚刚导入的模块中的get_list函数获取数据列表
data_list = global_product.get_list()
print(data_list)

动态加载配置

# 配置加载器,传入配置文件名,加载配置文件并解析文件内容
# 路径 ROOT_PATH/Lib/Config/Loader.py
# 配置文件目录 CONFIG_PATH = ROOT_PATH / 'Config'

# 导入yaml模块,用于解析和生成YAML文件
import yaml
import os
from pathlib import Path

# 定义一个ConfigLoader类,用于加载配置文件
class ConfigLoader:
    def __init__(self, config_name):
        # 接收一个配置文件的基本名字(不包含扩展名)
        self.config_name = config_name
        # 构建完整的配置文件名,配置文件为YAML格式
        self.file_name = f"{config_name}.yaml"
        # 获取环境变量中指定的配置文件目录路径
        config_dir = Path(os.environ['CONFIG_PATH']);
        # 构建配置文件的完整路径
        self.config_file = config_dir / f"{config_name}.yaml"
        # 加载配置文件,并存储配置数据
        self.config = self.load_config()
        
    def load_config(self):
        # 检查配置文件是否存在于指定路径
        if not os.path.exists(self.config_file):
            # 如果文件不存在,抛出FileNotFoundError异常
            raise FileNotFoundError(f"{self.file_name} not found")
        try:
            # 打开并读取配置文件
            with open(self.config_file, 'r', encoding='utf-8') as f:
                # 使用yaml.safe_load方法加载YAML内容,避免执行不安全的操作
                config = yaml.safe_load(f)
        except yaml.YAMLError as e:
            # 如果解析YAML时出错,抛出异常,提示解析错误
            raise Exception(f"Error parsing {self.file_name}: {e}")

        # 返回解析后的配置字典
        return config
    
    def get_config(self):
        # 提供一个公共方法,返回加载的配置字典
        return self.config
        
# 调用方法1,加载openai.yaml配置
loader = importlib.import_module('Lib.Config.Loader')
config = loader.ConfigLoader('openai').get_config()
# 获取API_KEY配置项
openai_key = config['API_KEY']

# 调用方法2,加载数据库database.yaml配置
loader = importlib.import_module('Lib.Config.Loader')
config = loader.ConfigLoader('database').get_config()
# 获取ERP数据库配置内容
db_config = config.get('ERP')

数据库单例模式

# MySQL/MariaDB数据库连接,单例模式实现
# 路径ROOT_PATH/Lib/Python/Connector.py

import importlib
# 从Peewee库中导入MySQLDatabase,它是用于创建和管理数据库连接的类
from peewee import MySQLDatabase # 导入Peewee的连接池支持类,使用连接池代替单一连接
# 导入Lock,用于实现线程安全的单例模式
from threading import Lock

# 定义一个名为Singleton的元类,用于实现单例模式
class Singleton(type):
    # 字典,用于存储每个数据库名称对应的实例
    _instances = {}
    # 线程锁,确保线程安全
    _lock: Lock = Lock()
    
    def __call__(cls, db_name='Default', *args, **kwargs):
        with cls._lock: # 使用锁确保创建实例的操作是线程安全的
            if db_name not in cls._instances: # 检查实例是否已存在
                # 如果不存在,调用父类方法创建实例
                instance = super().__call__(*args, db_name=db_name, **kwargs)
                # 将新创建的实例存储在字典中
                cls._instances[db_name] = instance
        # 返回对应数据库名称的实例
        return cls._instances[db_name]

# DbConnection类定义,使用Singleton元类使其成为单例
class DbConnection(metaclass=Singleton):
    def __init__(self, db_name='Default'):
        self.db_name = db_name # # 数据库标识名称
        self.db_config = self._get_db_config() # 加载数据库配置
        self.db_connection = self._db_connect() # 创建数据库连接
        
    def _get_db_config(self):
        # 动态加载配置模块和配置加载器
        loader = importlib.import_module('Lib.Config.Loader')
        # 创建配置加载器实例,加载数据库配置
        config = loader.ConfigLoader('database').get_config()
        # 从配置中获取当前数据库的配置
        db_config = config.get(self.db_name)
        if not db_config:
            # 如果配置中不存在指定的数据库,抛出异常
            raise ValueError(f'Database {self.db_name} not found in database.yaml')
        return db_config
        
    def _db_connect(self):
        db_config = self.db_config
        # 使用配置参数创建并返回一个MySQLDatabase实例
        return MySQLDatabase(
            db_config['Database'], # 连接的数据库名
            user=db_config['User'],
            password=db_config['Password'],
            host=db_config['Host'],
            port=db_config['Port'],
            charset=db_config['Charset']
        )
    
    def get_connection(self):
        # 提供外部获取数据库连接的方法
        return self.db_connection
    
    def close_connection(self):
        # 提供外部关闭数据库连接的方法
        self.db_connection.close()

# 调用方法
db_connector = importlib.import_module('Lib.Python.Connector')
# 连接默认配置(Default)的数据库
db_default = db_connector.DbConnection().get_connection();
# 连接ERP数据库
db_erp = db_connector.DbConnection('ERP').get_connection();

数据模型定义

# 利用peewee构建global_product表模型
# 路径 ROOT_PATH/Model/Python/GlobalProduct.py


import importlib
# 从Peewee库导入Model、CharField和IntegerField,用于定义模型和字段
from peewee import Model, CharField, IntegerField

# 动态加载数据库连接器模块
db_connector = importlib.import_module('Lib.Python.Connector')
# 从数据库连接器中获取数据库连接
db = db_connector.DbConnection('ERP').get_connection()

# 定义GlobalProduct模型,它映射到数据库的一个表
class GlobalProduct(Model):
    # 定义表中的字段和它们的类型
    Id = IntegerField()  # 整数字段
    SellerSKU = CharField(primary_key=True, max_length=45)  # 字符串字段,设为主键,并限制最大长度为45
    ItemName = CharField(max_length=255)  # 字符串字段,限制最大长度为255
    OnSale = IntegerField()  # 整数字段,用于标记是否在售

    class Meta:
        database = db  # 指定这个模型使用的数据库连接
        db_table = 'global_product'  # 指定这个模型对应的数据库表名

# 定义一个函数,用于获取商品列表
def get_list():
    # 使用Peewee的查询接口,选择GlobalProduct表中所有OnSale字段值为1的记录
    # 这表示查询所有当前在售的商品
    return GlobalProduct.select().where(GlobalProduct.OnSale == 1)

# 调用方式
# ROOT_PATH/Scripts/Test.py里的示例
global_product = importlib.import_module('Model.Python.GlobalProduct')
data_list = global_product.get_list()
print(data_list)

最后

通过上述代码封装,我们实现了动态配置加载器,单例模式的数据库连接器,以及定义了一个简单的数据表模型,实现了通用业务的基本逻辑框架。


kotgwbw
1 声望0 粉丝