具有数据类的SQLAlchemy默认不填充Postgres数据库

时间:2019-04-08 15:44:19

标签: python postgresql sqlalchemy

我将dataclasses与SQLAlchemy经典映射范例结合使用。当我定义一个dataclassintstr字段的默认值组合时,SQLAlchemy不会填充intstr,但是会填充Listdatetime字段。例如下面的代码:

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中产生了以下数据(nameage列是null ):

 id | name | age |    hobbies    |      birthday       
----+------+-----+---------------+---------------------
  1 |      |     | {golf,hiking} | 1985-07-25 00:00:00

如果我更改Person类以从nameage中删除默认值,则数据将正确填充在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对象时,nameage字段会正确地填充到内存中。

如何将SQLAlchemy经典映射与具有默认值的数据类一起使用?

(Python 3.6,SQLAlchemy 1.2.16,PostgreSQL 11.2)

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使用''来指定与0lambda不同的值:

@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