終於來到sora開發準備的最後部分python
peewee 管理數據庫mysql
1.明確地管理數據庫鏈接(也能夠考慮使用鏈接池)linux
database = SqliteDatabase('my_app.db')sql
def before_request_handler():數據庫
database.connect()app
def after_request_handler():post
database.close()測試
務必明確地處理數據庫鏈接ui
2.在表類中設置內部類,以使用數據庫this
class MyModel(Model):
some_field = CharField()
class Meta:
database = database
最佳實踐:定義一個基類指定要鏈接的數據庫,其餘的子類繼承該類定義表格
database = SqliteDatabase('my_app.db')
class BaseModel(Model):
class Meta:
database = database
class User(BaseModel):
username = CharField()
class Tweet(BaseModel):
user = ForeignKeyField(User, related_name='tweets')
message = TextField()
# etc, etc
注意:在建表時不用建BaseModel表,只需處理User和Tweet
3.鏈接數據庫(不一樣的數據庫用不一樣的類)
mysql:
mysql_db = MySQLDatabase('my_database')
或者:
db = MySQLDatabase('my_db',host='localhost',port=3306,user='root',password='root')
class BaseModel(Model):
"""A base model that will use our MySQL database"""
class Meta:
database = mysql_db
class User(BaseModel):
username = CharField()
# etc, etc
若是使用url的話,須要這樣操做:
import os
from peewee import *
from playhouse.db_url import connect
# Connect to the database URL defined in the environment, falling
# back to a local Sqlite database if no database URL is specified.
db = connect(os.environ.get('DATABASE') or 'sqlite:///default.db')
class BaseModel(Model):
class Meta:
database = db
url範例:
mysql://user:passwd@ip:3306/my_db
4.運行時修改數據庫配置(根據運行參數修改要鏈接的數據庫)
database = SqliteDatabase(None) # Un-initialized database.
class SomeModel(Model):
class Meta:
database = database
>>> database.connect()
Exception: Error, database not properly initialized before opening connection
database_name = raw_input('What is the name of the db? ')
database.init(database_name, host='localhost', user='postgres')
使用init修改配置
5.動態修改數據庫(使用proxy)
動態地換用不一樣的數據庫(sqlite,mysql,postgreSQL)
database_proxy = Proxy() # Create a proxy for our db.
class BaseModel(Model):
class Meta:
database = database_proxy # Use proxy for our DB.
class User(BaseModel):
username = CharField()
# Based on configuration, use a different database.
if app.config['DEBUG']:
database = SqliteDatabase('local.db')
elif app.config['TESTING']:
database = SqliteDatabase(':memory:')
else:
database = PostgresqlDatabase('mega_production_db')
# Configure our proxy to use the db we specified in config.
database_proxy.initialize(database)
若是隻是修改運行時的參數而非改變數據庫類型,建議使用第四點的方法
6.鏈接池
鏈接池支持:
一、超時以後,鏈接將被回收
二、能夠設置鏈接數的上限
from playhouse.pool import PooledPostgresqlExtDatabase
db = PooledPostgresqlExtDatabase(
'my_database',
max_connections=8,
stale_timeout=300,
user='postgres')
class BaseModel(Model):
class Meta:
database = db
Mysql相關的模塊是:PooledMySQLDatabase
7.訪問數據副本
使用ReadSlaveModel
from peewee import *
from playhouse.read_slave import ReadSlaveModel
# Declare a master and two read-replicas.
master = PostgresqlDatabase('master')
replica_1 = PostgresqlDatabase('replica', host='192.168.1.2')
replica_2 = PostgresqlDatabase('replica', host='192.168.1.3')
class BaseModel(ReadSlaveModel):
class Meta:
database = master
read_slaves = (replica_1, replica_2)
class User(BaseModel):
username = CharField()
注意:查詢會在多個從數據庫上以循環的方式執行
8.修改表的schema
經過playhouse的migrator
from playhouse.migrate import *
my_db = SqliteDatabase('my_database.db')
migrator = SqliteMigrator(my_db)
title_field = CharField(default='')
status_field = IntegerField(null=True)
with my_db.transaction():
migrate(
migrator.add_column('some_table', 'title', title_field),
migrator.add_column('some_table', 'status', status_field),
migrator.drop_column('some_table', 'old_column'),
)
更多內容要參考Schema Migrations部分的文檔
9.從現有數據庫生成相應的模塊
使用pwiz實現
python -m pwiz -e mysql -H localhost -u root -P root user > md.py
生成的md.py以下
from peewee import *
database = MySQLDatabase('user', **{'host': 'localhost', 'password': 'root', 'user': 'root'})
class UnknownField(object):
pass
class BaseModel(Model):
class Meta:
database = database
class Infos(BaseModel):
name = CharField(null=True)
sex = CharField(null=True)
users = CharField(db_column='users_id', null=True)
class Meta:
db_table = 'infos'
class Usersinfo(BaseModel):
description = TextField(null=True)
user = IntegerField(db_column='user_id', null=True)
user_name = CharField(null=True)
user_password = CharField(null=True)
class Meta:
db_table = 'usersinfo'
該數據庫的其中一個表:
mysql> desc infos;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| sex | varchar(20) | YES | | NULL | |
| name | char(20) | YES | | NULL | |
| users_id | varchar(40) | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
3 rows in set (0.01 sec)
10.model與fields
對應關係:
在peewee中,一個繼承model類的類定義爲某個數據庫的一個表格
類中的feild,爲數據庫的列(字段)
一個model類實例則是一行
用例:
from peewee import *
db = SqliteDatabase('my_app.db')
class BaseModel(Model):
class Meta:
database = db
class User(BaseModel):
username = CharField(unique=True)
class Tweet(BaseModel):
user = ForeignKeyField(User, related_name='tweets')
message = TextField()
created_date = DateTimeField(default=datetime.datetime.now)
is_published = BooleanField(default=True)
不一樣的field與數據類型對照:
Field Type Sqlite Postgresql MySQL
CharField varchar varchar varchar
TextField text text longtext
DateTimeField datetime timestamp datetime
IntegerField integer integer integer
BooleanField smallint boolean bool
FloatField real real real
DoubleField real double precision double precision
BigIntegerField integer bigint bigint
DecimalField decimal numeric numeric
PrimaryKeyField integer serial integer
ForeignKeyField integer integer integer
DateField date date date
TimeField time time time
BlobField blob bytea blob
UUIDField not supported uuid not supported
field的初始化參數:
null = False – boolean indicating whether null values are allowed to be stored
index = False – boolean indicating whether to create an index on this column
unique = False – boolean indicating whether to create a unique index on this column. See also adding composite indexes.
verbose_name = None – string representing the 「user-friendly」 name of this field
help_text = None – string representing any helpful text for this field
db_column = None – string representing the underlying column to use if different, useful for legacy databases
default = None – any value to use as a default for uninitialized models
choices = None – an optional iterable containing 2-tuples of value, display
primary_key = False – whether this field is the primary key for the table
sequence = None – sequence to populate field (if backend supports it)
constraints = None - a list of one or more constraints, e.g. [Check('price > 0')]
schema = None – optional name of the schema to use, if your db supports this.
有關DateTimeField, DateField and TimeField:
DateField有以下特性:
year
month
day
TimeField:
hour
minute
second
而DateTimeField有以上所有特性
簡單用例:
# Get the current time.
now = datetime.datetime.now()
# Get days that have events for the current month.
Event.select(Event.event_date.day.alias('day')).where(
(Event.event_date.year == now.year) &
(Event.event_date.month == now.month))
11.建立model tables
首次使用某類時,須要執行建立表格的操做:
# Connect to our database.
db.connect() #建議明確地控制鏈接
# Create the tables.
db.create_tables([User, Tweet])
能夠有條件地建立表格:
# Only create the tables if they do not exist.
db.create_tables([User, Tweet], safe=True)
12.Model的options和Metadata
若是想訪問某個表格類的meta,應該這樣作:
>>> Person.Meta
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'Preson' has no attribute 'Meta'
>>> Person._meta
<peewee.ModelOptions object at 0x7f51a2f03790>
查看ModelOptions:
>>> Person._meta.fields
{'id': <peewee.PrimaryKeyField object at 0x7f51a2e92750>, 'name': <peewee.CharField object at 0x7f51a2f0a510>}
>>> Person._meta.primary_key
<peewee.PrimaryKeyField object at 0x7f51a2e92750>
>>> Person._meta.database
<peewee.SqliteDatabase object at 0x7f519bff6dd0>
ModelOptions有以下成分:
Option Meaning Inheritable?
database database for model yes
db_table name of the table to store data no
indexes a list of fields to index yes
order_by a list of fields to use for default ordering yes
primary_key a CompositeKey instance yes
table_alias an alias to use for the table in queries no
schema the database schema for the model yes
validate_backrefs ensure backrefs do not conflict with other attributes. yes
簡單用例:
>>> db = SqliteDatabase(':memory:')
>>> class ModelOne(Model):
... class Meta:
... database = db
... db_table = 'model_one_tbl'
...
>>> class ModelTwo(ModelOne):
... pass
...
>>> ModelOne._meta.database is ModelTwo._meta.database
True
>>> ModelOne._meta.db_table == ModelTwo._meta.db_table
False
12.索引和惟一約束
簡單用例:
class User(Model):
username = CharField(unique=True)
email = CharField(index=True)
或者經過Meta來設置(第二個布爾型參數決定了是否建立惟一約束):
class Transaction(Model):
from_acct = CharField()
to_acct = CharField()
amount = DecimalField()
date = DateTimeField()
class Meta:
indexes = (
# create a unique on from/to/date
(('from_acct', 'to_acct', 'date'), True),
# create a non-unique on from/to
(('from_acct', 'to_acct'), False),
)
我的測試:
root@workgroup0:~# python
Python 2.7.6 (default, Mar 22 2014, 22:59:56)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from peewee import *
>>> db = MySQLDatabase('vmrecord',host='localhost',port=3306,user='root',password='root')
>>> class Performance(Model):
... vm_id = IntegerField(unique=True)
... desc = CharField(index=True)
... class Meta:
... database = db
...
>>> db.connect()
>>> db.create_tables([Performance,])
>>> for i in Performance.select():
... print i.vm_id
...
123
124
>>> try:
... a.save
... except IntegrityError:
... print 'error'
...
<bound method Performance.save of <__main__.Performance object at 0x7f047311a750>>
>>> try:
... a.save()
... except IntegrityError as e:
... print e
...
(1062, "Duplicate entry '123' for key 'performance_vm_id'")
>>>
能夠看到unique constraints已經起了做用
>>> b = Performance(vm_id='125',desc='use 10%')
>>> try:
... b.save()
... except IntegrityError as error:
... print error
... else:
... print 'success'
...
1
success
mysql> select * from performance;
+----+-------+---------+
| id | vm_id | desc |
+----+-------+---------+
| 1 | 123 | use 40% |
| 2 | 124 | use 40% |
| 5 | 125 | use 10% |
+----+-------+---------+
3 rows in set (0.01 sec)
13.複合主鍵
設置meta的primary key有多個,簡單用例:
class BlogToTag(Model):
"""A simple "through" table for many-to-many relationship."""
blog = ForeignKeyField(Blog)
tag = ForeignKeyField(Tag)
class Meta:
primary_key = CompositeKey('blog', 'tag')
14.手動指定主鍵
你能夠設置Meta中(用class._meta訪問)的auto_increment爲false(一次性),用例
data = load_user_csv() # load up a bunch of data
User._meta.auto_increment = False # turn off auto incrementing IDs
with db.transaction():
for row in data:
u = User(id=row[0], username=row[1])
u.save(force_insert=True) # <-- force peewee to insert row
User._meta.auto_increment = True
若是你想完全控制主鍵,能夠指定id這個field(可使用integerFiled取代PrimaryKeyField):
class User(BaseModel):
id = IntegerField(primary_key=True)
username = CharField()
>>> u = User.create(id=999, username='somebody')
>>> u.id
999
>>> User.get(User.username == 'somebody').id
999
#此例中id字段不是自增的
注:若是不用PrimaryKeyField,建立表格時也會自動生成一個自增的id字段
我的測試:
>>> db.connect()
>>> db.create_tables([network,])
>>> class test1(Model):
... username = CharField()
... class Meta:
... database = db
...
>>> class test2(Model):
... id = IntegerField(primary_key=True)
... username = CharField()
... class Meta:
... database = db
...
>>> db.create_tables([test1,test2])
>>>
mysql> desc test1;
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| username | varchar(255) | NO | | NULL | |
+----------+--------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
mysql> desc test2;
+----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| username | varchar(255) | NO | | NULL | |
+----------+--------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
15.Circular foreign key dependencies(我猜是兩個表之間創建聯繫)
例子:
class User(Model):
username = CharField()
favorite_tweet = ForeignKeyField(Tweet, null=True) # NameError!!
class Tweet(Model):
message = TextField()
user = ForeignKeyField(User, related_name='tweets')
由於在定義User時,Tweet還沒定義,會致使NameError
能夠用這個簡單地處理id:
class User(Model):
username = CharField()
favorite_tweet_id = IntegerField(null=True)
或者經過proxy繞過這個問題:
# Create a proxy object to stand in for our as-yet-undefined Tweet model.
TweetProxy = Proxy()
class User(Model):
username = CharField()
# Tweet has not been defined yet so use the proxy.
favorite_tweet = ForeignKeyField(TweetProxy, null=True)
class Tweet(Model):
message = TextField()
user = ForeignKeyField(User, related_name='tweets')
# Now that Tweet is defined, we can initialize the proxy object.
TweetProxy.initialize(Tweet)
可是建表時還要作出一些處理:
# Foreign key constraint from User -> Tweet will NOT be created because the
# Tweet table does not exist yet. `favorite_tweet` will just be a regular
# integer field:
User.create_table()
# Foreign key constraint from Tweet -> User will be created normally.
Tweet.create_table()
# Now that both tables exist, we can create the foreign key from User -> Tweet:
db.create_foreign_key(User, User.favorite_tweet)