#Sora#peewee管理数据库——笔记

#Sora#peewee管理数据库——笔记_第1张图片


终于来到sora开发准备的最后部分


peewee 管理数据库


1.明确地管理数据库连接(也可以考虑使用连接池)


database = SqliteDatabase('my_app.db')


def before_request_handler():

    database.connect()


def after_request_handler():

    database.close()


务必明确地处理数据库连接



2.在表类中设置内部类,以使用数据库


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.连接池

连接池支持:

1、超时之后,连接将被回收

2、可以设置连接数的上限


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)    




你可能感兴趣的:(peewee)