#Sora#peewee管理數據庫——筆記


終於來到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)    

相關文章
相關標籤/搜索