1. 单连接模式
peewee通过Database
类及其子类来建立数据库对象,目前peewee支持的数据库有Postgres, MySQL,MySQL,SQLite 以及BerkeleyDB。这里我们仅通过MySQL来进行了解,如果对其他数据库类型的相关内容感兴趣可以参见。
MySQLDatabase
类来初始化数据库对象,这是Database
的一个子类,继承了父类的构造函数: class Database(database[, threadlocals=True[, autocommit=True[, fields=None[, ops=None[, autorollback=False[, use_speedups=True[, **connect_kwargs]]]]]]])
这里
database
指数据库的名称, 字符串格式threadlocals
指 是否用本地线程保存连接autocommit
自动提交fields
添加orm中的数据类型到数据库中数据类型的映射ops
定义操作符映射autorollback
执行数据库请求时出错则自动回滚use_speedups
用Cpython speedup模块来加速某些queriesconnect_kwargs
数据库驱动中初始化所用的key 另外, MySQLDatabase类还添加了以下选项:
commit_select = True compound_operations = ['UNION'] for_update = True subquery_delete_same_table = False peewee中的mysql驱动有两个和,但是从源码中可以看到peewee是更倾向于使用MySQLdb的,只有当import MySQLdb as mysql
报错后才会开始引用import pymysql as mysql
。这里举例介绍一些常用的connect_kwargs,如果需要详细了解,可以参见源程序中的connections.connection类,里面的注释已经写得非常清晰了。例如: #! /usr/bin/env python# coding: utf-8from peewee import *db = MySQLDatabase( database = 'test',# string passwd = 'test', # string user = 'test', # string host = 'localhost', # string port = 3306, # int, 可不写)
官方文档中建议使用数据库前先调用其db.connect()
函数,这个函数功能上并不是必须的,但是可以帮助定位错误。
mysql://user:passwd@ip:port/my_db
这在中也有介绍 2 实时数据库实例化
Database
类中的database字段可以先设置为None, 数据库的实例化可以在随后得到数据可靠名字之后,具体操作如下:
db = MySQLDatabase(None)# 此时若是调用db.connect()则会报错# 数据库的名字db_name = 'mydb'db.init( database = 'mydb', host = 'test', user = 'test', passwd = 'test')
3 动态实例化
peewee甚至允许数据库的动态实例化,这里用到了代理模式:
# 创建数据库的代理db_proxy = Proxy() # 使用代理数据库创建表class BaseModel(Model): class Meta: database = db_proxy class User(BaseModel): username = CharField()# 根据配置条件来选择不同的数据库if app.config['DEBUG']: db= SqliteDatabase('local.db')elif app.config['TESTING']: db= SqliteDatabase(':memory:')else: db= PostgresqlDatabase('mega_production_db')# 通过代理来根据配置条件将选取的数据库初始化database_proxy.initialize(db)
4 连接池
peewee为通过一个'PooledDatabase'基类Mysql, sqlite 和Postgres 提供了连接池的功能。这个基类的定义如下:
class PooledDatabase(database[, max_connections=20[, stale_timeout=None[, **kwargs]]])
其中:max_connections
定义了最大连接数stale_timeout
定义了每个连接最大可用时间 具体它的MySQL子类为:class PooledMySQLDatabase
#! /usr/bin/env python# coding: utf-8# 1. 导入peewee的模块from peewee import *from datetime import datetime# 2. 建立数据库实例db = MySQLDatabase( database = 'test', host = 'localhost', port = 3306, user = 'test', passwd = 'test', charset = 'utf8' ) ######################################################################## 3. 建立数据表的模型# 4. 先建立基本模型,具体的模型在此基础上继承而来class BaseModel(Model): class Meta: # 指定表所在的数据库 database = dbclass User(BaseModel): username = CharField(unique=True)class Tweet(BaseModel): user = ForeignKeyField(User, related_name='tweets') message = TextField() created_date = DateTimeField(default=datetime.now) is_published = BooleanField(default=True)#########################################################################if __name__ == '__main__': try: # connect方法不是必须的,但是如果出错可以判断是否是在连接时出的错 db.connect() # 5. 创建表,这里有了safe=True选项,会在创建表之前先检查数据库里表是否已经存在,由于创建表的语句往往只需要使用一次,一般建议写入类或者方法中通过具体命令来调用 # 注意:peewee里面创建表有两个方法, create_tables是`Database`中的方法,创建表时会建立表之间的关系和表的索引,使用`somedb.create_tables([Models], safe=False)`来调用 # create_table是`Model`中的方法,仅仅创建表本身,而不包含索引和表之间的关系,使用`somemodel.create_table(safe=False)`来调用 db.create_tables([User, Tweet], safe=True) # 6. 断开连接 db.close() except Exception, e: print e
首先,需要理清一下python数据与数据库数据是怎么对应的:
- Model类:表
- Field类:表上的列的类型
- Model实例:表上的一行数据
1. Model Class
主要描述的是数据表以及其继承的顺序
这里主要是需要对Model类中的Meta类的定义规范进行小结:
- 当Meta类建立后,不应该通过
mymodel.Meta
来进行访问,而应该使用mymodel._meta
, 这是一个ModelOption
类的实例, 可以通过它获取model和Meta的一些属性 - Meta的可用属性有: 除了
db_table
和table_alias
之外,其他的字段均可以被继承 可以看到,peewee的主键,索引和其他约束均可以在meta中定义。 - 主键的定义
class mymodel1(Model): name = CharField(max_length=200, primary_key=True) #将该字段设为主键class mymodel2(Model): name = CharField(max_length=200) Meta: # 如果要不使用主键 primary_key = Falseclass mymodel3(Model): blog = ForeignKeyField(Blog) tag = ForeignKeyField(Tag) class Meta: primary_key = CompositeKey('blog', 'tag') # primary_key实际上是CompositeKey的一个实例当主键为非
auto_increment
时,插入数据调用save()会出错,应该调用save(force_insert=True)
- 添加索引 单列索引 多列索引
2. Field Class
主要描述python数据类型与数据库数据类型的对应关系和数据表之间的关系(外键,多对多)
Filed在peewee.py中有基本数据类型的定义
# 初始化函数,各个字段的默认值def __init__(self, null=False, index=False, unique=False, verbose_name=None, help_text=None, db_column=None, default=None, choices=None, primary_key=False, sequence=None, constraints=None, schema=None)
在playhouse.fields中定义的额外字段
包括
PasswordField ManyToManyField CompressedField PickledField AESEncryptedField
然而peewee中并不推荐使用这些Field, 例如ManyToManyField,完全可以用一个含两个外键的关系表来维护,ManyToMany关系在ORM中实际上也正是这样来实现的,并且使用这种方式可以描述更加复杂的关系。
自定义field字段
# 1. 定义MyField类class MyField(Field): # Field的标签 db_field = 'my' # 返回数据库类型的值 def db_value(value): pass # 返回python类型的值 def python_value(value): pass# 2. 将自定义Field子类与数据表中的列关联,有两种方法:# 1. 创建数据库时,给fields字段赋值db = MySQLDatabase('my_db', fields={'my': 'my'})# 2. 调用register_fields函数db.register_fields({'my':'my'})
Model
的初始化方法主要继承自BaseModel
类的__new__方法。 下面粗略的通过源码来看看:
class BaseModel(type): # 定义可被继承的属性列表(全局) inheritable = set([ 'constraints', 'database', 'db_table_func', 'indexes', 'order_by', 'primary_key', 'schema', 'validate_backrefs', 'only_save_dirty']) def __new__(cls, name, bases, attrs): # 如果没有父类,则之间创建BaseModel类 if not bases: return super(BaseModel, cls).__new__(cls, name, bases, attrs) # Meta类的属性通过meta_options存储在Model类中 meta_options = {} # 将Meta从属性中移除,将Meta中的非私有属性加入meta_options中 meta = attrs.pop('Meta', None) if meta: for k, v in meta.__dict__.items(): if not k.startswith('_'): meta_options[k] = v # 从meta中获取主键信息 model_pk = getattr(meta, 'primary_key', None) parent_pk = None ############################################################ # 开始考虑从父类中继承的情况 ############################################################# for b in bases: if not hasattr(b, '_meta'): continue base_meta = getattr(b, '_meta') if parent_pk is None: parent_pk = deepcopy(base_meta.primary_key) all_inheritable = cls.inheritable | base_meta._additional_keys # 获取父类中的Meta内部类字段,只考虑all_inheritable中的字段 for (k, v) in base_meta.__dict__.items(): if k in all_inheritable and k not in meta_options: meta_options[k] = v # 获取父类中的Fields, 即表的字段 for (k, v) in b.__dict__.items(): if k in attrs: continue if isinstance(v, FieldDescriptor): if not v.field.primary_key: attrs[k] = deepcopy(v.field) # initialize the new class and set the magic attributes cls = super(BaseModel, cls).__new__(cls, name, bases, attrs) ModelOptionsBase = meta_options.get('model_options_base', ModelOptions) cls._meta = ModelOptionsBase(cls, **meta_options) cls._data = None cls._meta.indexes = list(cls._meta.indexes) # 默认表名的设定,Model名的小写,然后将非数字和英文字符换成'_' if not cls._meta.db_table: cls._meta.db_table = re.sub('[^\w]+', '_', cls.__name__.lower()) # replace fields with field descriptors, calling the add_to_class hook # 这里筛选attr中的Field类型字段,设置Model中的数据类型 # 也许可以测试一下类里面的函数是怎么继承的 fields = [] for name, attr in cls.__dict__.items(): if isinstance(attr, Field): if attr.primary_key and model_pk: raise ValueError('primary key is overdetermined.') elif attr.primary_key: model_pk, pk_name = attr, name else: fields.append((attr, name)) composite_key = False # 默认主键的设置,如果无法从父类继承,,则使用'id'为key, 也就是行号 if model_pk is None: if parent_pk: model_pk, pk_name = parent_pk, parent_pk.name else: model_pk, pk_name = PrimaryKeyField(primary_key=True), 'id' elif isinstance(model_pk, CompositeKey): pk_name = '_composite_key' composite_key = True # 如果model本身有主键的情况 if model_pk is not False: model_pk.add_to_class(cls, pk_name) cls._meta.primary_key = model_pk cls._meta.auto_increment = ( isinstance(model_pk, PrimaryKeyField) or bool(model_pk.sequence)) cls._meta.composite_key = composite_key # 设置Fields for field, name in fields: field.add_to_class(cls, name) # create a repr and error class before finalizing # __unicode__的设置 if hasattr(cls, '__unicode__'): setattr(cls, '__repr__', lambda self: '<%s: %r>' % ( cls.__name__, self.__unicode__())) # 错误信息 exc_name = '%sDoesNotExist' % cls.__name__ exc_attrs = {'__module__': cls.__module__} exception_class = type(exc_name, (DoesNotExist,), exc_attrs) cls.DoesNotExist = exception_class cls._meta.prepared() if hasattr(cls, 'validate_model'): cls.validate_model() return cls