Model API (smells like django)¶
Models and their fields map directly to database tables and columns. Consider the following:
from peewee import *
db = SqliteDatabase('test.db')
# create a base model class that our application's models will extend
class BaseModel(Model):
class Meta:
database = db
class Blog(BaseModel):
name = CharField() # <-- VARCHAR
class Entry(BaseModel):
headline = CharField()
content = TextField() # <-- TEXT
pub_date = DateTimeField() # <-- DATETIME
blog = ForeignKeyField() # <-- INTEGER referencing the Blog table
This is a typical example of how to specify models with peewee. There are several things going on:
Create an instance of a
Database
db = SqliteDatabase('test.db')
This establishes an object,
db
, which is used by the models to connect to and query the database. There can be multiple database instances per application, but, as I hope is obvious,ForeignKeyField
related models must be on the same database.Create a base model class which specifies our database
class BaseModel(Model): class Meta: database = db
Model configuration is kept namespaced in a special class called
Meta
– this convention is borrowed from Django, which does the same thing.Meta
configuration is passed on to subclasses, so this code basically allows all our project’s models to connect to our database.Declare a model or two
class Blog(BaseModel): name = CharField()
Model definition is pretty similar to django or sqlalchemy – you basically define a class which represents a single table in the database, then its attributes (which are subclasses of
Field
) represent columns.Models provide methods for creating/reading/updating/deleting rows in the database.
Creating tables¶
In order to start using these models, its necessary to open a connection to the database and create the tables first:
# connect to our database
db.connect()
# create the tables
Blog.create_table()
Entry.create_table()
Note
Strictly speaking, the explicit call to connect()
is not
necessary, but it is good practice to be explicit about when you are opening
and closing connections.
Model instances¶
Assuming you’ve created the tables and connected to the database, you are now free to create models and execute queries.
Creating models in the interactive interpreter is a snap.
Use the
Model.create()
classmethod:>>> blog = Blog.create(name='Funny pictures of animals blog') >>> entry = Entry.create( ... headline='maru the kitty', ... content='http://www.youtube.com/watch?v=xdhLQCYQ-nQ', ... pub_date=datetime.datetime.now(), ... blog=blog ... ) >>> entry.blog.name 'Funny pictures of animals blog'
Build up the instance programmatically:
>>> blog = Blog() >>> blog.name = 'Another sweet blog' >>> blog.save()
Traversing foriegn keys¶
As you can see from above, the foreign key from Entry
to Blog
can be
traversed automatically:
>>> entry.blog.name
'Funny pictures of animals blog'
The reverse is also true, we can iterate a Blog
objects associated Entries
:
>>> for entry in blog.entry_set:
... print entry.headline
...
maru the kitty
Under the hood, the entry_set
attribute is just a SelectQuery
:
>>> blog.entry_set
<peewee.SelectQuery object at 0x151f510>
>>> blog.entry_set.sql()
('SELECT * FROM entry WHERE blog_id = ?', [1])
Model options¶
In order not to pollute the model namespace, model-specific configuration is
placed in a special class called Meta
, which is a convention borrowed from
the django framework:
from peewee import *
custom_db = SqliteDatabase('custom.db')
class CustomModel(Model):
class Meta:
database = custom_db
This instructs peewee that whenever a query is executed on CustomModel
to use
the custom database.
Note
Take a look at the sample models - you will notice that
we created a BaseModel
that defined the database, and then extended. This
is the preferred way to define a database and create models.
There are several options you can specify as Meta
attributes:
- database: specifies a
Database
instance to use with this model - db_table: the name of the database table this model maps to
- ordering: a sequence of columns to use as the default ordering for this model
- pk_sequence: name of sequence to create for the primary key (peewee will autogenerate one
- if not provided and the backend supports sequences).
Example of ordering:
class Entry(Model):
title = CharField()
body = TextField()
created = DateTimeField()
class Meta:
# order by created date descending, then title ascending
ordering = (('created', 'desc'), 'title')
Note
These options are “inheritable”, which means that you can define a database adapter on one model, then subclass that model and the child models will use that database.
my_db = PostgresqlDatabase('my_db')
class BaseModel(Model):
class Meta:
database = my_db
class SomeModel(BaseModel):
field1 = CharField()
class Meta:
ordering = ('field1',)
# no need to define database again since it will be inherited from
# the BaseModel
Model methods¶
-
class
Model
¶ -
save
()¶ Save the given instance, creating or updating depending on whether it has a primary key.
example:
>>> some_obj.title = 'new title' # <-- does not touch the database >>> some_obj.save() # <-- change is persisted to the db
-
classmethod
create
(**attributes)¶ Parameters: attributes – key/value pairs of model attributes Create an instance of the
Model
with the given attributes set.example:
>>> user = User.create(username='admin', password='test')
-
delete_instance
([recursive=False])¶ Delete the given instance. Any foreign keys set to cascade on delete will be deleted automatically. For more programmatic control, you can call with recursive=True, which will delete any non-nullable related models (those that are nullable will be set to NULL).
example:
>>> some_obj.delete_instance() # <-- it is gone forever
-
classmethod
filter
(*args, **kwargs)¶ Parameters: - args – a list of
Q
orNode
objects - kwargs – a mapping of column + lookup to value, e.g. “age__gt=55”
Return type: SelectQuery
with appropriateWHERE
clausesProvides a django-like syntax for building a query. The key difference between
filter()
andSelectQuery.where()
is thatfilter()
supports traversing joins using django’s “double-underscore” syntax:>>> sq = Entry.filter(blog__title='Some Blog')
This method is chainable:
>>> base_q = User.filter(active=True) >>> some_user = base_q.filter(username='charlie')
- args – a list of
-
classmethod
get
(*args, **kwargs)¶ Parameters: - args – a list of
Q
orNode
objects - kwargs – a mapping of column + lookup to value, e.g. “age__gt=55”
Return type: Model
instance or raisesDoesNotExist
exceptionGet a single row from the database that matches the given query. Raises a
<model-class>.DoesNotExist
if no rows are returned:>>> user = User.get(username=username, password=password)
This method is also expose via the
SelectQuery
:>>> active = User.select().where(active=True) >>> try: ... user = active.get(username=username, password=password) ... except User.DoesNotExist: ... user = None
- args – a list of
-
classmethod
get_or_create
(**attributes)¶ Parameters: attributes – key/value pairs of model attributes Return type: a Model
instanceGet the instance with the given attributes set. If the instance does not exist it will be created.
example:
>>> CachedObj.get_or_create(key=key, val=some_val)
-
classmethod
select
(query=None)¶ Return type: a SelectQuery
for the givenModel
example:
>>> User.select().where(active=True).order_by('username')
-
classmethod
update
(**query)¶ Return type: an UpdateQuery
for the givenModel
example:
>>> q = User.update(active=False).where(registration_expired=True) >>> q.sql() ('UPDATE user SET active=? WHERE registration_expired = ?', [0, 1]) >>> q.execute() # <-- execute it
-
classmethod
delete
(**query)¶ Return type: a DeleteQuery
for the givenModel
example:
>>> q = User.delete().where(active=False) >>> q.sql() ('DELETE FROM user WHERE active = ?', [0]) >>> q.execute() # <-- execute it
Warning
Assume you have a model instance – calling
model_instance.delete()
does not delete it.
-
classmethod
insert
(**query)¶ Return type: an InsertQuery
for the givenModel
example:
>>> q = User.insert(username='admin', active=True, registration_expired=False) >>> q.sql() ('INSERT INTO user (username,active,registration_expired) VALUES (?,?,?)', ['admin', 1, 0]) >>> q.execute() 1
-
classmethod
raw
(sql, *params)¶ Return type: a RawQuery
for the givenModel
example:
>>> q = User.raw('select id, username from users') >>> for user in q: ... print user.id, user.username
-
classmethod
create_table
([fail_silently=False])¶ Parameters: fail_silently – If set to True
, the method will check for the existence of the table before attempting to create.Create the table for the given model.
example:
>>> database.connect() >>> SomeModel.create_table() # <-- creates the table for SomeModel
-
classmethod
drop_table
([fail_silently=False])¶ Parameters: fail_silently – If set to True
, the query will check for the existence of the table before attempting to remove.Drop the table for the given model.
Note
Cascading deletes are not handled by this method, nor is the removal of any constraints.
-
classmethod
table_exists
()¶ Return type: Boolean whether the table for this model exists in the database
-