zoukankan      html  css  js  c++  java
  • 转载--SqlAlchemy ORM 学习

    转载原文地址:http://blog.csdn.net/yueguanghaidao/article/details/7485345,http://blog.csdn.net/yueguanghaidao/article/details/7485967

    --------------------------第一部分--------------------------

    一直觉得orm很高深,今天时间比较充分打算学习下

    安装说一点,我的系统是win7 64位家庭普通版

    用easy_install安装sqlalchemy出现问题,于是下载最新版0.7.6的zip压缩包,用python setup.py install出现错误:

    WARNING: The C extension could not be compiled, speedups are not enabled.
    Plain-Python build succeeded.

    自己未解决,向广大朋友们请教了

    没办法又下了0.6.8版的zip包,这次安装成功了。

    开始我的sqlalchemy之旅

    本文主要是翻译这篇文章(还有自己的操作和思考,我会竟可能做到有图有真相,呵呵):点击打开链接:如果有错请指正

    http://www.blog.pythonlibrary.org/2010/02/03/another-step-by-step-sqlalchemy-tutorial-part-1-of-2/

    创建我们第一个脚本:

            我们将利用SqlAlchemy创建我们第一个例子,创建一个简单的表,存储用户的姓名,年龄和密码

    from sqlalchemy import create_engine
    from sqlalchemy import MetaData, Column, Table, ForeignKey
    from sqlalchemy import Integer, String
    engine = create_engine('sqlite:///tutorial.db',echo=True)
    metadata = MetaData(bind=engine)
    users_table = Table('users', metadata,
                        Column('id', Integer, primary_key=True),
                        Column('name', String(40)),
                        Column('age', Integer),
                        Column('password', String),
                        )
    addresses_table = Table('addresses', metadata,
                            Column('id', Integer, primary_key=True),
                            Column('user_id', None, ForeignKey('users.id')),
                            Column('email_address', String, nullable=False)
                            )
    # create tables in database
    metadata.create_all()

    深入看下代码:
    首先需要导入一些包,然后我们创建一个数据库连接engine对象。在这里我们使用sqlite数据库,不需要提供认证信息。

    你肯定注意到了,我们将echo设置为True,目的是SqlAlchemy将会把执行sql命令的过程输出到标准输出。这主要是便于调试,但如果是用于生产环境中应该设置为False。

    然后我们创建一个MetaData对象,这个对象包含了数据库的所有元数据。它由表的描述信息和其它的数据库schema-level 的对象组成。我们可以在创建MetaData对象时就把它绑定到我们的数据库,也可以在上面代码的最后即 create_all 时绑定。在第二部分有示例(点击打开链接)

    下面部分就是创建表的过程。这是由SqlAlchemy的表对象和列对象完成的。我们可以使用各种字段类型,如String,Integer等等。在这里,我们创建一个名为users的表,然后传给了metadata对象。然后我们创建了4个字段,id设置为主键,当我们向这个表中增加用户呢时,SqlAlchemy 会自动将id加一(自增)。name字段设置为String类型,40个字符长度。age字段是简单的Integer类型,password字段也被设置为String类型,而且我们没有设置长度。addresses_table与users表的主要不同就是我们设置了外键属性用来联系两张表。

    最后一个片段实际上创建了数据库和表。每当你调用create_all()时,在创建表之前会检查表是否存在。也就是说,你可以创建额外的表,但当你调用create_all时SqlAlchemy

    仅仅会创建新的表。

    Tips:

    SqlAlchemy 也提供了装载已经创建的表的方法:

    someTable = Table("users", metadata, autoload=True, schema="schemaName")

    插入数据:

    有几种不同的方式插入数据和查询数据。我们先考虑低级的方式,然后再考虑这个系列的其它部分,我们将进入稍微抽象的Sessions和Declarative样式。

    让我们一起看看插入数据的几种不同方法吧。

    # create an Insert object
    ins = users_table.insert()
    # add values to the Insert object
    new_user = ins.values(name="Joe", age=20, password="pass")
     
    # create a database connection
    conn = engine.connect()
    # add user to database by executing SQL
    conn.execute(new_user)

    上面的代码展示了如何利用连接对象插入数据的方式。首先,你需要调用表的insert()方法创建一个Insert对象,然后你就可以用Insert的values()方法增加一行你所需要的数据

    。然后我们创建了Connection对象,最后对Insert对象调用Connection对象的execute方法。这听起来有点复杂,其实很简单。

     检验一下吧:我是用sqlite.exe打开tutorial.db

    我们发现,插入成功了,而且id字段自动设为1,符合我们定义的刚才对primary_key的解释。

    下面的片段展示了一些没有用Connection对象的插入数据方法:

    # a connectionless way to Insert a user
    ins = users_table.insert()
    result = engine.execute(ins, name="Shinji", age=15, password="nihongo")
     
    # another connectionless Insert
    result = users_table.insert().execute(name="Martha", age=45, password="dingbat")

    我们检验一下成功了没有?

    有没有发现id的确是自增的?

    在上面的两个列子中,你都需要调用表对象的insert方法。在插入方法的最后,我们看一下如何插入多行数据。

    conn.execute(users_table.insert(), [
        {"name": "Ted", "age":10, "password":"dink"},
        {"name": "Asahina", "age":25, "password":"nippon"},
        {"name": "Evan", "age":40, "password":"macaca"}
    ])

    这已经相当明白了,要点就是你需要先前创建的Connection对象,并传递两个参数。

    效果如下:

    现在,我们来看看查询操作。

    SqlAlchemy为查询操作提供了一组方法。这里,我们将关注简单的方法。

    最常见的例子就是做一个全查询,我们开始吧

    from sqlalchemy.sql import select
    s = select([users_table])
    result = s.execute()
    for row in result:
        print row

    结果如下:

    首先我们导入select方法,然后我们把表作为一个元素的列表传入,最后我们调用select对象的execute方法,并把结果赋给了result变量,最后就是迭代了。

    如果你需要所有的结果存储在一个由元组组成的列表而不是行对象,你可以这么做。

    # get all the results in a list of tuples
    conn = engine.connect()
    res = conn.execute(s)
    rows = res.fetchall()

    结果如下:

    如果你只需要第一条记录返回,你可以用fetchone()代替fetchall():

    res = conn.execute(s)
    row = res.fetchone()

    现在假设我们需要更小粒度的结果,下面,我们仅仅想返回name和age,而不要密码。

    s = select([users_table.c.name, users_table.c.age])
    result = conn.execute(s)
    for row in result:
        print row

    结果如下:

    很好,这是相当的简单。我们所需要做的只是在select语句中明确列名。那个'c'基本意思是:column.

    如果你有多个表,也许select语句应该有点像这样

    select([tableOne, tableTwo])

    当然这很可能返回重复的结果,因此你为了解决这个问题,你将这么做:

    s = select([tableOne, tableTwo], tableOne.c.id==tableTwo.c.user_id)

    SqlAlchemy文档中,称第一种结果集为笛卡尔积。上诉第二种语句消除了那些烦恼。怎么消除的?在这种查询的方式中使用了where子句是一种解决办法。在下面部分,我将用sessions展示一种与where查询子句不同的方法。

    下面是一些示列,注释中有解释

    from sqlalchemy.sql import and_
    # The following is the equivalent to 
    # SELECT * FROM users WHERE id > 3
    s = select([users_table], users_table.c.id > 3)
    # You can use the "and_" module to AND multiple fields together
    s = select(and_(users_table.c.name=="Martha", users_table.c.age < 25))

    --------------------------第二部分--------------------------

    在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='profx@dc.com'),
                            Address(email_address='xavier@yahoo.com')]

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

    # change the first address
    prof.addresses[0].email_address = "profx@marvel.com"

    现在,让我们来做一些join查询:

    for u, a in session.query(User, Address).filter(User.id==Address.user_id).filter(Address.email_address=='xavier@yahoo.com').all():
        print u, a

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

    sql = session.query(User, Address)
    sql = sql.filter(User.id==Address.user_id)
    sql = sql.filter(Address.email_address=='xavier@yahoo.com')
     
    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=='xavier@yahoo.com').all()

    这和上面两个示例做的是一样的事,但更加的明确。

  • 相关阅读:
    大数据基础1
    java之MySQL的使用
    java反射
    java多线程
    java异常
    指针综合
    指向函数的指针变量做函数的参数
    指向函数的指针
    字符串指针法赋值
    字符串冒泡排序和折半查找
  • 原文地址:https://www.cnblogs.com/mzpy1119/p/5394224.html
Copyright © 2011-2022 走看看