我将dataclasses
与SQLAlchemy经典映射范例结合使用。当我定义一个dataclass
和int
和str
字段的默认值组合时,SQLAlchemy不会填充int
和str
,但是会填充List
和datetime
字段。例如下面的代码:
from dataclasses import dataclass, field
from typing import List
from datetime import datetime
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, ARRAY, TIMESTAMP
from sqlalchemy.orm import sessionmaker, mapper
metadata = MetaData()
person_table = \
Table('people', metadata,
Column('id', Integer, primary_key=True, autoincrement=True),
Column('name', String(255)),
Column('age', Integer),
Column('hobbies', ARRAY(String)),
Column('birthday', TIMESTAMP)
)
@dataclass
class Person:
id: int = None
name: str = ''
age: int = 0
hobbies: List[str] = field(default_factory=list)
birthday: datetime = field(default_factory=datetime)
mapper(Person, person_table)
engine = create_engine('postgresql://postgres@localhost:32771/test', echo=True)
metadata.create_all(engine)
session = sessionmaker(bind=engine)()
person = Person(id=None, name='Robby', age=33, hobbies=['golf', 'hiking'], birthday=datetime(1985, 7, 25))
session.add(person)
session.commit()
这正确地填充了内存中的person
对象,但是commit
操作在postgres中产生了以下数据(name
和age
列是null
):
id | name | age | hobbies | birthday
----+------+-----+---------------+---------------------
1 | | | {golf,hiking} | 1985-07-25 00:00:00
如果我更改Person
类以从name
和age
中删除默认值,则数据将正确填充在postgres中:
@dataclass
class Person:
id: int = None
name: str
age: int
hobbies: List[str] = field(default_factory=list)
birthday: datetime = field(default_factory=datetime)
请注意,我已经验证,当在类的“非默认”版本中创建person
对象时,name
和age
字段会正确地填充到内存中。
如何将SQLAlchemy经典映射与具有默认值的数据类一起使用?
(Python 3.6,SQLAlchemy 1.2.16,PostgreSQL 11.2)
答案 0 :(得分:0)
由于''
和0
分别是str()
和int()
函数的默认返回值,因此您可以使用以下代码插入这些默认值:
@dataclass
class Person:
id: int = None
name: str = field(default_factory=str)
age: int = field(default_factory=int)
hobbies: List[str] = field(default_factory=list)
birthday: datetime = field(default_factory=datetime)
不幸的是,由于某些原因,使用default
函数的field()
参数无法正常工作(可能是dataclasses
移植的错误或误解...) 。但是您仍然可以使用default_factory
使用''
来指定与0
和lambda
不同的值:
@dataclass
class Person:
id: int = None
name: str = field(default_factory=lambda: 'john doe')
age: int = field(default_factory=lambda: 77)
hobbies: List[str] = field(default_factory=list)
birthday: datetime = field(default_factory=datetime)
答案 1 :(得分:0)
映射会更改您映射的类。因此,数据类属性被表中的列覆盖,属性在映射中描述。声明时,您会看到更多问题
@dataclass(frozen = True)
您应该在Column声明中使用默认值,也许声明的模型更适合您,类似于数据类。
如果要将基础结构/数据库与域代码分开,则需要另一种方法。为此,建议不要将您的数据类视为DB模型的接口。因此,数据库模型将实现您的DTO描述的行为。但是要使其正常工作,您将需要一个可以处理此问题的存储库。 我在此博客文章中描述了类似的情况:4 ways of mapping Value Object in SQLAlchemy