Skip to content

Feature Request: Standardized API for Custom Field Types with Py2DB/DB2Py Conversion Methods #750

@AUXStar

Description

@AUXStar

功能请求:支持自定义数据类型的标准化API,包含Py2DB/DB2Py转换方法

当前问题

PonyORM现有的内置字段类型可能无法满足所有使用场景,特别是处理特殊数据类型(如压缩字符串或numpy数组)时。开发者需要一种标准化的方式来创建具有适当数据库序列化/反序列化的自定义字段类型。

提议方案

建议实现一个BaseType基类,允许开发者定义自定义字段类型,包含:

  1. 数据库行为规范(__database_behavior__)
  2. 验证方法(validate)
  3. Python到数据库的转换方法(py2db)
  4. 数据库到Python的转换方法(db2py)

示例实现

import gzip
import numpy as np
import json

class BaseType:
    """PonyORM中自定义字段类型的基类"""
    __database_behavior__ = None  # 必须被重写为一个基本类型
    
    @staticmethod
    def validate(value):
        """验证值是否为正确类型"""
        raise NotImplementedError
        
    @staticmethod
    def py2db(value):
        """将Python值转换为数据库表示形式"""
        raise NotImplementedError
        
    @staticmethod
    def db2py(value):
        """将数据库表示形式转换回Python值"""
        raise NotImplementedError


class GzipStringType(BaseType):
    __database_behavior__ = bytes

    @staticmethod
    def validate(value: str):
        if not isinstance(value, str):
            raise ValueError("必须为字符串类型")

    @staticmethod
    def py2db(value: str) -> bytes:
        if len(value) < 128:  # 小字符串不压缩
            return value.encode()
        return gzip.compress(value.encode())  # 大字符串进行压缩

    @staticmethod
    def db2py(value: bytes) -> str:
        try:
            return gzip.decompress(value).decode()  # 尝试解压
        except gzip.BadGzipFile:
            return value.decode()  # 如果不是压缩数据,直接解码


class NDArrayType(BaseType):
    __database_behavior__ = bytes
    
    @staticmethod
    def validate(value: np.ndarray):
        if not isinstance(value, np.ndarray):
            raise ValueError("必须为numpy.ndarray类型")

    @staticmethod
    def py2db(value: np.ndarray) -> bytes:
        # 存储数组元数据和实际数据
        metadata = json.dumps({
            "shape": value.shape,
            "dtype": str(value.dtype)
        }).encode('utf-8')
        data = value.tobytes()
        return len(metadata).to_bytes(4, 'big') + metadata + data

    @staticmethod
    def db2py(value: bytes) -> np.ndarray:
        # 从字节流重建numpy数组
        meta_length = int.from_bytes(value[:4], 'big')
        metadata = json.loads(value[4:4+meta_length].decode('utf-8'))
        data = value[4+meta_length:]
        return np.frombuffer(
            data, 
            dtype=np.dtype(metadata["dtype"])
        ).reshape(metadata["shape"])

优势

  1. 可扩展性:开发者可以为特殊需求创建自定义类型
  2. 类型安全:内置验证确保数据完整性
  3. 高效性:针对特定数据类型的优化存储(如对大字符串的压缩)
  4. 一致性:自定义字段类型的标准化接口

建议的集成方式

可以这样集成到PonyORM的实体定义中:

from pony.orm import *

db = Database()

class MyEntity(db.Entity):
    compressed_data = Required(GzipStringType)  # 必填的压缩字符串字段
    array_data = Optional(NDArrayType)  # 可选的numpy数组字段

其他考虑

  1. 向后兼容:应确保不影响现有代码
  2. 性能影响:转换方法应尽可能高效
  3. 错误处理:需要完善的错误处理机制
  4. 文档支持:需要详细的文档说明如何使用自定义类型

这个提案是否与PonyORM的设计理念相符?期待听到您的想法和建议。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions