Step-by-Step SqlAlchemy Tutorial (part 2 of 2)

在Step-by-Step SqlAlchemy Tutorial的第一部分(点击打开链接),我们调用SqlAlchemy的SQL表达式与数据库交互。在我们开始更高级和更加抽象的方法之前,我们需要学习更加抽象的方式做

事情。这就像许多数学课程一样,比如微积分,在你知道捷径之前,你需要很长时间去学习发现一些微积分的标准偏差。

在第二部分,我们将用简单的方法去应用SqlAlchemy。它被称为‘关系对象’方法,SqlAlchemy的官方文档实际上也从这里开始。

适应数据映射:

这为什么叫数据映射呢?因为我们将把数据库的数据映射到Python的类。让我们开始吧!

from sqlalchemy import create_engine
from sqlalchemy import Column, MetaData, Table
from sqlalchemy import Integer, String, ForeignKey
from sqlalchemy.orm import mapper, sessionmaker
 
####################################################
class User(object):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self, name, fullname, password):
        """Constructor"""
        self.name = name
        self.fullname = fullname
        self.password = password
 
    def __repr__(self):
        return "<User('%s','%s', '%s')>" % (self.name, self.fullname, self.password)
 
# create a connection to a sqlite database
# turn echo on to see the auto-generated SQL
engine = create_engine("sqlite:///tutorial.db", echo=True)
 
# this is used to keep track of tables and their attributes
metadata = MetaData()
users_table = Table('users', metadata,
                    Column('user_id', Integer, primary_key=True),
                    Column('name', String),
                    Column('fullname', String),
                    Column('password', String)
                    )
email_table = Table('email', metadata,
                    Column('email_id', Integer, primary_key=True),
                    Column('email_address', String),
                    Column('user_id', Integer, ForeignKey('users.user_id'))
                    )
 
# create the table and tell it to create it in the 
# database engine that is passed
metadata.create_all(engine)
 
# create a mapping between the users_table and the User class
mapper(User, users_table)

我们注意到,与我们先前例子相比第一个不同就是User类。我们把最初的例子(见第一部分)稍微改变了一下,即现在的参数,name,full name和password。其余的部分都一样,仅仅最后多了mapper语句。这种方便的方法把User类映射到了user_table这个表。这看起来没什么大不了,但我们向数据库增加用户会变得更加简单明了。

然而在我们开始之前,我们需要讨论声明构型样式。虽然这个样式给了我们更加粒度的控制我们的表,映射和类,但大部分我们不需要那么的复杂。声明样式使得配置变得相当简单。我所知道的第一个SqlAlchemy附加的声明样式叫做Elixir.SqlAlchemy内嵌的声明样式没有Elixir那么强大,但是它很方便,因为你不需要安装额外的东西。那就让我们看看声明是怎样的与众不同吧。

from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import backref, mapper, relation, sessionmaker
 
Base = declarative_base()
 
########################################################################
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):
        """Constructor"""
        self.name = name
        self.fullname = fullname
        self.password = password
 
    def __repr__(self):
        return "<User('%s','%s', '%s')>" % (self.name, self.fullname, self.password)
 
########################################################################
class Address(Base):
    """
    Address Class
 
    Create some class properties before initilization
    """
    __tablename__ = "addresses"
    id = Column(Integer, primary_key=True)
    email_address = Column(String, nullable=False)
    user_id = Column(Integer, ForeignKey('users.id'))
 
    # creates a bidirectional relationship
    # from Address to User it's Many-to-One
    # from User to Address it's One-to-Many
    user = relation(User, backref=backref('addresses', order_by=id))
 
    #----------------------------------------------------------------------
    def __init__(self, email_address):
        """Constructor"""
        self.email_address = email_address
 
    def __repr__(self):
        return "<Address('%s')>" % self.email_address
 
 
# create a connection to a sqlite database
# turn echo on to see the auto-generated SQL
engine = create_engine("sqlite:///tutorial.db", echo=True)
 
# get a handle on the table object
users_table = User.__table__
# get a handle on the metadata
metadata = Base.metadata
metadata.create_all(engine)

如上所示,几乎所有的都在类中创建了。我们创建了类属性(就像类的全局变量)用来标识表的列。然后创建了初始化函数__init__,和上例差不多。当然,类继承的是declarative_base ,而不是基本的object。如果需要一个表的对象,我们需要调用魔力方法User.__table__。为了获得元数据,我们需要调用基类的Base.metadata.现在我们所关心的都解决了。

这里需要提一点的是,还记得在Step-by-Step SqlAlchemy Tutorial (part 1 of 2)中我在解释metadata = MetaData(bind=engine)时说过,“也可以在上面代码的最后即 create_all 时绑定",这一次是不是在create_all时绑定的。

下面我们看看如何向表中插入数据。


Class is now in Session

利用对象关系方法 之美在于可以用一小段简短的代码与数据库交互。让我们看看如何创建一行。

mike_user = User("mike", "Mike Driscoll", "password")
print "User name: %s, fullname: %s, password: %s" % (mike_user.name,
                                                     mike_user.fullname,
                                                     mike_user.password)

如上所示,我们用User类创建一个用户,我么可以使用.访问属性。你甚至还可以用来更新数据。例如,如果你需要改变用户对象,你可以这么做:

# this is how you would change the name field
mike_user.fullname = "Mike Dryskull"

当然上面所示的并不会向表中插入一行数据。为此我们需要一个Session对象才能完成插入数据。下面我们就开始Session的基本使用吧!

from sqlalchemy.orm import sessionmaker

Session = sessionmaker(bind=engine)
session = Session()
 
mike_user = User("mike", "Mike Driscoll", "password")
session.add(mike_user)

我们在这里停一下,解释上面到底发生了什么?

首先我们需要导入sessionmaker,然后把它绑定到engine.然后我们创建一个session实例,然后我们实例化一个user对象,并把它传给session的add方法。

到此,没有任何的SQL代码被运行,这个事物也是被挂起的。

这时我们查看一下数据库:


我们发现没有数据,为了保留这一行数据,我们需要调用session.commit()方法提交事物。

session.commit()
我们再查看一下:


如果你需要增加多个用户,你可以这么做:

session.add_all([
     User('Mary', 'Mary Wonka', 'foobar'),
     User('Sue', 'Sue Lawhead', 'xxg527'),
     User('Fay', 'Fay Ray', 'blah')])

如果在你提交事物后碰巧改变了其中一个用户的属性,你可以使用session.dirty去校验那个是被修改过的。如果你仅仅想知道有哪些行处在挂起中,你可以调用session.new.最后我们可以使用session.rollback()去回滚一个事物。

下面让我们看一下一些查询示例:

# do a Select all
all_users = session.query(User).all()
 
# Select just one user by the name of "mike"
our_user = session.query(User).filter_by(name='mike').first()
print our_user
 
# select users that match "Mary" or "Fay"
users = session.query(User).filter(User.name.in_(['Mary', 'Fay'])).all()
print users
 
# select all and print out all the results sorted by id
for instance in session.query(User).order_by(User.id):
    print instance.name, instance.fullname

下面我们将讨论joins话题

Join in the Fun

joins的SQL表达式语法这里就不说了,当然我们将利用对象关系方法来示例。如果你回头看看我们创建表的示例,你将注意到我们已经用外键对象来使用join了。声明格式像下面所示:

user_id = Column(Integer, ForeignKey('users.id'))
 
# creates a bidirectional relationship
# from Address to User it's Many-to-One
# from User to Address it's One-to-Many
user = relation(User, backref=backref('addresses', order_by=id))

我们通过创建一个新用户来看看这是怎么工作的。

prof = User("Prof", "Prof. Xavier", "fudge")
prof.addresses
由于外键和backref命令,user对象有个address属性。如果你运行上面的代码,你会发现prof.address是一个空值。让我们增加一些address吧!

prof.addresses = [Address(email_address='[email protected]'),
                        Address(email_address='[email protected]')]
看,这是多么的简单啊?同样也很简单从中获取数据。例如,如果你想取出第一个地址,你可以这样访问:prof.addresses[0]
假如现在你需要修改地址,这是易如反掌。

# change the first address
prof.addresses[0].email_address = "[email protected]"


现在,让我们来做一些join查询:
for u, a in session.query(User, Address).filter(User.id==Address.user_id).filter(Address.email_address=='[email protected]').all():
    print u, a

这是一个相当长的查询!我发现这对我来说比较困难,所以我经常这么做:

sql = session.query(User, Address)
sql = sql.filter(User.id==Address.user_id)
sql = sql.filter(Address.email_address=='[email protected]')
 
for u, a in sql.all():
    print u, a

如果你喜欢一行式的方式,第一个示例并没有什么不妥,两种方法产生相同的结果集。

我很幸运的发现更高版本的调试更加简单。最后,我还可以用一下真正的join:

from sqlalchemy.orm import join
session.query(User).select_from(join(User, Address)).filter(Address.email_address=='[email protected]').all()
这和上面两个示例做的是一样的事,但更加的明确。

 


你可能感兴趣的:(Step-by-Step SqlAlchemy Tutorial (part 2 of 2))