使用覆盖了新方法的类映射到SQLAlchemy表

2024-09-30 22:18:40 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个自定义类,它继承了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

另一方面,我有一张桌子:

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

我想在他们之间做一个炼金术映射。我搞不懂“声明式”的方法,所以我尝试了经典的映射,比如:

# 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类进行最小的修改?你知道吗


Tags: inpytestimportselfnonetruedate
1条回答
网友
1楼 · 发布于 2024-09-30 22:18:40

请注意,在Dateabd orm.date上调用mapper将使用SQLAlchemy的内部使用的元类更改Date类。如果您同时覆盖__new__,您将陷入一个冲突的情况。你知道吗

SQLAlchemy似乎试图为您提供一个默认的__init__实现。我不知道为什么会这样,但是如果同时覆盖__init__,就没有问题了。因为你不需要特殊的处理,所以把下面的内容放到你的课堂上是可以的。你知道吗

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

一个快速测试表明,它不会崩溃之后。你知道吗

相关问题 更多 >