# SQLAlchemy 中的对象关系映射器

> 原文： [http://zetcode.com/db/sqlalchemy/orm/](http://zetcode.com/db/sqlalchemy/orm/)

在 SQLAlchemy 教程的这一部分中，我们介绍了 SQLAlchemy 的对象关系映射器。

## 对象关系映射

使用 Python 数据库 API 进行编程可为开发者提供直接访问数据库的全部功能。 这种直接访问也有一些缺点。 它们在大型项目中尤其引人注目。 我们将两种语言混合在一起：SQL 和 Python。 结果是它使 SQL 语句更难测试和维护。 在典型的 Web 应用中，除了 Python 和 SQL（或任何其他服务器端编程语言）外，我们还具有 HTML，CSS，JavaScript。 Python 和 SQL 捆绑在一起使项目变得更加复杂。 在编程理论中，我们尝试将业务逻辑与数据访问以及表示分离。 因此，需要一种将 Python 代码与 SQL 代码分开的解决方案。

另一个问题是我们所谓的对象关系阻抗不匹配。 当关系数据库管理系统被以面向对象的编程语言或风格编写的程序使用时，通常会遇到一系列概念和技术难题。 在 Python 中，我们处理放置在对象内的数据。 在数据库系统中，数据存储在表中。 程序员需要在两种处理数据的方式之间进行转换。 这与我们应用的核心问题无关。

解决方案之一是对象关系映射。 ORM 工具解决了上述问题。 有几种用于 Python 语言的 ORM 工具。 SQLAlchemy 是使用最广泛的方法之一。

## SQLAlchemy ORM

SQLAlchemy 对象关系映射器将（a）用户定义的 Python 类映射到数据库表，（b）将表行映射到实例对象，并且（c）将列映射到实例属性。 SQLAlchemy ORM 建立在 SQLAlchemy 表达式语言之上。

使用 ORM 时，我们首先配置将要使用的数据库表。 然后，我们定义将要映射到它们的类。 现代 SQLAlchemy 使用声明式系统来执行这些任务。 将创建一个声明性基类，该基类维护类和表的目录。 使用`declarative_base()`函数创建一个声明性基类。

## 会话

完成配置后，我们创建一个会话。 会话是 SQLAlchemy ORM 中持久性操作的主要接口。 它建立并维护我们的程序与数据库之间的所有对话。

## 建立表

以下程序在内存中创建一个表，然后将数据打印到控制台。

`orm_create_table.py`

```py
#!/usr/bin/python
# -*- coding: utf-8 -*-

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker

eng = create_engine('sqlite:///:memory:')

Base = declarative_base()

class Car(Base):
    __tablename__ = "Cars"

    Id = Column(Integer, primary_key=True)
    Name = Column(String)  
    Price = Column(Integer)

Base.metadata.bind = eng        
Base.metadata.create_all()        

Session = sessionmaker(bind=eng)
ses = Session()    

ses.add_all(
   [Car(Id=1, Name='Audi', Price=52642), 
    Car(Id=2, Name='Mercedes', Price=57127),
    Car(Id=3, Name='Skoda', Price=9000),
    Car(Id=4, Name='Volvo', Price=29000),
    Car(Id=5, Name='Bentley', Price=350000),
    Car(Id=6, Name='Citroen', Price=21000),
    Car(Id=7, Name='Hummer', Price=41400),
    Car(Id=8, Name='Volkswagen', Price=21600)])
ses.commit()

rs = ses.query(Car).all()

for car in rs:
    print car.Name, car.Price

```

在`Cars`表中创建了八辆汽车。

```py
Base = declarative_base()

```

使用`declarative_base()`函数创建一个声明性基类。

```py
class Car(Base):
    __tablename__ = "Cars"

    Id = Column(Integer, primary_key=True)
    Name = Column(String)  
    Price = Column(Integer)

```

用户定义的`Car`类映射到`Cars`表。 该类继承自声明性基类。

```py
Base.metadata.bind = eng

```

声明式`Base`绑定到数据库引擎。

```py
Base.metadata.create_all() 

```

`create_all()`方法创建所有已配置的表； 在我们的例子中，只有一张表。

```py
Session = sessionmaker(bind=eng)
ses = Session() 

```

创建会话对象。

```py
ses.add_all(
   [Car(Id=1, Name='Audi', Price=52642), 
    Car(Id=2, Name='Mercedes', Price=57127),
...    

```

使用`add_all()`方法，我们将`Car`类的指定实例添加到会话中。

```py
ses.commit()

```

使用`commit()`方法将更改提交到数据库。

```py
rs = ses.query(Car).all()

```

我们从 Cars 表中查询所有数据。 `query()`方法加载`Car`类的所有实例，其`all()`方法将查询表示的所有结果作为列表返回。

```py
for car in rs:
    print car.Name, car.Price

```

我们遍历结果集并为所有返回的行打印两列。

```py
$ ./orm_create_table.py 
Audi 52642
Mercedes 57127
Skoda 9000
Volvo 29000
Bentley 350000
Citroen 21000
Hummer 41400
Volkswagen 21600

```

这是示例的输出。

## 添加新的汽车

在下一个示例中，我们将单个汽车添加到`Cars`表中。

`orm_add_car.py`

```py
#!/usr/bin/python
# -*- coding: utf-8 -*-

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker

eng = create_engine('sqlite:///test.db')

Base = declarative_base()

class Car(Base):
    __tablename__ = "Cars"

    Id = Column(Integer, primary_key=True)
    Name = Column(String)  
    Price = Column(Integer)

Session = sessionmaker(bind=eng)
ses = Session()    

c1 = Car(Name='Oldsmobile', Price=23450)
ses.add(c1)
ses.commit()

rs = ses.query(Car).all()

for car in rs:
    print car.Name, car.Price

```

该脚本连接到 SQLite 数据库，并向`Cars`表添加新行。

```py
eng = create_engine('sqlite:///test.db')

```

我们连接到 SQLite `test.db`数据库。

```py
Base = declarative_base()

class Car(Base):
    __tablename__ = "Cars"

    Id = Column(Integer, primary_key=True)
    Name = Column(String)  
    Price = Column(Integer)

```

执行用户定义的类到数据库表的映射。

```py
Session = sessionmaker(bind=eng)
ses = Session()    

```

创建会话对象，该对象是 ORM 到数据库的中介。

```py
c1 = Car(Name='Oldsmobile', Price=23450)

```

创建映射的`Car`类的新实例。

```py
ses.add(c1)

```

`add()`方法将新对象添加到会话中。

```py
ses.commit()

```

所做的更改将提交到数据库。

```py
$ ./orm_add_car.py 
Audi 52642
Mercedes 57127
Skoda 9000
Volvo 29000
Bentley 350000
Citroen 21000
Hummer 41400
Volkswagen 21600
Oldsmobile 23450

```

我们确认新车已成功添加到数据库中。

## 过滤数据

会话查询的`filter()`方法用于在查询对象上应用过滤条件。

`orm_query_like.py`

```py
#!/usr/bin/python
# -*- coding: utf-8 -*-

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker

eng = create_engine('sqlite:///test.db')

Base = declarative_base()
Base.metadata.bind = eng

class Car(Base):
    __tablename__ = "Cars"

    Id = Column(Integer, primary_key=True)
    Name = Column(String)  
    Price = Column(Integer)

Session = sessionmaker(bind=eng)
ses = Session()    

rs = ses.query(Car).filter(Car.Name.like('%en'))

for car in rs:
    print car.Name, car.Price

```

该示例打印名称以`"en"`字符串结尾的汽车。

```py
rs = ses.query(Car).filter(Car.Name.like('%en'))

```

`filter()`方法采用一个过滤条件，它是一个 SQL 表达式对象。 使用`like()`方法创建标准。

```py
$ ./orm_query_like.py 
Citroen 21000
Volkswagen 21600

```

表中有两辆以`"en"`字符串结尾的汽车。

`in_()`方法实现 SQL `IN`运算符。

`orm_query_in.py`

```py
#!/usr/bin/python
# -*- coding: utf-8 -*-

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker

eng = create_engine('sqlite:///test.db')

Base = declarative_base()

class Car(Base):
    __tablename__ = "Cars"

    Id = Column(Integer, primary_key=True)
    Name = Column(String)  
    Price = Column(Integer)

Session = sessionmaker(bind=eng)
ses = Session()    

rs = ses.query(Car).filter(Car.Id.in_([2, 4, 6, 8]))

for car in rs:
    print car.Id, car.Name, car.Price

```

该代码示例选择并打印 SQL `IN`运算符选择的具有 ID 的行的列。

```py
rs = ses.query(Car).filter(Car.Id.in_([2, 4, 6, 8]))

```

过滤条件是通过`in_()`方法创建的。 该方法采用 ID 列表。

```py
$ ./orm_query_in.py 
2 Mercedes 57127
4 Volvo 29000
6 Citroen 21000
8 Volkswagen 21600

```

这是示例的输出。

## 外键

在最后一个示例中，我们处理了两个表之间的关系。 建立外键。

`orm_foreign_key.py`

```py
#!/usr/bin/python
# -*- coding: utf-8 -*-

from sqlalchemy import create_engine, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker, relationship

eng = create_engine('sqlite:///test.db')

Base = declarative_base()

class Author(Base):
    __tablename__ = "Authors"

    AuthorId = Column(Integer, primary_key=True)
    Name = Column(String)  
    Books = relationship("Book")

class Book(Base):
    __tablename__ = "Books"

    BookId = Column(Integer, primary_key=True)
    Title = Column(String)      
    AuthorId = Column(Integer, ForeignKey("Authors.AuthorId"))    

    Author = relationship("Author")                           

Session = sessionmaker(bind=eng)
ses = Session()   

res = ses.query(Author).filter(Author.Name=="Leo Tolstoy").first()

for book in res.Books:
    print book.Title

res = ses.query(Book).filter(Book.Title=="Emma").first()    
print res.Author.Name

```

我们有`Author`和`Book`类，它们映射到`Authors`和`Books`数据库表。 （用于创建表的 SQL 在第一章中列出）。 在两个表之间实现了外键约束。 外键由`ForeignKey`类型和`relationship()`函数定义。

```py
Books = relationship("Book")

```

在两个类之间建立了一对多的关系。 `relationship()`函数的第一个参数是与之建立关系的类的名称。 结果，作者对象将具有`Books`属性。

```py
AuthorId = Column(Integer, ForeignKey("Authors.AuthorId"))

```

`Book`类的`AuthorId`是外键。 它由`ForeignKey`类型定义。 它引用`Authors`表中的`AuthorId`列。

```py
Author = relationship("Author")

```

此行为`Book`类创建`Author`属性。

```py
res = ses.query(Author).filter(Author.Name=="Leo Tolstoy").first()

```

在此查询中，我们获得了列夫·托尔斯泰（Leo Tolstoy）撰写的所有书籍。 `filter()`方法对查询应用过滤条件。 `first()`方法获取作者对象。

```py
for book in res.Books:
    print book.Title

```

我们浏览结果集并打印所有检索到的书。 `Books`属性是使用`relationship()`函数创建的。

```py
res = ses.query(Book).filter(Book.Title=="Emma").first()    
print res.Author.Name

```

该查询返回`Emma`标题的作者。 该查询返回具有内置`Author`属性的`book`对象。

```py
$ ./orm_foreign_key.py 
War and Peace
Anna Karenia
Jane Austen

```

这是示例的输出。

在 SQLAlchemy 教程的这一部分中，我们使用了 SQLAlchemy 的 ORM。