Python 3+SQLAlchemy+Sqlite 3 ORM教程を実現します。


一、Sqlite 3、SQLAlchemy取付
Sqlite 3はPython 3の標準ライブラリです。別にインストールする必要はありません。SQLAlchemyだけをインストールすればいいです。本論文のsqlalchemyバージョンは1.2.12です。

pip install sqlalchemy

二、ORM操作
第一歩エンジンを作る時の接続URLが違っていますが、他の操作はmysqlなどのデータベースとsqliteは同じです。
2.1データベース接続フォーマットの説明を作成する
sqliteでデータベース接続を作るということはデータベースを作ることです。他のmysqlなどはデータベースが存在してこそデータベース接続を作ることができます。データベース接続を作成する本文では、データベースエンジンの確立と呼ばれることがあります。
2.1.1 sqlite作成データベース接続
相対パス形式で、現在のディレクトリの下でデータベース形式を作成します。

# sqlite://<nohostname>/<path>
# where <path> is relative:
engine = create_engine('sqlite:///foo.db')
絶対パスでデータベースを作成します。フォーマットは以下の通りです。

#Unix/Mac - 4 initial slashes in total
engine = create_engine('sqlite:////absolute/path/to/foo.db')
#Windows
engine = create_engine('sqlite:///C:\\path\\to\\foo.db')
#Windows alternative using raw string
engine = create_engine(r'sqlite:///C:\path\to\foo.db')
sqliteはメモリデータベースを作成できます。フォーマットは以下の通りです。

# format 1
engine = create_engine('sqlite://')
# format 2
engine = create_engine('sqlite:///:memory:', echo=True)
2.1.2他のデータベースからデータベース接続を作成する
PostgreSQL:

# default
engine = create_engine('postgresql://scott:tiger@localhost/mydatabase')
# psycopg2
engine = create_engine('postgresql+psycopg2://scott:tiger@localhost/mydatabase')
# pg8000
engine = create_engine('postgresql+pg8000://scott:tiger@localhost/mydatabase')
MySQL:

# default
engine = create_engine('mysql://scott:tiger@localhost/foo')
# mysql-python
engine = create_engine('mysql+mysqldb://scott:tiger@localhost/foo')
# MySQL-connector-python
engine = create_engine('mysql+mysqlconnector://scott:tiger@localhost/foo')
# OurSQL
engine = create_engine('mysql+oursql://scott:tiger@localhost/foo')
Oracle:

engine = create_engine('oracle://scott:[email protected]:1521/sidname')
engine = create_engine('oracle+cx_oracle://scott:tiger@tnsname')
MSSQL:

# pyodbc
engine = create_engine('mssql+pyodbc://scott:tiger@mydsn')
# pymssql
engine = create_engine('mssql+pymssql://scott:tiger@hostname:port/dbname')
2.2データベース接続の作成
私たちは現在のディレクトリの下でfoo.dbを作成することを例にして、次の各ステップはこのデータベースを使用します。
create_engineには二つのものが追加されました。一つはecho=Tureです。一つはcheckです。same_thread=False。
echo=Ture---echoはデフォルトではFalseで、印刷しないで実行するSQL文などの詳細な実行情報を表し、Ture表示に変えて印刷させます。
checksame_thread=False--s-sqliteデフォルトで作成されたオブジェクトは、そのオブジェクトを作成するスレッドのみを使用することができますが、sqlalchemyはマルチスレッドですので、check(u)を指定する必要があります。same_thread=Falseは、作成したオブジェクトの任意のスレッドを使用することができます。そうしないと、時々エラーが発生します。sqlalchemy.exc.Program mingErrer:(sqlite 3.Programe mingErrror)SQLite Object created in CT a thread can only be same thread.The object was created in thread id id 3560 and.seris。id,users.name AS users_name,users.fullname AS users_fullname、users.password AS users_password FROM users WHERE users.name=?LIMITOFFISET?'[]parameters:[{}(Background on this error at):http://sqlalche.me/e/f405)

from sqlalchemy import create_engine

engine = create_engine('sqlite:///foo.db?check_same_thread=False', echo=True)

2.3マッピングを定義する
まず基本的なマッピングクラスを作成して、後は本物のマッピングクラスを継承します。

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

その後、本格的なマッピングクラスを作成します。ここではUserマッピングクラスを例にとって、usersテーブルにマッピングするように設定します。
まず、ORMでは一般的に下表が先に存在する必要がなく、むしろクラスと表の対応のためにマッピングクラスを介して作成されることを明確にする。もちろん表はもう存在してもいいです。次の結び目の中で自分で決められます。表が存在する時はどうやって操作すればいいですか?既存の表を使いますが、既存の表を使うと、クラスの変数名と表の各フィールド名が一致します。

from sqlalchemy import Column, Integer, String

#      User,         Base
class User(Base):
  #        users 
  __tablename__ = 'users'
  #             ,          extend_existing  True,             
  #       ,sqlalchemy        
  # __table_args__ = {'extend_existing': True}
  #             (datebase)       (schema),   schema          
  # __table_args__ = {'schema': 'test_database'}
  
  #                 ,                   
  #      ,                 ,     String(64),      String(32)
  #              ,                       
  # sqlalchemy                 ,                 ,                primary_key=True
  #                primary_key,             ,sqlalchemy                       
  #   id   id  ; id     ,   ,    (             )
  id = Column(Integer, primary_key=True, autoincrement=True)
  #   name   name  ; name        ,
  name = Column(String(20))
  fullname = Column(String(32))
  password = Column(String(32))

  # __repr__            print()       ,         
  def __repr__(self):
    return "<User(name='%s', fullname='%s', password='%s')>" % (
          self.name, self.fullname, self.password)

上での定義は私が言う。tablename me_.属性は書いて死んだものですが、外部からクラスに名前を伝えたい場合があります。この場合は以下のような融通のきく方法で実現できます。

def get_dynamic_table_name_class(table_name):
  #        
  class TestModel(Base):
    #      
    __tablename__ = table_name
    __table_args__ = {'extend_existing': True}

    username = Column(String(32), primary_key=True)
    password = Column(String(32))
  #             
  return TestModel
2.4データテーブルの作成

#         
User.__table__

#      。     engine      ,            Base        
# checkfirst=True,               ,            。      True
Base.metadata.create_all(engine, checkfirst=True)

#        engine             Base      ,                    
#       tables      ,        
# Base.metadata.create_all(engine,tables=[Base.metadata.tables['users']],checkfirst=True)
#       model         ,                  ,             
# User.__table__.create(engine, checkfirst=True)

#                ,                  ,         
#             ,       ,                

上の議論からもわかるように、モデルを定義してからモデルに基づいてデータテーブルを作成してもいいです。既存のテーブルによって自動的にモデルコードを生成してもいいです。答えは可能です。sqlacodegenを使ってください。
sqlacodegenインストール操作は以下の通りです。

#      ,  pip  
pip install sqlacodegen

#       ,           pip  sqlacodegen     
pip download sqlacodegen
#               pip  ,         。                
pip install sqlacodegen-2.1.0-py2.py3-none-any.whl
sqlacodegen生成model操作は以下の通りです。

# linux      /usr/local/bin/sqlacodegen
# mysql+pymysql  
#    --tables     model  ,           model
#    --outfile          ,       stdout
#           sqlacodegen      class,         Table()      
#        -h  
sqlacodegen mysql+pymysql://user:password@localhost/dbname [--tables table_name1,table_name2] [--outfile model.py]
私の模範的な動作は以下の通りです。指定表のためにモデルを生成することに成功しました。

2.5セッションの確立
添削操作はsessionを使って操作する必要があります。

from sqlalchemy.orm import sessionmaker

# engine 2.2      
Session = sessionmaker(bind=engine)

#   Session   
session = Session()

2.6増(users表に記録を挿入)

#   User   
ed_user = User(name='ed', fullname='Ed Jones', password='edspassword')

#        users 
session.add(ed_user)

#           
session.add_all(
  [User(name='wendy', fullname='Wendy Williams', password='foobar'),
  User(name='mary', fullname='Mary Contrary', password='xxg527'),
  User(name='fred', fullname='Fred Flinstone', password='blah')]
)

#        session ,    commit           
session.commit()

2.7検索(users表の記録を調べる)
2.7.1調査実現
queryはselect xxx fromのxxx部分に変わります。filter/filter_。by where部分に変えます。limit/order by/group byそれぞれlimit()/order_に対応します。by()/group_by()方法。この言葉は非常に重要です。理解してから、あなたはsqlを大量に減らします。
filter_byはwhere部分に相当し、外はfilterでも使用できます。彼らの違いはfilter_です。byパラメータの書き方はsql形式に似ています。filterパラメータはpython形式です。
もっと適切な書き方を見てください。https://docs.sqlalchemy.org/en/13/orm/tutorial.html#common-filter-operators

our_user = session.query(User).filter_by(name='ed').first()

our_user

#   ed_user     our_user        
ed_user is our_user

#        
#              ,               
# session.query(User.name).filter_by(name='ed').all()
# like  
# session.query(User).filter(User.name.like("ed%")).all()
#     
# session.query(User).filter(User.name.op("regexp")("^ed")).all()
#     
# session.query(User).filter(User.name.like("ed%")).count()
#          
#  count()  ,    func.func_name()    ,func_name            
# from sqlalchemy import func
# session.query(func.count(User3.name)).one()
#          
# column_name = "name"
# session.query(User).filter(User3.__table__.columns[column_name].like("ed%")).all()
#      sql  
#          all()/one()/first()     ,        ,            sql  ,        
# from sqlalchemy.dialects import mysql
# sql_obj = session.query(User).filter_by(name='ed')
# sql_command = sql_obj.statement.compile(dialect=mysql.dialect(), compile_kwargs={"literal_binds": True})
# sql_result = sql_obj.all()

また、このリンクCommon Filter Operators節にはequalsのquery.filter(User.name=='ed')という形がありますが、実際に使う時はsession.query.filter(User.name='ed')という形に変えなければなりません。
2.7.2パラメータ伝達問題
上のsqlはそのままour_です。user=session.query.filter_by(name='ed').first()形式ですが、実際にはUser部分とname='ed'という部分がパラメータを通して伝わってきますので、パラメータを使って渡す時は以下の2つの問題に注意してください。
まず、パラメータは引用符でくくらないでください。例えば、以下のような形でエラーが発生しました。sqlalchemy.exc.OperationalErr:(sqlite 3.OperationalErr)のsuch columnを間違えました。

table_and_column_name = "User"
filter = "name='ed'"

our_user = session.query(table_and_column_name).filter_by(filter).first()
第二に、等号パラメータがあるには変換形式が必要である。以下のように引用符を外してテーブルにセットします。and_コロムン.nameは大丈夫ですが、filter=(name='ed)という表記はpythonでは許されません。

table_and_column_name = User
#            
filter = (name='ed')

our_user = session.query(table_and_column_name).filter_by(filter).first()
パラメータに等号を付帯するという形は、今考えられるのはfilterを使ってfilterを代替することだけです。by sql文中の=号がpython文中の==に変わります。正しい書き方は以下の通りです。

table_and_column_name = User
filter = (User.name=='ed')

our_user = session.query(table_and_column_name).filter(filter).first()
2.8変更(users表の記録を修正)

#             
mod_user = session.query(User).filter_by(name='ed').first()

#  ed        modify_paswd
mod_user.password = 'modify_passwd'

#     
session.commit()

#        ,                ,       update   
#           ,        update  
# session.query(User).filter_by(name='ed').update({User.password: 'modify_passwd'})
# session.commit()
#   schema             
#     update/delete    synchronize_session=False        
# session.query(User).filter_by(User.name=User1.name).update({User.password: User2.password}, synchronize_session=False)
#   schema      schema     
#      schema   ,    model     __table_args__ = {'schema': 'test_database'}         schema

2.9削除(usersテーブルのレコードを削除)

#             
del_user = session.query(User).filter_by(name='ed').first()

#     ,          
del_user

#  ed      
session.delete(del_user)

#     
session.commit()

#     ,  ed    
for user in session.query(User):
  print(user)

#       ,      ,    mysql         ,       delete     
#           ,        delete  
# session.query(User).filter_by(name='ed').first().delete()

2.10 SQL文を直接実行する
フレーム規定形式を使用しても、各データベースのSQL差異はある程度解決できますが、例えば前の2つのレコードを取得する前に各データベース形式は以下の通りです。

# mssql/access
select top 2 * from table_name;

# mysql
select * from table_name limit 2;

# oracle
select * from table_name where rownum <= 2;
しかし、フレームメモリーは各データベースSQLの違いを解消すると同時に各フレームのCRUDの差異を導入します。開発者は往々にして一定のSQLの基礎があります。もし一つのフレームが強制的にユーザーに指定されたCRUD形式しか使えないなら、かえってユーザーの学習コストを増加させます。このフレームは成功の枠組みになりません。フレーム設定ではなくSQLを直接実行するCRUDは励ましの操作ではないが、人目につかない行為として扱われるべきではない。

#    SQL  
sql = "select * from users"

# sqlalchemy  execute      SQL
records = session.execute(sql)
Python 3 SQLAlchemy Sqlite 3に関する教程については、下記の関連リンクをご覧ください。