Indexes and Constraints

Peewee can create indexes on single or multiple columns, optionally including a UNIQUE constraint. Peewee also supports user-defined constraints on both models and fields.

Single-column indexes and constraints

Single column indexes are defined using field initialization parameters. The following example adds a unique index on the username field, and a normal index on the email field:

  1. class User(Model):
  2. username = CharField(unique=True)
  3. email = CharField(index=True)

To add a user-defined constraint on a column, you can pass it in using the constraints parameter. You may wish to specify a default value as part of the schema, or add a CHECK constraint, for example:

  1. class Product(Model):
  2. name = CharField(unique=True)
  3. price = DecimalField(constraints=[Check('price < 10000')])
  4. created = DateTimeField(
  5. constraints=[SQL("DEFAULT (datetime('now'))")])

Multi-column indexes

Multi-column indexes may be defined as Meta attributes using a nested tuple. Each database index is a 2-tuple, the first part of which is a tuple of the names of the fields, the second part a boolean indicating whether the index should be unique.

  1. class Transaction(Model):
  2. from_acct = CharField()
  3. to_acct = CharField()
  4. amount = DecimalField()
  5. date = DateTimeField()
  6. class Meta:
  7. indexes = (
  8. # create a unique on from/to/date
  9. (('from_acct', 'to_acct', 'date'), True),
  10. # create a non-unique on from/to
  11. (('from_acct', 'to_acct'), False),
  12. )

Note

Remember to add a trailing comma if your tuple of indexes contains only one item:

  1. class Meta:
  2. indexes = (
  3. (('first_name', 'last_name'), True), # Note the trailing comma!
  4. )

Advanced Index Creation

Peewee supports a more structured API for declaring indexes on a model using the Model.add_index() method or by directly using the ModelIndex helper class.

Examples:

  1. class Article(Model):
  2. name = TextField()
  3. timestamp = TimestampField()
  4. status = IntegerField()
  5. flags = IntegerField()
  6. # Add an index on "name" and "timestamp" columns.
  7. Article.add_index(Article.name, Article.timestamp)
  8. # Add a partial index on name and timestamp where status = 1.
  9. Article.add_index(Article.name, Article.timestamp,
  10. where=(Article.status == 1))
  11. # Create a unique index on timestamp desc, status & 4.
  12. idx = Article.index(
  13. Article.timestamp.desc(),
  14. Article.flags.bin_and(4),
  15. unique=True)
  16. Article.add_index(idx)

Warning

SQLite does not support parameterized CREATE INDEX queries. This means that when using SQLite to create an index that involves an expression or scalar value, you will need to declare the index using the SQL helper:

  1. # SQLite does not support parameterized CREATE INDEX queries, so
  2. # we declare it manually.
  3. Article.add_index(SQL('CREATE INDEX ...'))

See add_index() for details.

For more information, see:

Table constraints

Peewee allows you to add arbitrary constraints to your Model, that will be part of the table definition when the schema is created.

For instance, suppose you have a people table with a composite primary key of two columns, the person’s first and last name. You wish to have another table relate to the people table, and to do this, you will need to define a foreign key constraint:

  1. class Person(Model):
  2. first = CharField()
  3. last = CharField()
  4. class Meta:
  5. primary_key = CompositeKey('first', 'last')
  6. class Pet(Model):
  7. owner_first = CharField()
  8. owner_last = CharField()
  9. pet_name = CharField()
  10. class Meta:
  11. constraints = [SQL('FOREIGN KEY(owner_first, owner_last) '
  12. 'REFERENCES person(first, last)')]

You can also implement CHECK constraints at the table level:

  1. class Product(Model):
  2. name = CharField(unique=True)
  3. price = DecimalField()
  4. class Meta:
  5. constraints = [Check('price < 10000')]