SQLAlchemy(一)

目标

使用 SQLAlchemy 连接数据库,创建数据表

环境:

  • Python==3.6.8
  • SQLAlchemy==1.2.16
  • Psycopg2==2.7.6.1

连接与创建

使用 docker 启动一个 postgres 容器:

1
# docker run --rm --name one-postgres -p 5432:5432 -e POSTGRES_PASSWORD=123456 -d postgres

进入容器,使用命令创建一个名叫 mydb 的数据库:

1
2
# docker exec -it one-postgres bash
# createdb -U postgres mydb

现在可以访问下刚创建的数据库,验证有没有创建成功:

1
# psql -U postgres mydb

如果一切顺利,会返回该信息,并成功进入 mydb 数据库:

1
2
3
4
psql (11.1 (Debian 11.1-1.pgdg90+1))
Type "help" for help.

mydb=#

最后一行也可能是(意味着当前操作数据库用户非超级用户):

1
mydb=>

ORM 连接

要想使用 ORM,必须先实现类与表的映射。

在 SQLAlchemy 中,使用 Declarative 系统来处理这种映射关系。一般情况,我们只需创建一个基于此的一个基类,然后描述我们自己类与表的关系:

1
2
3
>>> from sqlalchemy.ext.declarative import declarative_base

>>> Base = declarative_base()

这个 Base 就是我们要继承的基类,现在我们创建一个 User 类:

1
2
3
4
5
6
7
8
9
class User(Base):
__tablename__ = 'users'

id = Column(Integer, primary_key=True)
name = Column(String(64), nullable=False, index=True)
password = Column(String(64))

def __repr__(self):
return '<User (%s)>' % self.name

其中 __tablename__ 是必须的,另外还需要一个为 primary keyColumn

Note

QLAlchemy 的 Base 基类不会对映射表做任何约束,反而鼓励使用者根据实际情况自定义最小基类,比如可以这么做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> from sqlalchemy.ext.declarative import declared_attr
>
> class MyMixin(object):
>
> @declared_attr
> def __tablename__(cls):
> return cls.__name__.lower()
>
> __table_args__ = {'mysql_engine': 'InnoDB'}
> __mapper_args__= {'always_refresh': True}
>
> id = Column(Integer, primary_key=True)
>
> class MyModel(MyMixin, Base):
> name = Column(String(1000))
>

因为在 SQLAlchemy 的 Base 中没有定义任何 __tablename____table_args__ 等等这些约束,所以在继承的时候顺讯没有关系,也可以这么写:

1
2
3
> class MyModel(Base, MyMixin):
> name = Column(String(1000))
>

但是如果继承的两个类含有同样的属性,那么根据 Python 该属性由写在前面的那个类决定。

当我们的类构建完之后,Declarative 系统会把所有的 Column 对象替换成叫 desciptors 的特殊 Python 访问器。这个过程就叫做 instrumentation

之后,该映射类就能给我们提供从数据库中持久化和载入 column 的 SQL 上下文。

除此之外,该映射类还是一个普通的 Python 类,我们依旧可以在里面定义属性,方法等等。

在 SQLAlchemy 的 Declarative 系统对定义的映射类描述阶段,会创建一个 Table 对象并和这个 Table 对象一起构建一个 Mapper 对象。这个 Mapper 对象一般不会用到,但当我们需要的时候,能从中获取大量的映射信息。

Table 对象是 MetaData 集合的一员。使用 Declarative 系统的时候,MetaData 对象属性能直接通过 .metadata 属性访问。

现在,我们使用 SQLALchemy 连接数据库:

1
2
3
>>> from sqlalchemy import create_engine

>>> engine = create_engine('postgresql+psycopg2://postgres:123456@localhost:5432/mydb')

然后调用 MetaData.create_all() 方法,传入 Engine 对象:

1
>>> Base.metadata.create_all(engine)

我们去数据库看看有没有创建对应的表:

1
2
3
4
5
6
mydb=# \dt
List of relations
Schema | Name | Type | Owner
--------+-------+-------+----------
public | users | table | postgres
(1 row)

查看对应的表结构:

1
2
3
4
5
6
7
8
9
10
mydb=# \d users
Table "public.users"
Column | Type | Collation | Nullable | Default
----------+-----------------------+-----------+----------+-----------------------------------
id | integer | | not null | nextval('users_id_seq'::regclass)
name | character varying(64) | | not null |
password | character varying(64) | | |
Indexes:
"users_pkey" PRIMARY KEY, btree (id)
"ix_users_name" btree (name)

成功!