The Field class is used to describe the mapping of Model attributes to database columns. Each field type has a corresponding SQL storage class (i.e. varchar, int), and conversion between python data types and underlying storage is handled transparently.
When creating a Model class, fields are defined as class-level attributes. This should look familiar to users of the django framework. Here’s an example:
from peewee import *
class User(Model):
username = CharField()
join_date = DateTimeField()
about_me = TextField()
There is one special type of field, ForeignKeyField, which allows you to expose foreign-key relationships between models in an intuitive way:
class Message(Model):
user = ForeignKeyField(User, related_name='messages')
body = TextField()
send_date = DateTimeField()
This allows you to write code like the following:
>>> print some_message.user.username
Some User
>>> for message in some_user.messages:
... print message.body
some message
another message
yet another message
Parameters accepted by all field types and their default values:
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 |
Field type | Special Parameters |
---|---|
CharField | max_length |
DateTimeField | formats |
DateField | formats |
TimeField | formats |
DecimalField | max_digits, decimal_places, auto_round, rounding |
PrimaryKeyField | column_class |
ForeignKeyField | to, related_name, cascade, extra |
Both default and choices could be implemented at the database level as DEFAULT and CHECK CONSTRAINT respectively, but any application change would require a schema change. Because of this, default is implemented purely in python and choices are not validated but exist for metadata purposes only.
Since the class is not available at the time the field is declared, when creating a self-referential foreign key pass in ‘self’ as the “to” relation:
class Category(Model):
name = CharField()
parent = ForeignKeyField('self', related_name='children', null=True)
Peewee does not provide a “field” for many to many relationships the way that django does – this is because the “field” really is hiding an intermediary table. To implement many-to-many with peewee, you will therefore create the intermediary table yourself and query through it:
class Student(Model):
name = CharField()
class Course(Model):
name = CharField()
class StudentCourse(Model):
student = ForeignKeyField(Student)
course = ForeignKeyField(Course)
To query, let’s say we want to find students who are enrolled in math class:
for student in Student.select().join(StudentCourse).join(Course).where(name='math'):
print student.name
You could also express this as:
for student in Student.filter(studentcourse_set__course__name='math'):
print student.name
To query what classes a given student is enrolled in:
for course in Course.select().join(StudentCourse).join(Student).where(name='da vinci'):
print course.name
# or, similarly
for course in Course.filter(studentcourse_set__student__name='da vinci'):
print course.name
First of all, let me say that I do not think using non-integer primary keys is a good idea. The cost in storage is higher, the index lookups will be slower, and foreign key joins will be more expensive. That being said, here is how you can use non-integer pks in peewee.
from peewee import Model, PrimaryKeyField, VarCharColumn
class UUIDModel(Model):
# explicitly declare a primary key field, and specify the class to use
id = PrimaryKeyField(column_class=VarCharColumn)
Auto-increment IDs are, as their name says, automatically generated for you when you insert a new row into the database. The way peewee determines whether to do an INSERT versus an UPDATE comes down to checking whether the primary key field is None. If None, it will do an insert, otherwise it does an update on the existing value. Since, with our uuid example, the database driver won’t generate a new ID, we need to specify it manually. When we call save() for the first time, pass in force_insert = True:
inst = UUIDModel(id=str(uuid.uuid4()))
inst.save() # <-- WRONG!! this will try to do an update
inst.save(force_insert=True) # <-- CORRECT
# to update the instance after it has been saved once
inst.save()
Note
Any foreign keys to a model with a non-integer primary key will have the ForeignKeyField use the same underlying column type as the primary key they are related to.
The base class from which all other field types extend.
Parameters: |
|
---|
Parameters: | value – python data type to prep for storage in the database |
---|---|
Return type: | converted python datatype |
Parameters: | value – data coming from the backend storage |
---|---|
Return type: | python data type |
Parameters: |
|
---|---|
Return type: | data type converted for use when querying |
Stores: small strings (0-255 bytes)
Stores: arbitrarily large strings
Stores: python datetime.datetime instances
Accepts a special parameter formats, which contains a list of formats the datetime can be encoded with. The default behavior is:
'%Y-%m-%d %H:%M:%S.%f' # year-month-day hour-minute-second.microsecond
'%Y-%m-%d %H:%M:%S' # year-month-day hour-minute-second
'%Y-%m-%d' # year-month-day
Note
If the incoming value does not match a format, it will be returned as-is
Stores: python datetime.date instances
Accepts a special parameter formats, which contains a list of formats the date can be encoded with. The default behavior is:
'%Y-%m-%d' # year-month-day
'%Y-%m-%d %H:%M:%S' # year-month-day hour-minute-second
'%Y-%m-%d %H:%M:%S.%f' # year-month-day hour-minute-second.microsecond
Note
If the incoming value does not match a format, it will be returned as-is
Stores: python datetime.time instances
Accepts a special parameter formats, which contains a list of formats the time can be encoded with. The default behavior is:
'%H:%M:%S.%f' # hour:minute:second.microsecond
'%H:%M:%S' # hour:minute:second
'%H:%M' # hour:minute
'%Y-%m-%d %H:%M:%S.%f' # year-month-day hour-minute-second.microsecond
'%Y-%m-%d %H:%M:%S' # year-month-day hour-minute-second
Note
If the incoming value does not match a format, it will be returned as-is
Stores: integers
Stores: True / False
Stores: floating-point numbers
Stores: decimal numbers
Stores: auto-incrementing integer fields suitable for use as primary key by default, though other types of data can be stored by specifying a column_class. See notes on non-integer primary keys.
Parameters: | column_class – a reference to a subclass of Column to use for the underlying storage, defaults to PrimaryKeyColumn. |
---|
Stores: relationship to another model
Parameters: |
|
---|
class Blog(Model):
name = CharField()
class Entry(Model):
blog = ForeignKeyField(Blog, related_name='entries')
title = CharField()
content = TextField()
# "blog" attribute
>>> some_entry.blog
<Blog: My Awesome Blog>
# "entries" related name attribute
>>> for entry in my_awesome_blog.entries:
... print entry.title
Some entry
Another entry
Yet another entry