PythonプログラムとFlaskフレームの中でSQLAlchemyの教程を使います。


各地を渡り歩く
いつか、プログラマーはSQLを恐れて、慎重にsqlを書いています。心の中ではいつもパニックが避けられません。もしsql文を間違えたら、データベースを壊したらどうすればいいですか?また、いくつかのデータを取得するために、内外左右の接続、関数格納プロセスなどがあります。疑いのないことです。これらを理解しないと、どうしてもねじれてしまいます。いつか穴に飛び込み、「毎日」と叫んで答えないかもしれません。
ORMの出現によって、SQLを恐れた開発者は、穴の中にはっていくロープを見ました。空はそんなに暗くないようです。少なくとも暗くて、私たちも目があります。名前の通り、ORMオブジェクト関係のマッピングは、簡単に言えば、データベースの一つずつのテーブルをプログラミング言語のクラスにマッピングすることです。
pythonには有名なORMの枠组みがたくさんあります。大名トップのSQLAlchemyはpythonの世界では譲れないORMの枠组みです。江湖の中でpeeweee、strom、pyorm、SQLObjectはそれぞれ乱れていますが、結局はSQLAlchemyが群雄を傲視しています。
SQLAlchemy概要
SQLAlchemyは二つの部分に分けられています。一つはORMに対するオブジェクトマッピング、もう一つはコアSQL exprestionです。最初はよく分かりました。純粋なORMです。これはORMではなく、DBAPIのパッケージです。もちろん、sqlを直接書くのを避けて、sql表現を通しています。SQLAlchemyを使うと、三つの方法に分けられます。
  • sql expressionを使ってSQLAlchemyの方法でsql表現を書きます。簡単なのはsql
  • を書きます。
  • .raw sqlを使って直接sqlを書きます。
  • ORMを使って直接書きません。
    本稿ではまずSQLAlchemyのsql exprestion部分の使い方を検討する。主に公式のSQL Expression Language Tutorialに従って紹介します。
    なぜsql exprestionを勉強しますか?直接ORMに行きません。後ろの二つはormの基礎ですから。また、ormを使用しないで、後の2つは作業をうまく行うことができ、コードの可読性がより良いです。純粋にSQLAlchemyをdbapiとして使っています。まずSQLAlchemy内にデータベース接続池を建設し、接続操作に関する煩わしい処理を解決しました。次に、便利で強力なロゴ機能を提供します。最後に、複雑な検索文は、単純なORMでは実現しにくいです。
    実戦
    データベースを接続
    まずsqlalchemyライブラリを導入して、データベース接続を確立します。ここでmysqlを使います。createを通じて(u)engine方法で行う
    
    from sqlalchemy import create_engine
    engine = create_engine("mysql://root:@localhost:3306/webpy?charset=utf8",encoding="utf-8", echo=True)
    
    クリアードengineメソッドはデータベース接続を行い、dbオブジェクトを返します。中のパラメータ表示
    データベースの種類://ユーザー名:パスワード(パスワードがないと空です。書かない)@データベースのホストアドレス/データベース名?コーディング
    echo=Trueはコンソールのロゴを便利にするためにsql情報を出力します。デフォルトはFalseです。
    このengineオブジェクトによって直接executeで照会することができ、例えばengine.execute(「SELECT*FROM user」)は、engineを介してクエリーに接続されているものを取得することもでき、例えばconn=engine.co nnect()は、conn.execute()方法によって照会することができる。両者の違いは何ですか?
    engineのexecuteを直接使用してsqlを実行する方式をconnnectionless実行といいます。
    engine.co nnectによってconnを取得し、connを通じてsqlを実行することをconnectionといいます。
    主な違いはtranspationモードを使うかどうかです。transactionに関係しないなら、2つの方法は同じです。公式サイトでは後者を推奨しています。
    定義テーブル
    データテーブルを定義してこそ、sql表式の操作ができます。結局sql表式の表の確定は、sqlalchemyによって制定されました。もしデータベースにデータテーブルが存在したら、まだ定義が必要ですか?もちろん、ここはマッピング関係です。指定しないと、クエリー表現はそのテーブルに付加された操作であることが分かりません。もちろん定義するときは、テーブル名とフィールド名に注意してください。コードとデータは一致していなければなりません。定義したら、データテーブルが作成されます。作成したコードを再実行したら、データベースは作成されません。
    
    # -*- coding: utf-8 -*-
    __author__ = 'ghost'
    
    from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, ForeignKey
    #       
    engine = create_engine("mysql://root:@localhost:3306/webpy?charset=utf8",encoding="utf-8", echo=True)
    #      
    metadata = MetaData()
    #    
    user = Table('user', metadata,
      Column('id', Integer, primary_key=True),
      Column('name', String(20)),
      Column('fullname', String(40)),
     )
    
    address = Table('address', metadata,
      Column('id', Integer, primary_key=True),
      Column('user_id', None, ForeignKey('user.id')),
      Column('email', String(60), nullable=False)
     )
    #      ,       ,   
    metadata.create_all(engine)
    #        
    conn = engine.connect()
    
    
    インサート
    データテーブルと接続先があれば、データベース操作に対応するのが簡単です。
    
    >>> i = user.insert() #     
    >>> i 
    <sqlalchemy.sql.dml.Insert object at 0x0000000002637748>
    >>> print i #      sql  
    INSERT INTO "user" (id, name, fullname) VALUES (:id, :name, :fullname)
    >>> u = dict(name='jack', fullname='jack Jone')
    >>> r = conn.execute(i, **u) #     ,        ,              ,          ,            
    >>> r
    <sqlalchemy.engine.result.ResultProxy object at 0x0000000002EF9390>
    >>> r.inserted_primary_key #          id
    [4L]
    >>> addresses
    [{'user_id': 1, 'email': '[email protected]'}, {'user_id': 1, 'email': '[email protected]'}, {'user_id': 2, 'email': '[email protected]'}, {'user_id': 2, 'email': '[email protected]'}]
    >>> i = address.insert()
    >>> r = conn.execute(i, addresses) #       
    >>> r
    <sqlalchemy.engine.result.ResultProxy object at 0x0000000002EB5080>
    >>> r.rowcount #       
    4L
    
    >>> i = user.insert().values(name='tom', fullname='tom Jim')
    >>> i.compile()
    <sqlalchemy.sql.compiler.SQLCompiler object at 0x0000000002F6F390>
    >>> print i.compile()
    INSERT INTO "user" (name, fullname) VALUES (:name, :fullname)
    >>> print i.compile().params
    {'fullname': 'tom Jim', 'name': 'tom'}
    >>> r = conn.execute(i)
    >>> r.rowcount
    1L
    
    
    selectを調べる
    検索方法はとても柔軟で、sqlalchemy.sql以下のselect方法を使うことが多いです。
    
    >>> s = select([user]) #    user 
    >>> s
    <sqlalchemy.sql.selectable.Select at 0x25a7748; Select object>
    >>> print s
    SELECT "user".id, "user".name, "user".fullname 
    FROM "user"
    
    ユーザー定義のフィールドを検索する必要がある場合は、例えば、userのcloumnオブジェクトを使用します。
    
    >>> user.c #   user    column  
    <sqlalchemy.sql.base.ImmutableColumnCollection object at 0x0000000002E804A8>
    >>> print user.c
    ['user.id', 'user.name', 'user.fullname']
    >>> s = select([user.c.name,user.c.fullname])
    >>> r = conn.execute(s)
    >>> r
    <sqlalchemy.engine.result.ResultProxy object at 0x00000000025A7748>
    >>> r.rowcount #      
    5L
    >>> ru = r.fetchall() 
    >>> ru
    [(u'hello', u'hello world'), (u'Jack', u'Jack Jone'), (u'Jack', u'Jack Jone'), (u'jack', u'jack Jone'), (u'tom', u'tom Jim')]
    >>> r 
    <sqlalchemy.engine.result.ResultProxy object at 0x00000000025A7748>
    >>> r.closed #    r.fetchall()   ,       ResultProxy   
    True
    
    二つの表を同時に調べます。
    
    >>> s = select([user.c.name, address.c.user_id]).where(user.c.id==address.c.user_id) #              
    >>> s
    <sqlalchemy.sql.selectable.Select at 0x2f03390; Select object>
    >>> print s
    SELECT "user".name, address.user_id 
    FROM "user", address 
    WHERE "user".id = address.user_id
    
    オペレータ
    
    >>> print user.c.id == address.c.user_id #           
    "user".id = address.user_id
    >>> print user.c.id == 7
    "user".id = :id_1 #         sql        
    >>> print user.c.id != 7
    "user".id != :id_1
    >>> print user.c.id > 7
    "user".id > :id_1
    >>> print user.c.id == None
    "user".id IS NULL
    >>> print user.c.id + address.c.id #           +
    "user".id + address.id
    >>> print user.c.name + address.c.email #            ||
    "user".name || address.email
    
    操作接続
    ここの接続とは、条件照会時の論理演算子の接続、すなわち、and orとnotのことです。
    
    >>> print and_(
      user.c.name.like('j%'),
      user.c.id == address.c.user_id,
      or_(
       address.c.email == '[email protected]',
       address.c.email == '[email protected]'
      ),
      not_(user.c.id>5))
    "user".name LIKE :name_1 AND "user".id = address.user_id AND (address.email = :email_1 OR address.email = :email_2) AND "user".id <= :id_1
    >>> 
    
    得られた結果はコンパイルされたsql文のセグメントです。
    
    >>> se_sql = [(user.c.fullname +", " + address.c.email).label('title')]
    >>> wh_sql = and_(
        user.c.id == address.c.user_id,
        user.c.name.between('m', 'z'),
        or_(
         address.c.email.like('%@aol.com'),
         address.c.email.like('%@msn.com')
        )
       )
    >>> print wh_sql
    "user".id = address.user_id AND "user".name BETWEEN :name_1 AND :name_2 AND (address.email LIKE :email_1 OR address.email LIKE :email_2)
    >>> s = select(se_sql).where(wh_sql)
    >>> print s
    SELECT "user".fullname || :fullname_1 || address.email AS title 
    FROM "user", address 
    WHERE "user".id = address.user_id AND "user".name BETWEEN :name_1 AND :name_2 AND (address.email LIKE :email_1 OR address.email LIKE :email_2)
    >>> r = conn.execute(s)
    >>> r.fetchall()
    
    raw sql方式を使う
    担当のsql文に出会ったときは、sqlalchemy.sqlの下のtext関数を使うことができます。文字列のsql文を包装してexecute実行に必要なsqlオブジェクトにコンパイルします。例えば:、
    
    >>> text_sql = "SELECT id, name, fullname FROM user WHERE id=:id" #   sql  ,   ( :value)  
    >>> s = text(text_sql)
    >>> print s
    SELECT id, name, fullname FROM user WHERE id=:id
    >>> s
    <sqlalchemy.sql.elements.TextClause object at 0x0000000002587668>
    >>> conn.execute(s, id=3).fetchall() # id=3   :id  
    [(3L, u'Jack', u'Jack Jone')]
    
    ジョインを接続
    joinとoutejoinの二つの方法があります。joinには二つのパラメータがあります。一つ目はジョンの表、二つ目はonの条件です。jingの後はselect(u)に協力しなければなりません。from方法:
    
    >>> print user.join(address)
    "user" JOIN address ON "user".id = address.user_id #         ,  join       on   
    >>> print user.join(address, address.c.user_id==user.c.id) #      on   
    "user" JOIN address ON address.user_id = "user".id
    
    >>> s = select([user.c.name, address.c.email]).select_from(user.join(address, user.c.id==address.c.user_id)) #  jion sql      select_from    
    
    
    >>> s
    <sqlalchemy.sql.selectable.Select at 0x2eb63c8; Select object>
    >>> print s
    SELECT "user".name, address.email 
    FROM "user" JOIN address ON "user".id = address.user_id
    >>> conn.execute(s).fetchall()
    [(u'hello', u'[email protected]'), (u'hello', u'[email protected]'), (u'hello', u'[email protected]'), (u'hello', u'[email protected]'), (u'Jack', u'[email protected]'), (u'Jack', u'[email protected]'), (u'Jack', u'[email protected]'), (u'Jack', u'[email protected]')]
    
    
    グループを並べ替える
    並べ替えにはorder_を使用しますbyメソッドは、グループ化はグループ化されます。by、改ページはもちろんlimitとoffsetの二つの方法で協力します。
    
    >>> s = select([user.c.name]).order_by(user.c.name) # order_by
    >>> print s
    SELECT "user".name 
    FROM "user" ORDER BY "user".name
    >>> s = select([user]).order_by(user.c.name.desc())
    >>> print s
    SELECT "user".id, "user".name, "user".fullname 
    FROM "user" ORDER BY "user".name DESC
    >>> s = select([user]).group_by(user.c.name)  # group_by
    >>> print s
    SELECT "user".id, "user".name, "user".fullname 
    FROM "user" GROUP BY "user".name
    >>> s = select([user]).order_by(user.c.name.desc()).limit(1).offset(3) # limit(1).offset(3)
    >>> print s
    SELECT "user".id, "user".name, "user".fullname 
    FROM "user" ORDER BY "user".name DESC
     LIMIT :param_1 OFFSET :param_2
    [(4L, u'jack', u'jack Jone')]
    
    アップデート
    前は全部いくつかの検索です。更新と挿入の方法は似ています。全部表の下の方法です。違いは、udateがもう一つのwhere方法を使ってフィルタリングを選択します。
    
    >>> s = user.update()
    >>> print s
    UPDATE "user" SET id=:id, name=:name, fullname=:fullname
    >>> s = user.update().values(fullname=user.c.name)   # values         
    >>> print s
    UPDATE "user" SET fullname="user".name
    >>> s = user.update().where(user.c.name == 'jack').values(name='ed') # where       
    >>> print s 
    UPDATE "user" SET name=:name WHERE "user".name = :name_1
    >>> r = conn.execute(s)
    >>> print r.rowcount   #     
    3
    
    もう一つの高級な使い方があります。一つの命令で複数の記録の更新を行います。
    
    >>> s = user.update().where(user.c.name==bindparam('oldname')).values(name=bindparam('newname')) # oldname               ,newname   
    >>> print s
    UPDATE "user" SET name=:newname WHERE "user".name = :oldname
    >>> u = [{'oldname':'hello', 'newname':'edd'},
    {'oldname':'ed', 'newname':'mary'},
    {'oldname':'tom', 'newname':'jake'}]
    >>> r = conn.execute(s, u)
    >>> r.rowcount
    5L
    
    deleteを削除
    削除は比較的簡単で、delete方法を呼び出してもいいです。whereフィルタをかけないと、すべてのデータは削除されます。しかし、dropが表に落ちないので、データテーブルが空になったのと同じです。
    
    >>> r = conn.execute(address.delete()) #    
    >>> print r
    <sqlalchemy.engine.result.ResultProxy object at 0x0000000002EAF550>
    >>> r.rowcount
    8L
    >>> r = conn.execute(users.delete().where(users.c.name > 'm')) #     
    >>> r.rowcount
    3L
    
    
    flak-sqlalchemy
    SQLAlchemyはすでにpython世界の中のormの標準になりました。flashkは軽いウェブフレームで、ormを自由に使えます。flak-sqlalchemyはflashkのために指定されたプラグインです。
    flashk-sqlalchemyをインストールします。
    
    pip install flask-sqlalchemy
    
    初期化sqlalchemy
    
    from flask import Flask
    from flask.ext.sqlalchemy import SQLAlchemy
    
    
    app = Flask(__name__)
    
    #     dialect+driver://username:password@host:port/database?charset=utf8
    #    sqlalchemy      ://      :  @    :  /   ?  
    app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:@localhost:3306/sqlalchemy?charset=utf8'
    #    
    db = SQLAlchemy(app)
    
    
    定義モデル
    
    class User(db.Model):
     """        ,       model   
     """
     id = db.Column(db.Integer, primary_key=True)
     username = db.Column(db.String(80), unique=True)
     email = db.Column(db.String(120), unique=True)
    
     def __init__(self, username, email):
      self.username = username
      self.email = email
    
     def __repr__(self):
      return '<User %r>' % self.username
    
     def save(self):
      db.session.add(self)
      db.session.commit()
    
    
    データテーブルの作成
    データパッケージの作成はsqlalchemyアプリを使っています。テーブルが既に存在している場合は無視します。存在しない場合は、新規に作成します。
    
    >>> from yourapp import db, User
    >>> u = User(username='admin', email='[email protected]') #     
    >>> db.session.add(u)          #   session
    >>> db.session.commit()         #     
    >>> users = User.query.all()        #   
    
    中国語を挿入するには、unicode文字列を挿入しなければなりません。
    
    >>> u = User(username=u'   ', email='[email protected]')
    >>> u.save()
    
    関係を定義
    関係データベースで一番重要なのは関係です。通常の関係は1対1(例えば、無限級欄)、1対複数(文章と欄)、多対多(文章とラベル)に分けられます。
    one to may:
    私たちはCategory(欄)とPost(文章)を定義します。両者は一対の多い関係で、一つの欄には多くの文章があり、一つの文章は一つの欄に属しています。
    
    class Category(db.Model):
     id = db.Column(db.Integer, primary_key=True)
     name = db.Column(db.String(50))
    
     def __init__(self, name):
      self.name = name
    
     def __repr__(self):
      return '<Category %r>' % self.name
    
    class Post(db.Model):
     """        ,    id,title,body,pub_date,category_id
     """
     id = db.Column(db.Integer, primary_key=True)
     title = db.Column(db.String(80))
     body = db.Column(db.Text)
     pub_date = db.Column(db.String(20))
     #        
     category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
     #     ,           
     # backref     ,     Category  backref(post_set)  Post
     category = db.relationship('Category', backref=db.backref('post_set', lazy='dynamic'))
    
    
     def __init__(self, title, body, category, pub_date=None):
      self.title = title
      self.body = body
      if pub_date is None:
       pub_date = time.time()
      self.pub_date = pub_date
      self.category = category
    
     def __repr__(self):
      return '<Post %r>' % self.title
    
     def save(self):
      db.session.add(self)
      db.session.commit()
    
    
    検索はどうやって使いますか?
    
    >>> c = Category(name='Python')
    >>> c
    <Category 'Python'>
    >>> c.post_set
    <sqlalchemy.orm.dynamic.AppenderBaseQuery object at 0x0000000003B58F60>
    >>> c.post_set.all()
    []
    >>> p = Post(title='hello python', body='python is cool', category=c)
    >>> p.save()
    >>> c.post_set
    <sqlalchemy.orm.dynamic.AppenderBaseQuery object at 0x0000000003B73710>
    >>> c.post_set.all() #     
    [<Post u'hello python'>]
    >>> p
    <Post u'hello python'>
    >>> p.category
    <Category u'Python'>
    #      category_id      
    >>> p = Post(title='hello flask', body='flask is cool', category_id=1)
    >>> p.save()
    
    many to many(コメントはすでに指摘されています。このようなやり方は関連して削除することができません。簡単な本は線のフォーマットを削除していません。多く例に対して廃棄します。ここで注意してください。誤解されないように。)
    複数のペアの複数の関係については、2つのモデルのIDを定義する他のテーブル、例えばPostとTagの間に複数のペアがあり、Post_を定義する必要がある。Tagの時計
    
    post_tag = db.Table('post_tag',
         db.Column('post_id', db.Integer, db.ForeignKey('post.id')),
         db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'))
        )
    
    class Post(db.Model):
    
     id = db.Column(db.Integer, primary_key=True)
     # ...   
     #         ,tag     post_set    post   
     tags = db.relationship('Tag', secondary=post_tag,
           backref=db.backref('post_set', lazy='dynamic'))
    
    class Tag(db.Model):
     id = db.Column(db.Integer, primary_key=True)
     content = db.Column(db.String(10), unique=True)
     #       
     posts = db.relationship('Post', secondary=post_tag,
           backref=db.backref('tag_set', lazy='dynamic'))
    
     def __init__(self, content):
      self.content = content
    
     def save(self):
      db.session.add(self)
      db.session.commit()
    
    
    クエリー:
    
    >>> tag_list = []
    >>> tags = ['python', 'flask', 'ruby', 'rails']
    >>> for tag in tags:
      t = Tag(tag)
      tag_list.append(t)
    >>> tag_list
    [<f_sqlalchemy.Tag object at 0x0000000003B7CF28>, <f_sqlalchemy.Tag object at 0x0000000003B7CF98>, <f_sqlalchemy.Tag object at 0x0000000003B7CEB8>, <f_sqlalchemy.Tag object at 0x0000000003B7CE80>]
    >>> p
    <Post u'hello python'>
    >>> p.tags
    []
    >>> p.tags = tag_list #         
    >>> p.save()
    >>> p.tags
    [<f_sqlalchemy.Tag object at 0x0000000003B7CF28>, <f_sqlalchemy.Tag object at 0x0000000003B7CF98>, <f_sqlalchemy.Tag object at 0x0000000003B7CEB8>, <f_sqlalchemy.Tag object at 0x0000000003B7CE80>]
    >>> p.tag_set   #     
    <sqlalchemy.orm.dynamic.AppenderBaseQuery object at 0x0000000003B7C080>
    >>> p.tag_set.all()
    [<f_sqlalchemy.Tag object at 0x0000000003B7CF28>, <f_sqlalchemy.Tag object at 0x0000000003B7CF98>, <f_sqlalchemy.Tag object at 0x0000000003B7CEB8>, <f_sqlalchemy.Tag object at 0x0000000003B7CE80>]
    >>> t = Tag.query.all()[1]
    >>> t
    <f_sqlalchemy.Tag object at 0x0000000003B7CF28>
    >>> t.content
    u'python'
    >>> t.posts
    [<Post u'hello python'>]
    >>> t.post_set
    <sqlalchemy.orm.dynamic.AppenderBaseQuery object at 0x0000000003B7C358>
    >>> t.post_set.all()
    [<Post u'hello python'>]
    self one to one
    
    自分の一対一もよく使う需要です。例えば、無限の等級付けの欄です。
    
    class Category(db.Model):
     id = db.Column(db.Integer, primary_key=True)
     name = db.Column(db.String(50))
     #    id
     pid = db.Column(db.Integer, db.ForeignKey('category.id'))
     #      
     pcategory = db.relationship('Category', uselist=False, remote_side=[id], backref=db.backref('scategory', uselist=False))
    
     def __init__(self, name, pcategory=None):
      self.name = name
      self.pcategory = pcategory
    
     def __repr__(self):
      return '<Category %r>' % self.name
    
     def save(self):
      db.session.add(self)
      db.session.commit()
    
    
    クエリー:
    
    >>> p = Category('Python')
    >>> p
    <Category 'Python'>
    >>> p.pid
    >>> p.pcategory #      
    >>> p.scategory #      
    >>> f = Category('Flask', p)
    >>> f.save()
    >>> f
    <Category u'Flask'>
    >>> f.pid
    1L
    >>> f.pcategory #      
    <Category u'Python'>
    >>> f.scategory #      
    >>> p.scategory #      
    <Category u'Flask'>
    
    flash-sqlalchemy定義modelsの簡単な応用についてはこれだけです。もっと多い技術はどうやって調べますか?