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 User(BaseModel): username = CharField() class Tweet(BaseModel): user = ForeignKeyField(User, related_name='tweets') message = TextField() created_date = DateTimeField(default=datetime.datetime.now) is_published = BooleanField(default=True)
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 User(BaseModel): username = 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.
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 User.create_table() Tweet.create_table()
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.
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:
>>> user = User.create(username='charlie') >>> tweet = Tweet.create( ... message='http://www.youtube.com/watch?v=xdhLQCYQ-nQ', ... user=user ... ) >>> tweet.user.username 'charlie'
Build up the instance programmatically:
>>> user = User() >>> user.username = 'charlie' >>> user.save()
Traversing foriegn keys¶
As you can see from above, the foreign key from Tweet to User can be traversed automatically:
>>> tweet.user.username 'charlie'
The reverse is also true, we can iterate a User objects associated Tweets:
>>> for tweet in user.tweets: ... print tweet.message ... http://www.youtube.com/watch?v=xdhLQCYQ-nQ
Under the hood, the tweets attribute is just a SelectQuery with the where clause prepopulated to point at the right User instance:
>>> user.tweets <peewee.SelectQuery object at 0x151f510>
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.
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
- indexes: a list of fields to index
- order_by: a sequence of columns to use as the default ordering for this model
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), )
Example of ordering:
class Tweet(Model): message = TextField() created = DateTimeField() class Meta: # order by created date descending ordering = ('-created',)
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
- class Model¶
Save the given instance, creating or updating depending on whether it has a primary key. If force_insert=True an INSERT will be issued regardless of whether or not the primary key exists.
>>> 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.
>>> user = User.create(username='admin', password='test')
- delete_instance([recursive=False[, delete_nullable=False]])¶
- recursive – Delete this instance and anything that depends on it, optionally updating those that have nullable dependencies
- delete_nullable – If doing a recursive delete, delete all dependent objects regardless of whether it could be updated to NULL
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). If you wish to delete all dependencies regardless of whether they are nullable, set delete_nullable=True.
>>> some_obj.delete_instance() # <-- it is gone forever
- classmethod get(*args, **kwargs)¶
- args – a list of query expressions, e.g. Usre.username == 'foo'
- kwargs – a mapping of column + lookup to value, e.g. “age__gt=55”
Model instance or raises DoesNotExist exception
Get a single row from the database that matches the given query. Raises a <model-class>.DoesNotExist if no rows are returned:
>>> user = User.get(User.username == username, User.password == password)
This method is also expose via the SelectQuery, though it takes no parameters:
>>> active = User.select().where(User.active == True) >>> try: ... users = active.where(User.username == username, User.password == password) ... user = users.get() ... except User.DoesNotExist: ... user = None
the “kwargs” style syntax is provided for compatibility with version 1.0. The expression-style syntax is preferable.
- classmethod get_or_create(**attributes)¶
Parameters: attributes – key/value pairs of model attributes Return type: a Model instance
Get the instance with the given attributes set. If the instance does not exist it will be created.
>>> CachedObj.get_or_create(key=key, val=some_val)
- classmethod select(*selection)¶
Parameters: selection – a list of model classes, field instances, functions or expressions Return type: a SelectQuery for the given Model
>>> User.select().where(User.active == True).order_by(User.username) >>> Tweet.select(Tweet, User).join(User).order_by(Tweet.created_date.desc())
- classmethod update(**query)¶
Return type: an UpdateQuery for the given Model
>>> q = User.update(active=False).where(User.registration_expired == True) >>> q.execute() # <-- execute it
- classmethod delete()¶
Return type: a DeleteQuery for the given Model
>>> q = User.delete().where(User.active == False) >>> q.execute() # <-- execute it
Assume you have a model instance – calling model_instance.delete() does not delete it.
- classmethod insert(**query)¶
Return type: an InsertQuery for the given Model
>>> q = User.insert(username='admin', active=True, registration_expired=False) >>> q.execute() 1
- classmethod raw(sql, *params)¶
Return type: a RawQuery for the given Model
>>> q = User.raw('select id, username from users') >>> for user in q: ... print user.id, user.username
- classmethod filter(*args, **kwargs)¶
- args – a list of DQ or Node objects
- kwargs – a mapping of column + lookup to value, e.g. “age__gt=55”
SelectQuery with appropriate WHERE clauses
>>> sq = Entry.filter(blog__title='Some Blog')
This method is chainable:
>>> base_q = User.filter(active=True) >>> some_user = base_q.filter(username='charlie')
this method is provided for compatibility with peewee 1.0
- 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.
>>> 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.
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