Fields

  • class Field([null=False[, index=False[, unique=False[, column_name=None[, default=None[, primary_key=False[, constraints=None[, sequence=None[, collation=None[, unindexed=False[, choices=None[, help_text=None[, verbose_name=None[, index_type=None]]]]]]]]]]]]]])

Parameters:

  • null (bool) – Field allows NULLs.
  • index (bool) – Create an index on field.
  • unique (bool) – Create a unique index on field.
  • column_name (str) – Specify column name for field.
  • default – Default value (enforced in Python, not on server).
  • primary_key (bool) – Field is the primary key.
  • constraints (list) – List of constraints to apply to column, forexample: [Check('price > 0')].
  • sequence (str) – Sequence name for field.
  • collation (str) – Collation name for field.
  • unindexed (bool) – Declare field UNINDEXED (sqlite only).
  • choices (list) – An iterable of 2-tuples mapping column values todisplay labels. Used for metadata purposes only, to help whendisplaying a dropdown of choices for field values, for example.
  • help_text (str) – Help-text for field, metadata purposes only.
  • verbose_name (str) – Verbose name for field, metadata purposes only.
  • index_type (str) – Specify index type (postgres only), e.g. ‘BRIN’.

Fields on a Model are analogous to columns on a table.

  • field_type = '<some field type>'
  • Attribute used to map this field to a column type, e.g. “INT”. Seethe FIELD object in the source for more information.

  • column

  • Retrieve a reference to the underlying Column object.

  • model

  • The model the field is bound to.

  • name

  • The name of the field.

  • dbvalue(_value)

  • Coerce a Python value into a value suitable for storage in thedatabase. Sub-classes operating on special data-types will most likelywant to override this method.

  • pythonvalue(_value)

  • Coerce a value from the database into a Python object. Sub-classesoperating on special data-types will most likely want to override thismethod.

  • coerce(value)

  • This method is a shorthand that is used, by default, by bothdb_value() and python_value().

Parameters:value – arbitrary data from app or backendReturn type:python data type

  • class IntegerField
  • Field class for storing integers.
  • class BigIntegerField
  • Field class for storing big integers (if supported by database).
  • class SmallIntegerField
  • Field class for storing small integers (if supported by database).
  • class AutoField
  • Field class for storing auto-incrementing primary keys.

Note

In SQLite, for performance reasons, the default primary key type simplyuses the max existing value + 1 for new values, as opposed to the maxever value + 1. This means deleted records can have their primary keysreused. In conjunction with SQLite having foreign keys disabled bydefault (meaning ON DELETE is ignored, even if you specify itexplicitly), this can lead to surprising and dangerous behaviour. Toavoid this, you may want to use one or both ofAutoIncrementField and pragmas=[('foreign_keys', 'on')]when you instantiate SqliteDatabase.

  • class BigAutoField
  • Field class for storing auto-incrementing primary keys using 64-bits.
  • class IdentityField([generate_always=False])

Parameters:generate_always (bool) – if specified, then the identity will always begenerated (and specifying the value explicitly during INSERT will raisea programming error). Otherwise, the identity value is only generatedas-needed.

Field class for storing auto-incrementing primary keys using the newPostgres 10 IDENTITY column type. The column definition ends up lookinglike this:

  1. id = IdentityField()
  2. # "id" INT GENERATED BY DEFAULT AS IDENTITY NOT NULL PRIMARY KEY

Attention

Only supported by Postgres 10.0 and newer.

  • class FloatField
  • Field class for storing floating-point numbers.
  • class DoubleField
  • Field class for storing double-precision floating-point numbers.
  • class DecimalField([max_digits=10[, decimal_places=5[, auto_round=False[, rounding=None[, **kwargs]]]]])

Parameters:

  • max_digits (int) – Maximum digits to store.
  • decimal_places (int) – Maximum precision.
  • auto_round (bool) – Automatically round values.
  • rounding –Defaults to decimal.DefaultContext.rounding.

Field class for storing decimal numbers. Values are represented asdecimal.Decimal objects.

  • class CharField([max_length=255])
  • Field class for storing strings.

Note

Values that exceed length are not truncated automatically.

  • class FixedCharField
  • Field class for storing fixed-length strings.

Note

Values that exceed length are not truncated automatically.

  • class TextField
  • Field class for storing text.
  • class BlobField
  • Field class for storing binary data.
  • class BitField
  • Field class for storing options in a 64-bit integer column.

Usage:

  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)
  9.  
  10. >>> p = Post()
  11. >>> p.is_sticky = True
  12. >>> p.is_minimized = True
  13. >>> print(p.flags) # Prints 4 | 2 --> "6"
  14. 6
  15. >>> p.is_favorite
  16. False
  17. >>> p.is_sticky
  18. True

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

  1. # Generates a WHERE clause that looks like:
  2. # WHERE (post.flags & 1 != 0)
  3. query = Post.select().where(Post.is_favorite)
  4.  
  5. # Query for sticky + favorite posts:
  6. query = Post.select().where(Post.is_sticky & Post.is_favorite)
  • flag([value=None])

Parameters:value (int) – Value associated with flag, typically a power of 2.

Returns a descriptor that can get or set specific bits in the overallvalue. When accessed on the class itself, it returns aExpression object suitable for use in a query.

If the value is not provided, it is assumed that each flag will be anincreasing power of 2, so if you had four flags, they would have thevalues 1, 2, 4, 8.

  • class BigBitField
  • Field class for storing arbitrarily-large bitmaps in a BLOB. The fieldwill grow the underlying buffer as necessary, ensuring there are enoughbytes of data to support the number of bits of data being stored.

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)
  • setbit(_idx)

Parameters:idx (int) – Bit to set, indexed starting from zero.

Sets the idx-th bit in the bitmap.

  • clearbit(_idx)

Parameters:idx (int) – Bit to clear, indexed starting from zero.

Clears the idx-th bit in the bitmap.

  • togglebit(_idx)

Parameters:idx (int) – Bit to toggle, indexed starting from zero.Returns:Whether the bit is set or not.

Toggles the idx-th bit in the bitmap and returns whether the bit isset or not.

Example:

  1. >>> bitmap = Bitmap()
  2. >>> bitmap.data.toggle_bit(10) # Toggle the 10th bit.
  3. True
  4. >>> bitmap.data.toggle_bit(10) # This will clear the 10th bit.
  5. False
  • isset(_idx)

Parameters:idx (int) – Bit index, indexed starting from zero.Returns:Whether the bit is set or not.

Returns boolean indicating whether the idx-th bit is set or not.

  • class UUIDField
  • Field class for storing uuid.UUID objects. With Postgres, theunderlying column’s data-type will be UUID. Since SQLite and MySQL do nothave a native UUID type, the UUID is stored as a VARCHAR instead.
  • class BinaryUUIDField
  • Field class for storing uuid.UUID objects efficiently in 16-bytes. Usesthe database’s BLOB data-type (or VARBINARY in MySQL, or BYTEA inPostgres).
  • class DateTimeField([formats=None[, **kwargs]])

Parameters:formats (list) – A list of format strings to use when coercing a stringto a date-time.

Field class for storing datetime.datetime objects.

Accepts a special parameter formats, which contains a list of formatsthe datetime can be encoded with (for databases that do not have supportfor a native datetime data-type). The default supported formats are:

  1. '%Y-%m-%d %H:%M:%S.%f' # year-month-day hour-minute-second.microsecond
  2. '%Y-%m-%d %H:%M:%S' # year-month-day hour-minute-second
  3. '%Y-%m-%d' # year-month-day

Note

SQLite does not have a native datetime data-type, so datetimes arestored as strings. This is handled transparently by Peewee, but if youhave pre-existing data you should ensure it is stored asYYYY-mm-dd HH:MM:SS or one of the other supported formats.

  • year
  • Reference the year of the value stored in the column in a query.
  1. Blog.select().where(Blog.pub_date.year == 2018)
  • month
  • Reference the month of the value stored in the column in a query.

  • day

  • Reference the day of the value stored in the column in a query.

  • hour

  • Reference the hour of the value stored in the column in a query.

  • minute

  • Reference the minute of the value stored in the column in a query.

  • second

  • Reference the second of the value stored in the column in a query.

  • to_timestamp()

  • Method that returns a database-specific function call that will allowyou to work with the given date-time value as a numeric timestamp. Thiscan sometimes simplify tasks like date math in a compatible way.

Example:

  1. # Find all events that are exactly 1 hour long.
  2. query = (Event
  3. .select()
  4. .where((Event.start.to_timestamp() + 3600) ==
  5. Event.stop.to_timestamp())
  6. .order_by(Event.start))
  • truncate(date_part)

Parameters:date_part (str) – year, month, day, hour, minute or second.Returns:expression node to truncate date/time to given resolution.

Truncates the value in the column to the given part. This method isuseful for finding all rows within a given month, for instance.

  • class DateField([formats=None[, **kwargs]])

Parameters:formats (list) – A list of format strings to use when coercing a stringto a date.

Field class for storing datetime.date objects.

Accepts a special parameter formats, which contains a list of formatsthe datetime can be encoded with (for databases that do not have supportfor a native date data-type). The default supported formats are:

  1. '%Y-%m-%d' # year-month-day
  2. '%Y-%m-%d %H:%M:%S' # year-month-day hour-minute-second
  3. '%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 is returned as-is.

  • year
  • Reference the year of the value stored in the column in a query.
  1. Person.select().where(Person.dob.year == 1983)
  • class TimeField([formats=None[, **kwargs]])

Parameters:formats (list) – A list of format strings to use when coercing a stringto a time.

Field class for storing datetime.time objects (not timedelta).

Accepts a special parameter formats, which contains a list of formatsthe datetime can be encoded with (for databases that do not have supportfor a native time data-type). The default supported formats are:

  1. '%H:%M:%S.%f' # hour:minute:second.microsecond
  2. '%H:%M:%S' # hour:minute:second
  3. '%H:%M' # hour:minute
  4. '%Y-%m-%d %H:%M:%S.%f' # year-month-day hour-minute-second.microsecond
  5. '%Y-%m-%d %H:%M:%S' # year-month-day hour-minute-second

Note

If the incoming value does not match a format, it is returned as-is.

  • hour
  • Reference the hour of the value stored in the column in a query.
  1. evening_events = Event.select().where(Event.time.hour > 17)
  • minute
  • Reference the minute of the value stored in the column in a query.

  • second

  • Reference the second of the value stored in the column in a query.
  • class TimestampField([resolution=1[, utc=False[, **kwargs]]])

Parameters:

  • resolution – Can be provided as either a power of 10, or as anexponent indicating how many decimal places to store.
  • utc (bool) – Treat timestamps as UTC.

Field class for storing date-times as integer timestamps. Sub-secondresolution is supported by multiplying by a power of 10 to get an integer.

If the resolution parameter is 0 or 1, then the timestamp isstored using second resolution. A resolution between 2 and 6 istreated as the number of decimal places, e.g. resolution=3 correspondsto milliseconds. Alternatively, the decimal can be provided as a multipleof 10, such that resolution=10 will store 1/10th of a secondresolution.

The resolution parameter can be either 0-6 or 10, 100, etc up to1000000 (for microsecond resolution). This allows sub-second precisionwhile still using an IntegerField for storage. The default issecond resolution.

Also accepts a boolean parameter utc, used to indicate whether thetimestamps should be UTC. Default is False.

Finally, the field default is the current timestamp. If you do not wantthis behavior, then explicitly pass in default=None.

  • class IPField
  • Field class for storing IPv4 addresses efficiently (as integers).
  • class BooleanField
  • Field class for storing boolean values.
  • class BareField([coerce=None[, **kwargs]])

Parameters:coerce – Optional function to use for converting raw values into aspecific format.

Field class that does not specify a data-type (SQLite-only).

Since data-types are not enforced, you can declare fields without _any_data-type. It is also common for SQLite virtual tables to use meta-columnsor untyped columns, so for those cases as well you may wish to use anuntyped field.

Accepts a special coerce parameter, a function that takes a valuecoming from the database and converts it into the appropriate Python type.

  • class ForeignKeyField(model[, field=None[, backref=None[, on_delete=None[, on_update=None[, deferrable=None[, object_id_name=None[, lazy_load=True[, **kwargs]]]]]]]])

Parameters:

  • model (Model) – Model to reference or the string ‘self’ if declaring aself-referential foreign key.
  • field (Field) – Field to reference on model (default is primarykey).
  • backref (str) – Accessor name for back-reference, or “+” to disablethe back-reference accessor.
  • on_delete (str) – ON DELETE action, e.g. 'CASCADE'..
  • on_update (str) – ON UPDATE action.
  • deferrable (str) – Control when constraint is enforced, e.g. 'INITIALLY DEFERRED'.
  • object_id_name (str) – Name for object-id accessor.
  • lazy_load (bool) – Fetch the related object when the foreign-key fieldattribute is accessed (if it was not already loaded). If this isdisabled, accessing the foreign-key field will return the value storedin the foreign-key column.

Field class for storing a foreign key.

  1. class User(Model):
  2. name = TextField()
  3.  
  4. class Tweet(Model):
  5. user = ForeignKeyField(User, backref='tweets')
  6. content = TextField()
  7.  
  8. # "user" attribute
  9. >>> some_tweet.user
  10. <User: charlie>
  11.  
  12. # "tweets" backref attribute
  13. >>> for tweet in charlie.tweets:
  14. ... print(tweet.content)
  15. Some tweet
  16. Another tweet
  17. Yet another tweet

For an in-depth discussion of foreign-keys, joins and relationships betweenmodels, refer to Relationships and Joins.

Note

Foreign keys do not have a particular field_type as they will taketheir field type depending on the type of primary key on the model theyare related to.

Note

If you manually specify a field, that field must be either aprimary key or have a unique constraint.

Note

Take care with foreign keys in SQLite. By default, ON DELETE has noeffect, which can have surprising (and usually unwanted) effects onyour database integrity. This can affect you even if you don’t specifyon_delete, since the default ON DELETE behaviour (to fail withoutmodifying your data) does not happen, and your data can be silentlyrelinked. The safest thing to do is to specifypragmas={'foreign_keys': 1} when you instantiateSqliteDatabase.

  • class DeferredForeignKey(rel_model_name[, **kwargs])

Parameters:rel_model_name (str) – Model name to reference.

Field class for representing a deferred foreign key. Useful for circularforeign-key references, for example:

  1. class Husband(Model):
  2. name = TextField()
  3. wife = DeferredForeignKey('Wife', deferrable='INITIALLY DEFERRED')
  4.  
  5. class Wife(Model):
  6. name = TextField()
  7. husband = ForeignKeyField(Husband, deferrable='INITIALLY DEFERRED')

In the above example, when the Wife model is declared, the foreign-keyHusband.wife is automatically resolved and turned into a regularForeignKeyField.

Warning

DeferredForeignKey references are resolved when modelclasses are declared and created. This means that if you declare aDeferredForeignKey to a model class that has already beenimported and created, the deferred foreign key instance will never beresolved. For example:

  1. class User(Model):
  2. username = TextField()
  3.  
  4. class Tweet(Model):
  5. # This will never actually be resolved, because the User
  6. # model has already been declared.
  7. user = DeferredForeignKey('user', backref='tweets')
  8. content = TextField()

In cases like these you should use the regularForeignKeyFieldor you can manually resolve deferredforeign keys like so:

  1. # Tweet.user will be resolved into a ForeignKeyField:
  2. DeferredForeignKey.resolve(User)
  • class ManyToManyField(model[, backref=None[, through_model=None[, on_delete=None[, on_update=None]]]])

Parameters:

  • model (Model) – Model to create relationship with.
  • backref (str) – Accessor name for back-reference
  • through_model (Model) – Model to use for the intermediarytable. If not provided, a simple through table will be automaticallycreated.
  • on_delete (str) – ON DELETE action, e.g. 'CASCADE'. Will be usedfor foreign-keys in through model.
  • on_update (str) – ON UPDATE action. Will be used for foreign-keys inthrough model.

The ManyToManyField provides a simple interface for workingwith many-to-many relationships, inspired by Django. A many-to-manyrelationship is typically implemented by creating a junction table withforeign keys to the two models being related. For instance, if you werebuilding a syllabus manager for college students, the relationship betweenstudents and courses would be many-to-many. Here is the schema usingstandard APIs:

Attention

This is not a field in the sense that there is no column associatedwith it. Rather, it provides a convenient interface for accessing rowsof data related via a through model.

Standard way of declaring a many-to-many relationship (without the use ofthe ManyToManyField):

  1. class Student(Model):
  2. name = CharField()
  3.  
  4. class Course(Model):
  5. name = CharField()
  6.  
  7. class StudentCourse(Model):
  8. student = ForeignKeyField(Student)
  9. course = ForeignKeyField(Course)

To query the courses for a particular student, you would join through thejunction table:

  1. # List the courses that "Huey" is enrolled in:
  2. courses = (Course
  3. .select()
  4. .join(StudentCourse)
  5. .join(Student)
  6. .where(Student.name == 'Huey'))
  7. for course in courses:
  8. print(course.name)

The ManyToManyField is designed to simplify this use-case byproviding a field-like API for querying and modifying data in thejunction table. Here is how our code looks usingManyToManyField:

  1. class Student(Model):
  2. name = CharField()
  3.  
  4. class Course(Model):
  5. name = CharField()
  6. students = ManyToManyField(Student, backref='courses')

Note

It does not matter from Peewee’s perspective which model theManyToManyField goes on, since the back-reference is justthe mirror image. In order to write valid Python, though, you will needto add the ManyToManyField on the second model so that the name ofthe first model is in the scope.

We still need a junction table to store the relationships between studentsand courses. This model can be accessed by calling theget_through_model() method. This is useful whencreating tables.

  1. # Create tables for the students, courses, and relationships between
  2. # the two.
  3. db.create_tables([
  4. Student,
  5. Course,
  6. Course.students.get_through_model()])

When accessed from a model instance, the ManyToManyFieldexposes a ModelSelect representing the set of related objects.Let’s use the interactive shell to see how all this works:

  1. >>> huey = Student.get(Student.name == 'huey')
  2. >>> [course.name for course in huey.courses]
  3. ['English 101', 'CS 101']
  4.  
  5. >>> engl_101 = Course.get(Course.name == 'English 101')
  6. >>> [student.name for student in engl_101.students]
  7. ['Huey', 'Mickey', 'Zaizee']

To add new relationships between objects, you can either assign the objectsdirectly to the ManyToManyField attribute, or call theadd() method. The difference between the two isthat simply assigning will clear out any existing relationships, whereasadd() can preserve existing relationships.

  1. >>> huey.courses = Course.select().where(Course.name.contains('english'))
  2. >>> for course in huey.courses.order_by(Course.name):
  3. ... print course.name
  4. English 101
  5. English 151
  6. English 201
  7. English 221
  8.  
  9. >>> cs_101 = Course.get(Course.name == 'CS 101')
  10. >>> cs_151 = Course.get(Course.name == 'CS 151')
  11. >>> huey.courses.add([cs_101, cs_151])
  12. >>> [course.name for course in huey.courses.order_by(Course.name)]
  13. ['CS 101', 'CS151', 'English 101', 'English 151', 'English 201',
  14. 'English 221']

This is quite a few courses, so let’s remove the 200-level english courses.To remove objects, use the remove() method.

  1. >>> huey.courses.remove(Course.select().where(Course.name.contains('2'))
  2. 2
  3. >>> [course.name for course in huey.courses.order_by(Course.name)]
  4. ['CS 101', 'CS151', 'English 101', 'English 151']

To remove all relationships from a collection, you can use theclear() method. Let’s say that English 101 iscanceled, so we need to remove all the students from it:

  1. >>> engl_101 = Course.get(Course.name == 'English 101')
  2. >>> engl_101.students.clear()

Note

For an overview of implementing many-to-many relationships usingstandard Peewee APIs, check out the Implementing Many to Many section. For allbut the most simple cases, you will be better off implementingmany-to-many using the standard APIs.

  • through_model
  • The Model representing the many-to-many junction table.Will be auto-generated if not explicitly declared.

  • add(value[, clear_existing=True])

Parameters:

  1. - **value** Either a [<code>Model</code>](#Model) instance, a list of modelinstances, or a [<code>SelectQuery</code>](#SelectQuery).
  2. - **clear_existing** (_bool_) Whether to remove existing relationships.

Associate value with the current instance. You can pass in a singlemodel instance, a list of model instances, or even a ModelSelect.

Example code:

  1. # Huey needs to enroll in a bunch of courses, including all
  2. # the English classes, and a couple Comp-Sci classes.
  3. huey = Student.get(Student.name == 'Huey')
  4.  
  5. # We can add all the objects represented by a query.
  6. english_courses = Course.select().where(
  7. Course.name.contains('english'))
  8. huey.courses.add(english_courses)
  9.  
  10. # We can also add lists of individual objects.
  11. cs101 = Course.get(Course.name == 'CS 101')
  12. cs151 = Course.get(Course.name == 'CS 151')
  13. huey.courses.add([cs101, cs151])
  • remove(value)

Parameters:value – Either a Model instance, a list of modelinstances, or a ModelSelect.

Disassociate value from the current instance. Likeadd(), you can pass in a model instance, alist of model instances, or even a ModelSelect.

Example code:

  1. # Huey is currently enrolled in a lot of english classes
  2. # as well as some Comp-Sci. He is changing majors, so we
  3. # will remove all his courses.
  4. english_courses = Course.select().where(
  5. Course.name.contains('english'))
  6. huey.courses.remove(english_courses)
  7.  
  8. # Remove the two Comp-Sci classes Huey is enrolled in.
  9. cs101 = Course.get(Course.name == 'CS 101')
  10. cs151 = Course.get(Course.name == 'CS 151')
  11. huey.courses.remove([cs101, cs151])
  • clear()
  • Remove all associated objects.

Example code:

  1. # English 101 is canceled this semester, so remove all
  2. # the enrollments.
  3. english_101 = Course.get(Course.name == 'English 101')
  4. english_101.students.clear()
  • get_through_model()
  • Return the Model representing the many-to-many junctiontable. This can be specified manually when the field is beinginstantiated using the through_model parameter. If athrough_model is not specified, one will automatically be created.

When creating tables for an application that usesManyToManyField, you must create the through table expicitly.

  1. # Get a reference to the automatically-created through table.
  2. StudentCourseThrough = Course.students.get_through_model()
  3.  
  4. # Create tables for our two models as well as the through model.
  5. db.create_tables([
  6. Student,
  7. Course,
  8. StudentCourseThrough])
  • class DeferredThroughModel
  • Place-holder for a through-model in cases where, due to a dependency, youcannot declare either a model or a many-to-many field without introducingNameErrors.

Example:

  1. class Note(BaseModel):
  2. content = TextField()
  3.  
  4. NoteThroughDeferred = DeferredThroughModel()
  5.  
  6. class User(BaseModel):
  7. username = TextField()
  8. notes = ManyToManyField(Note, through_model=NoteThroughDeferred)
  9.  
  10. # Cannot declare this before "User" since it has a foreign-key to
  11. # the User model.
  12. class NoteThrough(BaseModel):
  13. note = ForeignKeyField(Note)
  14. user = ForeignKeyField(User)
  15.  
  16. # Resolve dependencies.
  17. NoteThroughDeferred.set_model(NoteThrough)
  • class CompositeKey(*field_names)

Parameters:field_names – Names of fields that comprise the primary key.

A primary key composed of multiple columns. Unlike the other fields, acomposite key is defined in the model’s Meta class after the fieldshave been defined. It takes as parameters the string names of the fields touse as the primary key:

  1. class BlogTagThrough(Model):
  2. blog = ForeignKeyField(Blog, backref='tags')
  3. tag = ForeignKeyField(Tag, backref='blogs')
  4.  
  5. class Meta:
  6. primary_key = CompositeKey('blog', 'tag')