Fields

The Field class is used to describe the mapping ofModel attributes to database columns. Each field type has acorresponding SQL storage class (i.e. varchar, int), and conversion betweenpython data types and underlying storage is handled transparently.

When creating a Model class, fields are defined as classattributes. This should look familiar to users of the django framework. Here’san example:

  1. class User(Model):
  2. username = CharField()
  3. join_date = DateTimeField()
  4. about_me = TextField()

In the above example, because none of the fields are initialized withprimary_key=True, an auto-incrementing primary key will automatically becreated and named “id”. Peewee uses AutoField to signify anauto-incrementing integer primary key, which implies primary_key=True.

There is one special type of field, ForeignKeyField, which allowsyou to represent foreign-key relationships between models in an intuitive way:

  1. class Message(Model):
  2. user = ForeignKeyField(User, backref='messages')
  3. body = TextField()
  4. send_date = DateTimeField(default=datetime.datetime.now)

This allows you to write code like the following:

  1. >>> print(some_message.user.username)
  2. Some User
  3.  
  4. >>> for message in some_user.messages:
  5. ... print(message.body)
  6. some message
  7. another message
  8. yet another message

Note

Refer to the Relationships and Joins document for an in-depth discussion offoreign-keys, joins and relationships between models.

For full documentation on fields, see the Fields API notes

Field types table

Field TypeSqlitePostgresqlMySQL
AutoFieldintegerserialinteger
BigAutoFieldintegerbigserialbigint
IntegerFieldintegerintegerinteger
BigIntegerFieldintegerbigintbigint
SmallIntegerFieldintegersmallintsmallint
IdentityFieldnot supportedint identitynot supported
FloatFieldrealrealreal
DoubleFieldrealdouble precisiondouble precision
DecimalFielddecimalnumericnumeric
CharFieldvarcharvarcharvarchar
FixedCharFieldcharcharchar
TextFieldtexttexttext
BlobFieldblobbyteablob
BitFieldintegerbigintbigint
BigBitFieldblobbyteablob
UUIDFieldtextuuidvarchar(40)
BinaryUUIDFieldblobbyteavarbinary(16)
DateTimeFielddatetimetimestampdatetime
DateFielddatedatedate
TimeFieldtimetimetime
TimestampFieldintegerintegerinteger
IPFieldintegerbigintbigint
BooleanFieldintegerbooleanbool
BareFielduntypednot supportednot supported
ForeignKeyFieldintegerintegerinteger

Note

Don’t see the field you’re looking for in the above table? It’s easy tocreate custom field types and use them with your models.

Field initialization arguments

Parameters accepted by all field types and their default values:

  • null = False – allow null values
  • index = False – create an index on this column
  • unique = False – create a unique index on this column. See also adding composite indexes.
  • column_name = None – explicitly specify the column name in the database.
  • default = None – any value or callable to use as a default for uninitialized models
  • primary_key = False – primary key for the table
  • constraints = None - one or more constraints, e.g. [Check('price > 0')]
  • sequence = None – sequence name (if backend supports it)
  • collation = None – collation to use for ordering the field / index
  • unindexed = False – indicate field on virtual table should be unindexed (SQLite-only)
  • choices = None – optional iterable containing 2-tuples of value, display
  • help_text = None – string representing any helpful text for this field
  • verbose_name = None – string representing the “user-friendly” name of this field
  • index_type = None – specify a custom index-type, e.g. for Postgres you might specify a 'BRIN' or 'GIN' index.

Some fields take special parameters…

Field typeSpecial Parameters
CharFieldmax_length
FixedCharFieldmax_length
DateTimeFieldformats
DateFieldformats
TimeFieldformats
TimestampFieldresolution, utc
DecimalFieldmax_digits, decimal_places,auto_round, rounding
ForeignKeyFieldmodel, field, backref,on_delete, on_update, deferrablelazy_load
BareFieldadapt

Note

Both default and choices could be implemented at the database levelas DEFAULT and CHECK CONSTRAINT respectively, but any applicationchange would require a schema change. Because of this, default isimplemented purely in python and choices are not validated but existfor metadata purposes only.

To add database (server-side) constraints, use the constraintsparameter.

Default field values

Peewee can provide default values for fields when objects are created. Forexample to have an IntegerField default to zero rather than NULL, youcould declare the field with a default value:

  1. class Message(Model):
  2. context = TextField()
  3. read_count = IntegerField(default=0)

In some instances it may make sense for the default value to be dynamic. Acommon scenario is using the current date and time. Peewee allows you tospecify a function in these cases, whose return value will be used when theobject is created. Note we only provide the function, we do not actually _call_it:

  1. class Message(Model):
  2. context = TextField()
  3. timestamp = DateTimeField(default=datetime.datetime.now)

Note

If you are using a field that accepts a mutable type (list, dict, etc),and would like to provide a default, it is a good idea to wrap your defaultvalue in a simple function so that multiple model instances are not sharinga reference to the same underlying object:

  1. def house_defaults():
  2. return {'beds': 0, 'baths': 0}
  3.  
  4. class House(Model):
  5. number = TextField()
  6. street = TextField()
  7. attributes = JSONField(default=house_defaults)

The database can also provide the default value for a field. While peewee doesnot explicitly provide an API for setting a server-side default value, you canuse the constraints parameter to specify the server default:

  1. class Message(Model):
  2. context = TextField()
  3. timestamp = DateTimeField(constraints=[SQL('DEFAULT CURRENT_TIMESTAMP')])

Note

Remember: when using the default parameter, the values are set byPeewee rather than being a part of the actual table and column definition.

ForeignKeyField

ForeignKeyField is a special field type that allows one model toreference another. Typically a foreign key will contain the primary key of themodel it relates to (but you can specify a particular column by specifying afield).

Foreign keys allow data to be normalized.In our example models, there is a foreign key from Tweet to User. Thismeans that all the users are stored in their own table, as are the tweets, andthe foreign key from tweet to user allows each tweet to point to a particularuser object.

Note

Refer to the Relationships and Joins document for an in-depth discussion offoreign keys, joins and relationships between models.

In peewee, accessing the value of a ForeignKeyField will return theentire related object, e.g.:

  1. tweets = (Tweet
  2. .select(Tweet, User)
  3. .join(User)
  4. .order_by(Tweet.created_date.desc()))
  5. for tweet in tweets:
  6. print(tweet.user.username, tweet.message)

Note

In the example above the User data was selected as part of the query.For more examples of this technique, see the Avoiding N+1document.

If we did not select the User, though, then an additional query wouldbe issued to fetch the associated User data:

  1. tweets = Tweet.select().order_by(Tweet.created_date.desc())
  2. for tweet in tweets:
  3. # WARNING: an additional query will be issued for EACH tweet
  4. # to fetch the associated User data.
  5. print(tweet.user.username, tweet.message)

Sometimes you only need the associated primary key value from the foreign keycolumn. In this case, Peewee follows the convention established by Django, ofallowing you to access the raw foreign key value by appending "_id" to theforeign key field’s name:

  1. tweets = Tweet.select()
  2. for tweet in tweets:
  3. # Instead of "tweet.user", we will just get the raw ID value stored
  4. # in the column.
  5. print(tweet.user_id, tweet.message)

To prevent accidentally resolving a foreign-key and triggering an additionalquery, ForeignKeyField supports an initialization paramaterlazy_load which, when disabled, behaves like the "_id" attribute. Forexample:

  1. class Tweet(Model):
  2. # ... same fields, except we declare the user FK to have
  3. # lazy-load disabled:
  4. user = ForeignKeyField(User, backref='tweets', lazy_load=False)
  5.  
  6. for tweet in Tweet.select():
  7. print(tweet.user, tweet.message)
  8.  
  9. # With lazy-load disabled, accessing tweet.user will not perform an extra
  10. # query and the user ID value is returned instead.
  11. # e.g.:
  12. # 1 tweet from user1
  13. # 1 another from user1
  14. # 2 tweet from user2
  15.  
  16. # However, if we eagerly load the related user object, then the user
  17. # foreign key will behave like usual:
  18. for tweet in Tweet.select(Tweet, User).join(User):
  19. print(tweet.user.username, tweet.message)
  20.  
  21. # user1 tweet from user1
  22. # user1 another from user1
  23. # user2 tweet from user1

ForeignKeyField Back-references

ForeignKeyField allows for a backreferencing property to be boundto the target model. Implicitly, this property will be named classname_set,where classname is the lowercase name of the class, but can be overriddenusing the parameter backref:

  1. class Message(Model):
  2. from_user = ForeignKeyField(User, backref='outbox')
  3. to_user = ForeignKeyField(User, backref='inbox')
  4. text = TextField()
  5.  
  6. for message in some_user.outbox:
  7. # We are iterating over all Messages whose from_user is some_user.
  8. print(message)
  9.  
  10. for message in some_user.inbox:
  11. # We are iterating over all Messages whose to_user is some_user
  12. print(message)

DateTimeField, DateField and TimeField

The three fields devoted to working with dates and times have special propertieswhich allow access to things like the year, month, hour, etc.

DateField has properties for:

  • year
  • month
  • day

TimeField has properties for:

  • hour
  • minute
  • second

DateTimeField has all of the above.

These properties can be used just like any other expression. Let’s say we havean events calendar and want to highlight all the days in the current month thathave an event attached:

  1. # Get the current time.
  2. now = datetime.datetime.now()
  3.  
  4. # Get days that have events for the current month.
  5. Event.select(Event.event_date.day.alias('day')).where(
  6. (Event.event_date.year == now.year) &
  7. (Event.event_date.month == now.month))

Note

SQLite does not have a native date type, so dates are stored in formattedtext columns. To ensure that comparisons work correctly, the dates need tobe formatted so they are sorted lexicographically. That is why they arestored, by default, as YYYY-MM-DD HH:MM:SS.

BitField and BigBitField

The BitField and BigBitField are new as of 3.0.0. Theformer provides a subclass of IntegerField that is suitable forstoring feature toggles as an integer bitmask. The latter is suitable forstoring a bitmap for a large data-set, e.g. expressing membership orbitmap-type data.

As an example of using BitField, let’s say we have a Post modeland we wish to store certain True/False flags about how the post. We couldstore all these feature toggles in their own BooleanField objects,or we could use BitField instead:

  1. class Post(Model):
  2. content = TextField()
  3. flags = BitField()
  4.  
  5. is_favorite = flags.flag(1)
  6. is_sticky = flags.flag(2)
  7. is_minimized = flags.flag(4)
  8. is_deleted = flags.flag(8)

Using these flags is quite simple:

  1. >>> p = Post()
  2. >>> p.is_sticky = True
  3. >>> p.is_minimized = True
  4. >>> print(p.flags) # Prints 4 | 2 --> "6"
  5. 6
  6. >>> p.is_favorite
  7. False
  8. >>> p.is_sticky
  9. True

We can also use the flags on the Post class to build expressions in queries:

  1. # Generates a WHERE clause that looks like:
  2. # WHERE (post.flags & 1 != 0)
  3. favorites = Post.select().where(Post.is_favorite)
  4.  
  5. # Query for sticky + favorite posts:
  6. sticky_faves = Post.select().where(Post.is_sticky & Post.is_favorite)

Since the BitField is stored in an integer, there is a maximum of64 flags you can represent (64-bits is common size of integer column). Forstoring arbitrarily large bitmaps, you can instead use BigBitField,which uses an automatically managed buffer of bytes, stored in aBlobField.

Example usage:

  1. class Bitmap(Model):
  2. data = BigBitField()
  3.  
  4. bitmap = Bitmap()
  5.  
  6. # Sets the ith bit, e.g. the 1st bit, the 11th bit, the 63rd, etc.
  7. bits_to_set = (1, 11, 63, 31, 55, 48, 100, 99)
  8. for bit_idx in bits_to_set:
  9. bitmap.data.set_bit(bit_idx)
  10.  
  11. # We can test whether a bit is set using "is_set":
  12. assert bitmap.data.is_set(11)
  13. assert not bitmap.data.is_set(12)
  14.  
  15. # We can clear a bit:
  16. bitmap.data.clear_bit(11)
  17. assert not bitmap.data.is_set(11)
  18.  
  19. # We can also "toggle" a bit. Recall that the 63rd bit was set earlier.
  20. assert bitmap.data.toggle_bit(63) is False
  21. assert bitmap.data.toggle_bit(63) is True
  22. assert bitmap.data.is_set(63)

BareField

The BareField class is intended to be used only with SQLite. SinceSQLite uses dynamic typing and data-types are not enforced, it can be perfectlyfine to declare fields without any data-type. In those cases you can useBareField. It is also common for SQLite virtual tables to usemeta-columns or untyped columns, so for those cases as well you may wish to usean untyped field (although for full-text search, you should useSearchField instead!).

BareField accepts a special parameter adapt. This parameter isa function that takes a value coming from the database and converts it into theappropriate Python type. For instance, if you have a virtual table with anun-typed column but you know that it will return int objects, you canspecify adapt=int.

Example:

  1. db = SqliteDatabase(':memory:')
  2.  
  3. class Junk(Model):
  4. anything = BareField()
  5.  
  6. class Meta:
  7. database = db
  8.  
  9. # Store multiple data-types in the Junk.anything column:
  10. Junk.create(anything='a string')
  11. Junk.create(anything=12345)
  12. Junk.create(anything=3.14159)

Creating a custom field

It is easy to add support for custom field types in peewee. In this example wewill create a UUID field for postgresql (which has a native UUID column type).

To add a custom field type you need to first identify what type of column thefield data will be stored in. If you just want to add python behavior atop,say, a decimal field (for instance to make a currency field) you would justsubclass DecimalField. On the other hand, if the database offers acustom column type you will need to let peewee know. This is controlled by theField.field_type attribute.

Note

Peewee ships with a UUIDField, the following code is intendedonly as an example.

Let’s start by defining our UUID field:

  1. class UUIDField(Field):
  2. field_type = 'uuid'

We will store the UUIDs in a native UUID column. Since psycopg2 treats the dataas a string by default, we will add two methods to the field to handle:

  • The data coming out of the database to be used in our application
  • The data from our python app going into the database
  1. import uuid
  2.  
  3. class UUIDField(Field):
  4. field_type = 'uuid'
  5.  
  6. def db_value(self, value):
  7. return value.hex # convert UUID to hex string.
  8.  
  9. def python_value(self, value):
  10. return uuid.UUID(value) # convert hex string to UUID

This step is optional. By default, the fieldtype value will be usedfor the columns data-type in the database schema. If you need to supportmultiple databases which use different data-types for your field-data, we needto let the database know how to map this _uuid label to an actual _uuid_column type in the database. Specify the overrides in the Database constructor:

  1. # Postgres, we use UUID data-type.db = PostgresqlDatabase('my_db', field_types={'uuid': 'uuid'})# Sqlite doesn't have a UUID type, so we use text type.db = SqliteDatabase('my_db', field_types={'uuid': 'text'})

That is it! Some fields may support exotic operations, like the postgresqlHStore field acts like a key/value store and has custom operators for thingslike contains and update. You can specify custom operations as well. For example code, check out the source code forthe HStoreField, in playhouse.postgres_ext.

Field-naming conflicts

Model classes implement a number of class- and instance-methods,for example Model.save() or Model.create(). If you declare afield whose name coincides with a model method, it could cause problems.Consider:

  1. class LogEntry(Model):
  2. event = TextField()
  3. create = TimestampField() # Uh-oh.
  4. update = TimestampField() # Uh-oh.

To avoid this problem while still using the desired column name in the databaseschema, explicitly specify the column_name while providing an alternativename for the field attribute:

  1. class LogEntry(Model):
  2. event = TextField()
  3. create_ = TimestampField(column_name='create')
  4. update_ = TimestampField(column_name='update')