Model
- class
Metadata
(model[, database=None[, table_name=None[, indexes=None[, primary_key=None[, constraints=None[, schema=None[, only_save_dirty=False[, depends_on=None[, options=None[, without_rowid=False[, **kwargs]]]]]]]]]]]])
Parameters:
- model (Model) – Model class.
- database (Database) – database model is bound to.
- table_name (str) – Specify table name for model.
- indexes (list) – List of
ModelIndex
objects. - primary_key – Primary key for model (only specified if this is a
CompositeKey
orFalse
for no primary key. - constraints (list) – List of table constraints.
- schema (str) – Schema table exists in.
- only_save_dirty (bool) – When
save()
is called, onlysave the fields which have been modified. - options (dict) – Arbitrary options for the model.
- without_rowid (bool) – Specify WITHOUT ROWID (sqlite only).
- kwargs – Arbitrary setting attributes and values.
Store metadata for a Model
.
This class should not be instantiated directly, but is instantiated usingthe attributes of a Model
class’ inner Meta
class. Metadataattributes are then available on Model._meta
.
table
Return a reference to the underlying
Table
object.modelgraph
([_refs=True[, backrefs=True[, depth_first=True]]])
Parameters:
- **refs** (_bool_) – Follow foreign-key references.
- **backrefs** (_bool_) – Follow foreign-key back-references.
- **depth_first** (_bool_) – Do a depth-first search (<code>False</code> forbreadth-first).
Traverse the model graph and return a list of 3-tuples, consisting of(foreign key field, model class, is_backref)
.
Parameters:database (Database) – database object to bind Model to.
Bind the model class to the given Database
instance.
Warning
This API should not need to be used. Instead, to change aModel
database at run-time, use one of the following:
- [<code>Model.bind()</code>](#Model.bind)
- [<code>Model.bind_ctx()</code>](#Model.bind_ctx) (bind for scope of a context manager).
- [<code>Database.bind()</code>](#Database.bind)
- [<code>Database.bind_ctx()</code>](#Database.bind_ctx)
Parameters:table_name (str) – table name to bind Model to.
Bind the model class to the given table name at run-time.
- class
SubclassAwareMetadata
Metadata subclass that tracks
Model
subclasses.
Parameters:kwargs – Mapping of field-name to value to initialize model with.
Model class provides a high-level abstraction for working with databasetables. Models are a one-to-one mapping with a database table (or atable-like object, such as a view). Subclasses of Model
declare anynumber of Field
instances as class attributes. These fieldscorrespond to columns on the table.
Table-level operations, such as select()
,update()
, insert()
anddelete()
are implemented as classmethods. Row-leveloperations, such as save()
anddelete_instance()
are implemented as instancemethods.
Example:
- db = SqliteDatabase(':memory:')
- class User(Model):
- username = TextField()
- join_date = DateTimeField(default=datetime.datetime.now)
- is_admin = BooleanField(default=False)
- admin = User(username='admin', is_admin=True)
- admin.save()
Parameters:alias (str) – Optional name for alias.Returns:ModelAlias
instance.
Create an alias to the model-class. Model aliases allow you toreference the same Model
multiple times in a query, forexample when doing a self-join or sub-query.
Example:
- Parent = Category.alias()
- sq = (Category
- .select(Category, Parent)
- .join(Parent, on=(Category.parent == Parent.id))
- .where(Parent.name == 'parent category'))
Parameters:fields – A list of model classes, field instances, functions orexpressions. If no arguments are provided, all columns for thegiven model will be selected by default.Returns:ModelSelect
query.
Create a SELECT query. If no fields are explicitly provided, the querywill by default SELECT all the fields defined on the model, unless youare using the query as a sub-query, in which case only the primary keywill be selected by default.
Example of selecting all columns:
- query = User.select().where(User.active == True).order_by(User.username)
Example of selecting all columns on Tweet and the parent model,User. When the user
foreign key is accessed on a _Tweet_instance no additional query will be needed (see N+1for more details):
- query = (Tweet
- .select(Tweet, User)
- .join(User)
- .order_by(Tweet.created_date.desc()))
- for tweet in query:
- print(tweet.user.username, '->', tweet.content)
Example of subquery only selecting the primary key:
- inactive_users = User.select().where(User.active == False)
- # Here, instead of defaulting to all columns, Peewee will default
- # to only selecting the primary key.
- Tweet.delete().where(Tweet.user.in_(inactive_users)).execute()
Parameters:
- **__data** (_dict_) – <code>dict</code> of fields to values.
- **update** – Field-name to value mapping.
Create an UPDATE query.
Example showing users being marked inactive if their registration hasexpired:
- q = (User
- .update({User.active: False})
- .where(User.registration_expired == True))
- q.execute() # Execute the query, returning number of rows updated.
Example showing an atomic update:
- q = (PageView
- .update({PageView.count: PageView.count + 1})
- .where(PageView.url == url))
- q.execute() # Execute the query.
Note
When an update query is executed, the number of rows modified willbe returned.
Parameters:
- **__data** (_dict_) – <code>dict</code> of fields to values to insert.
- **insert** – Field-name to value mapping.
Create an INSERT query.
Insert a new row into the database. If any fields on the model havedefault values, these values will be used if the fields are notexplicitly set in the insert
dictionary.
Example showing creation of a new user:
- q = User.insert(username='admin', active=True, registration_expired=False)
- q.execute() # perform the insert.
You can also use Field
objects as the keys:
- new_id = User.insert({User.username: 'admin'}).execute()
If you have a model with a default value on one of the fields, andthat field is not specified in the insert
parameter, the defaultwill be used:
- class User(Model):
- username = CharField()
- active = BooleanField(default=True)
- # This INSERT query will automatically specify `active=True`:
- User.insert(username='charlie')
Note
When an insert query is executed on a table with anauto-incrementing primary key, the primary key of the new row willbe returned.
Parameters:
- **rows** – An iterable that yields rows to insert.
- **fields** (_list_) – List of fields being inserted.Returns:
number of rows modified (see note).
INSERT multiple rows of data.
The rows
parameter must be an iterable that yields dictionaries ortuples, where the ordering of the tuple values corresponds to thefields specified in the fields
argument. As withinsert()
, fields that are not specified in thedictionary will use their default value, if one exists.
Note
Due to the nature of bulk inserts, each row must contain the samefields. The following will not work:
- Person.insert_many([
- {'first_name': 'Peewee', 'last_name': 'Herman'},
- {'first_name': 'Huey'}, # Missing "last_name"!
- ]).execute()
Example of inserting multiple Users:
- data = [
- ('charlie', True),
- ('huey', False),
- ('zaizee', False)]
- query = User.insert_many(data, fields=[User.username, User.is_admin])
- query.execute()
Equivalent example using dictionaries:
- data = [
- {'username': 'charlie', 'is_admin': True},
- {'username': 'huey', 'is_admin': False},
- {'username': 'zaizee', 'is_admin': False}]
- # Insert new rows.
- User.insert_many(data).execute()
Because the rows
parameter can be an arbitrary iterable, you canalso use a generator:
- def get_usernames():
- for username in ['charlie', 'huey', 'peewee']:
- yield {'username': username}
- User.insert_many(get_usernames()).execute()
Warning
If you are using SQLite, your SQLite library must be version 3.7.11or newer to take advantage of bulk inserts.
Note
SQLite has a default limit of 999 bound variables per statement.This limit can be modified at compile-time or at run-time, butif modifying at run-time, you can only specify a lower value thanthe default limit.
For more information, check out the following SQLite documents:
- [Max variable number limit](https://www.sqlite.org/limits.html#max_variable_number)
- [Changing run-time limits](https://www.sqlite.org/c3ref/limit.html)
- [SQLite compile-time flags](https://www.sqlite.org/compile.html)
Note
The default return value is the number of rows modified. However,when using Postgres, Peewee will return a cursor by default thatyields the primary-keys of the inserted rows. To disable thisfunctionality with Postgres, use an empty call to returning()
.
Parameters:
- **query** ([_Select_](#Select)) – SELECT query to use as source of data.
- **fields** – Fields to insert data into.Returns:
number of rows modified (see note).
INSERT data using a SELECT query as the source. This API should be usedfor queries of the form INSERT INTO … SELECT FROM ….
Example of inserting data across tables for denormalization purposes:
- source = (User
- .select(User.username, fn.COUNT(Tweet.id))
- .join(Tweet, JOIN.LEFT_OUTER)
- .group_by(User.username))
- UserTweetDenorm.insert_from(
- source,
- [UserTweetDenorm.username, UserTweetDenorm.num_tweets]).execute()
Note
The default return value is the number of rows modified. However,when using Postgres, Peewee will return a cursor by default thatyields the primary-keys of the inserted rows. To disable thisfunctionality with Postgres, use an empty call to returning()
.
Parameters:
- **__data** (_dict_) – <code>dict</code> of fields to values to insert.
- **insert** – Field-name to value mapping.
Create an INSERT query that uses REPLACE for conflict-resolution.
See Model.insert()
for examples.
Parameters:
- **rows** – An iterable that yields rows to insert.
- **fields** (_list_) – List of fields being inserted.
INSERT multiple rows of data using REPLACE for conflict-resolution.
See Model.insert_many()
for examples.
Parameters:
- **sql** (_str_) – SQL query to execute.
- **params** – Parameters for query.
Execute a SQL query directly.
Example selecting rows from the User table:
- q = User.raw('select id, username from users')
- for user in q:
- print(user.id, user.username)
Note
Generally the use of raw
is reserved for those cases where youcan significantly optimize a select query. It is useful for selectqueries since it will return instances of the model.
Example showing the deletion of all inactive users:
- q = User.delete().where(User.active == False)
- q.execute() # Remove the rows, return number of rows removed.
Warning
This method performs a delete on the entire table. To delete asingle instance, see Model.delete_instance()
.
Parameters:query – Mapping of field-name to value.
INSERT new row into table and return corresponding model instance.
Example showing the creation of a user (a row will be added to thedatabase):
- user = User.create(username='admin', password='test')
Note
The create() method is a shorthand for instantiate-then-save.
Parameters:
- **model_list** (_iterable_) – a list or other iterable of unsaved[<code>Model</code>](#Model) instances.
- **batch_size** (_int_) – number of rows to batch per insert. Ifunspecified, all models will be inserted in a single query.Returns:
no return value.
Efficiently INSERT multiple unsaved model instances into the database.Unlike insert_many()
, which accepts row data as a listof either dictionaries or lists, this method accepts a list of unsavedmodel instances.
Example:
- # List of 10 unsaved users.
- user_list = [User(username='u%s' % i) for i in range(10)]
- # All 10 users are inserted in a single query.
- User.bulk_create(user_list)
Batches:
- user_list = [User(username='u%s' % i) for i in range(10)]
- with database.atomic():
- # Will execute 4 INSERT queries (3 batches of 3, 1 batch of 1).
- User.bulk_create(user_list, batch_size=3)
Warning
- The primary-key value for the newly-created models will only beset if you are using Postgresql (which supports the <code>RETURNING</code>clause).
- SQLite generally has a limit of 999 bound parameters for a query,so the batch size should be roughly 1000 / number-of-fields.
- When a batch-size is provided it is **strongly recommended** thatyou wrap the call in a transaction or savepoint using[<code>Database.atomic()</code>](#Database.atomic). Otherwise an error in a batch mid-waythrough could leave the database in an inconsistent state.
Parameters:
- **model_list** (_iterable_) – a list or other iterable of[<code>Model</code>](#Model) instances.
- **fields** (_list_) – list of fields to update.
- **batch_size** (_int_) – number of rows to batch per insert. Ifunspecified, all models will be inserted in a single query.Returns:
total number of rows updated.
Efficiently UPDATE multiple model instances.
Example:
- # First, create 3 users.
- u1, u2, u3 = [User.create(username='u%s' % i) for i in (1, 2, 3)]
- # Now let's modify their usernames.
- u1.username = 'u1-x'
- u2.username = 'u2-y'
- u3.username = 'u3-z'
- # Update all three rows using a single UPDATE query.
- User.bulk_update([u1, u2, u3], fields=[User.username])
If you have a large number of objects to update, it is stronglyrecommended that you specify a batch_size
and wrap the operation ina transaction:
- with database.atomic():
- User.bulk_update(user_list, fields=['username'], batch_size=50)
Warning
- SQLite generally has a limit of 999 bound parameters for a query.
- When a batch-size is provided it is **strongly recommended** thatyou wrap the call in a transaction or savepoint using[<code>Database.atomic()</code>](#Database.atomic). Otherwise an error in a batch mid-waythrough could leave the database in an inconsistent state.
Parameters:
- **query** – Zero or more [<code>Expression</code>](#Expression) objects.
- **filters** – Mapping of field-name to value for Django-style filter.Raises:
DoesNotExist
Returns:Model instance matching the specified filters.
Retrieve a single model instance matching the given filters. If nomodel is returned, a DoesNotExist
is raised.
- user = User.get(User.username == username, User.active == True)
This method is also exposed via the SelectQuery
, though ittakes no parameters:
- active = User.select().where(User.active == True)
- try:
- user = active.where(
- (User.username == username) &
- (User.active == True)
- ).get()
- except User.DoesNotExist:
- user = None
Note
The get()
method is shorthand for selecting with alimit of 1. It has the added behavior of raising an exception whenno matching row is found. If more than one row is found, the firstrow returned by the database cursor will be used.
- classmethod
getor_none
(query, *filters_) Identical to
Model.get()
but returnsNone
if no modelmatches the given filters.
Parameters:pk – Primary-key value.
Short-hand for calling Model.get()
specifying a lookup byprimary key. Raises a DoesNotExist
if instance with thegiven primary key value does not exist.
Example:
- user = User.get_by_id(1) # Returns user with id = 1.
Parameters:
- **key** – Primary-key value.
- **value** (_dict_) – Mapping of field to value to update.
Short-hand for updating the data with the given primary-key. If no rowexists with the given primary key, no exception will be raised.
Example:
- # Set "is_admin" to True on user with id=3.
- User.set_by_id(3, {'is_admin': True})
Parameters:pk – Primary-key value.
Short-hand for deleting the row with the given primary-key. If no rowexists with the given primary key, no exception will be raised.
Parameters:
- **kwargs** – Mapping of field-name to value.
- **defaults** – Default values to use if creating a new row.Returns:
Tuple of Model
instance and boolean indicatingif a new object was created.
Attempt to get the row matching the given filters. If no matching rowis found, create a new row.
Warning
Race-conditions are possible when using this method.
Example without get_or_create
:
- # Without `get_or_create`, we might write:
- try:
- person = Person.get(
- (Person.first_name == 'John') &
- (Person.last_name == 'Lennon'))
- except Person.DoesNotExist:
- person = Person.create(
- first_name='John',
- last_name='Lennon',
- birthday=datetime.date(1940, 10, 9))
Equivalent code using get_or_create
:
- person, created = Person.get_or_create(
- first_name='John',
- last_name='Lennon',
- defaults={'birthday': datetime.date(1940, 10, 9)})
Parameters:
- **dq_nodes** – Zero or more [<code>DQ</code>](#DQ) objects.
- **filters** – Django-style filters.Returns:
ModelSelect
query.
Returns:The primary-key of the model instance.
Parameters:
- **force_insert** (_bool_) – Force INSERT query.
- **only** (_list_) – Only save the given [<code>Field</code>](#Field) instances.Returns:
Number of rows modified.
Save the data in the model instance. By default, the presence of aprimary-key value will cause an UPDATE query to be executed.
Example showing saving a model instance:
- user = User()
- user.username = 'some-user' # does not touch the database
- user.save() # change is persisted to the db
Return type:list
Note
If you just want to persist modified fields, you can callmodel.save(only=model.dirty_fields)
.
If you always want to only save a model’s dirty fields, you can use the Metaoption only_save_dirty = True
. Then, any time you call Model.save()
,by default only the dirty fields will be saved, e.g.
- class Person(Model):
- first_name = CharField()
- last_name = CharField()
- dob = DateField()
- class Meta:
- database = db
- only_save_dirty = True
Warning
Peewee determines whether a field is “dirty” by observing when thefield attribute is set on a model instance. If the field contains avalue that is mutable, such as a dictionary instance, and thatdictionary is then modified, Peewee will not notice the change.
Parameters:
- **recursive** (_bool_) – Delete related models.
- **delete_nullable** (_bool_) – Delete related models that have a nullforeign key. If <code>False</code> nullable relations will be set to NULL.
Delete the given instance. Any foreign keys set to cascade ondelete will be deleted automatically. For more programmatic control,you can specify recursive=True
, which will delete any non-nullablerelated models (those that are nullable will be set to NULL). If youwish to delete all dependencies regardless of whether they are nullable,set delete_nullable=True
.
example:
- some_obj.delete_instance() # it is gone forever
Parameters:
- **database** ([_Database_](#Database)) – database to bind to.
- **bind_refs** (_bool_) – Bind related models.
- **bind_backrefs** (_bool_) – Bind back-reference related models.
Bind the model (and specified relations) to the given database.
See also: Database.bind()
.
- classmethod
bindctx
(_database[, bind_refs=True[, bind_backrefs=True]]) - Like
bind()
, but returns a context manager that onlybinds the models for the duration of the wrapped block.
See also: Database.bind_ctx()
.
Returns:boolean indicating whether the table exists.
Parameters:safe (bool) – If set to True
, the create table query willinclude an IF NOT EXISTS
clause.
Create the model table, indexes, constraints and sequences.
Example:
- with database:
- SomeModel.create_table() # Execute the create table query.
Parameters:safe (bool) – If set to True
, the create table query willinclude an IF EXISTS
clause.
Drop the model table.
Parameters:
- **restart_identity** (_bool_) – Restart the id sequence (postgres-only).
- **cascade** (_bool_) – Truncate related tables as well (postgres-only).
Truncate (delete all rows) for the model.
Parameters:
- **fields** – Fields to index.
- **unique** (_bool_) – Whether index is UNIQUE.
- **safe** (_bool_) – Whether to add IF NOT EXISTS clause.
- **where** ([_Expression_](#Expression)) – Optional WHERE clause for index.
- **using** (_str_) – Index algorithm.
- **name** (_str_) – Optional index name.
Expressive method for declaring an index on a model. Wraps thedeclaration of a ModelIndex
instance.
Examples:
- class Article(Model):
- name = TextField()
- timestamp = TimestampField()
- status = IntegerField()
- flags = BitField()
- is_sticky = flags.flag(1)
- is_favorite = flags.flag(2)
- # CREATE INDEX ... ON "article" ("name", "timestamp" DESC)
- idx = Article.index(Article.name, Article.timestamp.desc())
- # Be sure to add the index to the model:
- Article.add_index(idx)
- # CREATE UNIQUE INDEX ... ON "article" ("timestamp" DESC, "flags" & 2)
- # WHERE ("status" = 1)
- idx = (Article
- .index(Article.timestamp.desc(),
- Article.flags.bin_and(2),
- unique=True)
- .where(Article.status == 1))
- # Add index to model:
- Article.add_index(idx)
Parameters:
- **args** – a [<code>ModelIndex</code>](#ModelIndex) instance, Field(s) to index,or a [<code>SQL</code>](#SQL) instance that contains the SQL for creatingthe index.
- **kwargs** – Keyword arguments passed to [<code>ModelIndex</code>](#ModelIndex)constructor.
Add an index to the model’s definition.
Note
This method does not actually create the index in the database.Rather, it adds the index definition to the model’s metadata, sothat a subsequent call to create_table()
willcreate the new index (along with the table).
Examples:
- class Article(Model):
- name = TextField()
- timestamp = TimestampField()
- status = IntegerField()
- flags = BitField()
- is_sticky = flags.flag(1)
- is_favorite = flags.flag(2)
- # CREATE INDEX ... ON "article" ("name", "timestamp") WHERE "status" = 1
- idx = Article.index(Article.name, Article.timestamp).where(Article.status == 1)
- Article.add_index(idx)
- # CREATE UNIQUE INDEX ... ON "article" ("timestamp" DESC, "flags" & 2)
- ts_flags_idx = Article.index(
- Article.timestamp.desc(),
- Article.flags.bin_and(2),
- unique=True)
- Article.add_index(ts_flags_idx)
- # You can also specify a list of fields and use the same keyword
- # arguments that the ModelIndex constructor accepts:
- Article.add_index(
- Article.name,
- Article.timestamp.desc(),
- where=(Article.status == 1))
- # Or even specify a SQL query directly:
- Article.add_index(SQL('CREATE INDEX ...'))
Parameters:search_nullable (bool) – Search models related via a nullableforeign keyReturn type:Generator expression yielding queries and foreign key fields.
Generate a list of queries of dependent models. Yields a 2-tuplecontaining the query and corresponding foreign key field. Useful forsearching dependencies of a model, i.e. things that would be orphanedin the event of a delete.
Returns:a ModelSelect
for the given class.
Convenience function for iterating over all instances of a model.
Example:
- Setting.insert_many([
- {'key': 'host', 'value': '192.168.1.2'},
- {'key': 'port': 'value': '1337'},
- {'key': 'user': 'value': 'nuggie'}]).execute()
- # Load settings from db into dict.
- settings = {setting.key: setting.value for setting in Setting}
Returns:Count of rows in table.
Example:
- n_accounts = len(Account)
- # Is equivalent to:
- n_accounts = Account.select().count()
Parameters:
- model (Model) – Model class to reference.
- alias (str) – (optional) name for alias.
Provide a separate reference to a model in a query.
Parameters:
- model (Model) – Model class to select.
- fields_or_models – List of fields or model classes to select.
Model-specific implementation of SELECT query.
Parameters:ctx – A Model
, ModelAlias
, subquery, orother object that was joined-on.
Switch the join context - the source which subsequent calls tojoin()
will be joined against. Used forspecifying multiple joins against a single table.
If the ctx
is not given, then the query’s model will be used.
The following example selects from tweet and joins on both user andtweet-flag:
- sq = Tweet.select().join(User).switch(Tweet).join(TweetFlag)
- # Equivalent (since Tweet is the query's model)
- sq = Tweet.select().join(User).switch().join(TweetFlag)
Parameters:constructor – Constructor (defaults to returning model instances)
Return result rows as objects created using the given constructor. Thedefault behavior is to create model instances.
Note
This method can be used, when selecting field data from multiplesources/models, to make all data available as attributes on themodel being queried (as opposed to constructing the graph of joinedmodel instances). For very complex queries this can have a positiveperformance impact, especially iterating large result sets.
Similarly, you can use dicts()
,tuples()
or namedtuples()
to achieve even more performance.
Parameters:
- **dest** – A [<code>Model</code>](#Model), [<code>ModelAlias</code>](#ModelAlias),[<code>Select</code>](#Select) query, or other object to join to.
- **join_type** (_str_) – Join type, defaults to INNER.
- **on** – Join predicate or a [<code>ForeignKeyField</code>](#ForeignKeyField) to join on.
- **src** – Explicitly specify the source of the join. If not specifiedthen the current _join context_ will be used.
- **attr** (_str_) – Attribute to use when projecting columns from thejoined model.
Join with another table-like object.
Join type may be one of:
- <code>JOIN.INNER</code>
- <code>JOIN.LEFT_OUTER</code>
- <code>JOIN.RIGHT_OUTER</code>
- <code>JOIN.FULL</code>
- <code>JOIN.FULL_OUTER</code>
- <code>JOIN.CROSS</code>
Example selecting tweets and joining on user in order to restrict toonly those tweets made by “admin” users:
- sq = Tweet.select().join(User).where(User.is_admin == True)
Example selecting users and joining on a particular foreign key field.See the example app for a real-life usage:
- sq = User.select().join(Relationship, on=Relationship.to_user)
For an in-depth discussion of foreign-keys, joins and relationshipsbetween models, refer to Relationships and Joins.
Parameters:
- **src** – Source for join.
- **dest** – Table to join to.
Use same parameter order as the non-model-specificjoin()
. Bypasses the join context by requiringthe join source to be specified.
Parameters:
- **args** – Zero or more [<code>DQ</code>](#DQ) objects.
- **kwargs** – Django-style keyword-argument filters.
Use Django-style filters to express a WHERE clause.
Parameters:subqueries – A list of Model
classes or selectqueries to prefetch.Returns:a list of models with selected relations prefetched.
Execute the query, prefetching the given additional resources.
See also prefetch()
standalone function.
Example:
- # Fetch all Users and prefetch their associated tweets.
- query = User.select().prefetch(Tweet)
- for user in query:
- print(user.username)
- for tweet in user.tweets:
- print(' *', tweet.content)
Parameters:
- sq – Query to use as starting-point.
- subqueries – One or more models or
ModelSelect
queriesto eagerly fetch.Returns:a list of models with selected relations prefetched.
Eagerly fetch related objects, allowing efficient querying of multipletables when a 1-to-many relationship exists.
For example, it is simple to query a many-to-1 relationship efficiently:
- query = (Tweet
- .select(Tweet, User)
- .join(User))
- for tweet in query:
- # Looking up tweet.user.username does not require a query since
- # the related user's columns were selected.
- print(tweet.user.username, '->', tweet.content)
To efficiently do the inverse, query users and their tweets, you can useprefetch:
- query = User.select()
- for user in prefetch(query, Tweet):
- print(user.username)
- for tweet in user.tweets: # Does not require additional query.
- print(' ', tweet.content)