本文整理自网络,侵删。
一、Sqlite3、SQLAlchemy安装
Sqlite3是Python3标准库不需要另外安装,只需要安装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:tiger@127.0.0.1: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表示让其打印。
check_same_thread=False----sqlite默认建立的对象只能让建立该对象的线程使用,而sqlalchemy是多线程的所以我们需要指定check_same_thread=False来让建立的对象任意线程都可使用。否则不时就会报错:sqlalchemy.exc.ProgrammingError: (sqlite3.ProgrammingError) SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 35608 and this is thread id 34024. [SQL: 'SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password \nFROM users \nWHERE users.name = ?\n LIMIT ? OFFSET ?'] [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__属性是写死的,但有时我们可能想通过外部给类传递表名,此时可以通过以下变通的方法来实现:
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) # 另外我们说这一步的作用是创建表,当我们已经确定表已经在数据库中存在时,我完可以跳过这一步 # 针对已存放有关键数据的表,或大家共用的表,直接不写这创建代码更让人心里踏实
从上边的讨论可以知道,我们可以定义model然后根据model来创建数据表(当然也可以不创建),那可不可以反过来根据已有的表来自动生成model代码呢,答案是可以的,使用sqlacodegen。
相关阅读 >>
navicat for Sqlite导入csv中文数据的方法
将 ghost 从 Sqlite3 数据库迁移到 mysql 数据库
更多相关阅读请进入《Sqlite》频道 >>
数据库系统概念 第6版
本书主要讲述了数据模型、基于对象的数据库和XML、数据存储和查询、事务管理、体系结构等方面的内容。