【问题标题】:Mapping to SQLAlchemy Table using a class with overwritten __new__ method使用覆盖 __new__ 方法的类映射到 SQLAlchemy 表
【发布时间】:2019-09-19 15:25:57
【问题描述】:

我有一个自定义类,它继承自 Python 的内置 datetime.date 类:

# types.py

from datetime import date

class Date(date):
    def __new__(cls, *args, isExceptional: bool = None, **kwargs):
        if len(args) == 1 and isinstance(args[0], date):
            self = super().__new__(
                cls, args[0].year, args[0].month, args[0].day, **kwargs)
        else:
            self = super().__new__(cls, *args, **kwargs)
        if isExceptional is not None:
            self._isOff = True if self.isoweekday() in (6, 7) else False
            if isExceptional:
                self._isOff = not self._isOff
        else:
            self._isOff = None
        return self

    @property
    def isOff(self):
        return self._isOff

    @isOff.setter
    def isOff(self, value: bool):
        if self._isOff is not None:
            raise AttributeError(
                "'isOff' can be set only when it is currently undefined, i.e. None"
            )
        else:
            self._isOff = value

总而言之,Date 类似于内置的date,还有一个附加的_isOff 属性及其访问器。它的构造函数接受标准 date 对象或原始 date 构造函数可接受的任何参数作为参数,以及一个额外的仅限关键字的参数,以操纵 _isOff 值。

这个类基本上可以工作,因为

test = Date(2019, 9, 15)
print(test.isoformat(), "; isOff:", test.isOff)
test = Date(2019, 9, 15, isExceptional=False)
print(test.isoformat(), "; isOff:", test.isOff)
test = Date(2019, 9, 15, isExceptional=True)
print(test.isoformat(), "; isOff:", test.isOff)

给我:

2019-09-15 ; isOff: None
2019-09-15 ; isOff: True
2019-09-15 ; isOff: False

另一方面,我有一个 MariaDB 表:

CREATE TABLE `date` (
    `date` DATE NOT NULL,
    `isOff` BIT(1) NOT NULL DEFAULT b'0',
    PRIMARY KEY (`date`)
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
;

我想在它们之间做 SQLAlchemy 映射。我无法弄清楚“声明式”的方式,所以我尝试了经典映射,如下所示:

# orm.py
import sqlalchemy as ORM
date = ORM.Table(
    'date',
    ORM.MetaData(),
    ORM.Column('date', ORM.Date, primary_key=True),
    ORM.Column('isOff', ORM.Boolean)
)

# types.py, after the Date class definition
from . import orm
from sqlalchemy.orm import mapper
mapper(Date, orm.date)

以上,test = Date(2019, 9, 15) 给我

Traceback (most recent call last):
  File "c:\users\user\appdata\local\programs\python\python37-32\Lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "c:\users\user\appdata\local\programs\python\python37-32\Lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "D:\data\work\newsys\newsys\types.py", line 39, in <module>
    test = Date(2019, 9, 15)
TypeError: __init__() takes 1 positional argument but 4 were given

我猜错误消息中提到的__init__() 来自SQLAlchemy 的映射机制,因为我的类和标准的date 类中都没有定义__init__()。除此之外,我无法弄清楚发生了什么......那个“1位置参数”应该是什么以及作为那些4提供的?

如何在对我的 Date 类进行最少修改的情况下映射它们?

【问题讨论】:

    标签: python python-3.x inheritance sqlalchemy


    【解决方案1】:

    请注意,在 Date abd orm.date 上调用 mapper 将使用 SQLAlchemy 内部使用的元类更改您的 Date 类。如果您随后还覆盖__new__,您将陷入冲突的局面。

    似乎 SQLAlchemy 试图为您提供默认的 __init__ 实现。我不知道为什么会这样,但如果你也覆盖__init__ 也没有问题。由于您不需要特殊处理,因此只需将以下内容放入您的课程中即可。

    def __init__(self, *args, **kw):
        pass
    

    快速测试表明它不会在此之后崩溃。

    【讨论】:

    • 解决方法很简单:不要覆盖__new__方法。
    • 我可以覆盖 __init__() 吗?
    • 等一下。我目前正在调试您的示例。
    • 非常感谢。由于Date.__new__() 中有一个额外的仅关键字参数,其__init__ 必须是这样的:def __init__(self, *args, **kwargs): pass,否则会有TypeError: __init__() got an unexpected keyword argument
    猜你喜欢
    • 2015-04-26
    • 2016-04-19
    • 2012-04-14
    • 2012-02-20
    • 2019-06-28
    • 2014-11-21
    • 1970-01-01
    • 2016-11-30
    • 1970-01-01
    相关资源
    最近更新 更多