欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Python ORM框架SQLAlchemy学习笔记之关系映射实例

程序员文章站 2023-11-23 14:43:10
昨天简单介绍了sqlalchemy的使用,但是没有能够涉及其最精彩的orm部分,今天我将简单说明一下,当然主要还是讲解官方文档的内容,由于是学习笔记,有可能存在精简或者自己...

昨天简单介绍了sqlalchemy的使用,但是没有能够涉及其最精彩的orm部分,今天我将简单说明一下,当然主要还是讲解官方文档的内容,由于是学习笔记,有可能存在精简或者自己理解的部分,不做权威依据。

当我们开始使用orm,一种可配置的结构可以用于描述我们的数据库表,稍后我们定义的类将会被映射到这些表上。当然现代的sqlalchemy(新版本sqlalchemy,原文是modern sqlalchemy)使用declarative把这两件事一起做了,即允许我们把创建类和描述定义数据库表以及它们之间的映射关系一次搞定。

这段话是什么意思呢?简单来说吧,sqlalchemy分为classic (经典模式)和modern (现代模式),classic定义数据库表的模式比较传统,需要先描述这个表。

1. classic 映射

比如以官方文档中的例子,我们拥有表结构如下:

复制代码 代码如下:

create table [users] (
  [id]       integer primary key,
  [name]     text not null,
  [fullname] text not null,
  [password] text not null
);

下面我们描述这张表:

复制代码 代码如下:

from sqlalchemy import table, metadata, column, integer, string

metadata = metadata()

user = table('users', metadata,
            column('id', integer, primary_key=true),
            column('name', string(50)),
            column('fullname', string(50)),
            column('password', string(12))
        )

好,这样我们的表算是描述完成了,接下来我们需要定义我们的python类,比如这样的:
复制代码 代码如下:

class user(object):
    def __init__(self, name, fullname, password):
        self.name = name
        self.fullname = fullname
        self.password = password

如何让我们定义的类与之前描述的表结构发生映射关系就是我们接下来要做的:
复制代码 代码如下:

from sqlalchemy.orm import mapper
mapper(user, user)

大家注意到mapper函数,第一个参数是我们类的名称,第二个参数是我们先前描述的表定义。

这就是传统的定义orm的方法,有关这个方法的更多信息,可以阅读文档mapper configuration,以后有机会再和大家详谈。

2. modern 映射

当大家都乐此不疲的定义描述表,定义类,再映射来实现orm的时候,sqlalchemy团队搞出了更简单的映射方法,那就是modern模式了,即通过定义映射类来一次性完成所有任务。

为了定义的类能够被sqlalchemy管理,所以引入了declarative这个概念,也就是说我们所有的类必须是declarative基类的子类,而这个基类可以通过下面的办法来获取:

复制代码 代码如下:

from sqlalchemy.ext.declarative import declarative_base
base = declarative_base()

当然一个程序内,这个基类最好是唯一的,建议存储在全局变量比如base*所有映射类使用。

现在通过刚才的代码我们得到了名为base的基类,通过这个基类我们可以定义n多的映射子类,而这些子类都能被sqlalchemy declarative系统管理到。

下面我们还是看刚才的那个users表的例子:

复制代码 代码如下:

from sqlalchemy import column, integer, string
class user(base):
     __tablename__ = 'users'

     id = column(integer, primary_key=true)
     name = column(string)
     fullname = column(string)
     password = column(string)

     def __init__(self, name, fullname, password):
         self.name = name
         self.fullname = fullname
         self.password = password

     def __repr__(self):
        return "<user('%s','%s', '%s')>" % (self.name, self.fullname, self.password)

就这段代码就完成了我们先前在classic中需要的三步,代码比原先更简洁和容易管理了,同刚才classic中table定义的column,这个代表数据库表中的列,当然integer和string代表着数据库表的字段类型了。

这样user类就建立起与数据库表的映射,真实表的名字可以使用__tablename__指明,然后是表列的集合,包括id、name、fullname以及password,当然想必大家已经知道了,我们通过primary_key=true已经指明id为主键了。当然一些数据库表可能不包含有主键(例如视图view,当然视图也可以被映射),orm为了能够实际映射表需要至少一个列被定义为主键列。多列,比如复合多主键也能够被很好地映射支持。

大家可能注意到user类中还包含有通常意义上的python魔术方法,包含__init__()初始化类(构造方法)以及__repr__()字符串化支持方法,当然这些都是可选的,如果需要这个类可以加入程序所需要的任意多方法或者属性,你只要把这个类看作一个普通的python类就可以了。

当然user类唯一不能马虎的就是必须继承至base,这个base就是刚才我们通过declarative_base()生成的类,通过它我们可以接下来让sqlalchemy declarative系统管理并操作这些映射类和数据库表。

实际上包括继承的base类,所有的类都应该是python的新式类(new style class),关于新式类的更多信息可以参考python手册。

随着我们的user映射类通过declarative系统构造成功,我们就拥有了相关的定义信息,比如在classic定义中介绍的table()描述,也包含映射到表的类,就是user自身,我们可以通过user.__table__来查看我们的表描述情况:

复制代码 代码如下:

>>> user.__table__
table('users', metadata(none),
    column('id', integer(), table=<users>, primary_key=true, nullable=false),
    column('name', string(), table=<users>),
    column('fullname', string(), table=<users>),
    column('password', string(), table=<users>), schema=none)

当然找到描述表的数据结构,也应该能找到mapper,我们的mapper对象可以通过__mapper__属性来获取,比如这样的:
复制代码 代码如下:

>>> user.__mapper__
<mapper at 0x...; user>

同样的metadata可以通过.metadata属性找到。

好啦,下面轻松一下,见证奇迹的时刻,我们需不需要定义创建好实体数据库然后再定义orm?对于sqlalchemy来说这些都是小事一桩,其都可以给你一手包办,也就是说你可以完全不必理会数据库,交给sqlalchemy就可以了,比如通过metadata.create_all()并将engine参数传入即可(什么是engine?参考我的笔记1),比如通过下面的方式创建我们的users表。

复制代码 代码如下:

>>> base.metadata.create_all(engine)
pragma table_info("users")
()
create table users (
    id integer not null,
    name varchar,
    fullname varchar,
    password varchar,
    primary key (id)
)
()
commit

由于我们开启了engine的echo=true,所以在交互命令下sqlalchemy把sql语句也输出了,正好可以检验是否符合我们的要求。

这样简单的create_all()我们就轻松建立起先前orm映射定义的表啦。

时间不早了,今天先聊到这儿,下次再谈sqlalchemy的其他特性。