.\" Man page generated from reStructuredText. . .TH "PEEWEE" "1" "Mar 01, 2021" "3.14.1" "peewee" .SH NAME peewee \- peewee Documentation . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. [image] .sp Peewee is a simple and small ORM. It has few (but expressive) concepts, making it easy to learn and intuitive to use. .INDENT 0.0 .IP \(bu 2 a small, expressive ORM .IP \(bu 2 python 2.7+ and 3.4+ (developed with 3.6) .IP \(bu 2 supports sqlite, mysql, postgresql and cockroachdb .IP \(bu 2 tons of extensions .UNINDENT \fI\%postgresql\fP\fI\%mysql\fP\fI\%sqlite\fP\fI\%cockroachdb\fP .sp Peewee\(aqs source code hosted on \fI\%GitHub\fP\&. .sp New to peewee? These may help: .INDENT 0.0 .IP \(bu 2 Quickstart .IP \(bu 2 Example twitter app .IP \(bu 2 Using peewee interactively .IP \(bu 2 Models and fields .IP \(bu 2 Querying .IP \(bu 2 Relationships and joins .UNINDENT .SH CONTENTS: .SS Installing and Testing .sp Most users will want to simply install the latest version, hosted on PyPI: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C pip install peewee .ft P .fi .UNINDENT .UNINDENT .sp Peewee comes with a couple C extensions that will be built if Cython is available. .INDENT 0.0 .IP \(bu 2 Sqlite extensions, which includes Cython implementations of the SQLite date manipulation functions, the REGEXP operator, and full\-text search result ranking algorithms. .UNINDENT .SS Installing with git .sp The project is hosted at \fI\%https://github.com/coleifer/peewee\fP and can be installed using git: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C git clone https://github.com/coleifer/peewee.git cd peewee python setup.py install .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 On some systems you may need to use \fBsudo python setup.py install\fP to install peewee system\-wide. .UNINDENT .UNINDENT .sp If you would like to build the SQLite extension in a git checkout, you can run: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Build the C extension and place shared libraries alongside other modules. python setup.py build_ext \-i .ft P .fi .UNINDENT .UNINDENT .SS Running tests .sp You can test your installation by running the test suite. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C python runtests.py .ft P .fi .UNINDENT .UNINDENT .sp You can test specific features or specific database drivers using the \fBruntests.py\fP script. To view the available test runner options, use: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C python runtests.py \-\-help .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 To run tests against Postgres or MySQL you need to create a database named "peewee_test". To test the Postgres extension module, you will also want to install the HStore extension in the postgres test database: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \-\- install the hstore extension on the peewee_test postgres db. CREATE EXTENSION hstore; .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS Optional dependencies .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 To use Peewee, you typically won\(aqt need anything outside the standard library, since most Python distributions are compiled with SQLite support. You can test by running \fBimport sqlite3\fP in the Python console. If you wish to use another database, there are many DB\-API 2.0\-compatible drivers out there, such as \fBpymysql\fP or \fBpsycopg2\fP for MySQL and Postgres respectively. .UNINDENT .UNINDENT .INDENT 0.0 .IP \(bu 2 \fI\%Cython\fP: used to expose additional functionality when using SQLite and to implement things like search result ranking in a performant manner. Since the generated C files are included with the package distribution, Cython is no longer required to use the C extensions. .IP \(bu 2 \fI\%apsw\fP: an optional 3rd\-party SQLite binding offering greater performance and comprehensive support for SQLite\(aqs C APIs. Use with \fBAPSWDatabase\fP\&. .IP \(bu 2 \fI\%gevent\fP is an optional dependency for \fBSqliteQueueDatabase\fP (though it works with \fBthreading\fP just fine). .IP \(bu 2 \fI\%BerkeleyDB\fP can be compiled with a SQLite frontend, which works with Peewee. Compiling can be tricky so \fI\%here are instructions\fP\&. .IP \(bu 2 Lastly, if you use the \fIFlask\fP framework, there are helper extension modules available. .UNINDENT .SS Note on the SQLite extensions .sp Peewee includes two SQLite\-specific C extensions which provide additional functionality and improved performance for SQLite database users. Peewee will attempt to determine ahead\-of\-time if SQLite3 is installed, and only build the SQLite extensions if the SQLite shared\-library is available on your system. .sp If, however, you receive errors like the following when attempting to install Peewee, you can explicitly disable the compilation of the SQLite C extensions by settings the \fBNO_SQLITE\fP environment variable. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C fatal error: sqlite3.h: No such file or directory .ft P .fi .UNINDENT .UNINDENT .sp Here is how to install Peewee with the SQLite extensions explicitly disabled: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ NO_SQLITE=1 python setup.py install .ft P .fi .UNINDENT .UNINDENT .SS Quickstart .sp This document presents a brief, high\-level overview of Peewee\(aqs primary features. This guide will cover: .INDENT 0.0 .IP \(bu 2 \fI\%Model Definition\fP .IP \(bu 2 \fI\%Storing data\fP .IP \(bu 2 \fI\%Retrieving Data\fP .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 If you\(aqd like something a bit more meaty, there is a thorough tutorial on creating a "twitter"\-style web app using peewee and the Flask framework. In the projects \fBexamples/\fP folder you can find more self\-contained Peewee examples, like a \fI\%blog app\fP\&. .UNINDENT .UNINDENT .sp I \fBstrongly\fP recommend opening an interactive shell session and running the code. That way you can get a feel for typing in queries. .SS Model Definition .sp Model classes, fields and model instances all map to database concepts: .TS center; |l|l|. _ T{ Object T} T{ Corresponds to... T} _ T{ Model class T} T{ Database table T} _ T{ Field instance T} T{ Column on a table T} _ T{ Model instance T} T{ Row in a database table T} _ .TE .sp When starting a project with peewee, it\(aqs typically best to begin with your data model, by defining one or more \fBModel\fP classes: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from peewee import * db = SqliteDatabase(\(aqpeople.db\(aq) class Person(Model): name = CharField() birthday = DateField() class Meta: database = db # This model uses the "people.db" database. .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Peewee will automatically infer the database table name from the name of the class. You can override the default name by specifying a \fBtable_name\fP attribute in the inner "Meta" class (alongside the \fBdatabase\fP attribute). To learn more about how Peewee generates table names, refer to the table_names section. .sp Also note that we named our model \fBPerson\fP instead of \fBPeople\fP\&. This is a convention you should follow \-\- even though the table will contain multiple people, we always name the class using the singular form. .UNINDENT .UNINDENT .sp There are lots of field types suitable for storing various types of data. Peewee handles converting between \fIpythonic\fP values those used by the database, so you can use Python types in your code without having to worry. .sp Things get interesting when we set up relationships between models using foreign key relationships\&. This is simple with peewee: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Pet(Model): owner = ForeignKeyField(Person, backref=\(aqpets\(aq) name = CharField() animal_type = CharField() class Meta: database = db # this model uses the "people.db" database .ft P .fi .UNINDENT .UNINDENT .sp Now that we have our models, let\(aqs connect to the database. Although it\(aqs not necessary to open the connection explicitly, it is good practice since it will reveal any errors with your database connection immediately, as opposed to some arbitrary time later when the first query is executed. It is also good to close the connection when you are done \-\- for instance, a web app might open a connection when it receives a request, and close the connection when it sends the response. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db.connect() .ft P .fi .UNINDENT .UNINDENT .sp We\(aqll begin by creating the tables in the database that will store our data. This will create the tables with the appropriate columns, indexes, sequences, and foreign key constraints: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db.create_tables([Person, Pet]) .ft P .fi .UNINDENT .UNINDENT .SS Storing data .sp Let\(aqs begin by populating the database with some people. We will use the \fBsave()\fP and \fBcreate()\fP methods to add and update people\(aqs records. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from datetime import date uncle_bob = Person(name=\(aqBob\(aq, birthday=date(1960, 1, 15)) uncle_bob.save() # bob is now stored in the database # Returns: 1 .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 When you call \fBsave()\fP, the number of rows modified is returned. .UNINDENT .UNINDENT .sp You can also add a person by calling the \fBcreate()\fP method, which returns a model instance: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C grandma = Person.create(name=\(aqGrandma\(aq, birthday=date(1935, 3, 1)) herb = Person.create(name=\(aqHerb\(aq, birthday=date(1950, 5, 5)) .ft P .fi .UNINDENT .UNINDENT .sp To update a row, modify the model instance and call \fBsave()\fP to persist the changes. Here we will change Grandma\(aqs name and then save the changes in the database: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C grandma.name = \(aqGrandma L.\(aq grandma.save() # Update grandma\(aqs name in the database. # Returns: 1 .ft P .fi .UNINDENT .UNINDENT .sp Now we have stored 3 people in the database. Let\(aqs give them some pets. Grandma doesn\(aqt like animals in the house, so she won\(aqt have any, but Herb is an animal lover: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C bob_kitty = Pet.create(owner=uncle_bob, name=\(aqKitty\(aq, animal_type=\(aqcat\(aq) herb_fido = Pet.create(owner=herb, name=\(aqFido\(aq, animal_type=\(aqdog\(aq) herb_mittens = Pet.create(owner=herb, name=\(aqMittens\(aq, animal_type=\(aqcat\(aq) herb_mittens_jr = Pet.create(owner=herb, name=\(aqMittens Jr\(aq, animal_type=\(aqcat\(aq) .ft P .fi .UNINDENT .UNINDENT .sp After a long full life, Mittens sickens and dies. We need to remove him from the database: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C herb_mittens.delete_instance() # he had a great life # Returns: 1 .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 The return value of \fBdelete_instance()\fP is the number of rows removed from the database. .UNINDENT .UNINDENT .sp Uncle Bob decides that too many animals have been dying at Herb\(aqs house, so he adopts Fido: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C herb_fido.owner = uncle_bob herb_fido.save() .ft P .fi .UNINDENT .UNINDENT .SS Retrieving Data .sp The real strength of our database is in how it allows us to retrieve data through \fIqueries\fP\&. Relational databases are excellent for making ad\-hoc queries. .SS Getting single records .sp Let\(aqs retrieve Grandma\(aqs record from the database. To get a single record from the database, use \fBSelect.get()\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C grandma = Person.select().where(Person.name == \(aqGrandma L.\(aq).get() .ft P .fi .UNINDENT .UNINDENT .sp We can also use the equivalent shorthand \fBModel.get()\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C grandma = Person.get(Person.name == \(aqGrandma L.\(aq) .ft P .fi .UNINDENT .UNINDENT .SS Lists of records .sp Let\(aqs list all the people in the database: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C for person in Person.select(): print(person.name) # prints: # Bob # Grandma L. # Herb .ft P .fi .UNINDENT .UNINDENT .sp Let\(aqs list all the cats and their owner\(aqs name: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Pet.select().where(Pet.animal_type == \(aqcat\(aq) for pet in query: print(pet.name, pet.owner.name) # prints: # Kitty Bob # Mittens Jr Herb .ft P .fi .UNINDENT .UNINDENT .sp \fBATTENTION:\fP .INDENT 0.0 .INDENT 3.5 There is a big problem with the previous query: because we are accessing \fBpet.owner.name\fP and we did not select this relation in our original query, peewee will have to perform an additional query to retrieve the pet\(aqs owner. This behavior is referred to as N+1 and it should generally be avoided. .sp For an in\-depth guide to working with relationships and joins, refer to the relationships documentation. .UNINDENT .UNINDENT .sp We can avoid the extra queries by selecting both \fIPet\fP and \fIPerson\fP, and adding a \fIjoin\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Pet .select(Pet, Person) .join(Person) .where(Pet.animal_type == \(aqcat\(aq)) for pet in query: print(pet.name, pet.owner.name) # prints: # Kitty Bob # Mittens Jr Herb .ft P .fi .UNINDENT .UNINDENT .sp Let\(aqs get all the pets owned by Bob: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C for pet in Pet.select().join(Person).where(Person.name == \(aqBob\(aq): print(pet.name) # prints: # Kitty # Fido .ft P .fi .UNINDENT .UNINDENT .sp We can do another cool thing here to get bob\(aqs pets. Since we already have an object to represent Bob, we can do this instead: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C for pet in Pet.select().where(Pet.owner == uncle_bob): print(pet.name) .ft P .fi .UNINDENT .UNINDENT .SS Sorting .sp Let\(aqs make sure these are sorted alphabetically by adding an \fBorder_by()\fP clause: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C for pet in Pet.select().where(Pet.owner == uncle_bob).order_by(Pet.name): print(pet.name) # prints: # Fido # Kitty .ft P .fi .UNINDENT .UNINDENT .sp Let\(aqs list all the people now, youngest to oldest: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C for person in Person.select().order_by(Person.birthday.desc()): print(person.name, person.birthday) # prints: # Bob 1960\-01\-15 # Herb 1950\-05\-05 # Grandma L. 1935\-03\-01 .ft P .fi .UNINDENT .UNINDENT .SS Combining filter expressions .sp Peewee supports arbitrarily\-nested expressions. Let\(aqs get all the people whose birthday was either: .INDENT 0.0 .IP \(bu 2 before 1940 (grandma) .IP \(bu 2 after 1959 (bob) .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C d1940 = date(1940, 1, 1) d1960 = date(1960, 1, 1) query = (Person .select() .where((Person.birthday < d1940) | (Person.birthday > d1960))) for person in query: print(person.name, person.birthday) # prints: # Bob 1960\-01\-15 # Grandma L. 1935\-03\-01 .ft P .fi .UNINDENT .UNINDENT .sp Now let\(aqs do the opposite. People whose birthday is between 1940 and 1960: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Person .select() .where(Person.birthday.between(d1940, d1960))) for person in query: print(person.name, person.birthday) # prints: # Herb 1950\-05\-05 .ft P .fi .UNINDENT .UNINDENT .SS Aggregates and Prefetch .sp Now let\(aqs list all the people \fIand\fP how many pets they have: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C for person in Person.select(): print(person.name, person.pets.count(), \(aqpets\(aq) # prints: # Bob 2 pets # Grandma L. 0 pets # Herb 1 pets .ft P .fi .UNINDENT .UNINDENT .sp Once again we\(aqve run into a classic example of N+1 query behavior. In this case, we\(aqre executing an additional query for every \fBPerson\fP returned by the original \fBSELECT\fP! We can avoid this by performing a \fIJOIN\fP and using a SQL function to aggregate the results. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Person .select(Person, fn.COUNT(Pet.id).alias(\(aqpet_count\(aq)) .join(Pet, JOIN.LEFT_OUTER) # include people without pets. .group_by(Person) .order_by(Person.name)) for person in query: # "pet_count" becomes an attribute on the returned model instances. print(person.name, person.pet_count, \(aqpets\(aq) # prints: # Bob 2 pets # Grandma L. 0 pets # Herb 1 pets .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Peewee provides a magical helper \fBfn()\fP, which can be used to call any SQL function. In the above example, \fBfn.COUNT(Pet.id).alias(\(aqpet_count\(aq)\fP would be translated into \fBCOUNT(pet.id) AS pet_count\fP\&. .UNINDENT .UNINDENT .sp Now let\(aqs list all the people and the names of all their pets. As you may have guessed, this could easily turn into another N+1 situation if we\(aqre not careful. .sp Before diving into the code, consider how this example is different from the earlier example where we listed all the pets and their owner\(aqs name. A pet can only have one owner, so when we performed the join from \fBPet\fP to \fBPerson\fP, there was always going to be a single match. The situation is different when we are joining from \fBPerson\fP to \fBPet\fP because a person may have zero pets or they may have several pets. Because we\(aqre using a relational databases, if we were to do a join from \fBPerson\fP to \fBPet\fP then every person with multiple pets would be repeated, once for each pet. .sp It would look like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Person .select(Person, Pet) .join(Pet, JOIN.LEFT_OUTER) .order_by(Person.name, Pet.name)) for person in query: # We need to check if they have a pet instance attached, since not all # people have pets. if hasattr(person, \(aqpet\(aq): print(person.name, person.pet.name) else: print(person.name, \(aqno pets\(aq) # prints: # Bob Fido # Bob Kitty # Grandma L. no pets # Herb Mittens Jr .ft P .fi .UNINDENT .UNINDENT .sp Usually this type of duplication is undesirable. To accommodate the more common (and intuitive) workflow of listing a person and attaching \fBa list\fP of that person\(aqs pets, we can use a special method called \fBprefetch()\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Person.select().order_by(Person.name).prefetch(Pet) for person in query: print(person.name) for pet in person.pets: print(\(aq *\(aq, pet.name) # prints: # Bob # * Kitty # * Fido # Grandma L. # Herb # * Mittens Jr .ft P .fi .UNINDENT .UNINDENT .SS SQL Functions .sp One last query. This will use a SQL function to find all people whose names start with either an upper or lower\-case \fIG\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C expression = fn.Lower(fn.Substr(Person.name, 1, 1)) == \(aqg\(aq for person in Person.select().where(expression): print(person.name) # prints: # Grandma L. .ft P .fi .UNINDENT .UNINDENT .sp This is just the basics! You can make your queries as complex as you like. Check the documentation on querying for more info. .SS Database .sp We\(aqre done with our database, let\(aqs close the connection: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db.close() .ft P .fi .UNINDENT .UNINDENT .sp In an actual application, there are some established patterns for how you would manage your database connection lifetime. For example, a web application will typically open a connection at start of request, and close the connection after generating the response. A connection pool can help eliminate latency associated with startup costs. .sp To learn about setting up your database, see the database documentation, which provides many examples. Peewee also supports configuring the database at run\-time as well as setting or changing the database at any time. .SS Working with existing databases .sp If you already have a database, you can autogenerate peewee models using pwiz\&. For instance, if I have a postgresql database named \fIcharles_blog\fP, I might run: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C python \-m pwiz \-e postgresql charles_blog > blog_models.py .ft P .fi .UNINDENT .UNINDENT .SS What next? .sp That\(aqs it for the quickstart. If you want to look at a full web\-app, check out the example\-app\&. .SS Example app .sp We\(aqll be building a simple \fItwitter\fP\-like site. The source code for the example can be found in the \fBexamples/twitter\fP directory. You can also \fI\%browse the source\-code\fP on github. There is also an example \fI\%blog app\fP if that\(aqs more to your liking, however it is not covered in this guide. .sp The example app uses the \fI\%flask\fP web framework which is very easy to get started with. If you don\(aqt have flask already, you will need to install it to run the example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C pip install flask .ft P .fi .UNINDENT .UNINDENT .SS Running the example [image] .sp After ensuring that flask is installed, \fBcd\fP into the twitter example directory and execute the \fBrun_example.py\fP script: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C python run_example.py .ft P .fi .UNINDENT .UNINDENT .sp The example app will be accessible at \fI\%http://localhost:5000/\fP .SS Diving into the code .sp For simplicity all example code is contained within a single module, \fBexamples/twitter/app.py\fP\&. For a guide on structuring larger Flask apps with peewee, check out \fI\%Structuring Flask Apps\fP\&. .SS Models .sp In the spirit of the popular web framework Django, peewee uses declarative model definitions. If you\(aqre not familiar with Django, the idea is that you declare a model class for each table. The model class then defines one or more field attributes which correspond to the table\(aqs columns. For the twitter clone, there are just three models: .INDENT 0.0 .TP .B \fIUser\fP: Represents a user account and stores the username and password, an email address for generating avatars using \fIgravatar\fP, and a datetime field indicating when that account was created. .TP .B \fIRelationship\fP: This is a utility model that contains two foreign\-keys to the \fIUser\fP model and stores which users follow one another. .TP .B \fIMessage\fP: Analogous to a tweet. The Message model stores the text content of the tweet, when it was created, and who posted it (foreign key to User). .UNINDENT .sp If you like UML, these are the tables and relationships: [image] .sp In order to create these models we need to instantiate a \fBSqliteDatabase\fP object. Then we define our model classes, specifying the columns as \fBField\fP instances on the class. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # create a peewee database instance \-\- our models will use this database to # persist information database = SqliteDatabase(DATABASE) # model definitions \-\- the standard "pattern" is to define a base model class # that specifies which database to use. then, any subclasses will automatically # use the correct storage. class BaseModel(Model): class Meta: database = database # the user model specifies its fields (or columns) declaratively, like django class User(BaseModel): username = CharField(unique=True) password = CharField() email = CharField() join_date = DateTimeField() # this model contains two foreign keys to user \-\- it essentially allows us to # model a "many\-to\-many" relationship between users. by querying and joining # on different columns we can expose who a user is "related to" and who is # "related to" a given user class Relationship(BaseModel): from_user = ForeignKeyField(User, backref=\(aqrelationships\(aq) to_user = ForeignKeyField(User, backref=\(aqrelated_to\(aq) class Meta: # \(gaindexes\(ga is a tuple of 2\-tuples, where the 2\-tuples are # a tuple of column names to index and a boolean indicating # whether the index is unique or not. indexes = ( # Specify a unique multi\-column index on from/to\-user. ((\(aqfrom_user\(aq, \(aqto_user\(aq), True), ) # a dead simple one\-to\-many relationship: one user has 0..n messages, exposed by # the foreign key. because we didn\(aqt specify, a users messages will be accessible # as a special attribute, User.messages class Message(BaseModel): user = ForeignKeyField(User, backref=\(aqmessages\(aq) content = TextField() pub_date = DateTimeField() .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Note that we create a \fIBaseModel\fP class that simply defines what database we would like to use. All other models then extend this class and will also use the correct database connection. .UNINDENT .UNINDENT .sp Peewee supports many different field types which map to different column types commonly supported by database engines. Conversion between python types and those used in the database is handled transparently, allowing you to use the following in your application: .INDENT 0.0 .IP \(bu 2 Strings (unicode or otherwise) .IP \(bu 2 Integers, floats, and \fBDecimal\fP numbers. .IP \(bu 2 Boolean values .IP \(bu 2 Dates, times and datetimes .IP \(bu 2 \fBNone\fP (NULL) .IP \(bu 2 Binary data .UNINDENT .SS Creating tables .sp In order to start using the models, its necessary to create the tables. This is a one\-time operation and can be done quickly using the interactive interpreter. We can create a small helper function to accomplish this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C def create_tables(): with database: database.create_tables([User, Relationship, Message]) .ft P .fi .UNINDENT .UNINDENT .sp Open a python shell in the directory alongside the example app and execute the following: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> from app import * >>> create_tables() .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 If you encounter an \fIImportError\fP it means that either \fIflask\fP or \fIpeewee\fP was not found and may not be installed correctly. Check the installation document for instructions on installing peewee. .UNINDENT .UNINDENT .sp Every model has a \fBcreate_table()\fP classmethod which runs a SQL \fICREATE TABLE\fP statement in the database. This method will create the table, including all columns, foreign\-key constraints, indexes, and sequences. Usually this is something you\(aqll only do once, whenever a new model is added. .sp Peewee provides a helper method \fBDatabase.create_tables()\fP which will resolve inter\-model dependencies and call \fBcreate_table()\fP on each model, ensuring the tables are created in order. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Adding fields after the table has been created will require you to either drop the table and re\-create it or manually add the columns using an \fIALTER TABLE\fP query. .sp Alternatively, you can use the schema migrations extension to alter your database schema using Python. .UNINDENT .UNINDENT .SS Establishing a database connection .sp You may have noticed in the above model code that there is a class defined on the base model named \fIMeta\fP that sets the \fBdatabase\fP attribute. Peewee allows every model to specify which database it uses. There are many Meta options you can specify which control the behavior of your model. .sp This is a peewee idiom: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C DATABASE = \(aqtweepee.db\(aq # Create a database instance that will manage the connection and # execute queries database = SqliteDatabase(DATABASE) # Create a base\-class all our models will inherit, which defines # the database we\(aqll be using. class BaseModel(Model): class Meta: database = database .ft P .fi .UNINDENT .UNINDENT .sp When developing a web application, it\(aqs common to open a connection when a request starts, and close it when the response is returned. \fBYou should always manage your connections explicitly\fP\&. For instance, if you are using a connection pool, connections will only be recycled correctly if you call \fBconnect()\fP and \fBclose()\fP\&. .sp We will tell flask that during the request/response cycle we need to create a connection to the database. Flask provides some handy decorators to make this a snap: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C @app.before_request def before_request(): database.connect() @app.after_request def after_request(response): database.close() return response .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Peewee uses thread local storage to manage connection state, so this pattern can be used with multi\-threaded WSGI servers. .UNINDENT .UNINDENT .SS Making queries .sp In the \fIUser\fP model there are a few instance methods that encapsulate some user\-specific functionality: .INDENT 0.0 .IP \(bu 2 \fBfollowing()\fP: who is this user following? .IP \(bu 2 \fBfollowers()\fP: who is following this user? .UNINDENT .sp These methods are similar in their implementation but with an important difference in the SQL \fIJOIN\fP and \fIWHERE\fP clauses: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C def following(self): # query other users through the "relationship" table return (User .select() .join(Relationship, on=Relationship.to_user) .where(Relationship.from_user == self) .order_by(User.username)) def followers(self): return (User .select() .join(Relationship, on=Relationship.from_user) .where(Relationship.to_user == self) .order_by(User.username)) .ft P .fi .UNINDENT .UNINDENT .SS Creating new objects .sp When a new user wants to join the site we need to make sure the username is available, and if so, create a new \fIUser\fP record. Looking at the \fIjoin()\fP view, we can see that our application attempts to create the User using \fBModel.create()\fP\&. We defined the \fIUser.username\fP field with a unique constraint, so if the username is taken the database will raise an \fBIntegrityError\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C try: with database.atomic(): # Attempt to create the user. If the username is taken, due to the # unique constraint, the database will raise an IntegrityError. user = User.create( username=request.form[\(aqusername\(aq], password=md5(request.form[\(aqpassword\(aq]).hexdigest(), email=request.form[\(aqemail\(aq], join_date=datetime.datetime.now()) # mark the user as being \(aqauthenticated\(aq by setting the session vars auth_user(user) return redirect(url_for(\(aqhomepage\(aq)) except IntegrityError: flash(\(aqThat username is already taken\(aq) .ft P .fi .UNINDENT .UNINDENT .sp We will use a similar approach when a user wishes to follow someone. To indicate a following relationship, we create a row in the \fIRelationship\fP table pointing from one user to another. Due to the unique index on \fBfrom_user\fP and \fBto_user\fP, we will be sure not to end up with duplicate rows: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C user = get_object_or_404(User, username=username) try: with database.atomic(): Relationship.create( from_user=get_current_user(), to_user=user) except IntegrityError: pass .ft P .fi .UNINDENT .UNINDENT .SS Performing subqueries .sp If you are logged\-in and visit the twitter homepage, you will see tweets from the users that you follow. In order to implement this cleanly, we can use a subquery: .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 The subquery, \fBuser.following()\fP, by default would ordinarily select all the columns on the \fBUser\fP model. Because we\(aqre using it as a subquery, peewee will only select the primary key. .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # python code user = get_current_user() messages = (Message .select() .where(Message.user.in_(user.following())) .order_by(Message.pub_date.desc())) .ft P .fi .UNINDENT .UNINDENT .sp This code corresponds to the following SQL query: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT t1."id", t1."user_id", t1."content", t1."pub_date" FROM "message" AS t1 WHERE t1."user_id" IN ( SELECT t2."id" FROM "user" AS t2 INNER JOIN "relationship" AS t3 ON t2."id" = t3."to_user_id" WHERE t3."from_user_id" = ? ) .ft P .fi .UNINDENT .UNINDENT .SS Other topics of interest .sp There are a couple other neat things going on in the example app that are worth mentioning briefly. .INDENT 0.0 .IP \(bu 2 Support for paginating lists of results is implemented in a simple function called \fBobject_list\fP (after it\(aqs corollary in Django). This function is used by all the views that return lists of objects. .INDENT 2.0 .INDENT 3.5 .sp .nf .ft C def object_list(template_name, qr, var_name=\(aqobject_list\(aq, **kwargs): kwargs.update( page=int(request.args.get(\(aqpage\(aq, 1)), pages=qr.count() / 20 + 1) kwargs[var_name] = qr.paginate(kwargs[\(aqpage\(aq]) return render_template(template_name, **kwargs) .ft P .fi .UNINDENT .UNINDENT .IP \(bu 2 Simple authentication system with a \fBlogin_required\fP decorator. The first function simply adds user data into the current session when a user successfully logs in. The decorator \fBlogin_required\fP can be used to wrap view functions, checking for whether the session is authenticated and if not redirecting to the login page. .INDENT 2.0 .INDENT 3.5 .sp .nf .ft C def auth_user(user): session[\(aqlogged_in\(aq] = True session[\(aquser\(aq] = user session[\(aqusername\(aq] = user.username flash(\(aqYou are logged in as %s\(aq % (user.username)) def login_required(f): @wraps(f) def inner(*args, **kwargs): if not session.get(\(aqlogged_in\(aq): return redirect(url_for(\(aqlogin\(aq)) return f(*args, **kwargs) return inner .ft P .fi .UNINDENT .UNINDENT .IP \(bu 2 Return a 404 response instead of throwing exceptions when an object is not found in the database. .INDENT 2.0 .INDENT 3.5 .sp .nf .ft C def get_object_or_404(model, *expressions): try: return model.get(*expressions) except model.DoesNotExist: abort(404) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 To avoid having to frequently copy/paste \fBobject_list()\fP or \fBget_object_or_404()\fP, these functions are included as part of the playhouse flask extension module\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.flask_utils import get_object_or_404, object_list .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS More examples .sp There are more examples included in the peewee \fI\%examples directory\fP, including: .INDENT 0.0 .IP \(bu 2 \fI\%Example blog app\fP using Flask and peewee. Also see \fI\%accompanying blog post\fP\&. .IP \(bu 2 \fI\%An encrypted command\-line diary\fP\&. There is a \fI\%companion blog post\fP you might enjoy as well. .IP \(bu 2 \fI\%Analytics web\-service\fP (like a lite version of Google Analytics). Also check out the \fI\%companion blog post\fP\&. .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Like these snippets and interested in more? Check out \fI\%flask\-peewee\fP \- a flask plugin that provides a django\-like Admin interface, RESTful API, Authentication and more for your peewee models. .UNINDENT .UNINDENT .SS Using Peewee Interactively .sp Peewee contains helpers for working interactively from a Python interpreter or something like a Jupyter notebook. For this example, we\(aqll assume that we have a pre\-existing Sqlite database with the following simple schema: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C CREATE TABLE IF NOT EXISTS "event" ( "id" INTEGER NOT NULL PRIMARY KEY, "key" TEXT NOT NULL, "timestamp" DATETIME NOT NULL, "metadata" TEXT NOT NULL); .ft P .fi .UNINDENT .UNINDENT .sp To experiment with querying this database from an interactive interpreter session, we would start our interpreter and import the following helpers: .INDENT 0.0 .IP \(bu 2 \fBpeewee.SqliteDatabase\fP \- to reference the "events.db" .IP \(bu 2 \fBplayhouse.reflection.generate_models\fP \- to generate models from an existing database. .IP \(bu 2 \fBplayhouse.reflection.print_model\fP \- to view the model definition. .IP \(bu 2 \fBplayhouse.reflection.print_table_sql\fP \- to view the table SQL. .UNINDENT .sp Our terminal session might look like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> from peewee import SqliteDatabase >>> from playhouse.reflection import generate_models, print_model, print_table_sql >>> .ft P .fi .UNINDENT .UNINDENT .sp The \fBgenerate_models()\fP function will introspect the database and generate model classes for all the tables that are found. This is a handy way to get started and can save a lot of typing. The function returns a dictionary keyed by the table name, with the generated model as the corresponding value: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> db = SqliteDatabase(\(aqevents.db\(aq) >>> models = generate_models(db) >>> list(models.items()) [(\(aqevents\(aq, )] >>> globals().update(models) # Inject models into global namespace. >>> event .ft P .fi .UNINDENT .UNINDENT .sp To take a look at the model definition, which lists the model\(aqs fields and data\-type, we can use the \fBprint_model()\fP function: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> print_model(event) event id AUTO key TEXT timestamp DATETIME metadata TEXT .ft P .fi .UNINDENT .UNINDENT .sp We can also generate a SQL \fBCREATE TABLE\fP for the introspected model, if you find that easier to read. This should match the actual table definition in the introspected database: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> print_table_sql(event) CREATE TABLE IF NOT EXISTS "event" ( "id" INTEGER NOT NULL PRIMARY KEY, "key" TEXT NOT NULL, "timestamp" DATETIME NOT NULL, "metadata" TEXT NOT NULL) .ft P .fi .UNINDENT .UNINDENT .sp Now that we are familiar with the structure of the table we\(aqre working with, we can run some queries on the generated \fBevent\fP model: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for e in event.select().order_by(event.timestamp).limit(5): \&... print(e.key, e.timestamp) \&... e00 2019\-01\-01 00:01:00 e01 2019\-01\-01 00:02:00 e02 2019\-01\-01 00:03:00 e03 2019\-01\-01 00:04:00 e04 2019\-01\-01 00:05:00 >>> event.select(fn.MIN(event.timestamp), fn.MAX(event.timestamp)).scalar(as_tuple=True) (datetime.datetime(2019, 1, 1, 0, 1), datetime.datetime(2019, 1, 1, 1, 0)) >>> event.select().count() # Or, len(event) 60 .ft P .fi .UNINDENT .UNINDENT .sp For more information about these APIs and other similar reflection utilities, see the reflection section of the playhouse extensions document. .sp To generate an actual Python module containing model definitions for an existing database, you can use the command\-line pwiz tool. Here is a quick example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ pwiz \-e sqlite events.db > events.py .ft P .fi .UNINDENT .UNINDENT .sp The \fBevents.py\fP file will now be an import\-able module containing a database instance (referencing the \fBevents.db\fP) along with model definitions for any tables found in the database. \fBpwiz\fP does some additional nice things like introspecting indexes and adding proper flags for \fBNULL\fP/\fBNOT NULL\fP constraints, etc. .sp The APIs discussed in this section: .INDENT 0.0 .IP \(bu 2 \fBgenerate_models()\fP .IP \(bu 2 \fBprint_model()\fP .IP \(bu 2 \fBprint_table_sql()\fP .UNINDENT .sp More low\-level APIs are also available on the \fBDatabase\fP instance: .INDENT 0.0 .IP \(bu 2 \fBDatabase.get_tables()\fP .IP \(bu 2 \fBDatabase.get_indexes()\fP .IP \(bu 2 \fBDatabase.get_columns()\fP (for a given table) .IP \(bu 2 \fBDatabase.get_primary_keys()\fP (for a given table) .IP \(bu 2 \fBDatabase.get_foreign_keys()\fP (for a given table) .UNINDENT .SS Contributing .sp In order to continually improve, Peewee needs the help of developers like you. Whether it\(aqs contributing patches, submitting bug reports, or just asking and answering questions, you are helping to make Peewee a better library. .sp In this document I\(aqll describe some of the ways you can help. .SS Patches .sp Do you have an idea for a new feature, or is there a clunky API you\(aqd like to improve? Before coding it up and submitting a pull\-request, \fI\%open a new issue\fP on GitHub describing your proposed changes. This doesn\(aqt have to be anything formal, just a description of what you\(aqd like to do and why. .sp When you\(aqre ready, you can submit a pull\-request with your changes. Successful patches will have the following: .INDENT 0.0 .IP \(bu 2 Unit tests. .IP \(bu 2 Documentation, both prose form and general API documentation\&. .IP \(bu 2 Code that conforms stylistically with the rest of the Peewee codebase. .UNINDENT .SS Bugs .sp If you\(aqve found a bug, please check to see if it has \fI\%already been reported\fP, and if not \fI\%create an issue on GitHub\fP\&. The more information you include, the more quickly the bug will get fixed, so please try to include the following: .INDENT 0.0 .IP \(bu 2 Traceback and the error message (please \fI\%format your code\fP!) .IP \(bu 2 Relevant portions of your code or code to reproduce the error .IP \(bu 2 Peewee version: \fBpython \-c "from peewee import __version__; print(__version__)"\fP .IP \(bu 2 Which database you\(aqre using .UNINDENT .sp If you have found a bug in the code and submit a failing test\-case, then hats\-off to you, you are a hero! .SS Questions .sp If you have questions about how to do something with peewee, then I recommend either: .INDENT 0.0 .IP \(bu 2 Ask on StackOverflow. I check SO just about every day for new peewee questions and try to answer them. This has the benefit also of preserving the question and answer for other people to find. .IP \(bu 2 Ask on the mailing list, \fI\%https://groups.google.com/group/peewee\-orm\fP .UNINDENT .SS Database .sp The Peewee \fBDatabase\fP object represents a connection to a database. The \fBDatabase\fP class is instantiated with all the information needed to open a connection to a database, and then can be used to: .INDENT 0.0 .IP \(bu 2 Open and close connections. .IP \(bu 2 Execute queries. .IP \(bu 2 Manage transactions (and savepoints). .IP \(bu 2 Introspect tables, columns, indexes, and constraints. .UNINDENT .sp Peewee comes with support for SQLite, MySQL and Postgres. Each database class provides some basic, database\-specific configuration options. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from peewee import * # SQLite database using WAL journal mode and 64MB cache. sqlite_db = SqliteDatabase(\(aq/path/to/app.db\(aq, pragmas={ \(aqjournal_mode\(aq: \(aqwal\(aq, \(aqcache_size\(aq: \-1024 * 64}) # Connect to a MySQL database on network. mysql_db = MySQLDatabase(\(aqmy_app\(aq, user=\(aqapp\(aq, password=\(aqdb_password\(aq, host=\(aq10.1.0.8\(aq, port=3306) # Connect to a Postgres database. pg_db = PostgresqlDatabase(\(aqmy_app\(aq, user=\(aqpostgres\(aq, password=\(aqsecret\(aq, host=\(aq10.1.0.9\(aq, port=5432) .ft P .fi .UNINDENT .UNINDENT .sp Peewee provides advanced support for SQLite, Postgres and CockroachDB via database\-specific extension modules. To use the extended\-functionality, import the appropriate database\-specific module and use the database class provided: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.sqlite_ext import SqliteExtDatabase # Use SQLite (will register a REGEXP function and set busy timeout to 3s). db = SqliteExtDatabase(\(aq/path/to/app.db\(aq, regexp_function=True, timeout=3, pragmas={\(aqjournal_mode\(aq: \(aqwal\(aq}) from playhouse.postgres_ext import PostgresqlExtDatabase # Use Postgres (and register hstore extension). db = PostgresqlExtDatabase(\(aqmy_app\(aq, user=\(aqpostgres\(aq, register_hstore=True) from playhouse.cockroachdb import CockroachDatabase # Use CockroachDB. db = CockroachDatabase(\(aqmy_app\(aq, user=\(aqroot\(aq, port=26257, host=\(aq10.1.0.8\(aq) .ft P .fi .UNINDENT .UNINDENT .sp For more information on database extensions, see: .INDENT 0.0 .IP \(bu 2 postgres_ext .IP \(bu 2 sqlite_ext .IP \(bu 2 crdb .IP \(bu 2 sqlcipher_ext (encrypted SQLite database). .IP \(bu 2 apsw .IP \(bu 2 sqliteq .UNINDENT .SS Initializing a Database .sp The \fBDatabase\fP initialization method expects the name of the database as the first parameter. Subsequent keyword arguments are passed to the underlying database driver when establishing the connection, allowing you to pass vendor\-specific parameters easily. .sp For instance, with Postgresql it is common to need to specify the \fBhost\fP, \fBuser\fP and \fBpassword\fP when creating your connection. These are not standard Peewee \fBDatabase\fP parameters, so they will be passed directly back to \fBpsycopg2\fP when creating connections: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = PostgresqlDatabase( \(aqdatabase_name\(aq, # Required by Peewee. user=\(aqpostgres\(aq, # Will be passed directly to psycopg2. password=\(aqsecret\(aq, # Ditto. host=\(aqdb.mysite.com\(aq) # Ditto. .ft P .fi .UNINDENT .UNINDENT .sp As another example, the \fBpymysql\fP driver accepts a \fBcharset\fP parameter which is not a standard Peewee \fBDatabase\fP parameter. To set this value, simply pass in \fBcharset\fP alongside your other values: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = MySQLDatabase(\(aqdatabase_name\(aq, user=\(aqwww\-data\(aq, charset=\(aqutf8mb4\(aq) .ft P .fi .UNINDENT .UNINDENT .sp Consult your database driver\(aqs documentation for the available parameters: .INDENT 0.0 .IP \(bu 2 Postgres: \fI\%psycopg2\fP .IP \(bu 2 MySQL: \fI\%MySQLdb\fP .IP \(bu 2 MySQL: \fI\%pymysql\fP .IP \(bu 2 SQLite: \fI\%sqlite3\fP .IP \(bu 2 CockroachDB: see \fI\%psycopg2\fP .UNINDENT .SS Using Postgresql .sp To connect to a Postgresql database, we will use \fBPostgresqlDatabase\fP\&. The first parameter is always the name of the database, and after that you can specify arbitrary \fI\%psycopg2 parameters\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C psql_db = PostgresqlDatabase(\(aqmy_database\(aq, user=\(aqpostgres\(aq) class BaseModel(Model): """A base model that will use our Postgresql database""" class Meta: database = psql_db class User(BaseModel): username = CharField() .ft P .fi .UNINDENT .UNINDENT .sp The playhouse contains a Postgresql extension module which provides many postgres\-specific features such as: .INDENT 0.0 .IP \(bu 2 Arrays .IP \(bu 2 HStore .IP \(bu 2 JSON .IP \(bu 2 Server\-side cursors .IP \(bu 2 And more! .UNINDENT .sp If you would like to use these awesome features, use the \fBPostgresqlExtDatabase\fP from the \fBplayhouse.postgres_ext\fP module: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.postgres_ext import PostgresqlExtDatabase psql_db = PostgresqlExtDatabase(\(aqmy_database\(aq, user=\(aqpostgres\(aq) .ft P .fi .UNINDENT .UNINDENT .SS Isolation level .sp As of Peewee 3.9.7, the isolation level can be specified as an initialization parameter, using the symbolic constants in \fBpsycopg2.extensions\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from psycopg2.extensions import ISOLATION_LEVEL_SERIALIZABLE db = PostgresqlDatabase(\(aqmy_app\(aq, user=\(aqpostgres\(aq, host=\(aqdb\-host\(aq, isolation_level=ISOLATION_LEVEL_SERIALIZABLE) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 In older versions, you can manually set the isolation level on the underlying psycopg2 connection. This can be done in a one\-off fashion: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = PostgresqlDatabase(...) conn = db.connection() # returns current connection. from psycopg2.extensions import ISOLATION_LEVEL_SERIALIZABLE conn.set_isolation_level(ISOLATION_LEVEL_SERIALIZABLE) .ft P .fi .UNINDENT .UNINDENT .sp To run this every time a connection is created, subclass and implement the \fB_initialize_database()\fP hook, which is designed for this purpose: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class SerializedPostgresqlDatabase(PostgresqlDatabase): def _initialize_connection(self, conn): conn.set_isolation_level(ISOLATION_LEVEL_SERIALIZABLE) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS Using CockroachDB .sp Connect to CockroachDB (CRDB) using the \fBCockroachDatabase\fP database class, defined in \fBplayhouse.cockroachdb\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.cockroachdb import CockroachDatabase db = CockroachDatabase(\(aqmy_app\(aq, user=\(aqroot\(aq, port=26257, host=\(aqlocalhost\(aq) .ft P .fi .UNINDENT .UNINDENT .sp CRDB provides client\-side transaction retries, which are available using a special \fBCockroachDatabase.run_transaction()\fP helper\-method. This method accepts a callable, which is responsible for executing any transactional statements that may need to be retried. .sp Simplest possible example of \fBrun_transaction()\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C def create_user(email): # Callable that accepts a single argument (the database instance) and # which is responsible for executing the transactional SQL. def callback(db_ref): return User.create(email=email) return db.run_transaction(callback, max_attempts=10) huey = create_user(\(aqhuey@example.com\(aq) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 The \fBcockroachdb.ExceededMaxAttempts\fP exception will be raised if the transaction cannot be committed after the given number of attempts. If the SQL is mal\-formed, violates a constraint, etc., then the function will raise the exception to the caller. .UNINDENT .UNINDENT .sp For more information, see: .INDENT 0.0 .IP \(bu 2 CRDB extension documentation .IP \(bu 2 Arrays (postgres\-specific, but applies to CRDB) .IP \(bu 2 JSON (postgres\-specific, but applies to CRDB) .UNINDENT .SS Using SQLite .sp To connect to a SQLite database, we will use \fBSqliteDatabase\fP\&. The first parameter is the filename containing the database, or the string \fB\(aq:memory:\(aq\fP to create an in\-memory database. After the database filename, you can specify a list or pragmas or any other arbitrary \fI\%sqlite3 parameters\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C sqlite_db = SqliteDatabase(\(aqmy_app.db\(aq, pragmas={\(aqjournal_mode\(aq: \(aqwal\(aq}) class BaseModel(Model): """A base model that will use our Sqlite database.""" class Meta: database = sqlite_db class User(BaseModel): username = TextField() # etc, etc .ft P .fi .UNINDENT .UNINDENT .sp Peewee includes a SQLite extension module which provides many SQLite\-specific features such as full\-text search, json extension support, and much, much more. If you would like to use these awesome features, use the \fBSqliteExtDatabase\fP from the \fBplayhouse.sqlite_ext\fP module: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.sqlite_ext import SqliteExtDatabase sqlite_db = SqliteExtDatabase(\(aqmy_app.db\(aq, pragmas={ \(aqjournal_mode\(aq: \(aqwal\(aq, # WAL\-mode. \(aqcache_size\(aq: \-64 * 1000, # 64MB cache. \(aqsynchronous\(aq: 0}) # Let the OS manage syncing. .ft P .fi .UNINDENT .UNINDENT .SS PRAGMA statements .sp SQLite allows run\-time configuration of a number of parameters through \fBPRAGMA\fP statements (\fI\%SQLite documentation\fP). These statements are typically run when a new database connection is created. To run one or more \fBPRAGMA\fP statements against new connections, you can specify them as a dictionary or a list of 2\-tuples containing the pragma name and value: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = SqliteDatabase(\(aqmy_app.db\(aq, pragmas={ \(aqjournal_mode\(aq: \(aqwal\(aq, \(aqcache_size\(aq: 10000, # 10000 pages, or ~40MB \(aqforeign_keys\(aq: 1, # Enforce foreign\-key constraints }) .ft P .fi .UNINDENT .UNINDENT .sp PRAGMAs may also be configured dynamically using either the \fBpragma()\fP method or the special properties exposed on the \fBSqliteDatabase\fP object: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Set cache size to 64MB for *current connection*. db.pragma(\(aqcache_size\(aq, \-1024 * 64) # Same as above. db.cache_size = \-1024 * 64 # Read the value of several pragmas: print(\(aqcache_size:\(aq, db.cache_size) print(\(aqforeign_keys:\(aq, db.foreign_keys) print(\(aqjournal_mode:\(aq, db.journal_mode) print(\(aqpage_size:\(aq, db.page_size) # Set foreign_keys pragma on current connection *AND* on all # connections opened subsequently. db.pragma(\(aqforeign_keys\(aq, 1, permanent=True) .ft P .fi .UNINDENT .UNINDENT .sp \fBATTENTION:\fP .INDENT 0.0 .INDENT 3.5 Pragmas set using the \fBpragma()\fP method, by default, do not persist after the connection is closed. To configure a pragma to be run whenever a connection is opened, specify \fBpermanent=True\fP\&. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 A full list of PRAGMA settings, their meaning and accepted values can be found in the SQLite documentation: \fI\%http://sqlite.org/pragma.html\fP .UNINDENT .UNINDENT .SS Recommended Settings .sp The following settings are what I use with SQLite for a typical web application database. .TS center; |l|l|l|. _ T{ pragma T} T{ recommended setting T} T{ explanation T} _ T{ journal_mode T} T{ wal T} T{ allow readers and writers to co\-exist T} _ T{ cache_size T} T{ \-1 * data_size_kb T} T{ set page\-cache size in KiB, e.g. \-32000 = 32MB T} _ T{ foreign_keys T} T{ 1 T} T{ enforce foreign\-key constraints T} _ T{ ignore_check_constraints T} T{ 0 T} T{ enforce CHECK constraints T} _ T{ synchronous T} T{ 0 T} T{ let OS handle fsync (use with caution) T} _ .TE .sp Example database using the above options: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = SqliteDatabase(\(aqmy_app.db\(aq, pragmas={ \(aqjournal_mode\(aq: \(aqwal\(aq, \(aqcache_size\(aq: \-1 * 64000, # 64MB \(aqforeign_keys\(aq: 1, \(aqignore_check_constraints\(aq: 0, \(aqsynchronous\(aq: 0}) .ft P .fi .UNINDENT .UNINDENT .SS User\-defined functions .sp SQLite can be extended with user\-defined Python code. The \fBSqliteDatabase\fP class supports three types of user\-defined extensions: .INDENT 0.0 .IP \(bu 2 Functions \- which take any number of parameters and return a single value. .IP \(bu 2 Aggregates \- which aggregate parameters from multiple rows and return a single value. .IP \(bu 2 Collations \- which describe how to sort some value. .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 For even more extension support, see \fBSqliteExtDatabase\fP, which is in the \fBplayhouse.sqlite_ext\fP module. .UNINDENT .UNINDENT .sp Example user\-defined function: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = SqliteDatabase(\(aqanalytics.db\(aq) from urllib.parse import urlparse @db.func(\(aqhostname\(aq) def hostname(url): if url is not None: return urlparse(url).netloc # Call this function in our code: # The following finds the most common hostnames of referrers by count: query = (PageView .select(fn.hostname(PageView.referrer), fn.COUNT(PageView.id)) .group_by(fn.hostname(PageView.referrer)) .order_by(fn.COUNT(PageView.id).desc())) .ft P .fi .UNINDENT .UNINDENT .sp Example user\-defined aggregate: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from hashlib import md5 @db.aggregate(\(aqmd5\(aq) class MD5Checksum(object): def __init__(self): self.checksum = md5() def step(self, value): self.checksum.update(value.encode(\(aqutf\-8\(aq)) def finalize(self): return self.checksum.hexdigest() # Usage: # The following computes an aggregate MD5 checksum for files broken # up into chunks and stored in the database. query = (FileChunk .select(FileChunk.filename, fn.MD5(FileChunk.data)) .group_by(FileChunk.filename) .order_by(FileChunk.filename, FileChunk.sequence)) .ft P .fi .UNINDENT .UNINDENT .sp Example collation: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C @db.collation(\(aqireverse\(aq) def collate_reverse(s1, s2): # Case\-insensitive reverse. s1, s2 = s1.lower(), s2.lower() return (s1 < s2) \- (s1 > s2) # Equivalent to \-cmp(s1, s2) # To use this collation to sort books in reverse order... Book.select().order_by(collate_reverse.collation(Book.title)) # Or... Book.select().order_by(Book.title.asc(collation=\(aqreverse\(aq)) .ft P .fi .UNINDENT .UNINDENT .sp Example user\-defined table\-value function (see \fBTableFunction\fP and \fBtable_function\fP) for additional details: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.sqlite_ext import TableFunction db = SqliteDatabase(\(aqmy_app.db\(aq) @db.table_function(\(aqseries\(aq) class Series(TableFunction): columns = [\(aqvalue\(aq] params = [\(aqstart\(aq, \(aqstop\(aq, \(aqstep\(aq] def initialize(self, start=0, stop=None, step=1): """ Table\-functions declare an initialize() method, which is called with whatever arguments the user has called the function with. """ self.start = self.current = start self.stop = stop or float(\(aqInf\(aq) self.step = step def iterate(self, idx): """ Iterate is called repeatedly by the SQLite database engine until the required number of rows has been read **or** the function raises a \(gaStopIteration\(ga signalling no more rows are available. """ if self.current > self.stop: raise StopIteration ret, self.current = self.current, self.current + self.step return (ret,) # Usage: cursor = db.execute_sql(\(aqSELECT * FROM series(?, ?, ?)\(aq, (0, 5, 2)) for value, in cursor: print(value) # Prints: # 0 # 2 # 4 .ft P .fi .UNINDENT .UNINDENT .sp For more information, see: .INDENT 0.0 .IP \(bu 2 \fBSqliteDatabase.func()\fP .IP \(bu 2 \fBSqliteDatabase.aggregate()\fP .IP \(bu 2 \fBSqliteDatabase.collation()\fP .IP \(bu 2 \fBSqliteDatabase.table_function()\fP .IP \(bu 2 For even more SQLite extensions, see sqlite_ext .UNINDENT .SS Set locking mode for transaction .sp SQLite transactions can be opened in three different modes: .INDENT 0.0 .IP \(bu 2 \fIDeferred\fP (\fBdefault\fP) \- only acquires lock when a read or write is performed. The first read creates a \fI\%shared lock\fP and the first write creates a \fI\%reserved lock\fP\&. Because the acquisition of the lock is deferred until actually needed, it is possible that another thread or process could create a separate transaction and write to the database after the BEGIN on the current thread has executed. .IP \(bu 2 \fIImmediate\fP \- a \fI\%reserved lock\fP is acquired immediately. In this mode, no other database may write to the database or open an \fIimmediate\fP or \fIexclusive\fP transaction. Other processes can continue to read from the database, however. .IP \(bu 2 \fIExclusive\fP \- opens an \fI\%exclusive lock\fP which prevents all (except for read uncommitted) connections from accessing the database until the transaction is complete. .UNINDENT .sp Example specifying the locking mode: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = SqliteDatabase(\(aqapp.db\(aq) with db.atomic(\(aqEXCLUSIVE\(aq): do_something() @db.atomic(\(aqIMMEDIATE\(aq) def some_other_function(): # This function is wrapped in an "IMMEDIATE" transaction. do_something_else() .ft P .fi .UNINDENT .UNINDENT .sp For more information, see the SQLite \fI\%locking documentation\fP\&. To learn more about transactions in Peewee, see the \fI\%Managing Transactions\fP documentation. .SS APSW, an Advanced SQLite Driver .sp Peewee also comes with an alternate SQLite database that uses apsw, an advanced Python SQLite driver. More information on APSW can be obtained on the \fI\%APSW project website\fP\&. APSW provides special features like: .INDENT 0.0 .IP \(bu 2 Virtual tables, virtual file\-systems, Blob I/O, backups and file control. .IP \(bu 2 Connections can be shared across threads without any additional locking. .IP \(bu 2 Transactions are managed explicitly by your code. .IP \(bu 2 Unicode is handled \fIcorrectly\fP\&. .IP \(bu 2 APSW is faster that the standard library sqlite3 module. .IP \(bu 2 Exposes pretty much the entire SQLite C API to your Python app. .UNINDENT .sp If you would like to use APSW, use the \fBAPSWDatabase\fP from the \fIapsw_ext\fP module: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.apsw_ext import APSWDatabase apsw_db = APSWDatabase(\(aqmy_app.db\(aq) .ft P .fi .UNINDENT .UNINDENT .SS Using MySQL .sp To connect to a MySQL database, we will use \fBMySQLDatabase\fP\&. After the database name, you can specify arbitrary connection parameters that will be passed back to the driver (either MySQLdb or pymysql). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C mysql_db = MySQLDatabase(\(aqmy_database\(aq) class BaseModel(Model): """A base model that will use our MySQL database""" class Meta: database = mysql_db class User(BaseModel): username = CharField() # etc, etc .ft P .fi .UNINDENT .UNINDENT .SS Error 2006: MySQL server has gone away .sp This particular error can occur when MySQL kills an idle database connection. This typically happens with web apps that do not explicitly manage database connections. What happens is your application starts, a connection is opened to handle the first query that executes, and, since that connection is never closed, it remains open, waiting for more queries. .sp To fix this, make sure you are explicitly connecting to the database when you need to execute queries, and close your connection when you are done. In a web\-application, this typically means you will open a connection when a request comes in, and close the connection when you return a response. .sp See the \fI\%Framework Integration\fP section for examples of configuring common web frameworks to manage database connections. .SS Connecting using a Database URL .sp The playhouse module db_url provides a helper \fBconnect()\fP function that accepts a database URL and returns a \fBDatabase\fP instance. .sp Example code: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C import os from peewee import * from playhouse.db_url import connect # Connect to the database URL defined in the environment, falling # back to a local Sqlite database if no database URL is specified. db = connect(os.environ.get(\(aqDATABASE\(aq) or \(aqsqlite:///default.db\(aq) class BaseModel(Model): class Meta: database = db .ft P .fi .UNINDENT .UNINDENT .sp Example database URLs: .INDENT 0.0 .IP \(bu 2 \fBsqlite:///my_database.db\fP will create a \fBSqliteDatabase\fP instance for the file \fBmy_database.db\fP in the current directory. .IP \(bu 2 \fBsqlite:///:memory:\fP will create an in\-memory \fBSqliteDatabase\fP instance. .IP \(bu 2 \fBpostgresql://postgres:my_password@localhost:5432/my_database\fP will create a \fBPostgresqlDatabase\fP instance. A username and password are provided, as well as the host and port to connect to. .IP \(bu 2 \fBmysql://user:passwd@ip:port/my_db\fP will create a \fBMySQLDatabase\fP instance for the local MySQL database \fImy_db\fP\&. .IP \(bu 2 More examples in the db_url documentation\&. .UNINDENT .SS Run\-time database configuration .sp Sometimes the database connection settings are not known until run\-time, when these values may be loaded from a configuration file or the environment. In these cases, you can \fIdefer\fP the initialization of the database by specifying \fBNone\fP as the database_name. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C database = PostgresqlDatabase(None) # Un\-initialized database. class SomeModel(Model): class Meta: database = database .ft P .fi .UNINDENT .UNINDENT .sp If you try to connect or issue any queries while your database is uninitialized you will get an exception: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> database.connect() Exception: Error, database not properly initialized before opening connection .ft P .fi .UNINDENT .UNINDENT .sp To initialize your database, call the \fBinit()\fP method with the database name and any additional keyword arguments: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C database_name = input(\(aqWhat is the name of the db? \(aq) database.init(database_name, host=\(aqlocalhost\(aq, user=\(aqpostgres\(aq) .ft P .fi .UNINDENT .UNINDENT .sp For even more control over initializing your database, see the next section, \fI\%Dynamically defining a database\fP\&. .SS Dynamically defining a database .sp For even more control over how your database is defined/initialized, you can use the \fBDatabaseProxy\fP helper. \fBDatabaseProxy\fP objects act as a placeholder, and then at run\-time you can swap it out for a different object. In the example below, we will swap out the database depending on how the app is configured: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C database_proxy = DatabaseProxy() # Create a proxy for our db. class BaseModel(Model): class Meta: database = database_proxy # Use proxy for our DB. class User(BaseModel): username = CharField() # Based on configuration, use a different database. if app.config[\(aqDEBUG\(aq]: database = SqliteDatabase(\(aqlocal.db\(aq) elif app.config[\(aqTESTING\(aq]: database = SqliteDatabase(\(aq:memory:\(aq) else: database = PostgresqlDatabase(\(aqmega_production_db\(aq) # Configure our proxy to use the db we specified in config. database_proxy.initialize(database) .ft P .fi .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 Only use this method if your actual database driver varies at run\-time. For instance, if your tests and local dev environment run on SQLite, but your deployed app uses PostgreSQL, you can use the \fBDatabaseProxy\fP to swap out engines at run\-time. .sp However, if it is only connection values that vary at run\-time, such as the path to the database file, or the database host, you should instead use \fBDatabase.init()\fP\&. See \fI\%Run\-time database configuration\fP for more details. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 It may be easier to avoid the use of \fBDatabaseProxy\fP and instead use \fBDatabase.bind()\fP and related methods to set or change the database. See \fI\%Setting the database at run\-time\fP for details. .UNINDENT .UNINDENT .SS Setting the database at run\-time .sp We have seen three ways that databases can be configured with Peewee: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # The usual way: db = SqliteDatabase(\(aqmy_app.db\(aq, pragmas={\(aqjournal_mode\(aq: \(aqwal\(aq}) # Specify the details at run\-time: db = SqliteDatabase(None) \&... db.init(db_filename, pragmas={\(aqjournal_mode\(aq: \(aqwal\(aq}) # Or use a placeholder: db = DatabaseProxy() \&... db.initialize(SqliteDatabase(\(aqmy_app.db\(aq, pragmas={\(aqjournal_mode\(aq: \(aqwal\(aq})) .ft P .fi .UNINDENT .UNINDENT .sp Peewee can also set or change the database for your model classes. This technique is used by the Peewee test suite to bind test model classes to various database instances when running the tests. .sp There are two sets of complementary methods: .INDENT 0.0 .IP \(bu 2 \fBDatabase.bind()\fP and \fBModel.bind()\fP \- bind one or more models to a database. .IP \(bu 2 \fBDatabase.bind_ctx()\fP and \fBModel.bind_ctx()\fP \- which are the same as their \fBbind()\fP counterparts, but return a context\-manager and are useful when the database should only be changed temporarily. .UNINDENT .sp As an example, we\(aqll declare two models \fBwithout\fP specifying any database: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class User(Model): username = TextField() class Tweet(Model): user = ForeignKeyField(User, backref=\(aqtweets\(aq) content = TextField() timestamp = TimestampField() .ft P .fi .UNINDENT .UNINDENT .sp Bind the models to a database at run\-time: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C postgres_db = PostgresqlDatabase(\(aqmy_app\(aq, user=\(aqpostgres\(aq) sqlite_db = SqliteDatabase(\(aqmy_app.db\(aq) # At this point, the User and Tweet models are NOT bound to any database. # Let\(aqs bind them to the Postgres database: postgres_db.bind([User, Tweet]) # Now we will temporarily bind them to the sqlite database: with sqlite_db.bind_ctx([User, Tweet]): # User and Tweet are now bound to the sqlite database. assert User._meta.database is sqlite_db # User and Tweet are once again bound to the Postgres database. assert User._meta.database is postgres_db .ft P .fi .UNINDENT .UNINDENT .sp The \fBModel.bind()\fP and \fBModel.bind_ctx()\fP methods work the same for binding a given model class: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Bind the user model to the sqlite db. By default, Peewee will also # bind any models that are related to User via foreign\-key as well. User.bind(sqlite_db) assert User._meta.database is sqlite_db assert Tweet._meta.database is sqlite_db # Related models bound too. # Here we will temporarily bind *just* the User model to the postgres db. with User.bind_ctx(postgres_db, bind_backrefs=False): assert User._meta.database is postgres_db assert Tweet._meta.database is sqlite_db # Has not changed. # And now User is back to being bound to the sqlite_db. assert User._meta.database is sqlite_db .ft P .fi .UNINDENT .UNINDENT .sp The \fI\%Testing Peewee Applications\fP section of this document also contains some examples of using the \fBbind()\fP methods. .SS Connection Management .sp To open a connection to a database, use the \fBDatabase.connect()\fP method: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> db = SqliteDatabase(\(aq:memory:\(aq) # In\-memory SQLite database. >>> db.connect() True .ft P .fi .UNINDENT .UNINDENT .sp If we try to call \fBconnect()\fP on an already\-open database, we get a \fBOperationalError\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> db.connect() Traceback (most recent call last): File "", line 1, in File "/home/charles/pypath/peewee.py", line 2390, in connect raise OperationalError(\(aqConnection already opened.\(aq) peewee.OperationalError: Connection already opened. .ft P .fi .UNINDENT .UNINDENT .sp To prevent this exception from being raised, we can call \fBconnect()\fP with an additional argument, \fBreuse_if_open\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> db.close() # Close connection. True >>> db.connect() True >>> db.connect(reuse_if_open=True) False .ft P .fi .UNINDENT .UNINDENT .sp Note that the call to \fBconnect()\fP returns \fBFalse\fP if the database connection was already open. .sp To close a connection, use the \fBDatabase.close()\fP method: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> db.close() True .ft P .fi .UNINDENT .UNINDENT .sp Calling \fBclose()\fP on an already\-closed connection will not result in an exception, but will return \fBFalse\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> db.connect() # Open connection. True >>> db.close() # Close connection. True >>> db.close() # Connection already closed, returns False. False .ft P .fi .UNINDENT .UNINDENT .sp You can test whether the database is closed using the \fBDatabase.is_closed()\fP method: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> db.is_closed() True .ft P .fi .UNINDENT .UNINDENT .SS Using autoconnect .sp It is not necessary to explicitly connect to the database before using it if the database is initialized with \fBautoconnect=True\fP (the default). Managing connections explicitly is considered a \fBbest practice\fP, therefore you may consider disabling the \fBautoconnect\fP behavior. .sp It is very helpful to be explicit about your connection lifetimes. If the connection fails, for instance, the exception will be caught when the connection is being opened, rather than some arbitrary time later when a query is executed. Furthermore, if using a connection pool, it is necessary to call \fBconnect()\fP and \fBclose()\fP to ensure connections are recycled properly. .sp For the best guarantee of correctness, disable \fBautoconnect\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = PostgresqlDatabase(\(aqmy_app\(aq, user=\(aqpostgres\(aq, autoconnect=False) .ft P .fi .UNINDENT .UNINDENT .SS Thread Safety .sp Peewee keeps track of the connection state using thread\-local storage, making the Peewee \fBDatabase\fP object safe to use with multiple threads. Each thread will have it\(aqs own connection, and as a result any given thread will only have a single connection open at a given time. .SS Context managers .sp The database object itself can be used as a context\-manager, which opens a connection for the duration of the wrapped block of code. Additionally, a transaction is opened at the start of the wrapped block and committed before the connection is closed (unless an error occurs, in which case the transaction is rolled back). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> db.is_closed() True >>> with db: \&... print(db.is_closed()) # db is open inside context manager. \&... False >>> db.is_closed() # db is closed. True .ft P .fi .UNINDENT .UNINDENT .sp If you want to manage transactions separately, you can use the \fBDatabase.connection_context()\fP context manager. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> with db.connection_context(): \&... # db connection is open. \&... pass \&... >>> db.is_closed() # db connection is closed. True .ft P .fi .UNINDENT .UNINDENT .sp The \fBconnection_context()\fP method can also be used as a decorator: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C @db.connection_context() def prepare_database(): # DB connection will be managed by the decorator, which opens # a connection, calls function, and closes upon returning. db.create_tables(MODELS) # Create schema. load_fixture_data(db) .ft P .fi .UNINDENT .UNINDENT .SS DB\-API Connection Object .sp To obtain a reference to the underlying DB\-API 2.0 connection, use the \fBDatabase.connection()\fP method. This method will return the currently\-open connection object, if one exists, otherwise it will open a new connection. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> db.connection() .ft P .fi .UNINDENT .UNINDENT .SS Connection Pooling .sp Connection pooling is provided by the pool module, included in the playhouse extensions library. The pool supports: .INDENT 0.0 .IP \(bu 2 Timeout after which connections will be recycled. .IP \(bu 2 Upper bound on the number of open connections. .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.pool import PooledPostgresqlExtDatabase db = PooledPostgresqlExtDatabase( \(aqmy_database\(aq, max_connections=8, stale_timeout=300, user=\(aqpostgres\(aq) class BaseModel(Model): class Meta: database = db .ft P .fi .UNINDENT .UNINDENT .sp The following pooled database classes are available: .INDENT 0.0 .IP \(bu 2 \fBPooledPostgresqlDatabase\fP .IP \(bu 2 \fBPooledPostgresqlExtDatabase\fP .IP \(bu 2 \fBPooledMySQLDatabase\fP .IP \(bu 2 \fBPooledSqliteDatabase\fP .IP \(bu 2 \fBPooledSqliteExtDatabase\fP .UNINDENT .sp For an in\-depth discussion of peewee\(aqs connection pool, see the pool section of the playhouse documentation. .SS Testing Peewee Applications .sp When writing tests for an application that uses Peewee, it may be desirable to use a special database for tests. Another common practice is to run tests against a clean database, which means ensuring tables are empty at the start of each test. .sp To bind your models to a database at run\-time, you can use the following methods: .INDENT 0.0 .IP \(bu 2 \fBDatabase.bind_ctx()\fP, which returns a context\-manager that will bind the given models to the database instance for the duration of the wrapped block. .IP \(bu 2 \fBModel.bind_ctx()\fP, which likewise returns a context\-manager that binds the model (and optionally its dependencies) to the given database for the duration of the wrapped block. .IP \(bu 2 \fBDatabase.bind()\fP, which is a one\-time operation that binds the models (and optionally its dependencies) to the given database. .IP \(bu 2 \fBModel.bind()\fP, which is a one\-time operation that binds the model (and optionally its dependencies) to the given database. .UNINDENT .sp Depending on your use\-case, one of these options may make more sense. For the examples below, I will use \fBModel.bind()\fP\&. .sp Example test\-case setup: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # tests.py import unittest from my_app.models import EventLog, Relationship, Tweet, User MODELS = [User, Tweet, EventLog, Relationship] # use an in\-memory SQLite for tests. test_db = SqliteDatabase(\(aq:memory:\(aq) class BaseTestCase(unittest.TestCase): def setUp(self): # Bind model classes to test db. Since we have a complete list of # all models, we do not need to recursively bind dependencies. test_db.bind(MODELS, bind_refs=False, bind_backrefs=False) test_db.connect() test_db.create_tables(MODELS) def tearDown(self): # Not strictly necessary since SQLite in\-memory databases only live # for the duration of the connection, and in the next step we close # the connection...but a good practice all the same. test_db.drop_tables(MODELS) # Close connection to db. test_db.close() # If we wanted, we could re\-bind the models to their original # database here. But for tests this is probably not necessary. .ft P .fi .UNINDENT .UNINDENT .sp As an aside, and speaking from experience, I recommend testing your application using the same database backend you use in production, so as to avoid any potential compatibility issues. .sp If you\(aqd like to see some more examples of how to run tests using Peewee, check out Peewee\(aqs own \fI\%test\-suite\fP\&. .SS Async with Gevent .sp \fI\%gevent\fP is recommended for doing asynchronous I/O with Postgresql or MySQL. Reasons I prefer gevent: .INDENT 0.0 .IP \(bu 2 No need for special\-purpose "loop\-aware" re\-implementations of \fIeverything\fP\&. Third\-party libraries using asyncio usually have to re\-implement layers and layers of code as well as re\-implementing the protocols themselves. .IP \(bu 2 Gevent allows you to write your application in normal, clean, idiomatic Python. No need to litter every line with "async", "await" and other noise. No callbacks, futures, tasks, promises. No cruft. .IP \(bu 2 Gevent works with both Python 2 \fIand\fP Python 3. .IP \(bu 2 Gevent is \fIPythonic\fP\&. Asyncio is an un\-pythonic abomination. .UNINDENT .sp Besides monkey\-patching socket, no special steps are required if you are using \fBMySQL\fP with a pure Python driver like \fI\%pymysql\fP or are using \fI\%mysql\-connector\fP in pure\-python mode. MySQL drivers written in C will require special configuration which is beyond the scope of this document. .sp For \fBPostgres\fP and \fI\%psycopg2\fP, which is a C extension, you can use the following code snippet to register event hooks that will make your connection async: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from gevent.socket import wait_read, wait_write from psycopg2 import extensions # Call this function after monkey\-patching socket (etc). def patch_psycopg2(): extensions.set_wait_callback(_psycopg2_gevent_callback) def _psycopg2_gevent_callback(conn, timeout=None): while True: state = conn.poll() if state == extensions.POLL_OK: break elif state == extensions.POLL_READ: wait_read(conn.fileno(), timeout=timeout) elif state == extensions.POLL_WRITE: wait_write(conn.fileno(), timeout=timeout) else: raise ValueError(\(aqpoll() returned unexpected result\(aq) .ft P .fi .UNINDENT .UNINDENT .sp \fBSQLite\fP, because it is embedded in the Python application itself, does not do any socket operations that would be a candidate for non\-blocking. Async has no effect one way or the other on SQLite databases. .SS Framework Integration .sp For web applications, it is common to open a connection when a request is received, and to close the connection when the response is delivered. In this section I will describe how to add hooks to your web app to ensure the database connection is handled properly. .sp These steps will ensure that regardless of whether you\(aqre using a simple SQLite database, or a pool of multiple Postgres connections, peewee will handle the connections correctly. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Applications that receive lots of traffic may benefit from using a connection pool to mitigate the cost of setting up and tearing down connections on every request. .UNINDENT .UNINDENT .SS Flask .sp Flask and peewee are a great combo and my go\-to for projects of any size. Flask provides two hooks which we will use to open and close our db connection. We\(aqll open the connection when a request is received, then close it when the response is returned. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from flask import Flask from peewee import * database = SqliteDatabase(\(aqmy_app.db\(aq) app = Flask(__name__) # This hook ensures that a connection is opened to handle any queries # generated by the request. @app.before_request def _db_connect(): database.connect() # This hook ensures that the connection is closed when we\(aqve finished # processing the request. @app.teardown_request def _db_close(exc): if not database.is_closed(): database.close() .ft P .fi .UNINDENT .UNINDENT .SS Django .sp While it\(aqs less common to see peewee used with Django, it is actually very easy to use the two. To manage your peewee database connections with Django, the easiest way in my opinion is to add a middleware to your app. The middleware should be the very first in the list of middlewares, to ensure it runs first when a request is handled, and last when the response is returned. .sp If you have a django project named \fImy_blog\fP and your peewee database is defined in the module \fBmy_blog.db\fP, you might add the following middleware class: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # middleware.py from my_blog.db import database # Import the peewee database instance. def PeeweeConnectionMiddleware(get_response): def middleware(request): database.connect() try: response = get_response(request) finally: if not database.is_closed(): database.close() return response return middleware # Older Django < 1.10 middleware. class PeeweeConnectionMiddleware(object): def process_request(self, request): database.connect() def process_response(self, request, response): if not database.is_closed(): database.close() return response .ft P .fi .UNINDENT .UNINDENT .sp To ensure this middleware gets executed, add it to your \fBsettings\fP module: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # settings.py MIDDLEWARE_CLASSES = ( # Our custom middleware appears first in the list. \(aqmy_blog.middleware.PeeweeConnectionMiddleware\(aq, # These are the default Django 1.7 middlewares. Yours may differ, # but the important this is that our Peewee middleware comes first. \(aqdjango.middleware.common.CommonMiddleware\(aq, \(aqdjango.contrib.sessions.middleware.SessionMiddleware\(aq, \(aqdjango.middleware.csrf.CsrfViewMiddleware\(aq, \(aqdjango.contrib.auth.middleware.AuthenticationMiddleware\(aq, \(aqdjango.contrib.messages.middleware.MessageMiddleware\(aq, ) # ... other Django settings ... .ft P .fi .UNINDENT .UNINDENT .SS Bottle .sp I haven\(aqt used bottle myself, but looking at the documentation I believe the following code should ensure the database connections are properly managed: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # app.py from bottle import hook #, route, etc, etc. from peewee import * db = SqliteDatabase(\(aqmy\-bottle\-app.db\(aq) @hook(\(aqbefore_request\(aq) def _connect_db(): db.connect() @hook(\(aqafter_request\(aq) def _close_db(): if not db.is_closed(): db.close() # Rest of your bottle app goes here. .ft P .fi .UNINDENT .UNINDENT .SS Web.py .sp See the documentation for \fI\%application processors\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = SqliteDatabase(\(aqmy_webpy_app.db\(aq) def connection_processor(handler): db.connect() try: return handler() finally: if not db.is_closed(): db.close() app.add_processor(connection_processor) .ft P .fi .UNINDENT .UNINDENT .SS Tornado .sp It looks like Tornado\(aqs \fBRequestHandler\fP class implements two hooks which can be used to open and close connections when a request is handled. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from tornado.web import RequestHandler db = SqliteDatabase(\(aqmy_db.db\(aq) class PeeweeRequestHandler(RequestHandler): def prepare(self): db.connect() return super(PeeweeRequestHandler, self).prepare() def on_finish(self): if not db.is_closed(): db.close() return super(PeeweeRequestHandler, self).on_finish() .ft P .fi .UNINDENT .UNINDENT .sp In your app, instead of extending the default \fBRequestHandler\fP, now you can extend \fBPeeweeRequestHandler\fP\&. .sp Note that this does not address how to use peewee asynchronously with Tornado or another event loop. .SS Wheezy.web .sp The connection handling code can be placed in a \fI\%middleware\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C def peewee_middleware(request, following): db.connect() try: response = following(request) finally: if not db.is_closed(): db.close() return response app = WSGIApplication(middleware=[ lambda x: peewee_middleware, # ... other middlewares ... ]) .ft P .fi .UNINDENT .UNINDENT .sp Thanks to GitHub user \fI@tuukkamustonen\fP for submitting this code. .SS Falcon .sp The connection handling code can be placed in a \fI\%middleware component\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C import falcon from peewee import * database = SqliteDatabase(\(aqmy_app.db\(aq) class PeeweeConnectionMiddleware(object): def process_request(self, req, resp): database.connect() def process_response(self, req, resp, resource, req_succeeded): if not database.is_closed(): database.close() application = falcon.API(middleware=[ PeeweeConnectionMiddleware(), # ... other middlewares ... ]) .ft P .fi .UNINDENT .UNINDENT .SS Pyramid .sp Set up a Request factory that handles database connection lifetime as follows: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from pyramid.request import Request db = SqliteDatabase(\(aqpyramidapp.db\(aq) class MyRequest(Request): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) db.connect() self.add_finished_callback(self.finish) def finish(self, request): if not db.is_closed(): db.close() .ft P .fi .UNINDENT .UNINDENT .sp In your application \fImain()\fP make sure \fIMyRequest\fP is used as \fIrequest_factory\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C def main(global_settings, **settings): config = Configurator(settings=settings, ...) config.set_request_factory(MyRequest) .ft P .fi .UNINDENT .UNINDENT .SS CherryPy .sp See \fI\%Publish/Subscribe pattern\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C def _db_connect(): db.connect() def _db_close(): if not db.is_closed(): db.close() cherrypy.engine.subscribe(\(aqbefore_request\(aq, _db_connect) cherrypy.engine.subscribe(\(aqafter_request\(aq, _db_close) .ft P .fi .UNINDENT .UNINDENT .SS Sanic .sp In Sanic, the connection handling code can be placed in the request and response middleware \fI\%sanic middleware\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # app.py @app.middleware(\(aqrequest\(aq) async def handle_request(request): db.connect() @app.middleware(\(aqresponse\(aq) async def handle_response(request, response): if not db.is_closed(): db.close() .ft P .fi .UNINDENT .UNINDENT .SS FastAPI .sp Similar to Flask, FastAPI provides two event based hooks which we will use to open and close our db connection. We\(aqll open the connection when a request is received, then close it when the response is returned. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from fastapi import FastAPI from peewee import * db = SqliteDatabase(\(aqmy_app.db\(aq) app = FastAPI() # This hook ensures that a connection is opened to handle any queries # generated by the request. @app.on_event("startup") def startup(): db.connect() # This hook ensures that the connection is closed when we\(aqve finished # processing the request. @app.on_event("shutdown") def shutdown(): if not db.is_closed(): db.close() .ft P .fi .UNINDENT .UNINDENT .SS Other frameworks .sp Don\(aqt see your framework here? Please \fI\%open a GitHub ticket\fP and I\(aqll see about adding a section, or better yet, submit a documentation pull\-request. .SS Executing Queries .sp SQL queries will typically be executed by calling \fBexecute()\fP on a query constructed using the query\-builder APIs (or by simply iterating over a query object in the case of a \fBSelect\fP query). For cases where you wish to execute SQL directly, you can use the \fBDatabase.execute_sql()\fP method. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = SqliteDatabase(\(aqmy_app.db\(aq) db.connect() # Example of executing a simple query and ignoring the results. db.execute_sql("ATTACH DATABASE \(aq:memory:\(aq AS cache;") # Example of iterating over the results of a query using the cursor. cursor = db.execute_sql(\(aqSELECT * FROM users WHERE status = ?\(aq, (ACTIVE,)) for row in cursor.fetchall(): # Do something with row, which is a tuple containing column data. pass .ft P .fi .UNINDENT .UNINDENT .SS Managing Transactions .sp Peewee provides several interfaces for working with transactions. The most general is the \fBDatabase.atomic()\fP method, which also supports nested transactions. \fBatomic()\fP blocks will be run in a transaction or savepoint, depending on the level of nesting. .sp If an exception occurs in a wrapped block, the current transaction/savepoint will be rolled back. Otherwise the statements will be committed at the end of the wrapped block. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 While inside a block wrapped by the \fBatomic()\fP context manager, you can explicitly rollback or commit at any point by calling \fBTransaction.rollback()\fP or \fBTransaction.commit()\fP\&. When you do this inside a wrapped block of code, a new transaction will be started automatically. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C with db.atomic() as transaction: # Opens new transaction. try: save_some_objects() except ErrorSavingData: # Because this block of code is wrapped with "atomic", a # new transaction will begin automatically after the call # to rollback(). transaction.rollback() error_saving = True create_report(error_saving=error_saving) # Note: no need to call commit. Since this marks the end of the # wrapped block of code, the \(gaatomic\(ga context manager will # automatically call commit for us. .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 \fBatomic()\fP can be used as either a \fBcontext manager\fP or a \fBdecorator\fP\&. .UNINDENT .UNINDENT .SS Context manager .sp Using \fBatomic\fP as context manager: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = SqliteDatabase(\(aq:memory:\(aq) with db.atomic() as txn: # This is the outer\-most level, so this block corresponds to # a transaction. User.create(username=\(aqcharlie\(aq) with db.atomic() as nested_txn: # This block corresponds to a savepoint. User.create(username=\(aqhuey\(aq) # This will roll back the above create() query. nested_txn.rollback() User.create(username=\(aqmickey\(aq) # When the block ends, the transaction is committed (assuming no error # occurs). At that point there will be two users, "charlie" and "mickey". .ft P .fi .UNINDENT .UNINDENT .sp You can use the \fBatomic\fP method to perform \fIget or create\fP operations as well: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C try: with db.atomic(): user = User.create(username=username) return \(aqSuccess\(aq except peewee.IntegrityError: return \(aqFailure: %s is already in use.\(aq % username .ft P .fi .UNINDENT .UNINDENT .SS Decorator .sp Using \fBatomic\fP as a decorator: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C @db.atomic() def create_user(username): # This statement will run in a transaction. If the caller is already # running in an \(gaatomic\(ga block, then a savepoint will be used instead. return User.create(username=username) create_user(\(aqcharlie\(aq) .ft P .fi .UNINDENT .UNINDENT .SS Nesting Transactions .sp \fBatomic()\fP provides transparent nesting of transactions. When using \fBatomic()\fP, the outer\-most call will be wrapped in a transaction, and any nested calls will use savepoints. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C with db.atomic() as txn: perform_operation() with db.atomic() as nested_txn: perform_another_operation() .ft P .fi .UNINDENT .UNINDENT .sp Peewee supports nested transactions through the use of savepoints (for more information, see \fBsavepoint()\fP). .SS Explicit transaction .sp If you wish to explicitly run code in a transaction, you can use \fBtransaction()\fP\&. Like \fBatomic()\fP, \fBtransaction()\fP can be used as a context manager or as a decorator. .sp If an exception occurs in a wrapped block, the transaction will be rolled back. Otherwise the statements will be committed at the end of the wrapped block. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = SqliteDatabase(\(aq:memory:\(aq) with db.transaction() as txn: # Delete the user and their associated tweets. user.delete_instance(recursive=True) .ft P .fi .UNINDENT .UNINDENT .sp Transactions can be explicitly committed or rolled\-back within the wrapped block. When this happens, a new transaction will be started. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C with db.transaction() as txn: User.create(username=\(aqmickey\(aq) txn.commit() # Changes are saved and a new transaction begins. User.create(username=\(aqhuey\(aq) # Roll back. "huey" will not be saved, but since "mickey" was already # committed, that row will remain in the database. txn.rollback() with db.transaction() as txn: User.create(username=\(aqwhiskers\(aq) # Roll back changes, which removes "whiskers". txn.rollback() # Create a new row for "mr. whiskers" which will be implicitly committed # at the end of the \(gawith\(ga block. User.create(username=\(aqmr. whiskers\(aq) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 If you attempt to nest transactions with peewee using the \fBtransaction()\fP context manager, only the outer\-most transaction will be used. However if an exception occurs in a nested block, this can lead to unpredictable behavior, so it is strongly recommended that you use \fBatomic()\fP\&. .UNINDENT .UNINDENT .SS Explicit Savepoints .sp Just as you can explicitly create transactions, you can also explicitly create savepoints using the \fBsavepoint()\fP method. Savepoints must occur within a transaction, but can be nested arbitrarily deep. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C with db.transaction() as txn: with db.savepoint() as sp: User.create(username=\(aqmickey\(aq) with db.savepoint() as sp2: User.create(username=\(aqzaizee\(aq) sp2.rollback() # "zaizee" will not be saved, but "mickey" will be. .ft P .fi .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 If you manually commit or roll back a savepoint, a new savepoint \fBwill not\fP automatically be created. This differs from the behavior of \fBtransaction\fP, which will automatically open a new transaction after manual commit/rollback. .UNINDENT .UNINDENT .SS Autocommit Mode .sp By default, Peewee operates in \fIautocommit mode\fP, such that any statements executed outside of a transaction are run in their own transaction. To group multiple statements into a transaction, Peewee provides the \fBatomic()\fP context\-manager/decorator. This should cover all use\-cases, but in the unlikely event you want to temporarily disable Peewee\(aqs transaction management completely, you can use the \fBDatabase.manual_commit()\fP context\-manager/decorator. .sp Here is how you might emulate the behavior of the \fBtransaction()\fP context manager: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C with db.manual_commit(): db.begin() # Have to begin transaction explicitly. try: user.delete_instance(recursive=True) except: db.rollback() # Rollback! An error occurred. raise else: try: db.commit() # Commit changes. except: db.rollback() raise .ft P .fi .UNINDENT .UNINDENT .sp Again \-\- I don\(aqt anticipate anyone needing this, but it\(aqs here just in case. .SS Database Errors .sp The Python DB\-API 2.0 spec describes \fI\%several types of exceptions\fP\&. Because most database drivers have their own implementations of these exceptions, Peewee simplifies things by providing its own wrappers around any implementation\-specific exception classes. That way, you don\(aqt need to worry about importing any special exception classes, you can just use the ones from peewee: .INDENT 0.0 .IP \(bu 2 \fBDatabaseError\fP .IP \(bu 2 \fBDataError\fP .IP \(bu 2 \fBIntegrityError\fP .IP \(bu 2 \fBInterfaceError\fP .IP \(bu 2 \fBInternalError\fP .IP \(bu 2 \fBNotSupportedError\fP .IP \(bu 2 \fBOperationalError\fP .IP \(bu 2 \fBProgrammingError\fP .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 All of these error classes extend \fBPeeweeException\fP\&. .UNINDENT .UNINDENT .SS Logging queries .sp All queries are logged to the \fIpeewee\fP namespace using the standard library \fBlogging\fP module. Queries are logged using the \fIDEBUG\fP level. If you\(aqre interested in doing something with the queries, you can simply register a handler. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Print all queries to stderr. import logging logger = logging.getLogger(\(aqpeewee\(aq) logger.addHandler(logging.StreamHandler()) logger.setLevel(logging.DEBUG) .ft P .fi .UNINDENT .UNINDENT .SS Adding a new Database Driver .sp Peewee comes with built\-in support for Postgres, MySQL and SQLite. These databases are very popular and run the gamut from fast, embeddable databases to heavyweight servers suitable for large\-scale deployments. That being said, there are a ton of cool databases out there and adding support for your database\-of\-choice should be really easy, provided the driver supports the \fI\%DB\-API 2.0 spec\fP\&. .sp The DB\-API 2.0 spec should be familiar to you if you\(aqve used the standard library sqlite3 driver, psycopg2 or the like. Peewee currently relies on a handful of parts: .INDENT 0.0 .IP \(bu 2 \fIConnection.commit\fP .IP \(bu 2 \fIConnection.execute\fP .IP \(bu 2 \fIConnection.rollback\fP .IP \(bu 2 \fICursor.description\fP .IP \(bu 2 \fICursor.fetchone\fP .UNINDENT .sp These methods are generally wrapped up in higher\-level abstractions and exposed by the \fBDatabase\fP, so even if your driver doesn\(aqt do these exactly you can still get a lot of mileage out of peewee. An example is the \fI\%apsw sqlite driver\fP in the "playhouse" module. .sp The first thing is to provide a subclass of \fBDatabase\fP that will open a connection. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from peewee import Database import foodb # Our fictional DB\-API 2.0 driver. class FooDatabase(Database): def _connect(self, database, **kwargs): return foodb.connect(database, **kwargs) .ft P .fi .UNINDENT .UNINDENT .sp The \fBDatabase\fP provides a higher\-level API and is responsible for executing queries, creating tables and indexes, and introspecting the database to get lists of tables. The above implementation is the absolute minimum needed, though some features will not work \-\- for best results you will want to additionally add a method for extracting a list of tables and indexes for a table from the database. We\(aqll pretend that \fBFooDB\fP is a lot like MySQL and has special "SHOW" statements: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class FooDatabase(Database): def _connect(self, database, **kwargs): return foodb.connect(database, **kwargs) def get_tables(self): res = self.execute(\(aqSHOW TABLES;\(aq) return [r[0] for r in res.fetchall()] .ft P .fi .UNINDENT .UNINDENT .sp Other things the database handles that are not covered here include: .INDENT 0.0 .IP \(bu 2 \fBlast_insert_id()\fP and \fBrows_affected()\fP .IP \(bu 2 \fBparam\fP and \fBquote\fP, which tell the SQL\-generating code how to add parameter placeholders and quote entity names. .IP \(bu 2 \fBfield_types\fP for mapping data\-types like INT or TEXT to their vendor\-specific type names. .IP \(bu 2 \fBoperations\fP for mapping operations such as "LIKE/ILIKE" to their database equivalent .UNINDENT .sp Refer to the \fBDatabase\fP API reference or the \fI\%source code\fP\&. for details. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 If your driver conforms to the DB\-API 2.0 spec, there shouldn\(aqt be much work needed to get up and running. .UNINDENT .UNINDENT .sp Our new database can be used just like any of the other database subclasses: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from peewee import * from foodb_ext import FooDatabase db = FooDatabase(\(aqmy_database\(aq, user=\(aqfoo\(aq, password=\(aqsecret\(aq) class BaseModel(Model): class Meta: database = db class Blog(BaseModel): title = CharField() contents = TextField() pub_date = DateTimeField() .ft P .fi .UNINDENT .UNINDENT .SS Models and Fields .sp \fBModel\fP classes, \fBField\fP instances and model instances all map to database concepts: .TS center; |l|l|. _ T{ Thing T} T{ Corresponds to... T} _ T{ Model class T} T{ Database table T} _ T{ Field instance T} T{ Column on a table T} _ T{ Model instance T} T{ Row in a database table T} _ .TE .sp The following code shows the typical way you will define your database connection and model classes. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C import datetime from peewee import * db = SqliteDatabase(\(aqmy_app.db\(aq) class BaseModel(Model): class Meta: database = db class User(BaseModel): username = CharField(unique=True) class Tweet(BaseModel): user = ForeignKeyField(User, backref=\(aqtweets\(aq) message = TextField() created_date = DateTimeField(default=datetime.datetime.now) is_published = BooleanField(default=True) .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .IP 1. 3 Create an instance of a \fBDatabase\fP\&. .INDENT 3.0 .INDENT 3.5 .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = SqliteDatabase(\(aqmy_app.db\(aq) .ft P .fi .UNINDENT .UNINDENT .sp The \fBdb\fP object will be used to manage the connections to the Sqlite database. In this example we\(aqre using \fBSqliteDatabase\fP, but you could also use one of the other database engines\&. .UNINDENT .UNINDENT .IP 2. 3 Create a base model class which specifies our database. .INDENT 3.0 .INDENT 3.5 .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class BaseModel(Model): class Meta: database = db .ft P .fi .UNINDENT .UNINDENT .sp It is good practice to define a base model class which establishes the database connection. This makes your code DRY as you will not have to specify the database for subsequent models. .sp Model configuration is kept namespaced in a special class called \fBMeta\fP\&. This convention is borrowed from Django. \fI\%Meta\fP configuration is passed on to subclasses, so our project\(aqs models will all subclass \fIBaseModel\fP\&. There are \fI\%many different attributes\fP you can configure using \fIModel.Meta\fP\&. .UNINDENT .UNINDENT .IP 3. 3 Define a model class. .INDENT 3.0 .INDENT 3.5 .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class User(BaseModel): username = CharField(unique=True) .ft P .fi .UNINDENT .UNINDENT .sp Model definition uses the declarative style seen in other popular ORMs like SQLAlchemy or Django. Note that we are extending the \fIBaseModel\fP class so the \fIUser\fP model will inherit the database connection. .sp We have explicitly defined a single \fIusername\fP column with a unique constraint. Because we have not specified a primary key, peewee will automatically add an auto\-incrementing integer primary key field named \fIid\fP\&. .UNINDENT .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 If you would like to start using peewee with an existing database, you can use pwiz to automatically generate model definitions. .UNINDENT .UNINDENT .SS Fields .sp The \fBField\fP class is used to describe the mapping of \fBModel\fP 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. .sp When creating a \fBModel\fP class, fields are defined as class attributes. This should look familiar to users of the django framework. Here\(aqs an example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class User(Model): username = CharField() join_date = DateTimeField() about_me = TextField() .ft P .fi .UNINDENT .UNINDENT .sp In the above example, because none of the fields are initialized with \fBprimary_key=True\fP, an auto\-incrementing primary key will automatically be created and named "id". Peewee uses \fBAutoField\fP to signify an auto\-incrementing integer primary key, which implies \fBprimary_key=True\fP\&. .sp There is one special type of field, \fBForeignKeyField\fP, which allows you to represent foreign\-key relationships between models in an intuitive way: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Message(Model): user = ForeignKeyField(User, backref=\(aqmessages\(aq) body = TextField() send_date = DateTimeField(default=datetime.datetime.now) .ft P .fi .UNINDENT .UNINDENT .sp This allows you to write code like the following: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> print(some_message.user.username) Some User >>> for message in some_user.messages: \&... print(message.body) some message another message yet another message .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Refer to the relationships document for an in\-depth discussion of foreign\-keys, joins and relationships between models. .UNINDENT .UNINDENT .sp For full documentation on fields, see the Fields API notes .SS Field types table .TS center; |l|l|l|l|. _ T{ Field Type T} T{ Sqlite T} T{ Postgresql T} T{ MySQL T} _ T{ \fBAutoField\fP T} T{ integer T} T{ serial T} T{ integer T} _ T{ \fBBigAutoField\fP T} T{ integer T} T{ bigserial T} T{ bigint T} _ T{ \fBIntegerField\fP T} T{ integer T} T{ integer T} T{ integer T} _ T{ \fBBigIntegerField\fP T} T{ integer T} T{ bigint T} T{ bigint T} _ T{ \fBSmallIntegerField\fP T} T{ integer T} T{ smallint T} T{ smallint T} _ T{ \fBIdentityField\fP T} T{ not supported T} T{ int identity T} T{ not supported T} _ T{ \fBFloatField\fP T} T{ real T} T{ real T} T{ real T} _ T{ \fBDoubleField\fP T} T{ real T} T{ double precision T} T{ double precision T} _ T{ \fBDecimalField\fP T} T{ decimal T} T{ numeric T} T{ numeric T} _ T{ \fBCharField\fP T} T{ varchar T} T{ varchar T} T{ varchar T} _ T{ \fBFixedCharField\fP T} T{ char T} T{ char T} T{ char T} _ T{ \fBTextField\fP T} T{ text T} T{ text T} T{ text T} _ T{ \fBBlobField\fP T} T{ blob T} T{ bytea T} T{ blob T} _ T{ \fBBitField\fP T} T{ integer T} T{ bigint T} T{ bigint T} _ T{ \fBBigBitField\fP T} T{ blob T} T{ bytea T} T{ blob T} _ T{ \fBUUIDField\fP T} T{ text T} T{ uuid T} T{ varchar(40) T} _ T{ \fBBinaryUUIDField\fP T} T{ blob T} T{ bytea T} T{ varbinary(16) T} _ T{ \fBDateTimeField\fP T} T{ datetime T} T{ timestamp T} T{ datetime T} _ T{ \fBDateField\fP T} T{ date T} T{ date T} T{ date T} _ T{ \fBTimeField\fP T} T{ time T} T{ time T} T{ time T} _ T{ \fBTimestampField\fP T} T{ integer T} T{ integer T} T{ integer T} _ T{ \fBIPField\fP T} T{ integer T} T{ bigint T} T{ bigint T} _ T{ \fBBooleanField\fP T} T{ integer T} T{ boolean T} T{ bool T} _ T{ \fBBareField\fP T} T{ untyped T} T{ not supported T} T{ not supported T} _ T{ \fBForeignKeyField\fP T} T{ integer T} T{ integer T} T{ integer T} _ .TE .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Don\(aqt see the field you\(aqre looking for in the above table? It\(aqs easy to create custom field types and use them with your models. .INDENT 0.0 .IP \(bu 2 \fI\%Creating a custom field\fP .IP \(bu 2 \fBDatabase\fP, particularly the \fBfields\fP parameter. .UNINDENT .UNINDENT .UNINDENT .SS Field initialization arguments .sp Parameters accepted by all field types and their default values: .INDENT 0.0 .IP \(bu 2 \fBnull = False\fP \-\- allow null values .IP \(bu 2 \fBindex = False\fP \-\- create an index on this column .IP \(bu 2 \fBunique = False\fP \-\- create a unique index on this column. See also \fI\%adding composite indexes\fP\&. .IP \(bu 2 \fBcolumn_name = None\fP \-\- explicitly specify the column name in the database. .IP \(bu 2 \fBdefault = None\fP \-\- any value or callable to use as a default for uninitialized models .IP \(bu 2 \fBprimary_key = False\fP \-\- primary key for the table .IP \(bu 2 \fBconstraints = None\fP \- one or more constraints, e.g. \fB[Check(\(aqprice > 0\(aq)]\fP .IP \(bu 2 \fBsequence = None\fP \-\- sequence name (if backend supports it) .IP \(bu 2 \fBcollation = None\fP \-\- collation to use for ordering the field / index .IP \(bu 2 \fBunindexed = False\fP \-\- indicate field on virtual table should be unindexed (\fBSQLite\-only\fP) .IP \(bu 2 \fBchoices = None\fP \-\- optional iterable containing 2\-tuples of \fBvalue\fP, \fBdisplay\fP .IP \(bu 2 \fBhelp_text = None\fP \-\- string representing any helpful text for this field .IP \(bu 2 \fBverbose_name = None\fP \-\- string representing the "user\-friendly" name of this field .IP \(bu 2 \fBindex_type = None\fP \-\- specify a custom index\-type, e.g. for Postgres you might specify a \fB\(aqBRIN\(aq\fP or \fB\(aqGIN\(aq\fP index. .UNINDENT .SS Some fields take special parameters... .TS center; |l|l|. _ T{ Field type T} T{ Special Parameters T} _ T{ \fBCharField\fP T} T{ \fBmax_length\fP T} _ T{ \fBFixedCharField\fP T} T{ \fBmax_length\fP T} _ T{ \fBDateTimeField\fP T} T{ \fBformats\fP T} _ T{ \fBDateField\fP T} T{ \fBformats\fP T} _ T{ \fBTimeField\fP T} T{ \fBformats\fP T} _ T{ \fBTimestampField\fP T} T{ \fBresolution\fP, \fButc\fP T} _ T{ \fBDecimalField\fP T} T{ \fBmax_digits\fP, \fBdecimal_places\fP, \fBauto_round\fP, \fBrounding\fP T} _ T{ \fBForeignKeyField\fP T} T{ \fBmodel\fP, \fBfield\fP, \fBbackref\fP, \fBon_delete\fP, \fBon_update\fP, \fBdeferrable\fP \fBlazy_load\fP T} _ T{ \fBBareField\fP T} T{ \fBadapt\fP T} _ .TE .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Both \fBdefault\fP and \fBchoices\fP could be implemented at the database level as \fIDEFAULT\fP and \fICHECK CONSTRAINT\fP respectively, but any application change would require a schema change. Because of this, \fBdefault\fP is implemented purely in python and \fBchoices\fP are not validated but exist for metadata purposes only. .sp To add database (server\-side) constraints, use the \fBconstraints\fP parameter. .UNINDENT .UNINDENT .SS Default field values .sp Peewee can provide default values for fields when objects are created. For example to have an \fBIntegerField\fP default to zero rather than \fBNULL\fP, you could declare the field with a default value: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Message(Model): context = TextField() read_count = IntegerField(default=0) .ft P .fi .UNINDENT .UNINDENT .sp In some instances it may make sense for the default value to be dynamic. A common scenario is using the current date and time. Peewee allows you to specify a function in these cases, whose return value will be used when the object is created. Note we only provide the function, we do not actually \fIcall\fP it: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Message(Model): context = TextField() timestamp = DateTimeField(default=datetime.datetime.now) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 If you are using a field that accepts a mutable type (\fIlist\fP, \fIdict\fP, etc), and would like to provide a default, it is a good idea to wrap your default value in a simple function so that multiple model instances are not sharing a reference to the same underlying object: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C def house_defaults(): return {\(aqbeds\(aq: 0, \(aqbaths\(aq: 0} class House(Model): number = TextField() street = TextField() attributes = JSONField(default=house_defaults) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp The database can also provide the default value for a field. While peewee does not explicitly provide an API for setting a server\-side default value, you can use the \fBconstraints\fP parameter to specify the server default: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Message(Model): context = TextField() timestamp = DateTimeField(constraints=[SQL(\(aqDEFAULT CURRENT_TIMESTAMP\(aq)]) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 \fBRemember:\fP when using the \fBdefault\fP parameter, the values are set by Peewee rather than being a part of the actual table and column definition. .UNINDENT .UNINDENT .SS ForeignKeyField .sp \fBForeignKeyField\fP is a special field type that allows one model to reference another. Typically a foreign key will contain the primary key of the model it relates to (but you can specify a particular column by specifying a \fBfield\fP). .sp Foreign keys allow data to be \fI\%normalized\fP\&. In our example models, there is a foreign key from \fBTweet\fP to \fBUser\fP\&. This means that all the users are stored in their own table, as are the tweets, and the foreign key from tweet to user allows each tweet to \fIpoint\fP to a particular user object. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Refer to the relationships document for an in\-depth discussion of foreign keys, joins and relationships between models. .UNINDENT .UNINDENT .sp In peewee, accessing the value of a \fBForeignKeyField\fP will return the entire related object, e.g.: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C tweets = (Tweet .select(Tweet, User) .join(User) .order_by(Tweet.created_date.desc())) for tweet in tweets: print(tweet.user.username, tweet.message) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 In the example above the \fBUser\fP data was selected as part of the query. For more examples of this technique, see the Avoiding N+1 document. .UNINDENT .UNINDENT .sp If we did not select the \fBUser\fP, though, then an \fBadditional query\fP would be issued to fetch the associated \fBUser\fP data: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C tweets = Tweet.select().order_by(Tweet.created_date.desc()) for tweet in tweets: # WARNING: an additional query will be issued for EACH tweet # to fetch the associated User data. print(tweet.user.username, tweet.message) .ft P .fi .UNINDENT .UNINDENT .sp Sometimes you only need the associated primary key value from the foreign key column. In this case, Peewee follows the convention established by Django, of allowing you to access the raw foreign key value by appending \fB"_id"\fP to the foreign key field\(aqs name: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C tweets = Tweet.select() for tweet in tweets: # Instead of "tweet.user", we will just get the raw ID value stored # in the column. print(tweet.user_id, tweet.message) .ft P .fi .UNINDENT .UNINDENT .sp To prevent accidentally resolving a foreign\-key and triggering an additional query, \fBForeignKeyField\fP supports an initialization parameter \fBlazy_load\fP which, when disabled, behaves like the \fB"_id"\fP attribute. For example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Tweet(Model): # ... same fields, except we declare the user FK to have # lazy\-load disabled: user = ForeignKeyField(User, backref=\(aqtweets\(aq, lazy_load=False) for tweet in Tweet.select(): print(tweet.user, tweet.message) # With lazy\-load disabled, accessing tweet.user will not perform an extra # query and the user ID value is returned instead. # e.g.: # 1 tweet from user1 # 1 another from user1 # 2 tweet from user2 # However, if we eagerly load the related user object, then the user # foreign key will behave like usual: for tweet in Tweet.select(Tweet, User).join(User): print(tweet.user.username, tweet.message) # user1 tweet from user1 # user1 another from user1 # user2 tweet from user1 .ft P .fi .UNINDENT .UNINDENT .SS ForeignKeyField Back\-references .sp \fBForeignKeyField\fP allows for a backreferencing property to be bound to the target model. Implicitly, this property will be named \fBclassname_set\fP, where \fBclassname\fP is the lowercase name of the class, but can be overridden using the parameter \fBbackref\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Message(Model): from_user = ForeignKeyField(User, backref=\(aqoutbox\(aq) to_user = ForeignKeyField(User, backref=\(aqinbox\(aq) text = TextField() for message in some_user.outbox: # We are iterating over all Messages whose from_user is some_user. print(message) for message in some_user.inbox: # We are iterating over all Messages whose to_user is some_user print(message) .ft P .fi .UNINDENT .UNINDENT .SS DateTimeField, DateField and TimeField .sp The three fields devoted to working with dates and times have special properties which allow access to things like the year, month, hour, etc. .sp \fBDateField\fP has properties for: .INDENT 0.0 .IP \(bu 2 \fByear\fP .IP \(bu 2 \fBmonth\fP .IP \(bu 2 \fBday\fP .UNINDENT .sp \fBTimeField\fP has properties for: .INDENT 0.0 .IP \(bu 2 \fBhour\fP .IP \(bu 2 \fBminute\fP .IP \(bu 2 \fBsecond\fP .UNINDENT .sp \fBDateTimeField\fP has all of the above. .sp These properties can be used just like any other expression. Let\(aqs say we have an events calendar and want to highlight all the days in the current month that have an event attached: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Get the current time. now = datetime.datetime.now() # Get days that have events for the current month. Event.select(Event.event_date.day.alias(\(aqday\(aq)).where( (Event.event_date.year == now.year) & (Event.event_date.month == now.month)) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 SQLite does not have a native date type, so dates are stored in formatted text columns. To ensure that comparisons work correctly, the dates need to be formatted so they are sorted lexicographically. That is why they are stored, by default, as \fBYYYY\-MM\-DD HH:MM:SS\fP\&. .UNINDENT .UNINDENT .SS BitField and BigBitField .sp The \fBBitField\fP and \fBBigBitField\fP are new as of 3.0.0. The former provides a subclass of \fBIntegerField\fP that is suitable for storing feature toggles as an integer bitmask. The latter is suitable for storing a bitmap for a large data\-set, e.g. expressing membership or bitmap\-type data. .sp As an example of using \fBBitField\fP, let\(aqs say we have a \fIPost\fP model and we wish to store certain True/False flags about how the post. We could store all these feature toggles in their own \fBBooleanField\fP objects, or we could use \fBBitField\fP instead: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Post(Model): content = TextField() flags = BitField() is_favorite = flags.flag(1) is_sticky = flags.flag(2) is_minimized = flags.flag(4) is_deleted = flags.flag(8) .ft P .fi .UNINDENT .UNINDENT .sp Using these flags is quite simple: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> p = Post() >>> p.is_sticky = True >>> p.is_minimized = True >>> print(p.flags) # Prints 4 | 2 \-\-> "6" 6 >>> p.is_favorite False >>> p.is_sticky True .ft P .fi .UNINDENT .UNINDENT .sp We can also use the flags on the Post class to build expressions in queries: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Generates a WHERE clause that looks like: # WHERE (post.flags & 1 != 0) favorites = Post.select().where(Post.is_favorite) # Query for sticky + favorite posts: sticky_faves = Post.select().where(Post.is_sticky & Post.is_favorite) .ft P .fi .UNINDENT .UNINDENT .sp Since the \fBBitField\fP is stored in an integer, there is a maximum of 64 flags you can represent (64\-bits is common size of integer column). For storing arbitrarily large bitmaps, you can instead use \fBBigBitField\fP, which uses an automatically managed buffer of bytes, stored in a \fBBlobField\fP\&. .sp When bulk\-updating one or more bits in a \fBBitField\fP, you can use bitwise operators to set or clear one or more bits: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Set the 4th bit on all Post objects. Post.update(flags=Post.flags | 8).execute() # Clear the 1st and 3rd bits on all Post objects. Post.update(flags=Post.flags & ~(1 | 4)).execute() .ft P .fi .UNINDENT .UNINDENT .sp For simple operations, the flags provide handy \fBset()\fP and \fBclear()\fP methods for setting or clearing an individual bit: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Set the "is_deleted" bit on all posts. Post.update(flags=Post.is_deleted.set()).execute() # Clear the "is_deleted" bit on all posts. Post.update(flags=Post.is_deleted.clear()).execute() .ft P .fi .UNINDENT .UNINDENT .sp Example usage: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Bitmap(Model): data = BigBitField() bitmap = Bitmap() # Sets the ith bit, e.g. the 1st bit, the 11th bit, the 63rd, etc. bits_to_set = (1, 11, 63, 31, 55, 48, 100, 99) for bit_idx in bits_to_set: bitmap.data.set_bit(bit_idx) # We can test whether a bit is set using "is_set": assert bitmap.data.is_set(11) assert not bitmap.data.is_set(12) # We can clear a bit: bitmap.data.clear_bit(11) assert not bitmap.data.is_set(11) # We can also "toggle" a bit. Recall that the 63rd bit was set earlier. assert bitmap.data.toggle_bit(63) is False assert bitmap.data.toggle_bit(63) is True assert bitmap.data.is_set(63) .ft P .fi .UNINDENT .UNINDENT .SS BareField .sp The \fBBareField\fP class is intended to be used only with SQLite. Since SQLite uses dynamic typing and data\-types are not enforced, it can be perfectly fine to declare fields without \fIany\fP data\-type. In those cases you can use \fBBareField\fP\&. It is also common for SQLite virtual tables to use meta\-columns or untyped columns, so for those cases as well you may wish to use an untyped field (although for full\-text search, you should use \fBSearchField\fP instead!). .sp \fBBareField\fP accepts a special parameter \fBadapt\fP\&. This parameter is a function that takes a value coming from the database and converts it into the appropriate Python type. For instance, if you have a virtual table with an un\-typed column but you know that it will return \fBint\fP objects, you can specify \fBadapt=int\fP\&. .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = SqliteDatabase(\(aq:memory:\(aq) class Junk(Model): anything = BareField() class Meta: database = db # Store multiple data\-types in the Junk.anything column: Junk.create(anything=\(aqa string\(aq) Junk.create(anything=12345) Junk.create(anything=3.14159) .ft P .fi .UNINDENT .UNINDENT .SS Creating a custom field .sp It is easy to add support for custom field types in peewee. In this example we will create a UUID field for postgresql (which has a native UUID column type). .sp To add a custom field type you need to first identify what type of column the field 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 just subclass \fBDecimalField\fP\&. On the other hand, if the database offers a custom column type you will need to let peewee know. This is controlled by the \fBField.field_type\fP attribute. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Peewee ships with a \fBUUIDField\fP, the following code is intended only as an example. .UNINDENT .UNINDENT .sp Let\(aqs start by defining our UUID field: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class UUIDField(Field): field_type = \(aquuid\(aq .ft P .fi .UNINDENT .UNINDENT .sp We will store the UUIDs in a native UUID column. Since psycopg2 treats the data as a string by default, we will add two methods to the field to handle: .INDENT 0.0 .IP \(bu 2 The data coming out of the database to be used in our application .IP \(bu 2 The data from our python app going into the database .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C import uuid class UUIDField(Field): field_type = \(aquuid\(aq def db_value(self, value): return value.hex # convert UUID to hex string. def python_value(self, value): return uuid.UUID(value) # convert hex string to UUID .ft P .fi .UNINDENT .UNINDENT .sp \fBThis step is optional.\fP By default, the \fBfield_type\fP value will be used for the columns data\-type in the database schema. If you need to support multiple databases which use different data\-types for your field\-data, we need to let the database know how to map this \fIuuid\fP label to an actual \fIuuid\fP column type in the database. Specify the overrides in the \fBDatabase\fP constructor: .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Postgres, we use UUID data\-type. db = PostgresqlDatabase(\(aqmy_db\(aq, field_types={\(aquuid\(aq: \(aquuid\(aq}) # Sqlite doesn\(aqt have a UUID type, so we use text type. db = SqliteDatabase(\(aqmy_db\(aq, field_types={\(aquuid\(aq: \(aqtext\(aq}) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp That is it! Some fields may support exotic operations, like the postgresql HStore field acts like a key/value store and has custom operators for things like \fIcontains\fP and \fIupdate\fP\&. You can specify custom operations as well. For example code, check out the source code for the \fBHStoreField\fP, in \fBplayhouse.postgres_ext\fP\&. .SS Field\-naming conflicts .sp \fBModel\fP classes implement a number of class\- and instance\-methods, for example \fBModel.save()\fP or \fBModel.create()\fP\&. If you declare a field whose name coincides with a model method, it could cause problems. Consider: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class LogEntry(Model): event = TextField() create = TimestampField() # Uh\-oh. update = TimestampField() # Uh\-oh. .ft P .fi .UNINDENT .UNINDENT .sp To avoid this problem while still using the desired column name in the database schema, explicitly specify the \fBcolumn_name\fP while providing an alternative name for the field attribute: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class LogEntry(Model): event = TextField() create_ = TimestampField(column_name=\(aqcreate\(aq) update_ = TimestampField(column_name=\(aqupdate\(aq) .ft P .fi .UNINDENT .UNINDENT .SS Creating model tables .sp In order to start using our models, its necessary to open a connection to the database and create the tables first. Peewee will run the necessary \fICREATE TABLE\fP queries, additionally creating any constraints and indexes. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Connect to our database. db.connect() # Create the tables. db.create_tables([User, Tweet]) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Strictly speaking, it is not necessary to call \fBconnect()\fP but it is good practice to be explicit. That way if something goes wrong, the error occurs at the connect step, rather than some arbitrary time later. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 By default, Peewee includes an \fBIF NOT EXISTS\fP clause when creating tables. If you want to disable this, specify \fBsafe=False\fP\&. .UNINDENT .UNINDENT .sp After you have created your tables, if you choose to modify your database schema (by adding, removing or otherwise changing the columns) you will need to either: .INDENT 0.0 .IP \(bu 2 Drop the table and re\-create it. .IP \(bu 2 Run one or more \fIALTER TABLE\fP queries. Peewee comes with a schema migration tool which can greatly simplify this. Check the schema migrations docs for details. .UNINDENT .SS Model options and table metadata .sp In order not to pollute the model namespace, model\-specific configuration is placed in a special class called \fIMeta\fP (a convention borrowed from the django framework): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from peewee import * contacts_db = SqliteDatabase(\(aqcontacts.db\(aq) class Person(Model): name = CharField() class Meta: database = contacts_db .ft P .fi .UNINDENT .UNINDENT .sp This instructs peewee that whenever a query is executed on \fIPerson\fP to use the contacts database. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Take a look at \fI\%the sample models\fP \- you will notice that we created a \fBBaseModel\fP that defined the database, and then extended. This is the preferred way to define a database and create models. .UNINDENT .UNINDENT .sp Once the class is defined, you should not access \fBModelClass.Meta\fP, but instead use \fBModelClass._meta\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> Person.Meta Traceback (most recent call last): File "", line 1, in AttributeError: type object \(aqPerson\(aq has no attribute \(aqMeta\(aq >>> Person._meta .ft P .fi .UNINDENT .UNINDENT .sp The \fBModelOptions\fP class implements several methods which may be of use for retrieving model metadata (such as lists of fields, foreign key relationships, and more). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> Person._meta.fields {\(aqid\(aq: , \(aqname\(aq: } >>> Person._meta.primary_key >>> Person._meta.database .ft P .fi .UNINDENT .UNINDENT .sp There are several options you can specify as \fBMeta\fP attributes. While most options are inheritable, some are table\-specific and will not be inherited by subclasses. .TS center; |l|l|l|. _ T{ Option T} T{ Meaning T} T{ Inheritable? T} _ T{ \fBdatabase\fP T} T{ database for model T} T{ yes T} _ T{ \fBtable_name\fP T} T{ name of the table to store data T} T{ no T} _ T{ \fBtable_function\fP T} T{ function to generate table name dynamically T} T{ yes T} _ T{ \fBindexes\fP T} T{ a list of fields to index T} T{ yes T} _ T{ \fBprimary_key\fP T} T{ a \fBCompositeKey\fP instance T} T{ yes T} _ T{ \fBconstraints\fP T} T{ a list of table constraints T} T{ yes T} _ T{ \fBschema\fP T} T{ the database schema for the model T} T{ yes T} _ T{ \fBonly_save_dirty\fP T} T{ when calling model.save(), only save dirty fields T} T{ yes T} _ T{ \fBoptions\fP T} T{ dictionary of options for create table extensions T} T{ yes T} _ T{ \fBtable_settings\fP T} T{ list of setting strings to go after close parentheses T} T{ yes T} _ T{ \fBtemporary\fP T} T{ indicate temporary table T} T{ yes T} _ T{ \fBlegacy_table_names\fP T} T{ use legacy table name generation (enabled by default) T} T{ yes T} _ T{ \fBdepends_on\fP T} T{ indicate this table depends on another for creation T} T{ no T} _ T{ \fBwithout_rowid\fP T} T{ indicate table should not have rowid (SQLite only) T} T{ no T} _ .TE .sp Here is an example showing inheritable versus non\-inheritable attributes: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> db = SqliteDatabase(\(aq:memory:\(aq) >>> class ModelOne(Model): \&... class Meta: \&... database = db \&... table_name = \(aqmodel_one_tbl\(aq \&... >>> class ModelTwo(ModelOne): \&... pass \&... >>> ModelOne._meta.database is ModelTwo._meta.database True >>> ModelOne._meta.table_name == ModelTwo._meta.table_name False .ft P .fi .UNINDENT .UNINDENT .SS Meta.primary_key .sp The \fBMeta.primary_key\fP attribute is used to specify either a \fBCompositeKey\fP or to indicate that the model has \fIno\fP primary key. Composite primary keys are discussed in more detail here: \fI\%Composite primary keys\fP\&. .sp To indicate that a model should not have a primary key, then set \fBprimary_key = False\fP\&. .sp Examples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class BlogToTag(Model): """A simple "through" table for many\-to\-many relationship.""" blog = ForeignKeyField(Blog) tag = ForeignKeyField(Tag) class Meta: primary_key = CompositeKey(\(aqblog\(aq, \(aqtag\(aq) class NoPrimaryKey(Model): data = IntegerField() class Meta: primary_key = False .ft P .fi .UNINDENT .UNINDENT .SS Table Names .sp By default Peewee will automatically generate a table name based on the name of your model class. The way the table\-name is generated depends on the value of \fBMeta.legacy_table_names\fP\&. By default, \fBlegacy_table_names=True\fP so as to avoid breaking backwards\-compatibility. However, if you wish to use the new and improved table\-name generation, you can specify \fBlegacy_table_names=False\fP\&. .sp This table shows the differences in how a model name is converted to a SQL table name, depending on the value of \fBlegacy_table_names\fP: .TS center; |l|l|l|. _ T{ Model name T} T{ legacy_table_names=True T} T{ legacy_table_names=False (new) T} _ T{ User T} T{ user T} T{ user T} _ T{ UserProfile T} T{ userprofile T} T{ user_profile T} _ T{ APIResponse T} T{ apiresponse T} T{ api_response T} _ T{ WebHTTPRequest T} T{ webhttprequest T} T{ web_http_request T} _ T{ mixedCamelCase T} T{ mixedcamelcase T} T{ mixed_camel_case T} _ T{ Name2Numbers3XYZ T} T{ name2numbers3xyz T} T{ name2_numbers3_xyz T} _ .TE .sp \fBATTENTION:\fP .INDENT 0.0 .INDENT 3.5 To preserve backwards\-compatibility, the current release (Peewee 3.x) specifies \fBlegacy_table_names=True\fP by default. .sp In the next major release (Peewee 4.0), \fBlegacy_table_names\fP will have a default value of \fBFalse\fP\&. .UNINDENT .UNINDENT .sp To explicitly specify the table name for a model class, use the \fBtable_name\fP Meta option. This feature can be useful for dealing with pre\-existing database schemas that may have used awkward naming conventions: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class UserProfile(Model): class Meta: table_name = \(aquser_profile_tbl\(aq .ft P .fi .UNINDENT .UNINDENT .sp If you wish to implement your own naming convention, you can specify the \fBtable_function\fP Meta option. This function will be called with your model class and should return the desired table name as a string. Suppose our company specifies that table names should be lower\-cased and end with "_tbl", we can implement this as a table function: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C def make_table_name(model_class): model_name = model_class.__name__ return model_name.lower() + \(aq_tbl\(aq class BaseModel(Model): class Meta: table_function = make_table_name class User(BaseModel): # table_name will be "user_tbl". class UserProfile(BaseModel): # table_name will be "userprofile_tbl". .ft P .fi .UNINDENT .UNINDENT .SS Indexes and Constraints .sp Peewee can create indexes on single or multiple columns, optionally including a \fIUNIQUE\fP constraint. Peewee also supports user\-defined constraints on both models and fields. .SS Single\-column indexes and constraints .sp Single column indexes are defined using field initialization parameters. The following example adds a unique index on the \fIusername\fP field, and a normal index on the \fIemail\fP field: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class User(Model): username = CharField(unique=True) email = CharField(index=True) .ft P .fi .UNINDENT .UNINDENT .sp To add a user\-defined constraint on a column, you can pass it in using the \fBconstraints\fP parameter. You may wish to specify a default value as part of the schema, or add a \fBCHECK\fP constraint, for example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Product(Model): name = CharField(unique=True) price = DecimalField(constraints=[Check(\(aqprice < 10000\(aq)]) created = DateTimeField( constraints=[SQL("DEFAULT (datetime(\(aqnow\(aq))")]) .ft P .fi .UNINDENT .UNINDENT .SS Multi\-column indexes .sp Multi\-column indexes may be defined as \fIMeta\fP 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. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Transaction(Model): from_acct = CharField() to_acct = CharField() amount = DecimalField() date = DateTimeField() class Meta: indexes = ( # create a unique on from/to/date ((\(aqfrom_acct\(aq, \(aqto_acct\(aq, \(aqdate\(aq), True), # create a non\-unique on from/to ((\(aqfrom_acct\(aq, \(aqto_acct\(aq), False), ) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Remember to add a \fBtrailing comma\fP if your tuple of indexes contains only one item: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Meta: indexes = ( ((\(aqfirst_name\(aq, \(aqlast_name\(aq), True), # Note the trailing comma! ) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS Advanced Index Creation .sp Peewee supports a more structured API for declaring indexes on a model using the \fBModel.add_index()\fP method or by directly using the \fBModelIndex\fP helper class. .sp Examples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Article(Model): name = TextField() timestamp = TimestampField() status = IntegerField() flags = IntegerField() # Add an index on "name" and "timestamp" columns. Article.add_index(Article.name, Article.timestamp) # Add a partial index on name and timestamp where status = 1. Article.add_index(Article.name, Article.timestamp, where=(Article.status == 1)) # Create a unique index on timestamp desc, status & 4. idx = Article.index( Article.timestamp.desc(), Article.flags.bin_and(4), unique=True) Article.add_index(idx) .ft P .fi .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 SQLite does not support parameterized \fBCREATE INDEX\fP 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 \fBSQL\fP helper: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # SQLite does not support parameterized CREATE INDEX queries, so # we declare it manually. Article.add_index(SQL(\(aqCREATE INDEX ...\(aq)) .ft P .fi .UNINDENT .UNINDENT .sp See \fBadd_index()\fP for details. .UNINDENT .UNINDENT .sp For more information, see: .INDENT 0.0 .IP \(bu 2 \fBModel.add_index()\fP .IP \(bu 2 \fBModel.index()\fP .IP \(bu 2 \fBModelIndex\fP .IP \(bu 2 \fBIndex\fP .UNINDENT .SS Table constraints .sp Peewee allows you to add arbitrary constraints to your \fBModel\fP, that will be part of the table definition when the schema is created. .sp For instance, suppose you have a \fIpeople\fP table with a composite primary key of two columns, the person\(aqs first and last name. You wish to have another table relate to the \fIpeople\fP table, and to do this, you will need to define a foreign key constraint: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Person(Model): first = CharField() last = CharField() class Meta: primary_key = CompositeKey(\(aqfirst\(aq, \(aqlast\(aq) class Pet(Model): owner_first = CharField() owner_last = CharField() pet_name = CharField() class Meta: constraints = [SQL(\(aqFOREIGN KEY(owner_first, owner_last) \(aq \(aqREFERENCES person(first, last)\(aq)] .ft P .fi .UNINDENT .UNINDENT .sp You can also implement \fBCHECK\fP constraints at the table level: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Product(Model): name = CharField(unique=True) price = DecimalField() class Meta: constraints = [Check(\(aqprice < 10000\(aq)] .ft P .fi .UNINDENT .UNINDENT .SS Primary Keys, Composite Keys and other Tricks .sp The \fBAutoField\fP is used to identify an auto\-incrementing integer primary key. If you do not specify a primary key, Peewee will automatically create an auto\-incrementing primary key named "id". .sp To specify an auto\-incrementing ID using a different field name, you can write: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Event(Model): event_id = AutoField() # Event.event_id will be auto\-incrementing PK. name = CharField() timestamp = DateTimeField(default=datetime.datetime.now) metadata = BlobField() .ft P .fi .UNINDENT .UNINDENT .sp You can identify a different field as the primary key, in which case an "id" column will not be created. In this example we will use a person\(aqs email address as the primary key: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Person(Model): email = CharField(primary_key=True) name = TextField() dob = DateField() .ft P .fi .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 I frequently see people write the following, expecting an auto\-incrementing integer primary key: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class MyModel(Model): id = IntegerField(primary_key=True) .ft P .fi .UNINDENT .UNINDENT .sp Peewee understands the above model declaration as a model with an integer primary key, but the value of that ID is determined by the application. To create an auto\-incrementing integer primary key, you would instead write: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class MyModel(Model): id = AutoField() # primary_key=True is implied. .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp Composite primary keys can be declared using \fBCompositeKey\fP\&. Note that doing this may cause issues with \fBForeignKeyField\fP, as Peewee does not support the concept of a "composite foreign\-key". As such, I\(aqve found it only advisable to use composite primary keys in a handful of situations, such as trivial many\-to\-many junction tables: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Image(Model): filename = TextField() mimetype = CharField() class Tag(Model): label = CharField() class ImageTag(Model): # Many\-to\-many relationship. image = ForeignKeyField(Image) tag = ForeignKeyField(Tag) class Meta: primary_key = CompositeKey(\(aqimage\(aq, \(aqtag\(aq) .ft P .fi .UNINDENT .UNINDENT .sp In the extremely rare case you wish to declare a model with \fIno\fP primary key, you can specify \fBprimary_key = False\fP in the model \fBMeta\fP options. .SS Non\-integer primary keys .sp If you would like use a non\-integer primary key (which I generally don\(aqt recommend), you can specify \fBprimary_key=True\fP when creating a field. When you wish to create a new instance for a model using a non\-autoincrementing primary key, you need to be sure you \fBsave()\fP specifying \fBforce_insert=True\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from peewee import * class UUIDModel(Model): id = UUIDField(primary_key=True) .ft P .fi .UNINDENT .UNINDENT .sp Auto\-incrementing IDs are, as their name says, automatically generated for you when you insert a new row into the database. When you call \fBsave()\fP, peewee determines whether to do an \fIINSERT\fP versus an \fIUPDATE\fP based on the presence of a primary key value. Since, with our uuid example, the database driver won\(aqt generate a new ID, we need to specify it manually. When we call save() for the first time, pass in \fBforce_insert = True\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # This works because .create() will specify \(gaforce_insert=True\(ga. obj1 = UUIDModel.create(id=uuid.uuid4()) # This will not work, however. Peewee will attempt to do an update: obj2 = UUIDModel(id=uuid.uuid4()) obj2.save() # WRONG obj2.save(force_insert=True) # CORRECT # Once the object has been created, you can call save() normally. obj2.save() .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Any foreign keys to a model with a non\-integer primary key will have a \fBForeignKeyField\fP use the same underlying storage type as the primary key they are related to. .UNINDENT .UNINDENT .SS Composite primary keys .sp Peewee has very basic support for composite keys. In order to use a composite key, you must set the \fBprimary_key\fP attribute of the model options to a \fBCompositeKey\fP instance: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class BlogToTag(Model): """A simple "through" table for many\-to\-many relationship.""" blog = ForeignKeyField(Blog) tag = ForeignKeyField(Tag) class Meta: primary_key = CompositeKey(\(aqblog\(aq, \(aqtag\(aq) .ft P .fi .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 Peewee does not support foreign\-keys to models that define a \fBCompositeKey\fP primary key. If you wish to add a foreign\-key to a model that has a composite primary key, replicate the columns on the related model and add a custom accessor (e.g. a property). .UNINDENT .UNINDENT .SS Manually specifying primary keys .sp Sometimes you do not want the database to automatically generate a value for the primary key, for instance when bulk loading relational data. To handle this on a \fIone\-off\fP basis, you can simply tell peewee to turn off \fBauto_increment\fP during the import: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C data = load_user_csv() # load up a bunch of data User._meta.auto_increment = False # turn off auto incrementing IDs with db.atomic(): for row in data: u = User(id=row[0], username=row[1]) u.save(force_insert=True) # <\-\- force peewee to insert row User._meta.auto_increment = True .ft P .fi .UNINDENT .UNINDENT .sp Although a better way to accomplish the above, without resorting to hacks, is to use the \fBModel.insert_many()\fP API: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C data = load_user_csv() fields = [User.id, User.username] with db.atomic(): User.insert_many(data, fields=fields).execute() .ft P .fi .UNINDENT .UNINDENT .sp If you \fIalways\fP want to have control over the primary key, simply do not use the \fBAutoField\fP field type, but use a normal \fBIntegerField\fP (or other column type): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class User(BaseModel): id = IntegerField(primary_key=True) username = CharField() >>> u = User.create(id=999, username=\(aqsomebody\(aq) >>> u.id 999 >>> User.get(User.username == \(aqsomebody\(aq).id 999 .ft P .fi .UNINDENT .UNINDENT .SS Models without a Primary Key .sp If you wish to create a model with no primary key, you can specify \fBprimary_key = False\fP in the inner \fBMeta\fP class: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class MyData(BaseModel): timestamp = DateTimeField() value = IntegerField() class Meta: primary_key = False .ft P .fi .UNINDENT .UNINDENT .sp This will yield the following DDL: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C CREATE TABLE "mydata" ( "timestamp" DATETIME NOT NULL, "value" INTEGER NOT NULL ) .ft P .fi .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 Some model APIs may not work correctly for models without a primary key, for instance \fBsave()\fP and \fBdelete_instance()\fP (you can instead use \fBinsert()\fP, \fBupdate()\fP and \fBdelete()\fP). .UNINDENT .UNINDENT .SS Self\-referential foreign keys .sp When creating a hierarchical structure it is necessary to create a self\-referential foreign key which links a child object to its parent. Because the model class is not defined at the time you instantiate the self\-referential foreign key, use the special string \fB\(aqself\(aq\fP to indicate a self\-referential foreign key: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Category(Model): name = CharField() parent = ForeignKeyField(\(aqself\(aq, null=True, backref=\(aqchildren\(aq) .ft P .fi .UNINDENT .UNINDENT .sp As you can see, the foreign key points \fIupward\fP to the parent object and the back\-reference is named \fIchildren\fP\&. .sp \fBATTENTION:\fP .INDENT 0.0 .INDENT 3.5 Self\-referential foreign\-keys should always be \fBnull=True\fP\&. .UNINDENT .UNINDENT .sp When querying against a model that contains a self\-referential foreign key you may sometimes need to perform a self\-join. In those cases you can use \fBModel.alias()\fP to create a table reference. Here is how you might query the category and parent model using a self\-join: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Parent = Category.alias() GrandParent = Category.alias() query = (Category .select(Category, Parent) .join(Parent, on=(Category.parent == Parent.id)) .join(GrandParent, on=(Parent.parent == GrandParent.id)) .where(GrandParent.name == \(aqsome category\(aq) .order_by(Category.name)) .ft P .fi .UNINDENT .UNINDENT .SS Circular foreign key dependencies .sp Sometimes it happens that you will create a circular dependency between two tables. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 My personal opinion is that circular foreign keys are a code smell and should be refactored (by adding an intermediary table, for instance). .UNINDENT .UNINDENT .sp Adding circular foreign keys with peewee is a bit tricky because at the time you are defining either foreign key, the model it points to will not have been defined yet, causing a \fBNameError\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class User(Model): username = CharField() favorite_tweet = ForeignKeyField(Tweet, null=True) # NameError!! class Tweet(Model): message = TextField() user = ForeignKeyField(User, backref=\(aqtweets\(aq) .ft P .fi .UNINDENT .UNINDENT .sp One option is to simply use an \fBIntegerField\fP to store the raw ID: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class User(Model): username = CharField() favorite_tweet_id = IntegerField(null=True) .ft P .fi .UNINDENT .UNINDENT .sp By using \fBDeferredForeignKey\fP we can get around the problem and still use a foreign key field: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class User(Model): username = CharField() # Tweet has not been defined yet so use the deferred reference. favorite_tweet = DeferredForeignKey(\(aqTweet\(aq, null=True) class Tweet(Model): message = TextField() user = ForeignKeyField(User, backref=\(aqtweets\(aq) # Now that Tweet is defined, "favorite_tweet" has been converted into # a ForeignKeyField. print(User.favorite_tweet) # .ft P .fi .UNINDENT .UNINDENT .sp There is one more quirk to watch out for, though. When you call \fBcreate_table\fP we will again encounter the same issue. For this reason peewee will not automatically create a foreign key constraint for any \fIdeferred\fP foreign keys. .sp To create the tables \fIand\fP the foreign\-key constraint, you can use the \fBSchemaManager.create_foreign_key()\fP method to create the constraint after creating the tables: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Will create the User and Tweet tables, but does *not* create a # foreign\-key constraint on User.favorite_tweet. db.create_tables([User, Tweet]) # Create the foreign\-key constraint: User._schema.create_foreign_key(User.favorite_tweet) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Because SQLite has limited support for altering tables, foreign\-key constraints cannot be added to a table after it has been created. .UNINDENT .UNINDENT .SS Querying .sp This section will cover the basic CRUD operations commonly performed on a relational database: .INDENT 0.0 .IP \(bu 2 \fBModel.create()\fP, for executing \fIINSERT\fP queries. .IP \(bu 2 \fBModel.save()\fP and \fBModel.update()\fP, for executing \fIUPDATE\fP queries. .IP \(bu 2 \fBModel.delete_instance()\fP and \fBModel.delete()\fP, for executing \fIDELETE\fP queries. .IP \(bu 2 \fBModel.select()\fP, for executing \fISELECT\fP queries. .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 There is also a large collection of example queries taken from the \fI\%Postgresql Exercises\fP website. Examples are listed on the query examples document. .UNINDENT .UNINDENT .SS Creating a new record .sp You can use \fBModel.create()\fP to create a new model instance. This method accepts keyword arguments, where the keys correspond to the names of the model\(aqs fields. A new instance is returned and a row is added to the table. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> User.create(username=\(aqCharlie\(aq) <__main__.User object at 0x2529350> .ft P .fi .UNINDENT .UNINDENT .sp This will \fIINSERT\fP a new row into the database. The primary key will automatically be retrieved and stored on the model instance. .sp Alternatively, you can build up a model instance programmatically and then call \fBsave()\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> user = User(username=\(aqCharlie\(aq) >>> user.save() # save() returns the number of rows modified. 1 >>> user.id 1 >>> huey = User() >>> huey.username = \(aqHuey\(aq >>> huey.save() 1 >>> huey.id 2 .ft P .fi .UNINDENT .UNINDENT .sp When a model has a foreign key, you can directly assign a model instance to the foreign key field when creating a new record. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> tweet = Tweet.create(user=huey, message=\(aqHello!\(aq) .ft P .fi .UNINDENT .UNINDENT .sp You can also use the value of the related object\(aqs primary key: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> tweet = Tweet.create(user=2, message=\(aqHello again!\(aq) .ft P .fi .UNINDENT .UNINDENT .sp If you simply wish to insert data and do not need to create a model instance, you can use \fBModel.insert()\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> User.insert(username=\(aqMickey\(aq).execute() 3 .ft P .fi .UNINDENT .UNINDENT .sp After executing the insert query, the primary key of the new row is returned. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 There are several ways you can speed up bulk insert operations. Check out the \fI\%Bulk inserts\fP recipe section for more information. .UNINDENT .UNINDENT .SS Bulk inserts .sp There are a couple of ways you can load lots of data quickly. The naive approach is to simply call \fBModel.create()\fP in a loop: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C data_source = [ {\(aqfield1\(aq: \(aqval1\-1\(aq, \(aqfield2\(aq: \(aqval1\-2\(aq}, {\(aqfield1\(aq: \(aqval2\-1\(aq, \(aqfield2\(aq: \(aqval2\-2\(aq}, # ... ] for data_dict in data_source: MyModel.create(**data_dict) .ft P .fi .UNINDENT .UNINDENT .sp The above approach is slow for a couple of reasons: .INDENT 0.0 .IP 1. 3 If you are not wrapping the loop in a transaction then each call to \fBcreate()\fP happens in its own transaction. That is going to be really slow! .IP 2. 3 There is a decent amount of Python logic getting in your way, and each \fBInsertQuery\fP must be generated and parsed into SQL. .IP 3. 3 That\(aqs a lot of data (in terms of raw bytes of SQL) you are sending to your database to parse. .IP 4. 3 We are retrieving the \fIlast insert id\fP, which causes an additional query to be executed in some cases. .UNINDENT .sp You can get a significant speedup by simply wrapping this in a transaction with \fBatomic()\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # This is much faster. with db.atomic(): for data_dict in data_source: MyModel.create(**data_dict) .ft P .fi .UNINDENT .UNINDENT .sp The above code still suffers from points 2, 3 and 4. We can get another big boost by using \fBinsert_many()\fP\&. This method accepts a list of tuples or dictionaries, and inserts multiple rows in a single query: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C data_source = [ {\(aqfield1\(aq: \(aqval1\-1\(aq, \(aqfield2\(aq: \(aqval1\-2\(aq}, {\(aqfield1\(aq: \(aqval2\-1\(aq, \(aqfield2\(aq: \(aqval2\-2\(aq}, # ... ] # Fastest way to INSERT multiple rows. MyModel.insert_many(data_source).execute() .ft P .fi .UNINDENT .UNINDENT .sp The \fBinsert_many()\fP method also accepts a list of row\-tuples, provided you also specify the corresponding fields: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # We can INSERT tuples as well... data = [(\(aqval1\-1\(aq, \(aqval1\-2\(aq), (\(aqval2\-1\(aq, \(aqval2\-2\(aq), (\(aqval3\-1\(aq, \(aqval3\-2\(aq)] # But we need to indicate which fields the values correspond to. MyModel.insert_many(data, fields=[MyModel.field1, MyModel.field2]).execute() .ft P .fi .UNINDENT .UNINDENT .sp It is also a good practice to wrap the bulk insert in a transaction: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # You can, of course, wrap this in a transaction as well: with db.atomic(): MyModel.insert_many(data, fields=fields).execute() .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 SQLite users should be aware of some caveats when using bulk inserts. Specifically, your SQLite3 version must be 3.7.11.0 or newer to take advantage of the bulk insert API. Additionally, by default SQLite limits the number of bound variables in a SQL query to \fB999\fP for SQLite versions prior to 3.32.0 (2020\-05\-22) and 32766 for SQLite versions after 3.32.0. .UNINDENT .UNINDENT .SS Inserting rows in batches .sp Depending on the number of rows in your data source, you may need to break it up into chunks. SQLite in particular typically has a \fI\%limit of 999 or 32766\fP variables\-per\-query (batch size would then be 999 // row length or 32766 // row length). .sp You can write a loop to batch your data into chunks (in which case it is \fBstrongly recommended\fP you use a transaction): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Insert rows 100 at a time. with db.atomic(): for idx in range(0, len(data_source), 100): MyModel.insert_many(data_source[idx:idx+100]).execute() .ft P .fi .UNINDENT .UNINDENT .sp Peewee comes with a \fBchunked()\fP helper function which you can use for \fIefficiently\fP chunking a generic iterable into a series of \fIbatch\fP\-sized iterables: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from peewee import chunked # Insert rows 100 at a time. with db.atomic(): for batch in chunked(data_source, 100): MyModel.insert_many(batch).execute() .ft P .fi .UNINDENT .UNINDENT .SS Alternatives .sp The \fBModel.bulk_create()\fP method behaves much like \fBModel.insert_many()\fP, but instead it accepts a list of unsaved model instances to insert, and it optionally accepts a batch\-size parameter. To use the \fBbulk_create()\fP API: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Read list of usernames from a file, for example. with open(\(aquser_list.txt\(aq) as fh: # Create a list of unsaved User instances. users = [User(username=line.strip()) for line in fh.readlines()] # Wrap the operation in a transaction and batch INSERT the users # 100 at a time. with db.atomic(): User.bulk_create(users, batch_size=100) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 If you are using Postgresql (which supports the \fBRETURNING\fP clause), then the previously\-unsaved model instances will have their new primary key values automatically populated. .UNINDENT .UNINDENT .sp In addition, Peewee also offers \fBModel.bulk_update()\fP, which can efficiently update one or more columns on a list of models. For example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # First, create 3 users with usernames u1, u2, u3. u1, u2, u3 = [User.create(username=\(aqu%s\(aq % i) for i in (1, 2, 3)] # Now we\(aqll modify the user instances. u1.username = \(aqu1\-x\(aq u2.username = \(aqu2\-y\(aq u3.username = \(aqu3\-z\(aq # Update all three users with a single UPDATE query. User.bulk_update([u1, u2, u3], fields=[User.username]) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 For large lists of objects, you should specify a reasonable batch_size and wrap the call to \fBbulk_update()\fP with \fBDatabase.atomic()\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C with database.atomic(): User.bulk_update(list_of_users, fields=[\(aqusername\(aq], batch_size=50) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp Alternatively, you can use the \fBDatabase.batch_commit()\fP helper to process chunks of rows inside \fIbatch\fP\-sized transactions. This method also provides a workaround for databases besides Postgresql, when the primary\-key of the newly\-created rows must be obtained. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # List of row data to insert. row_data = [{\(aqusername\(aq: \(aqu1\(aq}, {\(aqusername\(aq: \(aqu2\(aq}, ...] # Assume there are 789 items in row_data. The following code will result in # 8 total transactions (7x100 rows + 1x89 rows). for row in db.batch_commit(row_data, 100): User.create(**row) .ft P .fi .UNINDENT .UNINDENT .SS Bulk\-loading from another table .sp If the data you would like to bulk load is stored in another table, you can also create \fIINSERT\fP queries whose source is a \fISELECT\fP query. Use the \fBModel.insert_from()\fP method: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C res = (TweetArchive .insert_from( Tweet.select(Tweet.user, Tweet.message), fields=[TweetArchive.user, TweetArchive.message]) .execute()) .ft P .fi .UNINDENT .UNINDENT .sp The above query is equivalent to the following SQL: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C INSERT INTO "tweet_archive" ("user_id", "message") SELECT "user_id", "message" FROM "tweet"; .ft P .fi .UNINDENT .UNINDENT .SS Updating existing records .sp Once a model instance has a primary key, any subsequent call to \fBsave()\fP will result in an \fIUPDATE\fP rather than another \fIINSERT\fP\&. The model\(aqs primary key will not change: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> user.save() # save() returns the number of rows modified. 1 >>> user.id 1 >>> user.save() >>> user.id 1 >>> huey.save() 1 >>> huey.id 2 .ft P .fi .UNINDENT .UNINDENT .sp If you want to update multiple records, issue an \fIUPDATE\fP query. The following example will update all \fBTweet\fP objects, marking them as \fIpublished\fP, if they were created before today. \fBModel.update()\fP accepts keyword arguments where the keys correspond to the model\(aqs field names: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> today = datetime.today() >>> query = Tweet.update(is_published=True).where(Tweet.creation_date < today) >>> query.execute() # Returns the number of rows that were updated. 4 .ft P .fi .UNINDENT .UNINDENT .sp For more information, see the documentation on \fBModel.update()\fP, \fBUpdate\fP and \fBModel.bulk_update()\fP\&. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 If you would like more information on performing atomic updates (such as incrementing the value of a column), check out the \fI\%atomic update\fP recipes. .UNINDENT .UNINDENT .SS Atomic updates .sp Peewee allows you to perform atomic updates. Let\(aqs suppose we need to update some counters. The naive approach would be to write something like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for stat in Stat.select().where(Stat.url == request.url): \&... stat.counter += 1 \&... stat.save() .ft P .fi .UNINDENT .UNINDENT .sp \fBDo not do this!\fP Not only is this slow, but it is also vulnerable to race conditions if multiple processes are updating the counter at the same time. .sp Instead, you can update the counters atomically using \fBupdate()\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> query = Stat.update(counter=Stat.counter + 1).where(Stat.url == request.url) >>> query.execute() .ft P .fi .UNINDENT .UNINDENT .sp You can make these update statements as complex as you like. Let\(aqs give all our employees a bonus equal to their previous bonus plus 10% of their salary: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> query = Employee.update(bonus=(Employee.bonus + (Employee.salary * .1))) >>> query.execute() # Give everyone a bonus! .ft P .fi .UNINDENT .UNINDENT .sp We can even use a subquery to update the value of a column. Suppose we had a denormalized column on the \fBUser\fP model that stored the number of tweets a user had made, and we updated this value periodically. Here is how you might write such a query: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> subquery = Tweet.select(fn.COUNT(Tweet.id)).where(Tweet.user == User.id) >>> update = User.update(num_tweets=subquery) >>> update.execute() .ft P .fi .UNINDENT .UNINDENT .SS Upsert .sp Peewee provides support for varying types of upsert functionality. With SQLite prior to 3.24.0 and MySQL, Peewee offers the \fBreplace()\fP, which allows you to insert a record or, in the event of a constraint violation, replace the existing record. .sp Example of using \fBreplace()\fP and \fBon_conflict_replace()\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class User(Model): username = TextField(unique=True) last_login = DateTimeField(null=True) # Insert or update the user. The "last_login" value will be updated # regardless of whether the user existed previously. user_id = (User .replace(username=\(aqthe\-user\(aq, last_login=datetime.now()) .execute()) # This query is equivalent: user_id = (User .insert(username=\(aqthe\-user\(aq, last_login=datetime.now()) .on_conflict_replace() .execute()) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 In addition to \fIreplace\fP, SQLite, MySQL and Postgresql provide an \fIignore\fP action (see: \fBon_conflict_ignore()\fP) if you simply wish to insert and ignore any potential constraint violation. .UNINDENT .UNINDENT .sp \fBMySQL\fP supports upsert via the \fION DUPLICATE KEY UPDATE\fP clause. For example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class User(Model): username = TextField(unique=True) last_login = DateTimeField(null=True) login_count = IntegerField() # Insert a new user. User.create(username=\(aqhuey\(aq, login_count=0) # Simulate the user logging in. The login count and timestamp will be # either created or updated correctly. now = datetime.now() rowid = (User .insert(username=\(aqhuey\(aq, last_login=now, login_count=1) .on_conflict( preserve=[User.last_login], # Use the value we would have inserted. update={User.login_count: User.login_count + 1}) .execute()) .ft P .fi .UNINDENT .UNINDENT .sp In the above example, we could safely invoke the upsert query as many times as we wanted. The login count will be incremented atomically, the last login column will be updated, and no duplicate rows will be created. .sp \fBPostgresql and SQLite\fP (3.24.0 and newer) provide a different syntax that allows for more granular control over which constraint violation should trigger the conflict resolution, and what values should be updated or preserved. .sp Example of using \fBon_conflict()\fP to perform a Postgresql\-style upsert (or SQLite 3.24+): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class User(Model): username = TextField(unique=True) last_login = DateTimeField(null=True) login_count = IntegerField() # Insert a new user. User.create(username=\(aqhuey\(aq, login_count=0) # Simulate the user logging in. The login count and timestamp will be # either created or updated correctly. now = datetime.now() rowid = (User .insert(username=\(aqhuey\(aq, last_login=now, login_count=1) .on_conflict( conflict_target=[User.username], # Which constraint? preserve=[User.last_login], # Use the value we would have inserted. update={User.login_count: User.login_count + 1}) .execute()) .ft P .fi .UNINDENT .UNINDENT .sp In the above example, we could safely invoke the upsert query as many times as we wanted. The login count will be incremented atomically, the last login column will be updated, and no duplicate rows will be created. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 The main difference between MySQL and Postgresql/SQLite is that Postgresql and SQLite require that you specify a \fBconflict_target\fP\&. .UNINDENT .UNINDENT .sp Here is a more advanced (if contrived) example using the \fBEXCLUDED\fP namespace. The \fBEXCLUDED\fP helper allows us to reference values in the conflicting data. For our example, we\(aqll assume a simple table mapping a unique key (string) to a value (integer): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class KV(Model): key = CharField(unique=True) value = IntegerField() # Create one row. KV.create(key=\(aqk1\(aq, value=1) # Demonstrate usage of EXCLUDED. # Here we will attempt to insert a new value for a given key. If that # key already exists, then we will update its value with the *sum* of its # original value and the value we attempted to insert \-\- provided that # the new value is larger than the original value. query = (KV.insert(key=\(aqk1\(aq, value=10) .on_conflict(conflict_target=[KV.key], update={KV.value: KV.value + EXCLUDED.value}, where=(EXCLUDED.value > KV.value))) # Executing the above query will result in the following data being # present in the "kv" table: # (key=\(aqk1\(aq, value=11) query.execute() # If we attempted to execute the query *again*, then nothing would be # updated, as the new value (10) is now less than the value in the # original row (11). .ft P .fi .UNINDENT .UNINDENT .sp For more information, see \fBInsert.on_conflict()\fP and \fBOnConflict\fP\&. .SS Deleting records .sp To delete a single model instance, you can use the \fBModel.delete_instance()\fP shortcut. \fBdelete_instance()\fP will delete the given model instance and can optionally delete any dependent objects recursively (by specifying \fIrecursive=True\fP). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> user = User.get(User.id == 1) >>> user.delete_instance() # Returns the number of rows deleted. 1 >>> User.get(User.id == 1) UserDoesNotExist: instance matching query does not exist: SQL: SELECT t1."id", t1."username" FROM "user" AS t1 WHERE t1."id" = ? PARAMS: [1] .ft P .fi .UNINDENT .UNINDENT .sp To delete an arbitrary set of rows, you can issue a \fIDELETE\fP query. The following will delete all \fBTweet\fP objects that are over one year old: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> query = Tweet.delete().where(Tweet.creation_date < one_year_ago) >>> query.execute() # Returns the number of rows deleted. 7 .ft P .fi .UNINDENT .UNINDENT .sp For more information, see the documentation on: .INDENT 0.0 .IP \(bu 2 \fBModel.delete_instance()\fP .IP \(bu 2 \fBModel.delete()\fP .IP \(bu 2 \fBDeleteQuery\fP .UNINDENT .SS Selecting a single record .sp You can use the \fBModel.get()\fP method to retrieve a single instance matching the given query. For primary\-key lookups, you can also use the shortcut method \fBModel.get_by_id()\fP\&. .sp This method is a shortcut that calls \fBModel.select()\fP with the given query, but limits the result set to a single row. Additionally, if no model matches the given query, a \fBDoesNotExist\fP exception will be raised. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> User.get(User.id == 1) <__main__.User object at 0x25294d0> >>> User.get_by_id(1) # Same as above. <__main__.User object at 0x252df10> >>> User[1] # Also same as above. <__main__.User object at 0x252dd10> >>> User.get(User.id == 1).username u\(aqCharlie\(aq >>> User.get(User.username == \(aqCharlie\(aq) <__main__.User object at 0x2529410> >>> User.get(User.username == \(aqnobody\(aq) UserDoesNotExist: instance matching query does not exist: SQL: SELECT t1."id", t1."username" FROM "user" AS t1 WHERE t1."username" = ? PARAMS: [\(aqnobody\(aq] .ft P .fi .UNINDENT .UNINDENT .sp For more advanced operations, you can use \fBSelectBase.get()\fP\&. The following query retrieves the latest tweet from the user named \fIcharlie\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> (Tweet \&... .select() \&... .join(User) \&... .where(User.username == \(aqcharlie\(aq) \&... .order_by(Tweet.created_date.desc()) \&... .get()) <__main__.Tweet object at 0x2623410> .ft P .fi .UNINDENT .UNINDENT .sp For more information, see the documentation on: .INDENT 0.0 .IP \(bu 2 \fBModel.get()\fP .IP \(bu 2 \fBModel.get_by_id()\fP .IP \(bu 2 \fBModel.get_or_none()\fP \- if no matching row is found, return \fBNone\fP\&. .IP \(bu 2 \fBModel.first()\fP .IP \(bu 2 \fBModel.select()\fP .IP \(bu 2 \fBSelectBase.get()\fP .UNINDENT .SS Create or get .sp Peewee has one helper method for performing "get/create" type operations: \fBModel.get_or_create()\fP, which first attempts to retrieve the matching row. Failing that, a new row will be created. .sp For "create or get" type logic, typically one would rely on a \fIunique\fP constraint or primary key to prevent the creation of duplicate objects. As an example, let\(aqs say we wish to implement registering a new user account using the example User model\&. The \fIUser\fP model has a \fIunique\fP constraint on the username field, so we will rely on the database\(aqs integrity guarantees to ensure we don\(aqt end up with duplicate usernames: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C try: with db.atomic(): return User.create(username=username) except peewee.IntegrityError: # \(gausername\(ga is a unique column, so this username already exists, # making it safe to call .get(). return User.get(User.username == username) .ft P .fi .UNINDENT .UNINDENT .sp You can easily encapsulate this type of logic as a \fBclassmethod\fP on your own \fBModel\fP classes. .sp The above example first attempts at creation, then falls back to retrieval, relying on the database to enforce a unique constraint. If you prefer to attempt to retrieve the record first, you can use \fBget_or_create()\fP\&. This method is implemented along the same lines as the Django function of the same name. You can use the Django\-style keyword argument filters to specify your \fBWHERE\fP conditions. The function returns a 2\-tuple containing the instance and a boolean value indicating if the object was created. .sp Here is how you might implement user account creation using \fBget_or_create()\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C user, created = User.get_or_create(username=username) .ft P .fi .UNINDENT .UNINDENT .sp Suppose we have a different model \fBPerson\fP and would like to get or create a person object. The only conditions we care about when retrieving the \fBPerson\fP are their first and last names, \fBbut\fP if we end up needing to create a new record, we will also specify their date\-of\-birth and favorite color: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C person, created = Person.get_or_create( first_name=first_name, last_name=last_name, defaults={\(aqdob\(aq: dob, \(aqfavorite_color\(aq: \(aqgreen\(aq}) .ft P .fi .UNINDENT .UNINDENT .sp Any keyword argument passed to \fBget_or_create()\fP will be used in the \fBget()\fP portion of the logic, except for the \fBdefaults\fP dictionary, which will be used to populate values on newly\-created instances. .sp For more details read the documentation for \fBModel.get_or_create()\fP\&. .SS Selecting multiple records .sp We can use \fBModel.select()\fP to retrieve rows from the table. When you construct a \fISELECT\fP query, the database will return any rows that correspond to your query. Peewee allows you to iterate over these rows, as well as use indexing and slicing operations: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> query = User.select() >>> [user.username for user in query] [\(aqCharlie\(aq, \(aqHuey\(aq, \(aqPeewee\(aq] >>> query[1] <__main__.User at 0x7f83e80f5550> >>> query[1].username \(aqHuey\(aq >>> query[:2] [<__main__.User at 0x7f83e80f53a8>, <__main__.User at 0x7f83e80f5550>] .ft P .fi .UNINDENT .UNINDENT .sp \fBSelect\fP queries are smart, in that you can iterate, index and slice the query multiple times but the query is only executed once. .sp In the following example, we will simply call \fBselect()\fP and iterate over the return value, which is an instance of \fBSelect\fP\&. This will return all the rows in the \fIUser\fP table: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for user in User.select(): \&... print user.username \&... Charlie Huey Peewee .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Subsequent iterations of the same query will not hit the database as the results are cached. To disable this behavior (to reduce memory usage), call \fBSelect.iterator()\fP when iterating. .UNINDENT .UNINDENT .sp When iterating over a model that contains a foreign key, be careful with the way you access values on related models. Accidentally resolving a foreign key or iterating over a back\-reference can cause N+1 query behavior\&. .sp When you create a foreign key, such as \fBTweet.user\fP, you can use the \fIbackref\fP to create a back\-reference (\fBUser.tweets\fP). Back\-references are exposed as \fBSelect\fP instances: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> tweet = Tweet.get() >>> tweet.user # Accessing a foreign key returns the related model. >>> user = User.get() >>> user.tweets # Accessing a back\-reference returns a query. .ft P .fi .UNINDENT .UNINDENT .sp You can iterate over the \fBuser.tweets\fP back\-reference just like any other \fBSelect\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for tweet in user.tweets: \&... print(tweet.message) \&... hello world this is fun look at this picture of my food .ft P .fi .UNINDENT .UNINDENT .sp In addition to returning model instances, \fBSelect\fP queries can return dictionaries, tuples and namedtuples. Depending on your use\-case, you may find it easier to work with rows as dictionaries, for example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> query = User.select().dicts() >>> for row in query: \&... print(row) {\(aqid\(aq: 1, \(aqusername\(aq: \(aqCharlie\(aq} {\(aqid\(aq: 2, \(aqusername\(aq: \(aqHuey\(aq} {\(aqid\(aq: 3, \(aqusername\(aq: \(aqPeewee\(aq} .ft P .fi .UNINDENT .UNINDENT .sp See \fBnamedtuples()\fP, \fBtuples()\fP, \fBdicts()\fP for more information. .SS Iterating over large result\-sets .sp By default peewee will cache the rows returned when iterating over a \fBSelect\fP query. This is an optimization to allow multiple iterations as well as indexing and slicing without causing additional queries. This caching can be problematic, however, when you plan to iterate over a large number of rows. .sp To reduce the amount of memory used by peewee when iterating over a query, use the \fBiterator()\fP method. This method allows you to iterate without caching each model returned, using much less memory when iterating over large result sets. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Let\(aqs assume we\(aqve got 10 million stat objects to dump to a csv file. stats = Stat.select() # Our imaginary serializer class serializer = CSVSerializer() # Loop over all the stats and serialize. for stat in stats.iterator(): serializer.serialize_object(stat) .ft P .fi .UNINDENT .UNINDENT .sp For simple queries you can see further speed improvements by returning rows as dictionaries, namedtuples or tuples. The following methods can be used on any \fBSelect\fP query to change the result row type: .INDENT 0.0 .IP \(bu 2 \fBdicts()\fP .IP \(bu 2 \fBnamedtuples()\fP .IP \(bu 2 \fBtuples()\fP .UNINDENT .sp Don\(aqt forget to append the \fBiterator()\fP method call to also reduce memory consumption. For example, the above code might look like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Let\(aqs assume we\(aqve got 10 million stat objects to dump to a csv file. stats = Stat.select() # Our imaginary serializer class serializer = CSVSerializer() # Loop over all the stats (rendered as tuples, without caching) and serialize. for stat_tuple in stats.tuples().iterator(): serializer.serialize_tuple(stat_tuple) .ft P .fi .UNINDENT .UNINDENT .sp When iterating over a large number of rows that contain columns from multiple tables, peewee will reconstruct the model graph for each row returned. This operation can be slow for complex graphs. For example, if we were selecting a list of tweets along with the username and avatar of the tweet\(aqs author, Peewee would have to create two objects for each row (a tweet and a user). In addition to the above row\-types, there is a fourth method \fBobjects()\fP which will return the rows as model instances, but will not attempt to resolve the model graph. .sp For example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Tweet .select(Tweet, User) # Select tweet and user data. .join(User)) # Note that the user columns are stored in a separate User instance # accessible at tweet.user: for tweet in query: print(tweet.user.username, tweet.content) # Using ".objects()" will not create the tweet.user object and assigns all # user attributes to the tweet instance: for tweet in query.objects(): print(tweet.username, tweet.content) .ft P .fi .UNINDENT .UNINDENT .sp For maximum performance, you can execute queries and then iterate over the results using the underlying database cursor. \fBDatabase.execute()\fP accepts a query object, executes the query, and returns a DB\-API 2.0 \fBCursor\fP object. The cursor will return the raw row\-tuples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Tweet.select(Tweet.content, User.username).join(User) cursor = database.execute(query) for (content, username) in cursor: print(username, \(aq\->\(aq, content) .ft P .fi .UNINDENT .UNINDENT .SS Filtering records .sp You can filter for particular records using normal python operators. Peewee supports a wide variety of query operators\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> user = User.get(User.username == \(aqCharlie\(aq) >>> for tweet in Tweet.select().where(Tweet.user == user, Tweet.is_published == True): \&... print(tweet.user.username, \(aq\->\(aq, tweet.message) \&... Charlie \-> hello world Charlie \-> this is fun >>> for tweet in Tweet.select().where(Tweet.created_date < datetime.datetime(2011, 1, 1)): \&... print(tweet.message, tweet.created_date) \&... Really old tweet 2010\-01\-01 00:00:00 .ft P .fi .UNINDENT .UNINDENT .sp You can also filter across joins: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for tweet in Tweet.select().join(User).where(User.username == \(aqCharlie\(aq): \&... print(tweet.message) hello world this is fun look at this picture of my food .ft P .fi .UNINDENT .UNINDENT .sp If you want to express a complex query, use parentheses and python\(aqs bitwise \fIor\fP and \fIand\fP operators: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> Tweet.select().join(User).where( \&... (User.username == \(aqCharlie\(aq) | \&... (User.username == \(aqPeewee Herman\(aq)) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Note that Peewee uses \fBbitwise\fP operators (\fB&\fP and \fB|\fP) rather than logical operators (\fBand\fP and \fBor\fP). The reason for this is that Python coerces the return value of logical operations to a boolean value. This is also the reason why "IN" queries must be expressed using \fB\&.in_()\fP rather than the \fBin\fP operator. .UNINDENT .UNINDENT .sp Check out the table of query operations to see what types of queries are possible. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 A lot of fun things can go in the where clause of a query, such as: .INDENT 0.0 .IP \(bu 2 A field expression, e.g. \fBUser.username == \(aqCharlie\(aq\fP .IP \(bu 2 A function expression, e.g. \fBfn.Lower(fn.Substr(User.username, 1, 1)) == \(aqa\(aq\fP .IP \(bu 2 A comparison of one column to another, e.g. \fBEmployee.salary < (Employee.tenure * 1000) + 40000\fP .UNINDENT .sp You can also nest queries, for example tweets by users whose username starts with "a": .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # get users whose username starts with "a" a_users = User.select().where(fn.Lower(fn.Substr(User.username, 1, 1)) == \(aqa\(aq) # the ".in_()" method signifies an "IN" query a_user_tweets = Tweet.select().where(Tweet.user.in_(a_users)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS More query examples .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 For a wide range of example queries, see the Query Examples document, which shows how to implements queries from the \fI\%PostgreSQL Exercises\fP website. .UNINDENT .UNINDENT .sp Get active users: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C User.select().where(User.active == True) .ft P .fi .UNINDENT .UNINDENT .sp Get users who are either staff or superusers: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C User.select().where( (User.is_staff == True) | (User.is_superuser == True)) .ft P .fi .UNINDENT .UNINDENT .sp Get tweets by user named "charlie": .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Tweet.select().join(User).where(User.username == \(aqcharlie\(aq) .ft P .fi .UNINDENT .UNINDENT .sp Get tweets by staff or superusers (assumes FK relationship): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Tweet.select().join(User).where( (User.is_staff == True) | (User.is_superuser == True)) .ft P .fi .UNINDENT .UNINDENT .sp Get tweets by staff or superusers using a subquery: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C staff_super = User.select(User.id).where( (User.is_staff == True) | (User.is_superuser == True)) Tweet.select().where(Tweet.user.in_(staff_super)) .ft P .fi .UNINDENT .UNINDENT .SS Sorting records .sp To return rows in order, use the \fBorder_by()\fP method: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for t in Tweet.select().order_by(Tweet.created_date): \&... print(t.pub_date) \&... 2010\-01\-01 00:00:00 2011\-06\-07 14:08:48 2011\-06\-07 14:12:57 >>> for t in Tweet.select().order_by(Tweet.created_date.desc()): \&... print(t.pub_date) \&... 2011\-06\-07 14:12:57 2011\-06\-07 14:08:48 2010\-01\-01 00:00:00 .ft P .fi .UNINDENT .UNINDENT .sp You can also use \fB+\fP and \fB\-\fP prefix operators to indicate ordering: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # The following queries are equivalent: Tweet.select().order_by(Tweet.created_date.desc()) Tweet.select().order_by(\-Tweet.created_date) # Note the "\-" prefix. # Similarly you can use "+" to indicate ascending order, though ascending # is the default when no ordering is otherwise specified. User.select().order_by(+User.username) .ft P .fi .UNINDENT .UNINDENT .sp You can also order across joins. Assuming you want to order tweets by the username of the author, then by created_date: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Tweet .select() .join(User) .order_by(User.username, Tweet.created_date.desc())) .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT t1."id", t1."user_id", t1."message", t1."is_published", t1."created_date" FROM "tweet" AS t1 INNER JOIN "user" AS t2 ON t1."user_id" = t2."id" ORDER BY t2."username", t1."created_date" DESC .ft P .fi .UNINDENT .UNINDENT .sp When sorting on a calculated value, you can either include the necessary SQL expressions, or reference the alias assigned to the value. Here are two examples illustrating these methods: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Let\(aqs start with our base query. We want to get all usernames and the number of # tweets they\(aqve made. We wish to sort this list from users with most tweets to # users with fewest tweets. query = (User .select(User.username, fn.COUNT(Tweet.id).alias(\(aqnum_tweets\(aq)) .join(Tweet, JOIN.LEFT_OUTER) .group_by(User.username)) .ft P .fi .UNINDENT .UNINDENT .sp You can order using the same COUNT expression used in the \fBselect\fP clause. In the example below we are ordering by the \fBCOUNT()\fP of tweet ids descending: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (User .select(User.username, fn.COUNT(Tweet.id).alias(\(aqnum_tweets\(aq)) .join(Tweet, JOIN.LEFT_OUTER) .group_by(User.username) .order_by(fn.COUNT(Tweet.id).desc())) .ft P .fi .UNINDENT .UNINDENT .sp Alternatively, you can reference the alias assigned to the calculated value in the \fBselect\fP clause. This method has the benefit of being a bit easier to read. Note that we are not referring to the named alias directly, but are wrapping it using the \fBSQL\fP helper: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (User .select(User.username, fn.COUNT(Tweet.id).alias(\(aqnum_tweets\(aq)) .join(Tweet, JOIN.LEFT_OUTER) .group_by(User.username) .order_by(SQL(\(aqnum_tweets\(aq).desc())) .ft P .fi .UNINDENT .UNINDENT .sp Or, to do things the "peewee" way: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ntweets = fn.COUNT(Tweet.id) query = (User .select(User.username, ntweets.alias(\(aqnum_tweets\(aq)) .join(Tweet, JOIN.LEFT_OUTER) .group_by(User.username) .order_by(ntweets.desc()) .ft P .fi .UNINDENT .UNINDENT .SS Getting random records .sp Occasionally you may want to pull a random record from the database. You can accomplish this by ordering by the \fIrandom\fP or \fIrand\fP function (depending on your database): .sp Postgresql and Sqlite use the \fIRandom\fP function: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Pick 5 lucky winners: LotteryNumber.select().order_by(fn.Random()).limit(5) .ft P .fi .UNINDENT .UNINDENT .sp MySQL uses \fIRand\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Pick 5 lucky winners: LotteryNumber.select().order_by(fn.Rand()).limit(5) .ft P .fi .UNINDENT .UNINDENT .SS Paginating records .sp The \fBpaginate()\fP method makes it easy to grab a \fIpage\fP or records. \fBpaginate()\fP takes two parameters, \fBpage_number\fP, and \fBitems_per_page\fP\&. .sp \fBATTENTION:\fP .INDENT 0.0 .INDENT 3.5 Page numbers are 1\-based, so the first page of results will be page 1. .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for tweet in Tweet.select().order_by(Tweet.id).paginate(2, 10): \&... print(tweet.message) \&... tweet 10 tweet 11 tweet 12 tweet 13 tweet 14 tweet 15 tweet 16 tweet 17 tweet 18 tweet 19 .ft P .fi .UNINDENT .UNINDENT .sp If you would like more granular control, you can always use \fBlimit()\fP and \fBoffset()\fP\&. .SS Counting records .sp You can count the number of rows in any select query: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> Tweet.select().count() 100 >>> Tweet.select().where(Tweet.id > 50).count() 50 .ft P .fi .UNINDENT .UNINDENT .sp Peewee will wrap your query in an outer query that performs a count, which results in SQL like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT COUNT(1) FROM ( ... your query ... ); .ft P .fi .UNINDENT .UNINDENT .SS Aggregating records .sp Suppose you have some users and want to get a list of them along with the count of tweets in each. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (User .select(User, fn.Count(Tweet.id).alias(\(aqcount\(aq)) .join(Tweet, JOIN.LEFT_OUTER) .group_by(User)) .ft P .fi .UNINDENT .UNINDENT .sp The resulting query will return \fIUser\fP objects with all their normal attributes plus an additional attribute \fIcount\fP which will contain the count of tweets for each user. We use a left outer join to include users who have no tweets. .sp Let\(aqs assume you have a tagging application and want to find tags that have a certain number of related objects. For this example we\(aqll use some different models in a many\-to\-many configuration: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Photo(Model): image = CharField() class Tag(Model): name = CharField() class PhotoTag(Model): photo = ForeignKeyField(Photo) tag = ForeignKeyField(Tag) .ft P .fi .UNINDENT .UNINDENT .sp Now say we want to find tags that have at least 5 photos associated with them: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Tag .select() .join(PhotoTag) .join(Photo) .group_by(Tag) .having(fn.Count(Photo.id) > 5)) .ft P .fi .UNINDENT .UNINDENT .sp This query is equivalent to the following SQL: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT t1."id", t1."name" FROM "tag" AS t1 INNER JOIN "phototag" AS t2 ON t1."id" = t2."tag_id" INNER JOIN "photo" AS t3 ON t2."photo_id" = t3."id" GROUP BY t1."id", t1."name" HAVING Count(t3."id") > 5 .ft P .fi .UNINDENT .UNINDENT .sp Suppose we want to grab the associated count and store it on the tag: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Tag .select(Tag, fn.Count(Photo.id).alias(\(aqcount\(aq)) .join(PhotoTag) .join(Photo) .group_by(Tag) .having(fn.Count(Photo.id) > 5)) .ft P .fi .UNINDENT .UNINDENT .SS Retrieving Scalar Values .sp You can retrieve scalar values by calling \fBQuery.scalar()\fP\&. For instance: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> PageView.select(fn.Count(fn.Distinct(PageView.url))).scalar() 100 .ft P .fi .UNINDENT .UNINDENT .sp You can retrieve multiple scalar values by passing \fBas_tuple=True\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> Employee.select( \&... fn.Min(Employee.salary), fn.Max(Employee.salary) \&... ).scalar(as_tuple=True) (30000, 50000) .ft P .fi .UNINDENT .UNINDENT .SS Window functions .sp A \fBWindow\fP function refers to an aggregate function that operates on a sliding window of data that is being processed as part of a \fBSELECT\fP query. Window functions make it possible to do things like: .INDENT 0.0 .IP 1. 3 Perform aggregations against subsets of a result\-set. .IP 2. 3 Calculate a running total. .IP 3. 3 Rank results. .IP 4. 3 Compare a row value to a value in the preceding (or succeeding!) row(s). .UNINDENT .sp peewee comes with support for SQL window functions, which can be created by calling \fBFunction.over()\fP and passing in your partitioning or ordering parameters. .sp For the following examples, we\(aqll use the following model and sample data: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Sample(Model): counter = IntegerField() value = FloatField() data = [(1, 10), (1, 20), (2, 1), (2, 3), (3, 100)] Sample.insert_many(data, fields=[Sample.counter, Sample.value]).execute() .ft P .fi .UNINDENT .UNINDENT .sp Our sample table now contains: .TS center; |l|l|l|. _ T{ id T} T{ counter T} T{ value T} _ T{ 1 T} T{ 1 T} T{ 10.0 T} _ T{ 2 T} T{ 1 T} T{ 20.0 T} _ T{ 3 T} T{ 2 T} T{ 1.0 T} _ T{ 4 T} T{ 2 T} T{ 3.0 T} _ T{ 5 T} T{ 3 T} T{ 100.0 T} _ .TE .SS Ordered Windows .sp Let\(aqs calculate a running sum of the \fBvalue\fP field. In order for it to be a "running" sum, we need it to be ordered, so we\(aqll order with respect to the Sample\(aqs \fBid\fP field: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Sample.select( Sample.counter, Sample.value, fn.SUM(Sample.value).over(order_by=[Sample.id]).alias(\(aqtotal\(aq)) for sample in query: print(sample.counter, sample.value, sample.total) # 1 10. 10. # 1 20. 30. # 2 1. 31. # 2 3. 34. # 3 100 134. .ft P .fi .UNINDENT .UNINDENT .sp For another example, we\(aqll calculate the difference between the current value and the previous value, when ordered by the \fBid\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C difference = Sample.value \- fn.LAG(Sample.value, 1).over(order_by=[Sample.id]) query = Sample.select( Sample.counter, Sample.value, difference.alias(\(aqdiff\(aq)) for sample in query: print(sample.counter, sample.value, sample.diff) # 1 10. NULL # 1 20. 10. \-\- (20 \- 10) # 2 1. \-19. \-\- (1 \- 20) # 2 3. 2. \-\- (3 \- 1) # 3 100 97. \-\- (100 \- 3) .ft P .fi .UNINDENT .UNINDENT .SS Partitioned Windows .sp Let\(aqs calculate the average \fBvalue\fP for each distinct "counter" value. Notice that there are three possible values for the \fBcounter\fP field (1, 2, and 3). We can do this by calculating the \fBAVG()\fP of the \fBvalue\fP column over a window that is partitioned depending on the \fBcounter\fP field: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Sample.select( Sample.counter, Sample.value, fn.AVG(Sample.value).over(partition_by=[Sample.counter]).alias(\(aqcavg\(aq)) for sample in query: print(sample.counter, sample.value, sample.cavg) # 1 10. 15. # 1 20. 15. # 2 1. 2. # 2 3. 2. # 3 100 100. .ft P .fi .UNINDENT .UNINDENT .sp We can use ordering within partitions by specifying both the \fBorder_by\fP and \fBpartition_by\fP parameters. For an example, let\(aqs rank the samples by value within each distinct \fBcounter\fP group. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Sample.select( Sample.counter, Sample.value, fn.RANK().over( order_by=[Sample.value], partition_by=[Sample.counter]).alias(\(aqrank\(aq)) for sample in query: print(sample.counter, sample.value, sample.rank) # 1 10. 1 # 1 20. 2 # 2 1. 1 # 2 3. 2 # 3 100 1 .ft P .fi .UNINDENT .UNINDENT .SS Bounded windows .sp By default, window functions are evaluated using an \fIunbounded preceding\fP start for the window, and the \fIcurrent row\fP as the end. We can change the bounds of the window our aggregate functions operate on by specifying a \fBstart\fP and/or \fBend\fP in the call to \fBFunction.over()\fP\&. Additionally, Peewee comes with helper\-methods on the \fBWindow\fP object for generating the appropriate boundary references: .INDENT 0.0 .IP \(bu 2 \fBWindow.CURRENT_ROW\fP \- attribute that references the current row. .IP \(bu 2 \fBWindow.preceding()\fP \- specify number of row(s) preceding, or omit number to indicate \fBall\fP preceding rows. .IP \(bu 2 \fBWindow.following()\fP \- specify number of row(s) following, or omit number to indicate \fBall\fP following rows. .UNINDENT .sp To examine how boundaries work, we\(aqll calculate a running total of the \fBvalue\fP column, ordered with respect to \fBid\fP, \fBbut\fP we\(aqll only look the running total of the current row and it\(aqs two preceding rows: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Sample.select( Sample.counter, Sample.value, fn.SUM(Sample.value).over( order_by=[Sample.id], start=Window.preceding(2), end=Window.CURRENT_ROW).alias(\(aqrsum\(aq)) for sample in query: print(sample.counter, sample.value, sample.rsum) # 1 10. 10. # 1 20. 30. \-\- (20 + 10) # 2 1. 31. \-\- (1 + 20 + 10) # 2 3. 24. \-\- (3 + 1 + 20) # 3 100 104. \-\- (100 + 3 + 1) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Technically we did not need to specify the \fBend=Window.CURRENT\fP because that is the default. It was shown in the example for demonstration. .UNINDENT .UNINDENT .sp Let\(aqs look at another example. In this example we will calculate the "opposite" of a running total, in which the total sum of all values is decreased by the value of the samples, ordered by \fBid\fP\&. To accomplish this, we\(aqll calculate the sum from the current row to the last row. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Sample.select( Sample.counter, Sample.value, fn.SUM(Sample.value).over( order_by=[Sample.id], start=Window.CURRENT_ROW, end=Window.following()).alias(\(aqrsum\(aq)) # 1 10. 134. \-\- (10 + 20 + 1 + 3 + 100) # 1 20. 124. \-\- (20 + 1 + 3 + 100) # 2 1. 104. \-\- (1 + 3 + 100) # 2 3. 103. \-\- (3 + 100) # 3 100 100. \-\- (100) .ft P .fi .UNINDENT .UNINDENT .SS Filtered Aggregates .sp Aggregate functions may also support filter functions (Postgres and Sqlite 3.25+), which get translated into a \fBFILTER (WHERE...)\fP clause. Filter expressions are added to an aggregate function with the \fBFunction.filter()\fP method. .sp For an example, we will calculate the running sum of the \fBvalue\fP field with respect to the \fBid\fP, but we will filter\-out any samples whose \fBcounter=2\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Sample.select( Sample.counter, Sample.value, fn.SUM(Sample.value).filter(Sample.counter != 2).over( order_by=[Sample.id]).alias(\(aqcsum\(aq)) for sample in query: print(sample.counter, sample.value, sample.csum) # 1 10. 10. # 1 20. 30. # 2 1. 30. # 2 3. 30. # 3 100 130. .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 The call to \fBfilter()\fP must precede the call to \fBover()\fP\&. .UNINDENT .UNINDENT .SS Reusing Window Definitions .sp If you intend to use the same window definition for multiple aggregates, you can create a \fBWindow\fP object. The \fBWindow\fP object takes the same parameters as \fBFunction.over()\fP, and can be passed to the \fBover()\fP method in\-place of the individual parameters. .sp Here we\(aqll declare a single window, ordered with respect to the sample \fBid\fP, and call several window functions using that window definition: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C win = Window(order_by=[Sample.id]) query = Sample.select( Sample.counter, Sample.value, fn.LEAD(Sample.value).over(win), fn.LAG(Sample.value).over(win), fn.SUM(Sample.value).over(win) ).window(win) # Include our window definition in query. for row in query.tuples(): print(row) # counter value lead() lag() sum() # 1 10. 20. NULL 10. # 1 20. 1. 10. 30. # 2 1. 3. 20. 31. # 2 3. 100. 1. 34. # 3 100. NULL 3. 134. .ft P .fi .UNINDENT .UNINDENT .SS Multiple window definitions .sp In the previous example, we saw how to declare a \fBWindow\fP definition and re\-use it for multiple different aggregations. You can include as many window definitions as you need in your queries, but it is necessary to ensure each window has a unique alias: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C w1 = Window(order_by=[Sample.id]).alias(\(aqw1\(aq) w2 = Window(partition_by=[Sample.counter]).alias(\(aqw2\(aq) query = Sample.select( Sample.counter, Sample.value, fn.SUM(Sample.value).over(w1).alias(\(aqrsum\(aq), # Running total. fn.AVG(Sample.value).over(w2).alias(\(aqcavg\(aq) # Avg per category. ).window(w1, w2) # Include our window definitions. for sample in query: print(sample.counter, sample.value, sample.rsum, sample.cavg) # counter value rsum cavg # 1 10. 10. 15. # 1 20. 30. 15. # 2 1. 31. 2. # 2 3. 34. 2. # 3 100 134. 100. .ft P .fi .UNINDENT .UNINDENT .sp Similarly, if you have multiple window definitions that share similar definitions, it is possible to extend a previously\-defined window definition. For example, here we will be partitioning the data\-set by the counter value, so we\(aqll be doing our aggregations with respect to the counter. Then we\(aqll define a second window that extends this partitioning, and adds an ordering clause: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C w1 = Window(partition_by=[Sample.counter]).alias(\(aqw1\(aq) # By extending w1, this window definition will also be partitioned # by "counter". w2 = Window(extends=w1, order_by=[Sample.value.desc()]).alias(\(aqw2\(aq) query = (Sample .select(Sample.counter, Sample.value, fn.SUM(Sample.value).over(w1).alias(\(aqgroup_sum\(aq), fn.RANK().over(w2).alias(\(aqrevrank\(aq)) .window(w1, w2) .order_by(Sample.id)) for sample in query: print(sample.counter, sample.value, sample.group_sum, sample.revrank) # counter value group_sum revrank # 1 10. 30. 2 # 1 20. 30. 1 # 2 1. 4. 2 # 2 3. 4. 1 # 3 100. 100. 1 .ft P .fi .UNINDENT .UNINDENT .SS Frame types: RANGE vs ROWS vs GROUPS .sp Depending on the frame type, the database will process ordered groups differently. Let\(aqs create two additional \fBSample\fP rows to visualize the difference: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> Sample.create(counter=1, value=20.) >>> Sample.create(counter=2, value=1.) .ft P .fi .UNINDENT .UNINDENT .sp Our table now contains: .TS center; |l|l|l|. _ T{ id T} T{ counter T} T{ value T} _ T{ 1 T} T{ 1 T} T{ 10.0 T} _ T{ 2 T} T{ 1 T} T{ 20.0 T} _ T{ 3 T} T{ 2 T} T{ 1.0 T} _ T{ 4 T} T{ 2 T} T{ 3.0 T} _ T{ 5 T} T{ 3 T} T{ 100.0 T} _ T{ 6 T} T{ 1 T} T{ 20.0 T} _ T{ 7 T} T{ 2 T} T{ 1.0 T} _ .TE .sp Let\(aqs examine the difference by calculating a "running sum" of the samples, ordered with respect to the \fBcounter\fP and \fBvalue\fP fields. To specify the frame type, we can use either: .INDENT 0.0 .IP \(bu 2 \fBWindow.RANGE\fP .IP \(bu 2 \fBWindow.ROWS\fP .IP \(bu 2 \fBWindow.GROUPS\fP .UNINDENT .sp The behavior of \fBRANGE\fP, when there are logical duplicates, may lead to unexpected results: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Sample.select( Sample.counter, Sample.value, fn.SUM(Sample.value).over( order_by=[Sample.counter, Sample.value], frame_type=Window.RANGE).alias(\(aqrsum\(aq)) for sample in query.order_by(Sample.counter, Sample.value): print(sample.counter, sample.value, sample.rsum) # counter value rsum # 1 10. 10. # 1 20. 50. # 1 20. 50. # 2 1. 52. # 2 1. 52. # 2 3. 55. # 3 100 155. .ft P .fi .UNINDENT .UNINDENT .sp With the inclusion of the new rows we now have some rows that have duplicate \fBcategory\fP and \fBvalue\fP values. The \fBRANGE\fP frame type causes these duplicates to be evaluated together rather than separately. .sp The more expected result can be achieved by using \fBROWS\fP as the frame\-type: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Sample.select( Sample.counter, Sample.value, fn.SUM(Sample.value).over( order_by=[Sample.counter, Sample.value], frame_type=Window.ROWS).alias(\(aqrsum\(aq)) for sample in query.order_by(Sample.counter, Sample.value): print(sample.counter, sample.value, sample.rsum) # counter value rsum # 1 10. 10. # 1 20. 30. # 1 20. 50. # 2 1. 51. # 2 1. 52. # 2 3. 55. # 3 100 155. .ft P .fi .UNINDENT .UNINDENT .sp Peewee uses these rules for determining what frame\-type to use: .INDENT 0.0 .IP \(bu 2 If the user specifies a \fBframe_type\fP, that frame type will be used. .IP \(bu 2 If \fBstart\fP and/or \fBend\fP boundaries are specified Peewee will default to using \fBROWS\fP\&. .IP \(bu 2 If the user did not specify frame type or start/end boundaries, Peewee will use the database default, which is \fBRANGE\fP\&. .UNINDENT .sp The \fBWindow.GROUPS\fP frame type looks at the window range specification in terms of groups of rows, based on the ordering term(s). Using \fBGROUPS\fP, we can define the frame so it covers distinct groupings of rows. Let\(aqs look at an example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Sample .select(Sample.counter, Sample.value, fn.SUM(Sample.value).over( order_by=[Sample.counter, Sample.value], frame_type=Window.GROUPS, start=Window.preceding(1)).alias(\(aqgsum\(aq)) .order_by(Sample.counter, Sample.value)) for sample in query: print(sample.counter, sample.value, sample.gsum) # counter value gsum # 1 10 10 # 1 20 50 # 1 20 50 (10) + (20+0) # 2 1 42 # 2 1 42 (20+20) + (1+1) # 2 3 5 (1+1) + 3 # 3 100 103 (3) + 100 .ft P .fi .UNINDENT .UNINDENT .sp As you can hopefully infer, the window is grouped by its ordering term, which is \fB(counter, value)\fP\&. We are looking at a window that extends between one previous group and the current group. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 For information about the window function APIs, see: .INDENT 0.0 .IP \(bu 2 \fBFunction.over()\fP .IP \(bu 2 \fBFunction.filter()\fP .IP \(bu 2 \fBWindow\fP .UNINDENT .sp For general information on window functions, read the postgres \fI\%window functions tutorial\fP .sp Additionally, the \fI\%postgres docs\fP and the \fI\%sqlite docs\fP contain a lot of good information. .UNINDENT .UNINDENT .SS Retrieving row tuples / dictionaries / namedtuples .sp Sometimes you do not need the overhead of creating model instances and simply want to iterate over the row data without needing all the APIs provided \fBModel\fP\&. To do this, use: .INDENT 0.0 .IP \(bu 2 \fBdicts()\fP .IP \(bu 2 \fBnamedtuples()\fP .IP \(bu 2 \fBtuples()\fP .IP \(bu 2 \fBobjects()\fP \-\- accepts an arbitrary constructor function which is called with the row tuple. .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C stats = (Stat .select(Stat.url, fn.Count(Stat.url)) .group_by(Stat.url) .tuples()) # iterate over a list of 2\-tuples containing the url and count for stat_url, stat_count in stats: print(stat_url, stat_count) .ft P .fi .UNINDENT .UNINDENT .sp Similarly, you can return the rows from the cursor as dictionaries using \fBdicts()\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C stats = (Stat .select(Stat.url, fn.Count(Stat.url).alias(\(aqct\(aq)) .group_by(Stat.url) .dicts()) # iterate over a list of 2\-tuples containing the url and count for stat in stats: print(stat[\(aqurl\(aq], stat[\(aqct\(aq]) .ft P .fi .UNINDENT .UNINDENT .SS Returning Clause .sp \fBPostgresqlDatabase\fP supports a \fBRETURNING\fP clause on \fBUPDATE\fP, \fBINSERT\fP and \fBDELETE\fP queries. Specifying a \fBRETURNING\fP clause allows you to iterate over the rows accessed by the query. .sp By default, the return values upon execution of the different queries are: .INDENT 0.0 .IP \(bu 2 \fBINSERT\fP \- auto\-incrementing primary key value of the newly\-inserted row. When not using an auto\-incrementing primary key, Postgres will return the new row\(aqs primary key, but SQLite and MySQL will not. .IP \(bu 2 \fBUPDATE\fP \- number of rows modified .IP \(bu 2 \fBDELETE\fP \- number of rows deleted .UNINDENT .sp When a returning clause is used the return value upon executing a query will be an iterable cursor object. .sp Postgresql allows, via the \fBRETURNING\fP clause, to return data from the rows inserted or modified by a query. .sp For example, let\(aqs say you have an \fBUpdate\fP that deactivates all user accounts whose registration has expired. After deactivating them, you want to send each user an email letting them know their account was deactivated. Rather than writing two queries, a \fBSELECT\fP and an \fBUPDATE\fP, you can do this in a single \fBUPDATE\fP query with a \fBRETURNING\fP clause: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (User .update(is_active=False) .where(User.registration_expired == True) .returning(User)) # Send an email to every user that was deactivated. for deactivate_user in query.execute(): send_deactivation_email(deactivated_user.email) .ft P .fi .UNINDENT .UNINDENT .sp The \fBRETURNING\fP clause is also available on \fBInsert\fP and \fBDelete\fP\&. When used with \fBINSERT\fP, the newly\-created rows will be returned. When used with \fBDELETE\fP, the deleted rows will be returned. .sp The only limitation of the \fBRETURNING\fP clause is that it can only consist of columns from tables listed in the query\(aqs \fBFROM\fP clause. To select all columns from a particular table, you can simply pass in the \fBModel\fP class. .sp As another example, let\(aqs add a user and set their creation\-date to the server\-generated current timestamp. We\(aqll create and retrieve the new user\(aqs ID, Email and the creation timestamp in a single query: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (User .insert(email=\(aqfoo@bar.com\(aq, created=fn.now()) .returning(User)) # Shorthand for all columns on User. # When using RETURNING, execute() returns a cursor. cursor = query.execute() # Get the user object we just inserted and log the data: user = cursor[0] logger.info(\(aqCreated user %s (id=%s) at %s\(aq, user.email, user.id, user.created) .ft P .fi .UNINDENT .UNINDENT .sp By default the cursor will return \fBModel\fP instances, but you can specify a different row type: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C data = [{\(aqname\(aq: \(aqcharlie\(aq}, {\(aqname\(aq: \(aqhuey\(aq}, {\(aqname\(aq: \(aqmickey\(aq}] query = (User .insert_many(data) .returning(User.id, User.username) .dicts()) for new_user in query.execute(): print(\(aqAdded user "%s", id=%s\(aq % (new_user[\(aqusername\(aq], new_user[\(aqid\(aq])) .ft P .fi .UNINDENT .UNINDENT .sp Just as with \fBSelect\fP queries, you can specify various \fI\%result row types\fP\&. .SS Common Table Expressions .sp Peewee supports the inclusion of common table expressions (CTEs) in all types of queries. CTEs may be useful for: .INDENT 0.0 .IP \(bu 2 Factoring out a common subquery. .IP \(bu 2 Grouping or filtering by a column derived in the CTE\(aqs result set. .IP \(bu 2 Writing recursive queries. .UNINDENT .sp To declare a \fBSelect\fP query for use as a CTE, use \fBcte()\fP method, which wraps the query in a \fBCTE\fP object. To indicate that a \fBCTE\fP should be included as part of a query, use the \fBQuery.with_cte()\fP method, passing a list of CTE objects. .SS Simple Example .sp For an example, let\(aqs say we have some data points that consist of a key and a floating\-point value. Let\(aqs define our model and populate some test data: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Sample(Model): key = TextField() value = FloatField() data = ( (\(aqa\(aq, (1.25, 1.5, 1.75)), (\(aqb\(aq, (2.1, 2.3, 2.5, 2.7, 2.9)), (\(aqc\(aq, (3.5, 3.5))) # Populate data. for key, values in data: Sample.insert_many([(key, value) for value in values], fields=[Sample.key, Sample.value]).execute() .ft P .fi .UNINDENT .UNINDENT .sp Let\(aqs use a CTE to calculate, for each distinct key, which values were above\-average for that key. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # First we\(aqll declare the query that will be used as a CTE. This query # simply determines the average value for each key. cte = (Sample .select(Sample.key, fn.AVG(Sample.value).alias(\(aqavg_value\(aq)) .group_by(Sample.key) .cte(\(aqkey_avgs\(aq, columns=(\(aqkey\(aq, \(aqavg_value\(aq))) # Now we\(aqll query the sample table, using our CTE to find rows whose value # exceeds the average for the given key. We\(aqll calculate how far above the # average the given sample\(aqs value is, as well. query = (Sample .select(Sample.key, Sample.value) .join(cte, on=(Sample.key == cte.c.key)) .where(Sample.value > cte.c.avg_value) .order_by(Sample.value) .with_cte(cte)) .ft P .fi .UNINDENT .UNINDENT .sp We can iterate over the samples returned by the query to see which samples had above\-average values for their given group: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for sample in query: \&... print(sample.key, sample.value) # \(aqa\(aq, 1.75 # \(aqb\(aq, 2.7 # \(aqb\(aq, 2.9 .ft P .fi .UNINDENT .UNINDENT .SS Complex Example .sp For a more complete example, let\(aqs consider the following query which uses multiple CTEs to find per\-product sales totals in only the top sales regions. Our model looks like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Order(Model): region = TextField() amount = FloatField() product = TextField() quantity = IntegerField() .ft P .fi .UNINDENT .UNINDENT .sp Here is how the query might be written in SQL. This example can be found in the \fI\%postgresql documentation\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C WITH regional_sales AS ( SELECT region, SUM(amount) AS total_sales FROM orders GROUP BY region ), top_regions AS ( SELECT region FROM regional_sales WHERE total_sales > (SELECT SUM(total_sales) / 10 FROM regional_sales) ) SELECT region, product, SUM(quantity) AS product_units, SUM(amount) AS product_sales FROM orders WHERE region IN (SELECT region FROM top_regions) GROUP BY region, product; .ft P .fi .UNINDENT .UNINDENT .sp With Peewee, we would write: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C reg_sales = (Order .select(Order.region, fn.SUM(Order.amount).alias(\(aqtotal_sales\(aq)) .group_by(Order.region) .cte(\(aqregional_sales\(aq)) top_regions = (reg_sales .select(reg_sales.c.region) .where(reg_sales.c.total_sales > ( reg_sales.select(fn.SUM(reg_sales.c.total_sales) / 10))) .cte(\(aqtop_regions\(aq)) query = (Order .select(Order.region, Order.product, fn.SUM(Order.quantity).alias(\(aqproduct_units\(aq), fn.SUM(Order.amount).alias(\(aqproduct_sales\(aq)) .where(Order.region.in_(top_regions.select(top_regions.c.region))) .group_by(Order.region, Order.product) .with_cte(regional_sales, top_regions)) .ft P .fi .UNINDENT .UNINDENT .SS Recursive CTEs .sp Peewee supports recursive CTEs. Recursive CTEs can be useful when, for example, you have a tree data\-structure represented by a parent\-link foreign key. Suppose, for example, that we have a hierarchy of categories for an online bookstore. We wish to generate a table showing all categories and their absolute depths, along with the path from the root to the category. .sp We\(aqll assume the following model definition, in which each category has a foreign\-key to its immediate parent category: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Category(Model): name = TextField() parent = ForeignKeyField(\(aqself\(aq, backref=\(aqchildren\(aq, null=True) .ft P .fi .UNINDENT .UNINDENT .sp To list all categories along with their depth and parents, we can use a recursive CTE: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Define the base case of our recursive CTE. This will be categories that # have a null parent foreign\-key. Base = Category.alias() level = Value(1).alias(\(aqlevel\(aq) path = Base.name.alias(\(aqpath\(aq) base_case = (Base .select(Base.id, Base.name, Base.parent, level, path) .where(Base.parent.is_null()) .cte(\(aqbase\(aq, recursive=True)) # Define the recursive terms. RTerm = Category.alias() rlevel = (base_case.c.level + 1).alias(\(aqlevel\(aq) rpath = base_case.c.path.concat(\(aq\->\(aq).concat(RTerm.name).alias(\(aqpath\(aq) recursive = (RTerm .select(RTerm.id, RTerm.name, RTerm.parent, rlevel, rpath) .join(base_case, on=(RTerm.parent == base_case.c.id))) # The recursive CTE is created by taking the base case and UNION ALL with # the recursive term. cte = base_case.union_all(recursive) # We will now query from the CTE to get the categories, their levels, and # their paths. query = (cte .select_from(cte.c.name, cte.c.level, cte.c.path) .order_by(cte.c.path)) # We can now iterate over a list of all categories and print their names, # absolute levels, and path from root \-> category. for category in query: print(category.name, category.level, category.path) # Example output: # root, 1, root # p1, 2, root\->p1 # c1\-1, 3, root\->p1\->c1\-1 # c1\-2, 3, root\->p1\->c1\-2 # p2, 2, root\->p2 # c2\-1, 3, root\->p2\->c2\-1 .ft P .fi .UNINDENT .UNINDENT .SS Foreign Keys and Joins .sp This section have been moved into its own document: relationships\&. .SS Query operators .sp The following types of comparisons are supported by peewee: .TS center; |l|l|. _ T{ Comparison T} T{ Meaning T} _ T{ \fB==\fP T} T{ x equals y T} _ T{ \fB<\fP T} T{ x is less than y T} _ T{ \fB<=\fP T} T{ x is less than or equal to y T} _ T{ \fB>\fP T} T{ x is greater than y T} _ T{ \fB>=\fP T} T{ x is greater than or equal to y T} _ T{ \fB!=\fP T} T{ x is not equal to y T} _ T{ \fB<<\fP T} T{ x IN y, where y is a list or query T} _ T{ \fB>>\fP T} T{ x IS y, where y is None/NULL T} _ T{ \fB%\fP T} T{ x LIKE y where y may contain wildcards T} _ T{ \fB**\fP T} T{ x ILIKE y where y may contain wildcards T} _ T{ \fB^\fP T} T{ x XOR y T} _ T{ \fB~\fP T} T{ Unary negation (e.g., NOT x) T} _ .TE .sp Because I ran out of operators to override, there are some additional query operations available as methods: .TS center; |l|l|. _ T{ Method T} T{ Meaning T} _ T{ \fB\&.in_(value)\fP T} T{ IN lookup (identical to \fB<<\fP). T} _ T{ \fB\&.not_in(value)\fP T} T{ NOT IN lookup. T} _ T{ \fB\&.is_null(is_null)\fP T} T{ IS NULL or IS NOT NULL. Accepts boolean param. T} _ T{ \fB\&.contains(substr)\fP T} T{ Wild\-card search for substring. T} _ T{ \fB\&.startswith(prefix)\fP T} T{ Search for values beginning with \fBprefix\fP\&. T} _ T{ \fB\&.endswith(suffix)\fP T} T{ Search for values ending with \fBsuffix\fP\&. T} _ T{ \fB\&.between(low, high)\fP T} T{ Search for values between \fBlow\fP and \fBhigh\fP\&. T} _ T{ \fB\&.regexp(exp)\fP T} T{ Regular expression match (case\-sensitive). T} _ T{ \fB\&.iregexp(exp)\fP T} T{ Regular expression match (case\-insensitive). T} _ T{ \fB\&.bin_and(value)\fP T} T{ Binary AND. T} _ T{ \fB\&.bin_or(value)\fP T} T{ Binary OR. T} _ T{ \fB\&.concat(other)\fP T} T{ Concatenate two strings or objects using \fB||\fP\&. T} _ T{ \fB\&.distinct()\fP T} T{ Mark column for DISTINCT selection. T} _ T{ \fB\&.collate(collation)\fP T} T{ Specify column with the given collation. T} _ T{ \fB\&.cast(type)\fP T} T{ Cast the value of the column to the given type. T} _ .TE .sp To combine clauses using logical operators, use: .TS center; |l|l|l|. _ T{ Operator T} T{ Meaning T} T{ Example T} _ T{ \fB&\fP T} T{ AND T} T{ \fB(User.is_active == True) & (User.is_admin == True)\fP T} _ T{ \fB|\fP (pipe) T} T{ OR T} T{ \fB(User.is_admin) | (User.is_superuser)\fP T} _ T{ \fB~\fP T} T{ NOT (unary negation) T} T{ \fB~(User.username.contains(\(aqadmin\(aq))\fP T} _ .TE .sp Here is how you might use some of these query operators: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Find the user whose username is "charlie". User.select().where(User.username == \(aqcharlie\(aq) # Find the users whose username is in [charlie, huey, mickey] User.select().where(User.username.in_([\(aqcharlie\(aq, \(aqhuey\(aq, \(aqmickey\(aq])) Employee.select().where(Employee.salary.between(50000, 60000)) Employee.select().where(Employee.name.startswith(\(aqC\(aq)) Blog.select().where(Blog.title.contains(search_string)) .ft P .fi .UNINDENT .UNINDENT .sp Here is how you might combine expressions. Comparisons can be arbitrarily complex. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Note that the actual comparisons are wrapped in parentheses. Python\(aqs operator precedence necessitates that comparisons be wrapped in parentheses. .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Find any users who are active administrations. User.select().where( (User.is_admin == True) & (User.is_active == True)) # Find any users who are either administrators or super\-users. User.select().where( (User.is_admin == True) | (User.is_superuser == True)) # Find any Tweets by users who are not admins (NOT IN). admins = User.select().where(User.is_admin == True) non_admin_tweets = Tweet.select().where(Tweet.user.not_in(admins)) # Find any users who are not my friends (strangers). friends = User.select().where(User.username.in_([\(aqcharlie\(aq, \(aqhuey\(aq, \(aqmickey\(aq])) strangers = User.select().where(User.id.not_in(friends)) .ft P .fi .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 Although you may be tempted to use python\(aqs \fBin\fP, \fBand\fP, \fBor\fP and \fBnot\fP operators in your query expressions, these \fBwill not work.\fP The return value of an \fBin\fP expression is always coerced to a boolean value. Similarly, \fBand\fP, \fBor\fP and \fBnot\fP all treat their arguments as boolean values and cannot be overloaded. .sp So just remember: .INDENT 0.0 .IP \(bu 2 Use \fB\&.in_()\fP and \fB\&.not_in()\fP instead of \fBin\fP and \fBnot in\fP .IP \(bu 2 Use \fB&\fP instead of \fBand\fP .IP \(bu 2 Use \fB|\fP instead of \fBor\fP .IP \(bu 2 Use \fB~\fP instead of \fBnot\fP .IP \(bu 2 Use \fB\&.is_null()\fP instead of \fBis None\fP or \fB== None\fP\&. .IP \(bu 2 \fBDon\(aqt forget to wrap your comparisons in parentheses when using logical operators.\fP .UNINDENT .UNINDENT .UNINDENT .sp For more examples, see the \fI\%Expressions\fP section. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 \fBLIKE and ILIKE with SQLite\fP .sp Because SQLite\(aqs \fBLIKE\fP operation is case\-insensitive by default, peewee will use the SQLite \fBGLOB\fP operation for case\-sensitive searches. The glob operation uses asterisks for wildcards as opposed to the usual percent\-sign. If you are using SQLite and want case\-sensitive partial string matching, remember to use asterisks for the wildcard. .UNINDENT .UNINDENT .SS Three valued logic .sp Because of the way SQL handles \fBNULL\fP, there are some special operations available for expressing: .INDENT 0.0 .IP \(bu 2 \fBIS NULL\fP .IP \(bu 2 \fBIS NOT NULL\fP .IP \(bu 2 \fBIN\fP .IP \(bu 2 \fBNOT IN\fP .UNINDENT .sp While it would be possible to use the \fBIS NULL\fP and \fBIN\fP operators with the negation operator (\fB~\fP), sometimes to get the correct semantics you will need to explicitly use \fBIS NOT NULL\fP and \fBNOT IN\fP\&. .sp The simplest way to use \fBIS NULL\fP and \fBIN\fP is to use the operator overloads: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Get all User objects whose last login is NULL. User.select().where(User.last_login >> None) # Get users whose username is in the given list. usernames = [\(aqcharlie\(aq, \(aqhuey\(aq, \(aqmickey\(aq] User.select().where(User.username << usernames) .ft P .fi .UNINDENT .UNINDENT .sp If you don\(aqt like operator overloads, you can call the Field methods instead: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Get all User objects whose last login is NULL. User.select().where(User.last_login.is_null(True)) # Get users whose username is in the given list. usernames = [\(aqcharlie\(aq, \(aqhuey\(aq, \(aqmickey\(aq] User.select().where(User.username.in_(usernames)) .ft P .fi .UNINDENT .UNINDENT .sp To negate the above queries, you can use unary negation, but for the correct semantics you may need to use the special \fBIS NOT\fP and \fBNOT IN\fP operators: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Get all User objects whose last login is *NOT* NULL. User.select().where(User.last_login.is_null(False)) # Using unary negation instead. User.select().where(~(User.last_login >> None)) # Get users whose username is *NOT* in the given list. usernames = [\(aqcharlie\(aq, \(aqhuey\(aq, \(aqmickey\(aq] User.select().where(User.username.not_in(usernames)) # Using unary negation instead. usernames = [\(aqcharlie\(aq, \(aqhuey\(aq, \(aqmickey\(aq] User.select().where(~(User.username << usernames)) .ft P .fi .UNINDENT .UNINDENT .SS Adding user\-defined operators .sp Because I ran out of python operators to overload, there are some missing operators in peewee, for instance \fBmodulo\fP\&. If you find that you need to support an operator that is not in the table above, it is very easy to add your own. .sp Here is how you might add support for \fBmodulo\fP in SQLite: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from peewee import * from peewee import Expression # the building block for expressions def mod(lhs, rhs): return Expression(lhs, \(aq%\(aq, rhs) .ft P .fi .UNINDENT .UNINDENT .sp Now you can use these custom operators to build richer queries: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Users with even ids. User.select().where(mod(User.id, 2) == 0) .ft P .fi .UNINDENT .UNINDENT .sp For more examples check out the source to the \fBplayhouse.postgresql_ext\fP module, as it contains numerous operators specific to postgresql\(aqs hstore. .SS Expressions .sp Peewee is designed to provide a simple, expressive, and pythonic way of constructing SQL queries. This section will provide a quick overview of some common types of expressions. .sp There are two primary types of objects that can be composed to create expressions: .INDENT 0.0 .IP \(bu 2 \fBField\fP instances .IP \(bu 2 SQL aggregations and functions using \fBfn\fP .UNINDENT .sp We will assume a simple "User" model with fields for username and other things. It looks like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class User(Model): username = CharField() is_admin = BooleanField() is_active = BooleanField() last_login = DateTimeField() login_count = IntegerField() failed_logins = IntegerField() .ft P .fi .UNINDENT .UNINDENT .sp Comparisons use the \fI\%Query operators\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # username is equal to \(aqcharlie\(aq User.username == \(aqcharlie\(aq # user has logged in less than 5 times User.login_count < 5 .ft P .fi .UNINDENT .UNINDENT .sp Comparisons can be combined using \fBbitwise\fP \fIand\fP and \fIor\fP\&. Operator precedence is controlled by python and comparisons can be nested to an arbitrary depth: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # User is both and admin and has logged in today (User.is_admin == True) & (User.last_login >= today) # User\(aqs username is either charlie or charles (User.username == \(aqcharlie\(aq) | (User.username == \(aqcharles\(aq) .ft P .fi .UNINDENT .UNINDENT .sp Comparisons can be used with functions as well: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # user\(aqs username starts with a \(aqg\(aq or a \(aqG\(aq: fn.Lower(fn.Substr(User.username, 1, 1)) == \(aqg\(aq .ft P .fi .UNINDENT .UNINDENT .sp We can do some fairly interesting things, as expressions can be compared against other expressions. Expressions also support arithmetic operations: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # users who entered the incorrect more than half the time and have logged # in at least 10 times (User.failed_logins > (User.login_count * .5)) & (User.login_count > 10) .ft P .fi .UNINDENT .UNINDENT .sp Expressions allow us to do \fIatomic updates\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # when a user logs in we want to increment their login count: User.update(login_count=User.login_count + 1).where(User.id == user_id) .ft P .fi .UNINDENT .UNINDENT .sp Expressions can be used in all parts of a query, so experiment! .SS Row values .sp Many databases support \fI\%row values\fP, which are similar to Python \fItuple\fP objects. In Peewee, it is possible to use row\-values in expressions via \fBTuple\fP\&. For example, .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # If for some reason your schema stores dates in separate columns ("year", # "month" and "day"), you can use row\-values to find all rows that happened # in a given month: Tuple(Event.year, Event.month) == (2019, 1) .ft P .fi .UNINDENT .UNINDENT .sp The more common use for row\-values is to compare against multiple columns from a subquery in a single expression. There are other ways to express these types of queries, but row\-values may offer a concise and readable approach. .sp For example, assume we have a table "EventLog" which contains an event type, an event source, and some metadata. We also have an "IncidentLog", which has incident type, incident source, and metadata columns. We can use row\-values to correlate incidents with certain events: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class EventLog(Model): event_type = TextField() source = TextField() data = TextField() timestamp = TimestampField() class IncidentLog(Model): incident_type = TextField() source = TextField() traceback = TextField() timestamp = TimestampField() # Get a list of all the incident types and sources that have occurred today. incidents = (IncidentLog .select(IncidentLog.incident_type, IncidentLog.source) .where(IncidentLog.timestamp >= datetime.date.today())) # Find all events that correlate with the type and source of the # incidents that occurred today. events = (EventLog .select() .where(Tuple(EventLog.event_type, EventLog.source).in_(incidents)) .order_by(EventLog.timestamp)) .ft P .fi .UNINDENT .UNINDENT .sp Other ways to express this type of query would be to use a join or to join on a subquery\&. The above example is there just to give you and idea how \fBTuple\fP might be used. .sp You can also use row\-values to update multiple columns in a table, when the new data is derived from a subquery. For an example, see \fI\%here\fP\&. .SS SQL Functions .sp SQL functions, like \fBCOUNT()\fP or \fBSUM()\fP, can be expressed using the \fBfn()\fP helper: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Get all users and the number of tweets they\(aqve authored. Sort the # results from most tweets \-> fewest tweets. query = (User .select(User, fn.COUNT(Tweet.id).alias(\(aqtweet_count\(aq)) .join(Tweet, JOIN.LEFT_OUTER) .group_by(User) .order_by(fn.COUNT(Tweet.id).desc())) for user in query: print(\(aq%s \-\- %s tweets\(aq % (user.username, user.tweet_count)) .ft P .fi .UNINDENT .UNINDENT .sp The \fBfn\fP helper exposes any SQL function as if it were a method. The parameters can be fields, values, subqueries, or even nested functions. .SS Nesting function calls .sp Suppose you need to want to get a list of all users whose username begins with \fIa\fP\&. There are a couple ways to do this, but one method might be to use some SQL functions like \fILOWER\fP and \fISUBSTR\fP\&. To use arbitrary SQL functions, use the special \fBfn()\fP object to construct queries: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Select the user\(aqs id, username and the first letter of their username, lower\-cased first_letter = fn.LOWER(fn.SUBSTR(User.username, 1, 1)) query = User.select(User, first_letter.alias(\(aqfirst_letter\(aq)) # Alternatively we could select only users whose username begins with \(aqa\(aq a_users = User.select().where(first_letter == \(aqa\(aq) >>> for user in a_users: \&... print(user.username) .ft P .fi .UNINDENT .UNINDENT .SS SQL Helper .sp There are times when you may want to simply pass in some arbitrary sql. You can do this using the special \fBSQL\fP class. One use\-case is when referencing an alias: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # We\(aqll query the user table and annotate it with a count of tweets for # the given user query = (User .select(User, fn.Count(Tweet.id).alias(\(aqct\(aq)) .join(Tweet) .group_by(User)) # Now we will order by the count, which was aliased to "ct" query = query.order_by(SQL(\(aqct\(aq)) # You could, of course, also write this as: query = query.order_by(fn.COUNT(Tweet.id)) .ft P .fi .UNINDENT .UNINDENT .sp There are two ways to execute hand\-crafted SQL statements with peewee: .INDENT 0.0 .IP 1. 3 \fBDatabase.execute_sql()\fP for executing any type of query .IP 2. 3 \fBRawQuery\fP for executing \fBSELECT\fP queries and returning model instances. .UNINDENT .SS Security and SQL Injection .sp By default peewee will parameterize queries, so any parameters passed in by the user will be escaped. The only exception to this rule is if you are writing a raw SQL query or are passing in a \fBSQL\fP object which may contain untrusted data. To mitigate this, ensure that any user\-defined data is passed in as a query parameter and not part of the actual SQL query: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Bad! DO NOT DO THIS! query = MyModel.raw(\(aqSELECT * FROM my_table WHERE data = %s\(aq % (user_data,)) # Good. \(gauser_data\(ga will be treated as a parameter to the query. query = MyModel.raw(\(aqSELECT * FROM my_table WHERE data = %s\(aq, user_data) # Bad! DO NOT DO THIS! query = MyModel.select().where(SQL(\(aqSome SQL expression %s\(aq % user_data)) # Good. \(gauser_data\(ga will be treated as a parameter. query = MyModel.select().where(SQL(\(aqSome SQL expression %s\(aq, user_data)) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 MySQL and Postgresql use \fB\(aq%s\(aq\fP to denote parameters. SQLite, on the other hand, uses \fB\(aq?\(aq\fP\&. Be sure to use the character appropriate to your database. You can also find this parameter by checking \fBDatabase.param\fP\&. .UNINDENT .UNINDENT .SS Relationships and Joins .sp In this document we\(aqll cover how Peewee handles relationships between models. .SS Model definitions .sp We\(aqll use the following model definitions for our examples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C import datetime from peewee import * db = SqliteDatabase(\(aq:memory:\(aq) class BaseModel(Model): class Meta: database = db class User(BaseModel): username = TextField() class Tweet(BaseModel): content = TextField() timestamp = DateTimeField(default=datetime.datetime.now) user = ForeignKeyField(User, backref=\(aqtweets\(aq) class Favorite(BaseModel): user = ForeignKeyField(User, backref=\(aqfavorites\(aq) tweet = ForeignKeyField(Tweet, backref=\(aqfavorites\(aq) .ft P .fi .UNINDENT .UNINDENT .sp Peewee uses \fBForeignKeyField\fP to define foreign\-key relationships between models. Every foreign\-key field has an implied back\-reference, which is exposed as a pre\-filtered \fBSelect\fP query using the provided \fBbackref\fP attribute. .SS Creating test data .sp To follow along with the examples, let\(aqs populate this database with some test data: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C def populate_test_data(): db.create_tables([User, Tweet, Favorite]) data = ( (\(aqhuey\(aq, (\(aqmeow\(aq, \(aqhiss\(aq, \(aqpurr\(aq)), (\(aqmickey\(aq, (\(aqwoof\(aq, \(aqwhine\(aq)), (\(aqzaizee\(aq, ())) for username, tweets in data: user = User.create(username=username) for tweet in tweets: Tweet.create(user=user, content=tweet) # Populate a few favorites for our users, such that: favorite_data = ( (\(aqhuey\(aq, [\(aqwhine\(aq]), (\(aqmickey\(aq, [\(aqpurr\(aq]), (\(aqzaizee\(aq, [\(aqmeow\(aq, \(aqpurr\(aq])) for username, favorites in favorite_data: user = User.get(User.username == username) for content in favorites: tweet = Tweet.get(Tweet.content == content) Favorite.create(user=user, tweet=tweet) .ft P .fi .UNINDENT .UNINDENT .sp This gives us the following: .TS center; |l|l|l|. _ T{ User T} T{ Tweet T} T{ Favorited by T} _ T{ huey T} T{ meow T} T{ zaizee T} _ T{ huey T} T{ hiss T} T{ T} _ T{ huey T} T{ purr T} T{ mickey, zaizee T} _ T{ mickey T} T{ woof T} T{ T} _ T{ mickey T} T{ whine T} T{ huey T} _ .TE .sp \fBATTENTION:\fP .INDENT 0.0 .INDENT 3.5 In the following examples we will be executing a number of queries. If you are unsure how many queries are being executed, you can add the following code, which will log all queries to the console: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C import logging logger = logging.getLogger(\(aqpeewee\(aq) logger.addHandler(logging.StreamHandler()) logger.setLevel(logging.DEBUG) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 In SQLite, foreign keys are not enabled by default. Most things, including the Peewee foreign\-key API, will work fine, but ON DELETE behaviour will be ignored, even if you explicitly specify \fBon_delete\fP in your \fBForeignKeyField\fP\&. In conjunction with the default \fBAutoField\fP behaviour (where deleted record IDs can be reused), this can lead to subtle bugs. To avoid problems, I recommend that you enable foreign\-key constraints when using SQLite, by setting \fBpragmas={\(aqforeign_keys\(aq: 1}\fP when you instantiate \fBSqliteDatabase\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Ensure foreign\-key constraints are enforced. db = SqliteDatabase(\(aqmy_app.db\(aq, pragmas={\(aqforeign_keys\(aq: 1}) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS Performing simple joins .sp As an exercise in learning how to perform joins with Peewee, let\(aqs write a query to print out all the tweets by "huey". To do this we\(aqll select from the \fBTweet\fP model and join on the \fBUser\fP model, so we can then filter on the \fBUser.username\fP field: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> query = Tweet.select().join(User).where(User.username == \(aqhuey\(aq) >>> for tweet in query: \&... print(tweet.content) \&... meow hiss purr .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 We did not have to explicitly specify the join predicate (the "ON" clause), because Peewee inferred from the models that when we joined from Tweet to User, we were joining on the \fBTweet.user\fP foreign\-key. .sp The following code is equivalent, but more explicit: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Tweet .select() .join(User, on=(Tweet.user == User.id)) .where(User.username == \(aqhuey\(aq)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp If we already had a reference to the \fBUser\fP object for "huey", we could use the \fBUser.tweets\fP back\-reference to list all of huey\(aqs tweets: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> huey = User.get(User.username == \(aqhuey\(aq) >>> for tweet in huey.tweets: \&... print(tweet.content) \&... meow hiss purr .ft P .fi .UNINDENT .UNINDENT .sp Taking a closer look at \fBhuey.tweets\fP, we can see that it is just a simple pre\-filtered \fBSELECT\fP query: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> huey.tweets >>> huey.tweets.sql() (\(aqSELECT "t1"."id", "t1"."content", "t1"."timestamp", "t1"."user_id" FROM "tweet" AS "t1" WHERE ("t1"."user_id" = ?)\(aq, [1]) .ft P .fi .UNINDENT .UNINDENT .SS Joining multiple tables .sp Let\(aqs take another look at joins by querying the list of users and getting the count of how many tweet\(aqs they\(aqve authored that were favorited. This will require us to join twice: from user to tweet, and from tweet to favorite. We\(aqll add the additional requirement that users should be included who have not created any tweets, as well as users whose tweets have not been favorited. The query, expressed in SQL, would be: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT user.username, COUNT(favorite.id) FROM user LEFT OUTER JOIN tweet ON tweet.user_id = user.id LEFT OUTER JOIN favorite ON favorite.tweet_id = tweet.id GROUP BY user.username .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 In the above query both joins are LEFT OUTER, since a user may not have any tweets or, if they have tweets, none of them may have been favorited. .UNINDENT .UNINDENT .sp Peewee has a concept of a \fIjoin context\fP, meaning that whenever we call the \fBjoin()\fP method, we are implicitly joining on the previously\-joined model (or if this is the first call, the model we are selecting from). Since we are joining straight through, from user to tweet, then from tweet to favorite, we can simply write: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (User .select(User.username, fn.COUNT(Favorite.id).alias(\(aqcount\(aq)) .join(Tweet, JOIN.LEFT_OUTER) # Joins user \-> tweet. .join(Favorite, JOIN.LEFT_OUTER) # Joins tweet \-> favorite. .group_by(User.username)) .ft P .fi .UNINDENT .UNINDENT .sp Iterating over the results: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for user in query: \&... print(user.username, user.count) \&... huey 3 mickey 1 zaizee 0 .ft P .fi .UNINDENT .UNINDENT .sp For a more complicated example involving multiple joins and switching join contexts, let\(aqs find all the tweets by Huey and the number of times they\(aqve been favorited. To do this we\(aqll need to perform two joins and we\(aqll also use an aggregate function to calculate the favorite count. .sp Here is how we would write this query in SQL: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT tweet.content, COUNT(favorite.id) FROM tweet INNER JOIN user ON tweet.user_id = user.id LEFT OUTER JOIN favorite ON favorite.tweet_id = tweet.id WHERE user.username = \(aqhuey\(aq GROUP BY tweet.content; .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 We use a LEFT OUTER join from tweet to favorite since a tweet may not have any favorites, yet we still wish to display it\(aqs content (along with a count of zero) in the result set. .UNINDENT .UNINDENT .sp With Peewee, the resulting Python code looks very similar to what we would write in SQL: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Tweet .select(Tweet.content, fn.COUNT(Favorite.id).alias(\(aqcount\(aq)) .join(User) # Join from tweet \-> user. .switch(Tweet) # Move "join context" back to tweet. .join(Favorite, JOIN.LEFT_OUTER) # Join from tweet \-> favorite. .where(User.username == \(aqhuey\(aq) .group_by(Tweet.content)) .ft P .fi .UNINDENT .UNINDENT .sp Note the call to \fBswitch()\fP \- that instructs Peewee to set the \fIjoin context\fP back to \fBTweet\fP\&. If we had omitted the explicit call to switch, Peewee would have used \fBUser\fP (the last model we joined) as the join context and constructed the join from User to Favorite using the \fBFavorite.user\fP foreign\-key, which would have given us incorrect results. .sp If we wanted to omit the join\-context switching we could instead use the \fBjoin_from()\fP method. The following query is equivalent to the previous one: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Tweet .select(Tweet.content, fn.COUNT(Favorite.id).alias(\(aqcount\(aq)) .join_from(Tweet, User) # Join tweet \-> user. .join_from(Tweet, Favorite, JOIN.LEFT_OUTER) # Join tweet \-> favorite. .where(User.username == \(aqhuey\(aq) .group_by(Tweet.content)) .ft P .fi .UNINDENT .UNINDENT .sp We can iterate over the results of the above query to print the tweet\(aqs content and the favorite count: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for tweet in query: \&... print(\(aq%s favorited %d times\(aq % (tweet.content, tweet.count)) \&... meow favorited 1 times hiss favorited 0 times purr favorited 2 times .ft P .fi .UNINDENT .UNINDENT .SS Selecting from multiple sources .sp If we wished to list all the tweets in the database, along with the username of their author, you might try writing this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for tweet in Tweet.select(): \&... print(tweet.user.username, \(aq\->\(aq, tweet.content) \&... huey \-> meow huey \-> hiss huey \-> purr mickey \-> woof mickey \-> whine .ft P .fi .UNINDENT .UNINDENT .sp There is a big problem with the above loop: it executes an additional query for every tweet to look up the \fBtweet.user\fP foreign\-key. For our small table the performance penalty isn\(aqt obvious, but we would find the delays grew as the number of rows increased. .sp If you\(aqre familiar with SQL, you might remember that it\(aqs possible to SELECT from multiple tables, allowing us to get the tweet content \fIand\fP the username in a single query: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT tweet.content, user.username FROM tweet INNER JOIN user ON tweet.user_id = user.id; .ft P .fi .UNINDENT .UNINDENT .sp Peewee makes this quite easy. In fact, we only need to modify our query a little bit. We tell Peewee we wish to select \fBTweet.content\fP as well as the \fBUser.username\fP field, then we include a join from tweet to user. To make it a bit more obvious that it\(aqs doing the correct thing, we can ask Peewee to return the rows as dictionaries. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for row in Tweet.select(Tweet.content, User.username).join(User).dicts(): \&... print(row) \&... {\(aqcontent\(aq: \(aqmeow\(aq, \(aqusername\(aq: \(aqhuey\(aq} {\(aqcontent\(aq: \(aqhiss\(aq, \(aqusername\(aq: \(aqhuey\(aq} {\(aqcontent\(aq: \(aqpurr\(aq, \(aqusername\(aq: \(aqhuey\(aq} {\(aqcontent\(aq: \(aqwoof\(aq, \(aqusername\(aq: \(aqmickey\(aq} {\(aqcontent\(aq: \(aqwhine\(aq, \(aqusername\(aq: \(aqmickey\(aq} .ft P .fi .UNINDENT .UNINDENT .sp Now we\(aqll leave off the call to ".dicts()" and return the rows as \fBTweet\fP objects. Notice that Peewee assigns the \fBusername\fP value to \fBtweet.user.username\fP \-\- NOT \fBtweet.username\fP! Because there is a foreign\-key from tweet to user, and we have selected fields from both models, Peewee will reconstruct the model\-graph for us: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for tweet in Tweet.select(Tweet.content, User.username).join(User): \&... print(tweet.user.username, \(aq\->\(aq, tweet.content) \&... huey \-> meow huey \-> hiss huey \-> purr mickey \-> woof mickey \-> whine .ft P .fi .UNINDENT .UNINDENT .sp If we wish to, we can control where Peewee puts the joined \fBUser\fP instance in the above query, by specifying an \fBattr\fP in the \fBjoin()\fP method: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> query = Tweet.select(Tweet.content, User.username).join(User, attr=\(aqauthor\(aq) >>> for tweet in query: \&... print(tweet.author.username, \(aq\->\(aq, tweet.content) \&... huey \-> meow huey \-> hiss huey \-> purr mickey \-> woof mickey \-> whine .ft P .fi .UNINDENT .UNINDENT .sp Conversely, if we simply wish \fIall\fP attributes we select to be attributes of the \fBTweet\fP instance, we can add a call to \fBobjects()\fP at the end of our query (similar to how we called \fBdicts()\fP): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for tweet in query.objects(): \&... print(tweet.username, \(aq\->\(aq, tweet.content) \&... huey \-> meow (etc) .ft P .fi .UNINDENT .UNINDENT .SS More complex example .sp As a more complex example, in this query, we will write a single query that selects all the favorites, along with the user who created the favorite, the tweet that was favorited, and that tweet\(aqs author. .sp In SQL we would write: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT owner.username, tweet.content, author.username AS author FROM favorite INNER JOIN user AS owner ON (favorite.user_id = owner.id) INNER JOIN tweet ON (favorite.tweet_id = tweet.id) INNER JOIN user AS author ON (tweet.user_id = author.id); .ft P .fi .UNINDENT .UNINDENT .sp Note that we are selecting from the user table twice \- once in the context of the user who created the favorite, and again as the author of the tweet. .sp With Peewee, we use \fBModel.alias()\fP to alias a model class so it can be referenced twice in a single query: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Owner = User.alias() query = (Favorite .select(Favorite, Tweet.content, User.username, Owner.username) .join(Owner) # Join favorite \-> user (owner of favorite). .switch(Favorite) .join(Tweet) # Join favorite \-> tweet .join(User)) # Join tweet \-> user .ft P .fi .UNINDENT .UNINDENT .sp We can iterate over the results and access the joined values in the following way. Note how Peewee has resolved the fields from the various models we selected and reconstructed the model graph: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for fav in query: \&... print(fav.user.username, \(aqliked\(aq, fav.tweet.content, \(aqby\(aq, fav.tweet.user.username) \&... huey liked whine by mickey mickey liked purr by huey zaizee liked meow by huey zaizee liked purr by huey .ft P .fi .UNINDENT .UNINDENT .SS Subqueries .sp Peewee allows you to join on any table\-like object, including subqueries or common table expressions (CTEs). To demonstrate joining on a subquery, let\(aqs query for all users and their latest tweet. .sp Here is the SQL: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT tweet.*, user.* FROM tweet INNER JOIN ( SELECT latest.user_id, MAX(latest.timestamp) AS max_ts FROM tweet AS latest GROUP BY latest.user_id) AS latest_query ON ((tweet.user_id = latest_query.user_id) AND (tweet.timestamp = latest_query.max_ts)) INNER JOIN user ON (tweet.user_id = user.id) .ft P .fi .UNINDENT .UNINDENT .sp We\(aqll do this by creating a subquery which selects each user and the timestamp of their latest tweet. Then we can query the tweets table in the outer query and join on the user and timestamp combination from the subquery. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Define our subquery first. We\(aqll use an alias of the Tweet model, since # we will be querying from the Tweet model directly in the outer query. Latest = Tweet.alias() latest_query = (Latest .select(Latest.user, fn.MAX(Latest.timestamp).alias(\(aqmax_ts\(aq)) .group_by(Latest.user) .alias(\(aqlatest_query\(aq)) # Our join predicate will ensure that we match tweets based on their # timestamp *and* user_id. predicate = ((Tweet.user == latest_query.c.user_id) & (Tweet.timestamp == latest_query.c.max_ts)) # We put it all together, querying from tweet and joining on the subquery # using the above predicate. query = (Tweet .select(Tweet, User) # Select all columns from tweet and user. .join(latest_query, on=predicate) # Join tweet \-> subquery. .join_from(Tweet, User)) # Join from tweet \-> user. .ft P .fi .UNINDENT .UNINDENT .sp Iterating over the query, we can see each user and their latest tweet. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for tweet in query: \&... print(tweet.user.username, \(aq\->\(aq, tweet.content) \&... huey \-> purr mickey \-> whine .ft P .fi .UNINDENT .UNINDENT .sp There are a couple things you may not have seen before in the code we used to create the query in this section: .INDENT 0.0 .IP \(bu 2 We used \fBjoin_from()\fP to explicitly specify the join context. We wrote \fB\&.join_from(Tweet, User)\fP, which is equivalent to \fB\&.switch(Tweet).join(User)\fP\&. .IP \(bu 2 We referenced columns in the subquery using the magic \fB\&.c\fP attribute, for example \fBlatest_query.c.max_ts\fP\&. The \fB\&.c\fP attribute is used to dynamically create column references. .IP \(bu 2 Instead of passing individual fields to \fBTweet.select()\fP, we passed the \fBTweet\fP and \fBUser\fP models. This is shorthand for selecting all fields on the given model. .UNINDENT .SS Common\-table Expressions .sp In the previous section we joined on a subquery, but we could just as easily have used a common\-table expression (CTE)\&. We will repeat the same query as before, listing users and their latest tweets, but this time we will do it using a CTE. .sp Here is the SQL: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C WITH latest AS ( SELECT user_id, MAX(timestamp) AS max_ts FROM tweet GROUP BY user_id) SELECT tweet.*, user.* FROM tweet INNER JOIN latest ON ((latest.user_id = tweet.user_id) AND (latest.max_ts = tweet.timestamp)) INNER JOIN user ON (tweet.user_id = user.id) .ft P .fi .UNINDENT .UNINDENT .sp This example looks very similar to the previous example with the subquery: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Define our CTE first. We\(aqll use an alias of the Tweet model, since # we will be querying from the Tweet model directly in the main query. Latest = Tweet.alias() cte = (Latest .select(Latest.user, fn.MAX(Latest.timestamp).alias(\(aqmax_ts\(aq)) .group_by(Latest.user) .cte(\(aqlatest\(aq)) # Our join predicate will ensure that we match tweets based on their # timestamp *and* user_id. predicate = ((Tweet.user == cte.c.user_id) & (Tweet.timestamp == cte.c.max_ts)) # We put it all together, querying from tweet and joining on the CTE # using the above predicate. query = (Tweet .select(Tweet, User) # Select all columns from tweet and user. .join(cte, on=predicate) # Join tweet \-> CTE. .join_from(Tweet, User) # Join from tweet \-> user. .with_cte(cte)) .ft P .fi .UNINDENT .UNINDENT .sp We can iterate over the result\-set, which consists of the latest tweets for each user: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for tweet in query: \&... print(tweet.user.username, \(aq\->\(aq, tweet.content) \&... huey \-> purr mickey \-> whine .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 For more information about using CTEs, including information on writing recursive CTEs, see the cte section of the "Querying" document. .UNINDENT .UNINDENT .SS Multiple foreign\-keys to the same Model .sp When there are multiple foreign keys to the same model, it is good practice to explicitly specify which field you are joining on. .sp Referring back to the example app\(aqs models, consider the \fIRelationship\fP model, which is used to denote when one user follows another. Here is the model definition: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Relationship(BaseModel): from_user = ForeignKeyField(User, backref=\(aqrelationships\(aq) to_user = ForeignKeyField(User, backref=\(aqrelated_to\(aq) class Meta: indexes = ( # Specify a unique multi\-column index on from/to\-user. ((\(aqfrom_user\(aq, \(aqto_user\(aq), True), ) .ft P .fi .UNINDENT .UNINDENT .sp Since there are two foreign keys to \fIUser\fP, we should always specify which field we are using in a join. .sp For example, to determine which users I am following, I would write: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C (User .select() .join(Relationship, on=Relationship.to_user) .where(Relationship.from_user == charlie)) .ft P .fi .UNINDENT .UNINDENT .sp On the other hand, if I wanted to determine which users are following me, I would instead join on the \fIfrom_user\fP column and filter on the relationship\(aqs \fIto_user\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C (User .select() .join(Relationship, on=Relationship.from_user) .where(Relationship.to_user == charlie)) .ft P .fi .UNINDENT .UNINDENT .SS Joining on arbitrary fields .sp If a foreign key does not exist between two tables you can still perform a join, but you must manually specify the join predicate. .sp In the following example, there is no explicit foreign\-key between \fIUser\fP and \fIActivityLog\fP, but there is an implied relationship between the \fIActivityLog.object_id\fP field and \fIUser.id\fP\&. Rather than joining on a specific \fBField\fP, we will join using an \fBExpression\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C user_log = (User .select(User, ActivityLog) .join(ActivityLog, on=(User.id == ActivityLog.object_id), attr=\(aqlog\(aq) .where( (ActivityLog.activity_type == \(aquser_activity\(aq) & (User.username == \(aqcharlie\(aq))) for user in user_log: print(user.username, user.log.description) #### Print something like #### charlie logged in charlie posted a tweet charlie retweeted charlie posted a tweet charlie logged out .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Recall that we can control the attribute Peewee will assign the joined instance to by specifying the \fBattr\fP parameter in the \fBjoin()\fP method. In the previous example, we used the following \fIjoin\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C join(ActivityLog, on=(User.id == ActivityLog.object_id), attr=\(aqlog\(aq) .ft P .fi .UNINDENT .UNINDENT .sp Then when iterating over the query, we were able to directly access the joined \fIActivityLog\fP without incurring an additional query: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C for user in user_log: print(user.username, user.log.description) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS Self\-joins .sp Peewee supports constructing queries containing a self\-join. .SS Using model aliases .sp To join on the same model (table) twice, it is necessary to create a model alias to represent the second instance of the table in a query. Consider the following model: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Category(Model): name = CharField() parent = ForeignKeyField(\(aqself\(aq, backref=\(aqchildren\(aq) .ft P .fi .UNINDENT .UNINDENT .sp What if we wanted to query all categories whose parent category is \fIElectronics\fP\&. One way would be to perform a self\-join: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Parent = Category.alias() query = (Category .select() .join(Parent, on=(Category.parent == Parent.id)) .where(Parent.name == \(aqElectronics\(aq)) .ft P .fi .UNINDENT .UNINDENT .sp When performing a join that uses a \fBModelAlias\fP, it is necessary to specify the join condition using the \fBon\fP keyword argument. In this case we are joining the category with its parent category. .SS Using subqueries .sp Another less common approach involves the use of subqueries. Here is another way we might construct a query to get all the categories whose parent category is \fIElectronics\fP using a subquery: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Parent = Category.alias() join_query = Parent.select().where(Parent.name == \(aqElectronics\(aq) # Subqueries used as JOINs need to have an alias. join_query = join_query.alias(\(aqjq\(aq) query = (Category .select() .join(join_query, on=(Category.parent == join_query.c.id))) .ft P .fi .UNINDENT .UNINDENT .sp This will generate the following SQL query: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT t1."id", t1."name", t1."parent_id" FROM "category" AS t1 INNER JOIN ( SELECT t2."id" FROM "category" AS t2 WHERE (t2."name" = ?)) AS jq ON (t1."parent_id" = "jq"."id") .ft P .fi .UNINDENT .UNINDENT .sp To access the \fBid\fP value from the subquery, we use the \fB\&.c\fP magic lookup which will generate the appropriate SQL expression: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Category.parent == join_query.c.id # Becomes: (t1."parent_id" = "jq"."id") .ft P .fi .UNINDENT .UNINDENT .SS Implementing Many to Many .sp Peewee provides a field for representing many\-to\-many relationships, much like Django does. This feature was added due to many requests from users, but I strongly advocate against using it, since it conflates the idea of a field with a junction table and hidden joins. It\(aqs just a nasty hack to provide convenient accessors. .sp To implement many\-to\-many \fBcorrectly\fP with peewee, you will therefore create the intermediary table yourself and query through it: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Student(Model): name = CharField() class Course(Model): name = CharField() class StudentCourse(Model): student = ForeignKeyField(Student) course = ForeignKeyField(Course) .ft P .fi .UNINDENT .UNINDENT .sp To query, let\(aqs say we want to find students who are enrolled in math class: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Student .select() .join(StudentCourse) .join(Course) .where(Course.name == \(aqmath\(aq)) for student in query: print(student.name) .ft P .fi .UNINDENT .UNINDENT .sp To query what classes a given student is enrolled in: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C courses = (Course .select() .join(StudentCourse) .join(Student) .where(Student.name == \(aqda vinci\(aq)) for course in courses: print(course.name) .ft P .fi .UNINDENT .UNINDENT .sp To efficiently iterate over a many\-to\-many relation, i.e., list all students and their respective courses, we will query the \fIthrough\fP model \fBStudentCourse\fP and \fIprecompute\fP the Student and Course: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (StudentCourse .select(StudentCourse, Student, Course) .join(Course) .switch(StudentCourse) .join(Student) .order_by(Student.name)) .ft P .fi .UNINDENT .UNINDENT .sp To print a list of students and their courses you might do the following: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C for student_course in query: print(student_course.student.name, \(aq\->\(aq, student_course.course.name) .ft P .fi .UNINDENT .UNINDENT .sp Since we selected all fields from \fBStudent\fP and \fBCourse\fP in the \fIselect\fP clause of the query, these foreign key traversals are "free" and we\(aqve done the whole iteration with just 1 query. .SS ManyToManyField .sp The \fBManyToManyField\fP provides a \fIfield\-like\fP API over many\-to\-many fields. For all but the simplest many\-to\-many situations, you\(aqre better off using the standard peewee APIs. But, if your models are very simple and your querying needs are not very complex, \fBManyToManyField\fP may work. .sp Modeling students and courses using \fBManyToManyField\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from peewee import * db = SqliteDatabase(\(aqschool.db\(aq) class BaseModel(Model): class Meta: database = db class Student(BaseModel): name = CharField() class Course(BaseModel): name = CharField() students = ManyToManyField(Student, backref=\(aqcourses\(aq) StudentCourse = Course.students.get_through_model() db.create_tables([ Student, Course, StudentCourse]) # Get all classes that "huey" is enrolled in: huey = Student.get(Student.name == \(aqHuey\(aq) for course in huey.courses.order_by(Course.name): print(course.name) # Get all students in "English 101": engl_101 = Course.get(Course.name == \(aqEnglish 101\(aq) for student in engl_101.students: print(student.name) # When adding objects to a many\-to\-many relationship, we can pass # in either a single model instance, a list of models, or even a # query of models: huey.courses.add(Course.select().where(Course.name.contains(\(aqEnglish\(aq))) engl_101.students.add(Student.get(Student.name == \(aqMickey\(aq)) engl_101.students.add([ Student.get(Student.name == \(aqCharlie\(aq), Student.get(Student.name == \(aqZaizee\(aq)]) # The same rules apply for removing items from a many\-to\-many: huey.courses.remove(Course.select().where(Course.name.startswith(\(aqCS\(aq))) engl_101.students.remove(huey) # Calling .clear() will remove all associated objects: cs_150.students.clear() .ft P .fi .UNINDENT .UNINDENT .sp \fBATTENTION:\fP .INDENT 0.0 .INDENT 3.5 Before many\-to\-many relationships can be added, the objects being referenced will need to be saved first. In order to create relationships in the many\-to\-many through table, Peewee needs to know the primary keys of the models being referenced. .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 It is \fBstrongly recommended\fP that you do not attempt to subclass models containing \fBManyToManyField\fP instances. .sp A \fBManyToManyField\fP, despite its name, is not a field in the usual sense. Instead of being a column on a table, the many\-to\-many field covers the fact that behind\-the\-scenes there\(aqs actually a separate table with two foreign\-key pointers (the \fIthrough table\fP). .sp Therefore, when a subclass is created that inherits a many\-to\-many field, what actually needs to be inherited is the \fIthrough table\fP\&. Because of the potential for subtle bugs, Peewee does not attempt to automatically subclass the through model and modify its foreign\-key pointers. As a result, many\-to\-many fields typically will not work with inheritance. .UNINDENT .UNINDENT .sp For more examples, see: .INDENT 0.0 .IP \(bu 2 \fBManyToManyField.add()\fP .IP \(bu 2 \fBManyToManyField.remove()\fP .IP \(bu 2 \fBManyToManyField.clear()\fP .IP \(bu 2 \fBManyToManyField.get_through_model()\fP .UNINDENT .SS Avoiding the N+1 problem .sp The \fIN+1 problem\fP refers to a situation where an application performs a query, then for each row of the result set, the application performs at least one other query (another way to conceptualize this is as a nested loop). In many cases, these \fIn\fP queries can be avoided through the use of a SQL join or subquery. The database itself may do a nested loop, but it will usually be more performant than doing \fIn\fP queries in your application code, which involves latency communicating with the database and may not take advantage of indices or other optimizations employed by the database when joining or executing a subquery. .sp Peewee provides several APIs for mitigating \fIN+1\fP query behavior. Recollecting the models used throughout this document, \fIUser\fP and \fITweet\fP, this section will try to outline some common \fIN+1\fP scenarios, and how peewee can help you avoid them. .sp \fBATTENTION:\fP .INDENT 0.0 .INDENT 3.5 In some cases, N+1 queries will not result in a significant or measurable performance hit. It all depends on the data you are querying, the database you are using, and the latency involved in executing queries and retrieving results. As always when making optimizations, profile before and after to ensure the changes do what you expect them to. .UNINDENT .UNINDENT .SS List recent tweets .sp The twitter timeline displays a list of tweets from multiple users. In addition to the tweet\(aqs content, the username of the tweet\(aqs author is also displayed. The N+1 scenario here would be: .INDENT 0.0 .IP 1. 3 Fetch the 10 most recent tweets. .IP 2. 3 For each tweet, select the author (10 queries). .UNINDENT .sp By selecting both tables and using a \fIjoin\fP, peewee makes it possible to accomplish this in a single query: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Tweet .select(Tweet, User) # Note that we are selecting both models. .join(User) # Use an INNER join because every tweet has an author. .order_by(Tweet.id.desc()) # Get the most recent tweets. .limit(10)) for tweet in query: print(tweet.user.username, \(aq\-\(aq, tweet.message) .ft P .fi .UNINDENT .UNINDENT .sp Without the join, accessing \fBtweet.user.username\fP would trigger a query to resolve the foreign key \fBtweet.user\fP and retrieve the associated user. But since we have selected and joined on \fBUser\fP, peewee will automatically resolve the foreign\-key for us. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 This technique is discussed in more detail in \fI\%Selecting from multiple sources\fP\&. .UNINDENT .UNINDENT .SS List users and all their tweets .sp Let\(aqs say you want to build a page that shows several users and all of their tweets. The N+1 scenario would be: .INDENT 0.0 .IP 1. 3 Fetch some users. .IP 2. 3 For each user, fetch their tweets. .UNINDENT .sp This situation is similar to the previous example, but there is one important difference: when we selected tweets, they only have a single associated user, so we could directly assign the foreign key. The reverse is not true, however, as one user may have any number of tweets (or none at all). .sp Peewee provides an approach to avoiding \fIO(n)\fP queries in this situation. Fetch users first, then fetch all the tweets associated with those users. Once peewee has the big list of tweets, it will assign them out, matching them with the appropriate user. This method is usually faster but will involve a query for each table being selected. .SS Using prefetch .sp peewee supports pre\-fetching related data using sub\-queries. This method requires the use of a special API, \fBprefetch()\fP\&. Prefetch, as its name implies, will eagerly load the appropriate tweets for the given users using subqueries. This means instead of \fIO(n)\fP queries for \fIn\fP rows, we will do \fIO(k)\fP queries for \fIk\fP tables. .sp Here is an example of how we might fetch several users and any tweets they created within the past week. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C week_ago = datetime.date.today() \- datetime.timedelta(days=7) users = User.select() tweets = (Tweet .select() .where(Tweet.timestamp >= week_ago)) # This will perform two queries. users_with_tweets = prefetch(users, tweets) for user in users_with_tweets: print(user.username) for tweet in user.tweets: print(\(aq \(aq, tweet.message) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Note that neither the \fBUser\fP query, nor the \fBTweet\fP query contained a JOIN clause. When using \fBprefetch()\fP you do not need to specify the join. .UNINDENT .UNINDENT .sp \fBprefetch()\fP can be used to query an arbitrary number of tables. Check the API documentation for more examples. .sp Some things to consider when using \fBprefetch()\fP: .INDENT 0.0 .IP \(bu 2 Foreign keys must exist between the models being prefetched. .IP \(bu 2 \fILIMIT\fP works as you\(aqd expect on the outer\-most query, but may be difficult to implement correctly if trying to limit the size of the sub\-selects. .UNINDENT .SS API Documentation .sp This document specifies Peewee\(aqs APIs. .SS Database .INDENT 0.0 .TP .B class Database(database[, thread_safe=True[, autorollback=False[, field_types=None[, operations=None[, autoconnect=True[, **kwargs]]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdatabase\fP (\fIstr\fP) \-\- Database name or filename for SQLite (or \fBNone\fP to defer initialization, in which case you must call \fI\%Database.init()\fP, specifying the database name). .IP \(bu 2 \fBthread_safe\fP (\fIbool\fP) \-\- Whether to store connection state in a thread\-local. .IP \(bu 2 \fBautorollback\fP (\fIbool\fP) \-\- Automatically rollback queries that fail when \fBnot\fP in an explicit transaction. .IP \(bu 2 \fBfield_types\fP (\fIdict\fP) \-\- A mapping of additional field types to support. .IP \(bu 2 \fBoperations\fP (\fIdict\fP) \-\- A mapping of additional operations to support. .IP \(bu 2 \fBautoconnect\fP (\fIbool\fP) \-\- Automatically connect to database if attempting to execute a query on a closed database. .IP \(bu 2 \fBkwargs\fP \-\- Arbitrary keyword arguments that will be passed to the database driver when a connection is created, for example \fBpassword\fP, \fBhost\fP, etc. .UNINDENT .UNINDENT .sp The \fI\%Database\fP is responsible for: .INDENT 7.0 .IP \(bu 2 Executing queries .IP \(bu 2 Managing connections .IP \(bu 2 Transactions .IP \(bu 2 Introspection .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 The database can be instantiated with \fBNone\fP as the database name if the database is not known until run\-time. In this way you can create a database instance and then configure it elsewhere when the settings are known. This is called deferred* initialization\&. .UNINDENT .UNINDENT .sp Examples: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Sqlite database using WAL\-mode and 32MB page\-cache. db = SqliteDatabase(\(aqapp.db\(aq, pragmas={ \(aqjournal_mode\(aq: \(aqwal\(aq, \(aqcache_size\(aq: \-32 * 1000}) # Postgresql database on remote host. db = PostgresqlDatabase(\(aqmy_app\(aq, user=\(aqpostgres\(aq, host=\(aq10.1.0.3\(aq, password=\(aqsecret\(aq) .ft P .fi .UNINDENT .UNINDENT .sp Deferred initialization example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C db = PostgresqlDatabase(None) class BaseModel(Model): class Meta: database = db # Read database connection info from env, for example: db_name = os.environ[\(aqDATABASE\(aq] db_host = os.environ[\(aqPGHOST\(aq] # Initialize database. db.init(db_name, host=db_host, user=\(aqpostgres\(aq) .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .TP .B param = \(aq?\(aq String used as parameter placeholder in SQL queries. .UNINDENT .INDENT 7.0 .TP .B quote = \(aq"\(aq Type of quotation\-mark to use to denote entities such as tables or columns. .UNINDENT .INDENT 7.0 .TP .B init(database[, **kwargs]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdatabase\fP (\fIstr\fP) \-\- Database name or filename for SQLite. .IP \(bu 2 \fBkwargs\fP \-\- Arbitrary keyword arguments that will be passed to the database driver when a connection is created, for example \fBpassword\fP, \fBhost\fP, etc. .UNINDENT .UNINDENT .sp Initialize a \fIdeferred\fP database. See deferring_initialization for more info. .UNINDENT .INDENT 7.0 .TP .B __enter__() The \fI\%Database\fP instance can be used as a context\-manager, in which case a connection will be held open for the duration of the wrapped block. .sp Additionally, any SQL executed within the wrapped block will be executed in a transaction. .UNINDENT .INDENT 7.0 .TP .B connection_context() Create a context\-manager that will hold open a connection for the duration of the wrapped block. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C def on_app_startup(): # When app starts up, create the database tables, being sure # the connection is closed upon completion. with database.connection_context(): database.create_tables(APP_MODELS) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B connect([reuse_if_open=False]) .INDENT 7.0 .TP .B Parameters \fBreuse_if_open\fP (\fIbool\fP) \-\- Do not raise an exception if a connection is already opened. .TP .B Returns whether a new connection was opened. .TP .B Return type bool .TP .B Raises \fBOperationalError\fP if connection already open and \fBreuse_if_open\fP is not set to \fBTrue\fP\&. .UNINDENT .sp Open a connection to the database. .UNINDENT .INDENT 7.0 .TP .B close() .INDENT 7.0 .TP .B Returns Whether a connection was closed. If the database was already closed, this returns \fBFalse\fP\&. .TP .B Return type bool .UNINDENT .sp Close the connection to the database. .UNINDENT .INDENT 7.0 .TP .B is_closed() .INDENT 7.0 .TP .B Returns return \fBTrue\fP if database is closed, \fBFalse\fP if open. .TP .B Return type bool .UNINDENT .UNINDENT .INDENT 7.0 .TP .B connection() Return the open connection. If a connection is not open, one will be opened. The connection will be whatever the underlying database\-driver uses to encapsulate a database connection. .UNINDENT .INDENT 7.0 .TP .B cursor([commit=None]) .INDENT 7.0 .TP .B Parameters \fBcommit\fP \-\- For internal use. .UNINDENT .sp Return a \fBcursor\fP object on the current connection. If a connection is not open, one will be opened. The cursor will be whatever the underlying database\-driver uses to encapsulate a database cursor. .UNINDENT .INDENT 7.0 .TP .B execute_sql(sql[, params=None[, commit=SENTINEL]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBsql\fP (\fIstr\fP) \-\- SQL string to execute. .IP \(bu 2 \fBparams\fP (\fItuple\fP) \-\- Parameters for query. .IP \(bu 2 \fBcommit\fP \-\- Boolean flag to override the default commit logic. .UNINDENT .TP .B Returns cursor object. .UNINDENT .sp Execute a SQL query and return a cursor over the results. .UNINDENT .INDENT 7.0 .TP .B execute(query[, commit=SENTINEL[, **context_options]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBquery\fP \-\- A \fI\%Query\fP instance. .IP \(bu 2 \fBcommit\fP \-\- Boolean flag to override the default commit logic. .IP \(bu 2 \fBcontext_options\fP \-\- Arbitrary options to pass to the SQL generator. .UNINDENT .TP .B Returns cursor object. .UNINDENT .sp Execute a SQL query by compiling a \fBQuery\fP instance and executing the resulting SQL. .UNINDENT .INDENT 7.0 .TP .B last_insert_id(cursor[, query_type=None]) .INDENT 7.0 .TP .B Parameters \fBcursor\fP \-\- cursor object. .TP .B Returns primary key of last\-inserted row. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B rows_affected(cursor) .INDENT 7.0 .TP .B Parameters \fBcursor\fP \-\- cursor object. .TP .B Returns number of rows modified by query. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B in_transaction() .INDENT 7.0 .TP .B Returns whether or not a transaction is currently open. .TP .B Return type bool .UNINDENT .UNINDENT .INDENT 7.0 .TP .B atomic() Create a context\-manager which runs any queries in the wrapped block in a transaction (or save\-point if blocks are nested). .sp Calls to \fI\%atomic()\fP can be nested. .sp \fI\%atomic()\fP can also be used as a decorator. .sp Example code: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C with db.atomic() as txn: perform_operation() with db.atomic() as nested_txn: perform_another_operation() .ft P .fi .UNINDENT .UNINDENT .sp Transactions and save\-points can be explicitly committed or rolled\-back within the wrapped block. If this occurs, a new transaction or savepoint is begun after the commit/rollback. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C with db.atomic() as txn: User.create(username=\(aqmickey\(aq) txn.commit() # Changes are saved and a new transaction begins. User.create(username=\(aqhuey\(aq) txn.rollback() # "huey" will not be saved. User.create(username=\(aqzaizee\(aq) # Print the usernames of all users. print [u.username for u in User.select()] # Prints ["mickey", "zaizee"] .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B manual_commit() Create a context\-manager which disables all transaction management for the duration of the wrapped block. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C with db.manual_commit(): db.begin() # Begin transaction explicitly. try: user.delete_instance(recursive=True) except: db.rollback() # Rollback \-\- an error occurred. raise else: try: db.commit() # Attempt to commit changes. except: db.rollback() # Error committing, rollback. raise .ft P .fi .UNINDENT .UNINDENT .sp The above code is equivalent to the following: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C with db.atomic(): user.delete_instance(recursive=True) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B session_start() Begin a new transaction (without using a context\-manager or decorator). This method is useful if you intend to execute a sequence of operations inside a transaction, but using a decorator or context\-manager would not be appropriate. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 It is strongly advised that you use the \fI\%Database.atomic()\fP method whenever possible for managing transactions/savepoints. The \fBatomic\fP method correctly manages nesting, uses the appropriate construction (e.g., transaction\-vs\-savepoint), and always cleans up after itself. .sp The \fI\%session_start()\fP method should only be used if the sequence of operations does not easily lend itself to wrapping using either a context\-manager or decorator. .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 You must \fIalways\fP call either \fI\%session_commit()\fP or \fI\%session_rollback()\fP after calling the \fBsession_start\fP method. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B session_commit() Commit any changes made during a transaction begun with \fI\%session_start()\fP\&. .UNINDENT .INDENT 7.0 .TP .B session_rollback() Roll back any changes made during a transaction begun with \fI\%session_start()\fP\&. .UNINDENT .INDENT 7.0 .TP .B transaction() Create a context\-manager that runs all queries in the wrapped block in a transaction. .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 Calls to \fBtransaction\fP cannot be nested. Only the top\-most call will take effect. Rolling\-back or committing a nested transaction context\-manager has undefined behavior. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B savepoint() Create a context\-manager that runs all queries in the wrapped block in a savepoint. Savepoints can be nested arbitrarily. .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 Calls to \fBsavepoint\fP must occur inside of a transaction. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B begin() Begin a transaction when using manual\-commit mode. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 This method should only be used in conjunction with the \fI\%manual_commit()\fP context manager. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B commit() Manually commit the currently\-active transaction. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 This method should only be used in conjunction with the \fI\%manual_commit()\fP context manager. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B rollback() Manually roll\-back the currently\-active transaction. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 This method should only be used in conjunction with the \fI\%manual_commit()\fP context manager. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B batch_commit(it, n) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBit\fP (\fIiterable\fP) \-\- an iterable whose items will be yielded. .IP \(bu 2 \fBn\fP (\fIint\fP) \-\- commit every \fIn\fP items. .UNINDENT .TP .B Returns an equivalent iterable to the one provided, with the addition that groups of \fIn\fP items will be yielded in a transaction. .UNINDENT .sp The purpose of this method is to simplify batching large operations, such as inserts, updates, etc. You pass in an iterable and the number of items\-per\-batch, and the items will be returned by an equivalent iterator that wraps each batch in a transaction. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Some list or iterable containing data to insert. row_data = [{\(aqusername\(aq: \(aqu1\(aq}, {\(aqusername\(aq: \(aqu2\(aq}, ...] # Insert all data, committing every 100 rows. If, for example, # there are 789 items in the list, then there will be a total of # 8 transactions (7x100 and 1x89). for row in db.batch_commit(row_data, 100): User.create(**row) .ft P .fi .UNINDENT .UNINDENT .sp An alternative that may be more efficient is to batch the data into a multi\-value \fBINSERT\fP statement (for example, using \fI\%Model.insert_many()\fP): .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C with db.atomic(): for idx in range(0, len(row_data), 100): # Insert 100 rows at a time. rows = row_data[idx:idx + 100] User.insert_many(rows).execute() .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B table_exists(table[, schema=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Table name. .IP \(bu 2 \fBschema\fP (\fIstr\fP) \-\- Schema name (optional). .UNINDENT .TP .B Returns \fBbool\fP indicating whether table exists. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B get_tables([schema=None]) .INDENT 7.0 .TP .B Parameters \fBschema\fP (\fIstr\fP) \-\- Schema name (optional). .TP .B Returns a list of table names in the database. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B get_indexes(table[, schema=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Table name. .IP \(bu 2 \fBschema\fP (\fIstr\fP) \-\- Schema name (optional). .UNINDENT .UNINDENT .sp Return a list of \fBIndexMetadata\fP tuples. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C print(db.get_indexes(\(aqentry\(aq)) [IndexMetadata( name=\(aqentry_public_list\(aq, sql=\(aqCREATE INDEX "entry_public_list" ...\(aq, columns=[\(aqtimestamp\(aq], unique=False, table=\(aqentry\(aq), IndexMetadata( name=\(aqentry_slug\(aq, sql=\(aqCREATE UNIQUE INDEX "entry_slug" ON "entry" ("slug")\(aq, columns=[\(aqslug\(aq], unique=True, table=\(aqentry\(aq)] .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B get_columns(table[, schema=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Table name. .IP \(bu 2 \fBschema\fP (\fIstr\fP) \-\- Schema name (optional). .UNINDENT .UNINDENT .sp Return a list of \fBColumnMetadata\fP tuples. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C print(db.get_columns(\(aqentry\(aq)) [ColumnMetadata( name=\(aqid\(aq, data_type=\(aqINTEGER\(aq, null=False, primary_key=True, table=\(aqentry\(aq), ColumnMetadata( name=\(aqtitle\(aq, data_type=\(aqTEXT\(aq, null=False, primary_key=False, table=\(aqentry\(aq), ...] .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B get_primary_keys(table[, schema=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Table name. .IP \(bu 2 \fBschema\fP (\fIstr\fP) \-\- Schema name (optional). .UNINDENT .UNINDENT .sp Return a list of column names that comprise the primary key. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C print(db.get_primary_keys(\(aqentry\(aq)) [\(aqid\(aq] .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B get_foreign_keys(table[, schema=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Table name. .IP \(bu 2 \fBschema\fP (\fIstr\fP) \-\- Schema name (optional). .UNINDENT .UNINDENT .sp Return a list of \fBForeignKeyMetadata\fP tuples for keys present on the table. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C print(db.get_foreign_keys(\(aqentrytag\(aq)) [ForeignKeyMetadata( column=\(aqentry_id\(aq, dest_table=\(aqentry\(aq, dest_column=\(aqid\(aq, table=\(aqentrytag\(aq), ...] .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B get_views([schema=None]) .INDENT 7.0 .TP .B Parameters \fBschema\fP (\fIstr\fP) \-\- Schema name (optional). .UNINDENT .sp Return a list of \fBViewMetadata\fP tuples for VIEWs present in the database. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C print(db.get_views()) [ViewMetadata( name=\(aqentries_public\(aq, sql=\(aqCREATE VIEW entries_public AS SELECT ... \(aq), ...] .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B sequence_exists(seq) .INDENT 7.0 .TP .B Parameters \fBseq\fP (\fIstr\fP) \-\- Name of sequence. .TP .B Returns Whether sequence exists. .TP .B Return type bool .UNINDENT .UNINDENT .INDENT 7.0 .TP .B create_tables(models[, **options]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmodels\fP (\fIlist\fP) \-\- A list of \fI\%Model\fP classes. .IP \(bu 2 \fBoptions\fP \-\- Options to specify when calling \fI\%Model.create_table()\fP\&. .UNINDENT .UNINDENT .sp Create tables, indexes and associated metadata for the given list of models. .sp Dependencies are resolved so that tables are created in the appropriate order. .UNINDENT .INDENT 7.0 .TP .B drop_tables(models[, **options]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmodels\fP (\fIlist\fP) \-\- A list of \fI\%Model\fP classes. .IP \(bu 2 \fBkwargs\fP \-\- Options to specify when calling \fI\%Model.drop_table()\fP\&. .UNINDENT .UNINDENT .sp Drop tables, indexes and associated metadata for the given list of models. .sp Dependencies are resolved so that tables are dropped in the appropriate order. .UNINDENT .INDENT 7.0 .TP .B bind(models[, bind_refs=True[, bind_backrefs=True]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmodels\fP (\fIlist\fP) \-\- One or more \fI\%Model\fP classes to bind. .IP \(bu 2 \fBbind_refs\fP (\fIbool\fP) \-\- Bind related models. .IP \(bu 2 \fBbind_backrefs\fP (\fIbool\fP) \-\- Bind back\-reference related models. .UNINDENT .UNINDENT .sp Bind the given list of models, and specified relations, to the database. .UNINDENT .INDENT 7.0 .TP .B bind_ctx(models[, bind_refs=True[, bind_backrefs=True]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmodels\fP (\fIlist\fP) \-\- List of models to bind to the database. .IP \(bu 2 \fBbind_refs\fP (\fIbool\fP) \-\- Bind models that are referenced using foreign\-keys. .IP \(bu 2 \fBbind_backrefs\fP (\fIbool\fP) \-\- Bind models that reference the given model with a foreign\-key. .UNINDENT .UNINDENT .sp Create a context\-manager that binds (associates) the given models with the current database for the duration of the wrapped block. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C MODELS = (User, Account, Note) # Bind the given models to the db for the duration of wrapped block. def use_test_database(fn): @wraps(fn) def inner(self): with test_db.bind_ctx(MODELS): test_db.create_tables(MODELS) try: fn(self) finally: test_db.drop_tables(MODELS) return inner class TestSomething(TestCase): @use_test_database def test_something(self): # ... models are bound to test database ... pass .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B extract_date(date_part, date_field) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdate_part\fP (\fIstr\fP) \-\- date part to extract, e.g. \(aqyear\(aq. .IP \(bu 2 \fBdate_field\fP (\fINode\fP) \-\- a SQL node containing a date/time, for example a \fI\%DateTimeField\fP\&. .UNINDENT .TP .B Returns a SQL node representing a function call that will return the provided date part. .UNINDENT .sp Provides a compatible interface for extracting a portion of a datetime. .UNINDENT .INDENT 7.0 .TP .B truncate_date(date_part, date_field) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdate_part\fP (\fIstr\fP) \-\- date part to truncate to, e.g. \(aqday\(aq. .IP \(bu 2 \fBdate_field\fP (\fINode\fP) \-\- a SQL node containing a date/time, for example a \fI\%DateTimeField\fP\&. .UNINDENT .TP .B Returns a SQL node representing a function call that will return the truncated date part. .UNINDENT .sp Provides a compatible interface for truncating a datetime to the given resolution. .UNINDENT .INDENT 7.0 .TP .B random() .INDENT 7.0 .TP .B Returns a SQL node representing a function call that returns a random value. .UNINDENT .sp A compatible interface for calling the appropriate random number generation function provided by the database. For Postgres and Sqlite, this is equivalent to \fBfn.random()\fP, for MySQL \fBfn.rand()\fP\&. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class SqliteDatabase(database[, pragmas=None[, timeout=5[, **kwargs]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBpragmas\fP \-\- Either a dictionary or a list of 2\-tuples containing pragma key and value to set every time a connection is opened. .IP \(bu 2 \fBtimeout\fP \-\- Set the busy\-timeout on the SQLite driver (in seconds). .UNINDENT .UNINDENT .sp Sqlite database implementation. \fI\%SqliteDatabase\fP that provides some advanced features only offered by Sqlite. .INDENT 7.0 .IP \(bu 2 Register custom aggregates, collations and functions .IP \(bu 2 Load C extensions .IP \(bu 2 Advanced transactions (specify lock type) .IP \(bu 2 For even more features, see \fBSqliteExtDatabase\fP\&. .UNINDENT .sp Example of initializing a database and configuring some PRAGMAs: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C db = SqliteDatabase(\(aqmy_app.db\(aq, pragmas=( (\(aqcache_size\(aq, \-16000), # 16MB (\(aqjournal_mode\(aq, \(aqwal\(aq), # Use write\-ahead\-log journal mode. )) # Alternatively, pragmas can be specified using a dictionary. db = SqliteDatabase(\(aqmy_app.db\(aq, pragmas={\(aqjournal_mode\(aq: \(aqwal\(aq}) .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .TP .B pragma(key[, value=SENTINEL[, permanent=False]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBkey\fP \-\- Setting name. .IP \(bu 2 \fBvalue\fP \-\- New value for the setting (optional). .IP \(bu 2 \fBpermanent\fP \-\- Apply this pragma whenever a connection is opened. .UNINDENT .UNINDENT .sp Execute a PRAGMA query once on the active connection. If a value is not specified, then the current value will be returned. .sp If \fBpermanent\fP is specified, then the PRAGMA query will also be executed whenever a new connection is opened, ensuring it is always in\-effect. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 By default this only affects the current connection. If the PRAGMA being executed is not persistent, then you must specify \fBpermanent=True\fP to ensure the pragma is set on subsequent connections. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B cache_size Get or set the cache_size pragma for the current connection. .UNINDENT .INDENT 7.0 .TP .B foreign_keys Get or set the foreign_keys pragma for the current connection. .UNINDENT .INDENT 7.0 .TP .B journal_mode Get or set the journal_mode pragma. .UNINDENT .INDENT 7.0 .TP .B journal_size_limit Get or set the journal_size_limit pragma. .UNINDENT .INDENT 7.0 .TP .B mmap_size Get or set the mmap_size pragma for the current connection. .UNINDENT .INDENT 7.0 .TP .B page_size Get or set the page_size pragma. .UNINDENT .INDENT 7.0 .TP .B read_uncommitted Get or set the read_uncommitted pragma for the current connection. .UNINDENT .INDENT 7.0 .TP .B synchronous Get or set the synchronous pragma for the current connection. .UNINDENT .INDENT 7.0 .TP .B wal_autocheckpoint Get or set the wal_autocheckpoint pragma for the current connection. .UNINDENT .INDENT 7.0 .TP .B timeout Get or set the busy timeout (seconds). .UNINDENT .INDENT 7.0 .TP .B register_aggregate(klass[, name=None[, num_params=\-1]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBklass\fP \-\- Class implementing aggregate API. .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Aggregate function name (defaults to name of class). .IP \(bu 2 \fBnum_params\fP (\fIint\fP) \-\- Number of parameters the aggregate accepts, or \-1 for any number. .UNINDENT .UNINDENT .sp Register a user\-defined aggregate function. .sp The function will be registered each time a new connection is opened. Additionally, if a connection is already open, the aggregate will be registered with the open connection. .UNINDENT .INDENT 7.0 .TP .B aggregate([name=None[, num_params=\-1]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Name of the aggregate (defaults to class name). .IP \(bu 2 \fBnum_params\fP (\fIint\fP) \-\- Number of parameters the aggregate accepts, or \-1 for any number. .UNINDENT .UNINDENT .sp Class decorator to register a user\-defined aggregate function. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C @db.aggregate(\(aqmd5\(aq) class MD5(object): def initialize(self): self.md5 = hashlib.md5() def step(self, value): self.md5.update(value) def finalize(self): return self.md5.hexdigest() @db.aggregate() class Product(object): \(aq\(aq\(aqLike SUM() except calculates cumulative product.\(aq\(aq\(aq def __init__(self): self.product = 1 def step(self, value): self.product *= value def finalize(self): return self.product .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B register_collation(fn[, name=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBfn\fP \-\- The collation function. .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Name of collation (defaults to function name) .UNINDENT .UNINDENT .sp Register a user\-defined collation. The collation will be registered each time a new connection is opened. Additionally, if a connection is already open, the collation will be registered with the open connection. .UNINDENT .INDENT 7.0 .TP .B collation([name=None]) .INDENT 7.0 .TP .B Parameters \fBname\fP (\fIstr\fP) \-\- Name of collation (defaults to function name) .UNINDENT .sp Decorator to register a user\-defined collation. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C @db.collation(\(aqreverse\(aq) def collate_reverse(s1, s2): return \-cmp(s1, s2) # Usage: Book.select().order_by(collate_reverse.collation(Book.title)) # Equivalent: Book.select().order_by(Book.title.asc(collation=\(aqreverse\(aq)) .ft P .fi .UNINDENT .UNINDENT .sp As you might have noticed, the original \fBcollate_reverse\fP function has a special attribute called \fBcollation\fP attached to it. This extra attribute provides a shorthand way to generate the SQL necessary to use our custom collation. .UNINDENT .INDENT 7.0 .TP .B register_function(fn[, name=None[, num_params=\-1]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBfn\fP \-\- The user\-defined scalar function. .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Name of function (defaults to function name) .IP \(bu 2 \fBnum_params\fP (\fIint\fP) \-\- Number of arguments the function accepts, or \-1 for any number. .UNINDENT .UNINDENT .sp Register a user\-defined scalar function. The function will be registered each time a new connection is opened. Additionally, if a connection is already open, the function will be registered with the open connection. .UNINDENT .INDENT 7.0 .TP .B func([name=None[, num_params=\-1]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Name of the function (defaults to function name). .IP \(bu 2 \fBnum_params\fP (\fIint\fP) \-\- Number of parameters the function accepts, or \-1 for any number. .UNINDENT .UNINDENT .sp Decorator to register a user\-defined scalar function. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C @db.func(\(aqtitle_case\(aq) def title_case(s): return s.title() if s else \(aq\(aq # Usage: title_case_books = Book.select(fn.title_case(Book.title)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B register_window_function(klass[, name=None[, num_params=\-1]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBklass\fP \-\- Class implementing window function API. .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Window function name (defaults to name of class). .IP \(bu 2 \fBnum_params\fP (\fIint\fP) \-\- Number of parameters the function accepts, or \-1 for any number. .UNINDENT .UNINDENT .sp Register a user\-defined window function. .sp \fBATTENTION:\fP .INDENT 7.0 .INDENT 3.5 This feature requires SQLite >= 3.25.0 \fBand\fP \fI\%pysqlite3\fP >= 0.2.0. .UNINDENT .UNINDENT .sp The window function will be registered each time a new connection is opened. Additionally, if a connection is already open, the window function will be registered with the open connection. .UNINDENT .INDENT 7.0 .TP .B window_function([name=None[, num_params=\-1]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Name of the window function (defaults to class name). .IP \(bu 2 \fBnum_params\fP (\fIint\fP) \-\- Number of parameters the function accepts, or \-1 for any number. .UNINDENT .UNINDENT .sp Class decorator to register a user\-defined window function. Window functions must define the following methods: .INDENT 7.0 .IP \(bu 2 \fBstep()\fP \- receive values from a row and update state. .IP \(bu 2 \fBinverse()\fP \- inverse of \fBstep()\fP for the given values. .IP \(bu 2 \fBvalue()\fP \- return the current value of the window function. .IP \(bu 2 \fBfinalize()\fP \- return the final value of the window function. .UNINDENT .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C @db.window_function(\(aqmy_sum\(aq) class MySum(object): def __init__(self): self._value = 0 def step(self, value): self._value += value def inverse(self, value): self._value \-= value def value(self): return self._value def finalize(self): return self._value .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B table_function([name=None]) Class\-decorator for registering a \fBTableFunction\fP\&. Table functions are user\-defined functions that, rather than returning a single, scalar value, can return any number of rows of tabular data. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C from playhouse.sqlite_ext import TableFunction @db.table_function(\(aqseries\(aq) class Series(TableFunction): columns = [\(aqvalue\(aq] params = [\(aqstart\(aq, \(aqstop\(aq, \(aqstep\(aq] def initialize(self, start=0, stop=None, step=1): """ Table\-functions declare an initialize() method, which is called with whatever arguments the user has called the function with. """ self.start = self.current = start self.stop = stop or float(\(aqInf\(aq) self.step = step def iterate(self, idx): """ Iterate is called repeatedly by the SQLite database engine until the required number of rows has been read **or** the function raises a \(gaStopIteration\(ga signalling no more rows are available. """ if self.current > self.stop: raise StopIteration ret, self.current = self.current, self.current + self.step return (ret,) # Usage: cursor = db.execute_sql(\(aqSELECT * FROM series(?, ?, ?)\(aq, (0, 5, 2)) for value, in cursor: print(value) # Prints: # 0 # 2 # 4 .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B unregister_aggregate(name) .INDENT 7.0 .TP .B Parameters \fBname\fP \-\- Name of the user\-defined aggregate function. .UNINDENT .sp Unregister the user\-defined aggregate function. .UNINDENT .INDENT 7.0 .TP .B unregister_collation(name) .INDENT 7.0 .TP .B Parameters \fBname\fP \-\- Name of the user\-defined collation. .UNINDENT .sp Unregister the user\-defined collation. .UNINDENT .INDENT 7.0 .TP .B unregister_function(name) .INDENT 7.0 .TP .B Parameters \fBname\fP \-\- Name of the user\-defined scalar function. .UNINDENT .sp Unregister the user\-defined scalar function. .UNINDENT .INDENT 7.0 .TP .B unregister_table_function(name) .INDENT 7.0 .TP .B Parameters \fBname\fP \-\- Name of the user\-defined table function. .TP .B Returns True or False, depending on whether the function was removed. .UNINDENT .sp Unregister the user\-defined scalar function. .UNINDENT .INDENT 7.0 .TP .B load_extension(extension_module) Load the given C extension. If a connection is currently open in the calling thread, then the extension will be loaded for that connection as well as all subsequent connections. .sp For example, if you\(aqve compiled the closure table extension and wish to use it in your application, you might write: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C db = SqliteExtDatabase(\(aqmy_app.db\(aq) db.load_extension(\(aqclosure\(aq) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B attach(filename, name) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBfilename\fP (\fIstr\fP) \-\- Database to attach (or \fB:memory:\fP for in\-memory) .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Schema name for attached database. .UNINDENT .TP .B Returns boolean indicating success .UNINDENT .sp Register another database file that will be attached to every database connection. If the main database is currently connected, the new database will be attached on the open connection. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Databases that are attached using this method will be attached every time a database connection is opened. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B detach(name) .INDENT 7.0 .TP .B Parameters \fBname\fP (\fIstr\fP) \-\- Schema name for attached database. .TP .B Returns boolean indicating success .UNINDENT .sp Unregister another database file that was attached previously with a call to \fI\%attach()\fP\&. If the main database is currently connected, the attached database will be detached from the open connection. .UNINDENT .INDENT 7.0 .TP .B transaction([lock_type=None]) .INDENT 7.0 .TP .B Parameters \fBlock_type\fP (\fIstr\fP) \-\- Locking strategy: DEFERRED, IMMEDIATE, EXCLUSIVE. .UNINDENT .sp Create a transaction context\-manager using the specified locking strategy (defaults to DEFERRED). .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class PostgresqlDatabase(database[, register_unicode=True[, encoding=None[, isolation_level=None]]]) Postgresql database implementation. .sp Additional optional keyword\-parameters: .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBregister_unicode\fP (\fIbool\fP) \-\- Register unicode types. .IP \(bu 2 \fBencoding\fP (\fIstr\fP) \-\- Database encoding. .IP \(bu 2 \fBisolation_level\fP (\fIint\fP) \-\- Isolation level constant, defined in the \fBpsycopg2.extensions\fP module. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B set_time_zone(timezone) .INDENT 7.0 .TP .B Parameters \fBtimezone\fP (\fIstr\fP) \-\- timezone name, e.g. "US/Central". .TP .B Returns no return value. .UNINDENT .sp Set the timezone on the current connection. If no connection is open, then one will be opened. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class MySQLDatabase(database[, **kwargs]) MySQL database implementation. .UNINDENT .SS Query\-builder .INDENT 0.0 .TP .B class Node Base\-class for all components which make up the AST for a SQL query. .INDENT 7.0 .TP .B static copy(method) Decorator to use with Node methods that mutate the node\(aqs state. This allows method\-chaining, e.g.: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C query = MyModel.select() new_query = query.where(MyModel.field == \(aqvalue\(aq) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B unwrap() API for recursively unwrapping "wrapped" nodes. Base case is to return self. .UNINDENT .INDENT 7.0 .TP .B is_alias() API for determining if a node, at any point, has been explicitly aliased by the user. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Source([alias=None]) A source of row tuples, for example a table, join, or select query. By default provides a "magic" attribute named "c" that is a factory for column/attribute lookups, for example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C User = Table(\(aqusers\(aq) query = (User .select(User.c.username) .where(User.c.active == True) .order_by(User.c.username)) .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .TP .B alias(name) Returns a copy of the object with the given alias applied. .UNINDENT .INDENT 7.0 .TP .B select(*columns) .INDENT 7.0 .TP .B Parameters \fBcolumns\fP \-\- \fI\%Column\fP instances, expressions, functions, sub\-queries, or anything else that you would like to select. .UNINDENT .sp Create a \fI\%Select\fP query on the table. If the table explicitly declares columns and no columns are provided, then by default all the table\(aqs defined columns will be selected. .UNINDENT .INDENT 7.0 .TP .B join(dest[, join_type=\(aqINNER\(aq[, on=None]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdest\fP (\fISource\fP) \-\- Join the table with the given destination. .IP \(bu 2 \fBjoin_type\fP (\fIstr\fP) \-\- Join type. .IP \(bu 2 \fBon\fP \-\- Expression to use as join predicate. .UNINDENT .TP .B Returns a \fI\%Join\fP instance. .UNINDENT .sp Join type may be one of: .INDENT 7.0 .IP \(bu 2 \fBJOIN.INNER\fP .IP \(bu 2 \fBJOIN.LEFT_OUTER\fP .IP \(bu 2 \fBJOIN.RIGHT_OUTER\fP .IP \(bu 2 \fBJOIN.FULL\fP .IP \(bu 2 \fBJOIN.FULL_OUTER\fP .IP \(bu 2 \fBJOIN.CROSS\fP .UNINDENT .UNINDENT .INDENT 7.0 .TP .B left_outer_join(dest[, on=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdest\fP (\fISource\fP) \-\- Join the table with the given destination. .IP \(bu 2 \fBon\fP \-\- Expression to use as join predicate. .UNINDENT .TP .B Returns a \fI\%Join\fP instance. .UNINDENT .sp Convenience method for calling \fI\%join()\fP using a LEFT OUTER join. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class BaseTable Base class for table\-like objects, which support JOINs via operator overloading. .INDENT 7.0 .TP .B __and__(dest) Perform an INNER join on \fBdest\fP\&. .UNINDENT .INDENT 7.0 .TP .B __add__(dest) Perform a LEFT OUTER join on \fBdest\fP\&. .UNINDENT .INDENT 7.0 .TP .B __sub__(dest) Perform a RIGHT OUTER join on \fBdest\fP\&. .UNINDENT .INDENT 7.0 .TP .B __or__(dest) Perform a FULL OUTER join on \fBdest\fP\&. .UNINDENT .INDENT 7.0 .TP .B __mul__(dest) Perform a CROSS join on \fBdest\fP\&. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Table(name[, columns=None[, primary_key=None[, schema=None[, alias=None]]]]) Represents a table in the database (or a table\-like object such as a view). .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Database table name .IP \(bu 2 \fBcolumns\fP (\fItuple\fP) \-\- List of column names (optional). .IP \(bu 2 \fBprimary_key\fP (\fIstr\fP) \-\- Name of primary key column. .IP \(bu 2 \fBschema\fP (\fIstr\fP) \-\- Schema name used to access table (if necessary). .IP \(bu 2 \fBalias\fP (\fIstr\fP) \-\- Alias to use for table in SQL queries. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 If columns are specified, the magic "c" attribute will be disabled. .UNINDENT .UNINDENT .sp When columns are not explicitly defined, tables have a special attribute "c" which is a factory that provides access to table columns dynamically. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C User = Table(\(aqusers\(aq) query = (User .select(User.c.id, User.c.username) .order_by(User.c.username)) .ft P .fi .UNINDENT .UNINDENT .sp Equivalent example when columns \fBare\fP specified: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C User = Table(\(aqusers\(aq, (\(aqid\(aq, \(aqusername\(aq)) query = (User .select(User.id, User.username) .order_by(User.username)) .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .TP .B bind([database=None]) .INDENT 7.0 .TP .B Parameters \fBdatabase\fP \-\- \fI\%Database\fP object. .UNINDENT .sp Bind this table to the given database (or unbind by leaving empty). .sp When a table is \fIbound\fP to a database, queries may be executed against it without the need to specify the database in the query\(aqs execute method. .UNINDENT .INDENT 7.0 .TP .B bind_ctx([database=None]) .INDENT 7.0 .TP .B Parameters \fBdatabase\fP \-\- \fI\%Database\fP object. .UNINDENT .sp Return a context manager that will bind the table to the given database for the duration of the wrapped block. .UNINDENT .INDENT 7.0 .TP .B select(*columns) .INDENT 7.0 .TP .B Parameters \fBcolumns\fP \-\- \fI\%Column\fP instances, expressions, functions, sub\-queries, or anything else that you would like to select. .UNINDENT .sp Create a \fI\%Select\fP query on the table. If the table explicitly declares columns and no columns are provided, then by default all the table\(aqs defined columns will be selected. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C User = Table(\(aqusers\(aq, (\(aqid\(aq, \(aqusername\(aq)) # Because columns were defined on the Table, we will default to # selecting both of the User table\(aqs columns. # Evaluates to SELECT id, username FROM users query = User.select() Note = Table(\(aqnotes\(aq) query = (Note .select(Note.c.content, Note.c.timestamp, User.username) .join(User, on=(Note.c.user_id == User.id)) .where(Note.c.is_published == True) .order_by(Note.c.timestamp.desc())) # Using a function to select users and the number of notes they # have authored. query = (User .select( User.username, fn.COUNT(Note.c.id).alias(\(aqn_notes\(aq)) .join( Note, JOIN.LEFT_OUTER, on=(User.id == Note.c.user_id)) .order_by(fn.COUNT(Note.c.id).desc())) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B insert([insert=None[, columns=None[, **kwargs]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBinsert\fP \-\- A dictionary mapping column to value, an iterable that yields dictionaries (i.e. list), or a \fI\%Select\fP query. .IP \(bu 2 \fBcolumns\fP (\fIlist\fP) \-\- The list of columns to insert into when the data being inserted is not a dictionary. .IP \(bu 2 \fBkwargs\fP \-\- Mapping of column\-name to value. .UNINDENT .UNINDENT .sp Create a \fI\%Insert\fP query into the table. .UNINDENT .INDENT 7.0 .TP .B replace([insert=None[, columns=None[, **kwargs]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBinsert\fP \-\- A dictionary mapping column to value, an iterable that yields dictionaries (i.e. list), or a \fI\%Select\fP query. .IP \(bu 2 \fBcolumns\fP (\fIlist\fP) \-\- The list of columns to insert into when the data being inserted is not a dictionary. .IP \(bu 2 \fBkwargs\fP \-\- Mapping of column\-name to value. .UNINDENT .UNINDENT .sp Create a \fI\%Insert\fP query into the table whose conflict resolution method is to replace. .UNINDENT .INDENT 7.0 .TP .B update([update=None[, **kwargs]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBupdate\fP \-\- A dictionary mapping column to value. .IP \(bu 2 \fBkwargs\fP \-\- Mapping of column\-name to value. .UNINDENT .UNINDENT .sp Create a \fI\%Update\fP query for the table. .UNINDENT .INDENT 7.0 .TP .B delete() Create a \fI\%Delete\fP query for the table. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Join(lhs, rhs[, join_type=JOIN.INNER[, on=None[, alias=None]]]) Represent a JOIN between to table\-like objects. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBlhs\fP \-\- Left\-hand side of the join. .IP \(bu 2 \fBrhs\fP \-\- Right\-hand side of the join. .IP \(bu 2 \fBjoin_type\fP \-\- Type of join. e.g. JOIN.INNER, JOIN.LEFT_OUTER, etc. .IP \(bu 2 \fBon\fP \-\- Expression describing the join predicate. .IP \(bu 2 \fBalias\fP (\fIstr\fP) \-\- Alias to apply to joined data. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B on(predicate) .INDENT 7.0 .TP .B Parameters \fBpredicate\fP (\fIExpression\fP) \-\- join predicate. .UNINDENT .sp Specify the predicate expression used for this join. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class ValuesList(values[, columns=None[, alias=None]]) Represent a values list that can be used like a table. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBvalues\fP \-\- a list\-of\-lists containing the row data to represent. .IP \(bu 2 \fBcolumns\fP (\fIlist\fP) \-\- the names to give to the columns in each row. .IP \(bu 2 \fBalias\fP (\fIstr\fP) \-\- alias to use for values\-list. .UNINDENT .UNINDENT .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C data = [(1, \(aqfirst\(aq), (2, \(aqsecond\(aq)] vl = ValuesList(data, columns=(\(aqidx\(aq, \(aqname\(aq)) query = (vl .select(vl.c.idx, vl.c.name) .order_by(vl.c.idx)) # Yields: # SELECT t1.idx, t1.name # FROM (VALUES (1, \(aqfirst\(aq), (2, \(aqsecond\(aq)) AS t1(idx, name) # ORDER BY t1.idx .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .TP .B columns(*names) .INDENT 7.0 .TP .B Parameters \fBnames\fP \-\- names to apply to the columns of data. .UNINDENT .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C vl = ValuesList([(1, \(aqfirst\(aq), (2, \(aqsecond\(aq)]) vl = vl.columns(\(aqidx\(aq, \(aqname\(aq).alias(\(aqv\(aq) query = vl.select(vl.c.idx, vl.c.name) # Yields: # SELECT v.idx, v.name # FROM (VALUES (1, \(aqfirst\(aq), (2, \(aqsecond\(aq)) AS v(idx, name) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class CTE(name, query[, recursive=False[, columns=None]]) Represent a common\-table\-expression. For example queries, see cte\&. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBname\fP \-\- Name for the CTE. .IP \(bu 2 \fBquery\fP \-\- \fI\%Select\fP query describing CTE. .IP \(bu 2 \fBrecursive\fP (\fIbool\fP) \-\- Whether the CTE is recursive. .IP \(bu 2 \fBcolumns\fP (\fIlist\fP) \-\- Explicit list of columns produced by CTE (optional). .UNINDENT .UNINDENT .INDENT 7.0 .TP .B select_from(*columns) Create a SELECT query that utilizes the given common table expression as the source for a new query. .INDENT 7.0 .TP .B Parameters \fBcolumns\fP \-\- One or more columns to select from the CTE. .TP .B Returns \fI\%Select\fP query utilizing the common table expression .UNINDENT .UNINDENT .INDENT 7.0 .TP .B union_all(other) Used on the base\-case CTE to construct the recursive term of the CTE. .INDENT 7.0 .TP .B Parameters \fBother\fP \-\- recursive term, generally a \fI\%Select\fP query. .TP .B Returns a recursive \fI\%CTE\fP with the given recursive term. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class ColumnBase Base\-class for column\-like objects, attributes or expressions. .sp Column\-like objects can be composed using various operators and special methods. .INDENT 7.0 .IP \(bu 2 \fB&\fP: Logical AND .IP \(bu 2 \fB|\fP: Logical OR .IP \(bu 2 \fB+\fP: Addition .IP \(bu 2 \fB\-\fP: Subtraction .IP \(bu 2 \fB*\fP: Multiplication .IP \(bu 2 \fB/\fP: Division .IP \(bu 2 \fB^\fP: Exclusive\-OR .IP \(bu 2 \fB==\fP: Equality .IP \(bu 2 \fB!=\fP: Inequality .IP \(bu 2 \fB>\fP: Greater\-than .IP \(bu 2 \fB<\fP: Less\-than .IP \(bu 2 \fB>=\fP: Greater\-than or equal .IP \(bu 2 \fB<=\fP: Less\-than or equal .IP \(bu 2 \fB<<\fP: \fBIN\fP .IP \(bu 2 \fB>>\fP: \fBIS\fP (i.e. \fBIS NULL\fP) .IP \(bu 2 \fB%\fP: \fBLIKE\fP .IP \(bu 2 \fB**\fP: \fBILIKE\fP .IP \(bu 2 \fBbin_and()\fP: Binary AND .IP \(bu 2 \fBbin_or()\fP: Binary OR .IP \(bu 2 \fBin_()\fP: \fBIN\fP .IP \(bu 2 \fBnot_in()\fP: \fBNOT IN\fP .IP \(bu 2 \fBregexp()\fP: \fBREGEXP\fP .IP \(bu 2 \fBis_null(True/False)\fP: \fBIS NULL\fP or \fBIS NOT NULL\fP .IP \(bu 2 \fBcontains(s)\fP: \fBLIKE %s%\fP .IP \(bu 2 \fBstartswith(s)\fP: \fBLIKE s%\fP .IP \(bu 2 \fBendswith(s)\fP: \fBLIKE %s\fP .IP \(bu 2 \fBbetween(low, high)\fP: \fBBETWEEN low AND high\fP .IP \(bu 2 \fBconcat()\fP: \fB||\fP .UNINDENT .INDENT 7.0 .TP .B alias(alias) .INDENT 7.0 .TP .B Parameters \fBalias\fP (\fIstr\fP) \-\- Alias for the given column\-like object. .TP .B Returns a \fI\%Alias\fP object. .UNINDENT .sp Indicate the alias that should be given to the specified column\-like object. .UNINDENT .INDENT 7.0 .TP .B cast(as_type) .INDENT 7.0 .TP .B Parameters \fBas_type\fP (\fIstr\fP) \-\- Type name to cast to. .TP .B Returns a \fI\%Cast\fP object. .UNINDENT .sp Create a \fBCAST\fP expression. .UNINDENT .INDENT 7.0 .TP .B asc([collation=None[, nulls=None]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBcollation\fP (\fIstr\fP) \-\- Collation name to use for sorting. .IP \(bu 2 \fBnulls\fP (\fIstr\fP) \-\- Sort nulls (FIRST or LAST). .UNINDENT .TP .B Returns an ascending \fI\%Ordering\fP object for the column. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B desc([collation=None[, nulls=None]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBcollation\fP (\fIstr\fP) \-\- Collation name to use for sorting. .IP \(bu 2 \fBnulls\fP (\fIstr\fP) \-\- Sort nulls (FIRST or LAST). .UNINDENT .TP .B Returns an descending \fI\%Ordering\fP object for the column. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B __invert__() .INDENT 7.0 .TP .B Returns a \fI\%Negated\fP wrapper for the column. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Column(source, name) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBsource\fP (\fISource\fP) \-\- Source for column. .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Column name. .UNINDENT .UNINDENT .sp Column on a table or a column returned by a sub\-query. .UNINDENT .INDENT 0.0 .TP .B class Alias(node, alias) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBnode\fP (\fINode\fP) \-\- a column\-like object. .IP \(bu 2 \fBalias\fP (\fIstr\fP) \-\- alias to assign to column. .UNINDENT .UNINDENT .sp Create a named alias for the given column\-like object. .INDENT 7.0 .TP .B alias([alias=None]) .INDENT 7.0 .TP .B Parameters \fBalias\fP (\fIstr\fP) \-\- new name (or None) for aliased column. .UNINDENT .sp Create a new \fI\%Alias\fP for the aliased column\-like object. If the new alias is \fBNone\fP, then the original column\-like object is returned. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Negated(node) Represents a negated column\-like object. .UNINDENT .INDENT 0.0 .TP .B class Value(value[, converterNone[, unpack=True]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBvalue\fP \-\- Python object or scalar value. .IP \(bu 2 \fBconverter\fP \-\- Function used to convert value into type the database understands. .IP \(bu 2 \fBunpack\fP (\fIbool\fP) \-\- Whether lists or tuples should be unpacked into a list of values or treated as\-is. .UNINDENT .UNINDENT .sp Value to be used in a parameterized query. It is the responsibility of the caller to ensure that the value passed in can be adapted to a type the database driver understands. .UNINDENT .INDENT 0.0 .TP .B AsIs(value) Represents a \fI\%Value\fP that is treated as\-is, and passed directly back to the database driver. This may be useful if you are using database extensions that accept native Python data\-types and you do not wish Peewee to impose any handling of the values. .UNINDENT .INDENT 0.0 .TP .B class Cast(node, cast) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBnode\fP \-\- A column\-like object. .IP \(bu 2 \fBcast\fP (\fIstr\fP) \-\- Type to cast to. .UNINDENT .UNINDENT .sp Represents a \fBCAST( AS )\fP expression. .UNINDENT .INDENT 0.0 .TP .B class Ordering(node, direction[, collation=None[, nulls=None]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBnode\fP \-\- A column\-like object. .IP \(bu 2 \fBdirection\fP (\fIstr\fP) \-\- ASC or DESC .IP \(bu 2 \fBcollation\fP (\fIstr\fP) \-\- Collation name to use for sorting. .IP \(bu 2 \fBnulls\fP (\fIstr\fP) \-\- Sort nulls (FIRST or LAST). .UNINDENT .UNINDENT .sp Represent ordering by a column\-like object. .sp Postgresql supports a non\-standard clause ("NULLS FIRST/LAST"). Peewee will automatically use an equivalent \fBCASE\fP statement for databases that do not support this (Sqlite / MySQL). .INDENT 7.0 .TP .B collate([collation=None]) .INDENT 7.0 .TP .B Parameters \fBcollation\fP (\fIstr\fP) \-\- Collation name to use for sorting. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Asc(node[, collation=None[, nulls=None]]) Short\-hand for instantiating an ascending \fI\%Ordering\fP object. .UNINDENT .INDENT 0.0 .TP .B Desc(node[, collation=None[, nulls=None]]) Short\-hand for instantiating an descending \fI\%Ordering\fP object. .UNINDENT .INDENT 0.0 .TP .B class Expression(lhs, op, rhs[, flat=True]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBlhs\fP \-\- Left\-hand side. .IP \(bu 2 \fBop\fP \-\- Operation. .IP \(bu 2 \fBrhs\fP \-\- Right\-hand side. .IP \(bu 2 \fBflat\fP (\fIbool\fP) \-\- Whether to wrap expression in parentheses. .UNINDENT .UNINDENT .sp Represent a binary expression of the form (lhs op rhs), e.g. (foo + 1). .UNINDENT .INDENT 0.0 .TP .B class Entity(*path) .INDENT 7.0 .TP .B Parameters \fBpath\fP \-\- Components that make up the dotted\-path of the entity name. .UNINDENT .sp Represent a quoted entity in a query, such as a table, column, alias. The name may consist of multiple components, e.g. "a_table"."column_name". .INDENT 7.0 .TP .B __getattr__(self, attr) Factory method for creating sub\-entities. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class SQL(sql[, params=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBsql\fP (\fIstr\fP) \-\- SQL query string. .IP \(bu 2 \fBparams\fP (\fItuple\fP) \-\- Parameters for query (optional). .UNINDENT .UNINDENT .sp Represent a parameterized SQL query or query\-fragment. .UNINDENT .INDENT 0.0 .TP .B Check(constraint) .INDENT 7.0 .TP .B Parameters \fBconstraint\fP (\fIstr\fP) \-\- Constraint SQL. .UNINDENT .sp Represent a CHECK constraint. .UNINDENT .INDENT 0.0 .TP .B class Function(name, arguments[, coerce=True[, python_value=None]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Function name. .IP \(bu 2 \fBarguments\fP (\fItuple\fP) \-\- Arguments to function. .IP \(bu 2 \fBcoerce\fP (\fIbool\fP) \-\- Whether to coerce the function result to a particular data\-type when reading function return values from the cursor. .IP \(bu 2 \fBpython_value\fP (\fIcallable\fP) \-\- Function to use for converting the return value from the cursor. .UNINDENT .UNINDENT .sp Represent an arbitrary SQL function call. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Rather than instantiating this class directly, it is recommended to use the \fBfn\fP helper. .UNINDENT .UNINDENT .sp Example of using \fBfn\fP to call an arbitrary SQL function: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Query users and count of tweets authored. query = (User .select(User.username, fn.COUNT(Tweet.id).alias(\(aqct\(aq)) .join(Tweet, JOIN.LEFT_OUTER, on=(User.id == Tweet.user_id)) .group_by(User.username) .order_by(fn.COUNT(Tweet.id).desc())) .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .TP .B over([partition_by=None[, order_by=None[, start=None[, end=None[, window=None[, exclude=None]]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBpartition_by\fP (\fIlist\fP) \-\- List of columns to partition by. .IP \(bu 2 \fBorder_by\fP (\fIlist\fP) \-\- List of columns / expressions to order window by. .IP \(bu 2 \fBstart\fP \-\- A \fI\%SQL\fP instance or a string expressing the start of the window range. .IP \(bu 2 \fBend\fP \-\- A \fI\%SQL\fP instance or a string expressing the end of the window range. .IP \(bu 2 \fBframe_type\fP (\fIstr\fP) \-\- \fBWindow.RANGE\fP, \fBWindow.ROWS\fP or \fBWindow.GROUPS\fP\&. .IP \(bu 2 \fBwindow\fP (\fIWindow\fP) \-\- A \fI\%Window\fP instance. .IP \(bu 2 \fBexclude\fP \-\- Frame exclusion, one of \fBWindow.CURRENT_ROW\fP, \fBWindow.GROUP\fP, \fBWindow.TIES\fP or \fBWindow.NO_OTHERS\fP\&. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 For an in\-depth guide to using window functions with Peewee, see the window\-functions section. .UNINDENT .UNINDENT .sp Examples: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Using a simple partition on a single column. query = (Sample .select( Sample.counter, Sample.value, fn.AVG(Sample.value).over([Sample.counter])) .order_by(Sample.counter)) # Equivalent example Using a Window() instance instead. window = Window(partition_by=[Sample.counter]) query = (Sample .select( Sample.counter, Sample.value, fn.AVG(Sample.value).over(window)) .window(window) # Note call to ".window()" .order_by(Sample.counter)) # Example using bounded window. query = (Sample .select(Sample.value, fn.SUM(Sample.value).over( partition_by=[Sample.counter], start=Window.CURRENT_ROW, # current row end=Window.following())) # unbounded following .order_by(Sample.id)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B filter(where) .INDENT 7.0 .TP .B Parameters \fBwhere\fP \-\- Expression for filtering aggregate. .UNINDENT .sp Add a \fBFILTER (WHERE...)\fP clause to an aggregate function. The where expression is evaluated to determine which rows are fed to the aggregate function. This SQL feature is supported for Postgres and SQLite. .UNINDENT .INDENT 7.0 .TP .B coerce([coerce=True]) .INDENT 7.0 .TP .B Parameters \fBcoerce\fP (\fIbool\fP) \-\- Whether to attempt to coerce function\-call result to a Python data\-type. .UNINDENT .sp When coerce is \fBTrue\fP, the target data\-type is inferred using several heuristics. Read the source for \fBBaseModelCursorWrapper._initialize_columns\fP method to see how this works. .UNINDENT .INDENT 7.0 .TP .B python_value([func=None]) .INDENT 7.0 .TP .B Parameters \fBpython_value\fP (\fIcallable\fP) \-\- Function to use for converting the return value from the cursor. .UNINDENT .sp Specify a particular function to use when converting values returned by the database cursor. For example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Get user and a list of their tweet IDs. The tweet IDs are # returned as a comma\-separated string by the db, so we\(aqll split # the result string and convert the values to python ints. tweet_ids = (fn .GROUP_CONCAT(Tweet.id) .python_value(lambda idlist: [int(i) for i in idlist])) query = (User .select(User.username, tweet_ids.alias(\(aqtweet_ids\(aq)) .group_by(User.username)) for user in query: print(user.username, user.tweet_ids) # e.g., # huey [1, 4, 5, 7] # mickey [2, 3, 6] # zaizee [] .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B fn() The \fI\%fn()\fP helper is actually an instance of \fI\%Function\fP that implements a \fB__getattr__\fP hook to provide a nice API for calling SQL functions. .sp To create a node representative of a SQL function call, use the function name as an attribute on \fBfn\fP and then provide the arguments as you would if calling a Python function: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # List users and the number of tweets they have authored, # from highest\-to\-lowest: sql_count = fn.COUNT(Tweet.id) query = (User .select(User, sql_count.alias(\(aqcount\(aq)) .join(Tweet, JOIN.LEFT_OUTER) .group_by(User) .order_by(sql_count.desc())) # Get the timestamp of the most recent tweet: query = Tweet.select(fn.MAX(Tweet.timestamp)) max_timestamp = query.scalar() # Retrieve scalar result from query. .ft P .fi .UNINDENT .UNINDENT .sp Function calls can, like anything else, be composed and nested: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Get users whose username begins with "A" or "a": a_users = User.select().where(fn.LOWER(fn.SUBSTR(User.username, 1, 1)) == \(aqa\(aq) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Window([partition_by=None[, order_by=None[, start=None[, end=None[, frame_type=None[, extends=None[, exclude=None[, alias=None]]]]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBpartition_by\fP (\fIlist\fP) \-\- List of columns to partition by. .IP \(bu 2 \fBorder_by\fP (\fIlist\fP) \-\- List of columns to order by. .IP \(bu 2 \fBstart\fP \-\- A \fI\%SQL\fP instance or a string expressing the start of the window range. .IP \(bu 2 \fBend\fP \-\- A \fI\%SQL\fP instance or a string expressing the end of the window range. .IP \(bu 2 \fBframe_type\fP (\fIstr\fP) \-\- \fBWindow.RANGE\fP, \fBWindow.ROWS\fP or \fBWindow.GROUPS\fP\&. .IP \(bu 2 \fBextends\fP \-\- A \fI\%Window\fP definition to extend. Alternately, you may specify the window\(aqs alias instead. .IP \(bu 2 \fBexclude\fP \-\- Frame exclusion, one of \fBWindow.CURRENT_ROW\fP, \fBWindow.GROUP\fP, \fBWindow.TIES\fP or \fBWindow.NO_OTHERS\fP\&. .IP \(bu 2 \fBalias\fP (\fIstr\fP) \-\- Alias for the window. .UNINDENT .UNINDENT .sp Represent a WINDOW clause. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 For an in\-depth guide to using window functions with Peewee, see the window\-functions section. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B RANGE .UNINDENT .INDENT 7.0 .TP .B ROWS .UNINDENT .INDENT 7.0 .TP .B GROUPS Specify the window \fBframe_type\fP\&. See window\-frame\-types\&. .UNINDENT .INDENT 7.0 .TP .B CURRENT_ROW Reference to current row for use in start/end clause or the frame exclusion parameter. .UNINDENT .INDENT 7.0 .TP .B NO_OTHERS .UNINDENT .INDENT 7.0 .TP .B GROUP .UNINDENT .INDENT 7.0 .TP .B TIES Specify the window frame exclusion parameter. .UNINDENT .INDENT 7.0 .TP .B static preceding([value=None]) .INDENT 7.0 .TP .B Parameters \fBvalue\fP \-\- Number of rows preceding. If \fBNone\fP is UNBOUNDED. .UNINDENT .sp Convenience method for generating SQL suitable for passing in as the \fBstart\fP parameter for a window range. .UNINDENT .INDENT 7.0 .TP .B static following([value=None]) .INDENT 7.0 .TP .B Parameters \fBvalue\fP \-\- Number of rows following. If \fBNone\fP is UNBOUNDED. .UNINDENT .sp Convenience method for generating SQL suitable for passing in as the \fBend\fP parameter for a window range. .UNINDENT .INDENT 7.0 .TP .B as_rows() .UNINDENT .INDENT 7.0 .TP .B as_range() .UNINDENT .INDENT 7.0 .TP .B as_groups() Specify the frame type. .UNINDENT .INDENT 7.0 .TP .B extends([window=None]) .INDENT 7.0 .TP .B Parameters \fBwindow\fP (\fIWindow\fP) \-\- A \fI\%Window\fP definition to extend. Alternately, you may specify the window\(aqs alias instead. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B exclude([frame_exclusion=None]) .INDENT 7.0 .TP .B Parameters \fBframe_exclusion\fP \-\- Frame exclusion, one of \fBWindow.CURRENT_ROW\fP, \fBWindow.GROUP\fP, \fBWindow.TIES\fP or \fBWindow.NO_OTHERS\fP\&. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B alias([alias=None]) .INDENT 7.0 .TP .B Parameters \fBalias\fP (\fIstr\fP) \-\- Alias to use for window. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Case(predicate, expression_tuples[, default=None]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBpredicate\fP \-\- Predicate for CASE query (optional). .IP \(bu 2 \fBexpression_tuples\fP \-\- One or more cases to evaluate. .IP \(bu 2 \fBdefault\fP \-\- Default value (optional). .UNINDENT .TP .B Returns Representation of CASE statement. .UNINDENT .sp Examples: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C Number = Table(\(aqnumbers\(aq, (\(aqval\(aq,)) num_as_str = Case(Number.val, ( (1, \(aqone\(aq), (2, \(aqtwo\(aq), (3, \(aqthree\(aq)), \(aqa lot\(aq) query = Number.select(Number.val, num_as_str.alias(\(aqnum_str\(aq)) # The above is equivalent to: # SELECT "val", # CASE "val" # WHEN 1 THEN \(aqone\(aq # WHEN 2 THEN \(aqtwo\(aq # WHEN 3 THEN \(aqthree\(aq # ELSE \(aqa lot\(aq END AS "num_str" # FROM "numbers" num_as_str = Case(None, ( (Number.val == 1, \(aqone\(aq), (Number.val == 2, \(aqtwo\(aq), (Number.val == 3, \(aqthree\(aq)), \(aqa lot\(aq) query = Number.select(Number.val, num_as_str.alias(\(aqnum_str\(aq)) # The above is equivalent to: # SELECT "val", # CASE # WHEN "val" = 1 THEN \(aqone\(aq # WHEN "val" = 2 THEN \(aqtwo\(aq # WHEN "val" = 3 THEN \(aqthree\(aq # ELSE \(aqa lot\(aq END AS "num_str" # FROM "numbers" .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class NodeList(nodes[, glue=\(aq \(aq[, parens=False]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBnodes\fP (\fIlist\fP) \-\- Zero or more nodes. .IP \(bu 2 \fBglue\fP (\fIstr\fP) \-\- How to join the nodes when converting to SQL. .IP \(bu 2 \fBparens\fP (\fIbool\fP) \-\- Whether to wrap the resulting SQL in parentheses. .UNINDENT .UNINDENT .sp Represent a list of nodes, a multi\-part clause, a list of parameters, etc. .UNINDENT .INDENT 0.0 .TP .B CommaNodeList(nodes) .INDENT 7.0 .TP .B Parameters \fBnodes\fP (\fIlist\fP) \-\- Zero or more nodes. .TP .B Returns a \fI\%NodeList\fP .UNINDENT .sp Represent a list of nodes joined by commas. .UNINDENT .INDENT 0.0 .TP .B EnclosedNodeList(nodes) .INDENT 7.0 .TP .B Parameters \fBnodes\fP (\fIlist\fP) \-\- Zero or more nodes. .TP .B Returns a \fI\%NodeList\fP .UNINDENT .sp Represent a list of nodes joined by commas and wrapped in parentheses. .UNINDENT .INDENT 0.0 .TP .B class DQ(**query) .INDENT 7.0 .TP .B Parameters \fBquery\fP \-\- Arbitrary filter expressions using Django\-style lookups. .UNINDENT .sp Represent a composable Django\-style filter expression suitable for use with the \fI\%Model.filter()\fP or \fI\%ModelSelect.filter()\fP methods. .UNINDENT .INDENT 0.0 .TP .B class Tuple(*args) Represent a SQL \fI\%row value\fP\&. Row\-values are supported by most databases. .UNINDENT .INDENT 0.0 .TP .B class OnConflict([action=None[, update=None[, preserve=None[, where=None[, conflict_target=None[, conflict_where=None[, conflict_constraint=None]]]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBaction\fP (\fIstr\fP) \-\- Action to take when resolving conflict. .IP \(bu 2 \fBupdate\fP \-\- A dictionary mapping column to new value. .IP \(bu 2 \fBpreserve\fP \-\- A list of columns whose values should be preserved from the original INSERT. See also \fI\%EXCLUDED\fP\&. .IP \(bu 2 \fBwhere\fP \-\- Expression to restrict the conflict resolution. .IP \(bu 2 \fBconflict_target\fP \-\- Column(s) that comprise the constraint. .IP \(bu 2 \fBconflict_where\fP \-\- Expressions needed to match the constraint target if it is a partial index (index with a WHERE clause). .IP \(bu 2 \fBconflict_constraint\fP (\fIstr\fP) \-\- Name of constraint to use for conflict resolution. Currently only supported by Postgres. .UNINDENT .UNINDENT .sp Represent a conflict resolution clause for a data\-modification query. .sp Depending on the database\-driver being used, one or more of the above parameters may be required. .INDENT 7.0 .TP .B preserve(*columns) .INDENT 7.0 .TP .B Parameters \fBcolumns\fP \-\- Columns whose values should be preserved. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B update([_data=None[, **kwargs]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fB_data\fP (\fIdict\fP) \-\- Dictionary mapping column to new value. .IP \(bu 2 \fBkwargs\fP \-\- Dictionary mapping column name to new value. .UNINDENT .UNINDENT .sp The \fBupdate()\fP method supports being called with either a dictionary of column\-to\-value, \fBor\fP keyword arguments representing the same. .UNINDENT .INDENT 7.0 .TP .B where(*expressions) .INDENT 7.0 .TP .B Parameters \fBexpressions\fP \-\- Expressions that restrict the action of the conflict resolution clause. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B conflict_target(*constraints) .INDENT 7.0 .TP .B Parameters \fBconstraints\fP \-\- Column(s) to use as target for conflict resolution. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B conflict_where(*expressions) .INDENT 7.0 .TP .B Parameters \fBexpressions\fP \-\- Expressions that match the conflict target index, in the case the conflict target is a partial index. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B conflict_constraint(constraint) .INDENT 7.0 .TP .B Parameters \fBconstraint\fP (\fIstr\fP) \-\- Name of constraints to use as target for conflict resolution. Currently only supported by Postgres. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class EXCLUDED Helper object that exposes the \fBEXCLUDED\fP namespace that is used with \fBINSERT ... ON CONFLICT\fP to reference values in the conflicting data. This is a "magic" helper, such that one uses it by accessing attributes on it that correspond to a particular column. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class KV(Model): key = CharField(unique=True) value = IntegerField() # Create one row. KV.create(key=\(aqk1\(aq, value=1) # Demonstrate usage of EXCLUDED. # Here we will attempt to insert a new value for a given key. If that # key already exists, then we will update its value with the *sum* of its # original value and the value we attempted to insert \-\- provided that # the new value is larger than the original value. query = (KV.insert(key=\(aqk1\(aq, value=10) .on_conflict(conflict_target=[KV.key], update={KV.value: KV.value + EXCLUDED.value}, where=(EXCLUDED.value > KV.value))) # Executing the above query will result in the following data being # present in the "kv" table: # (key=\(aqk1\(aq, value=11) query.execute() # If we attempted to execute the query *again*, then nothing would be # updated, as the new value (10) is now less than the value in the # original row (11). .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class BaseQuery The parent class from which all other query classes are derived. While you will not deal with \fI\%BaseQuery\fP directly in your code, it implements some methods that are common across all query types. .INDENT 7.0 .TP .B default_row_type = ROW.DICT .UNINDENT .INDENT 7.0 .TP .B bind([database=None]) .INDENT 7.0 .TP .B Parameters \fBdatabase\fP (\fIDatabase\fP) \-\- Database to execute query against. .UNINDENT .sp Bind the query to the given database for execution. .UNINDENT .INDENT 7.0 .TP .B dicts([as_dict=True]) .INDENT 7.0 .TP .B Parameters \fBas_dict\fP (\fIbool\fP) \-\- Specify whether to return rows as dictionaries. .UNINDENT .sp Return rows as dictionaries. .UNINDENT .INDENT 7.0 .TP .B tuples([as_tuples=True]) .INDENT 7.0 .TP .B Parameters \fBas_tuple\fP (\fIbool\fP) \-\- Specify whether to return rows as tuples. .UNINDENT .sp Return rows as tuples. .UNINDENT .INDENT 7.0 .TP .B namedtuples([as_namedtuple=True]) .INDENT 7.0 .TP .B Parameters \fBas_namedtuple\fP (\fIbool\fP) \-\- Specify whether to return rows as named tuples. .UNINDENT .sp Return rows as named tuples. .UNINDENT .INDENT 7.0 .TP .B objects([constructor=None]) .INDENT 7.0 .TP .B Parameters \fBconstructor\fP \-\- Function that accepts row dict and returns an arbitrary object. .UNINDENT .sp Return rows as arbitrary objects using the given constructor. .UNINDENT .INDENT 7.0 .TP .B sql() .INDENT 7.0 .TP .B Returns A 2\-tuple consisting of the query\(aqs SQL and parameters. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B execute(database) .INDENT 7.0 .TP .B Parameters \fBdatabase\fP (\fIDatabase\fP) \-\- Database to execute query against. Not required if query was previously bound to a database. .UNINDENT .sp Execute the query and return result (depends on type of query being executed). For example, select queries the return result will be an iterator over the query results. .UNINDENT .INDENT 7.0 .TP .B iterator([database=None]) .INDENT 7.0 .TP .B Parameters \fBdatabase\fP (\fIDatabase\fP) \-\- Database to execute query against. Not required if query was previously bound to a database. .UNINDENT .sp Execute the query and return an iterator over the result\-set. For large result\-sets this method is preferable as rows are not cached in\-memory during iteration. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 .INDENT 0.0 .INDENT 3.5 Because rows are not cached, the query may only be iterated over once. Subsequent iterations will return empty result\-sets as the cursor will have been consumed. .UNINDENT .UNINDENT .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = StatTbl.select().order_by(StatTbl.timestamp).tuples() for row in query.iterator(db): process_row(row) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B __iter__() Execute the query and return an iterator over the result\-set. .sp Unlike \fI\%iterator()\fP, this method will cause rows to be cached in order to allow efficient iteration, indexing and slicing. .UNINDENT .INDENT 7.0 .TP .B __getitem__(value) .INDENT 7.0 .TP .B Parameters \fBvalue\fP \-\- Either an integer index or a slice. .UNINDENT .sp Retrieve a row or range of rows from the result\-set. .UNINDENT .INDENT 7.0 .TP .B __len__() Return the number of rows in the result\-set. .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 This does not issue a \fBCOUNT()\fP query. Instead, the result\-set is loaded as it would be during normal iteration, and the length is determined from the size of the result set. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class RawQuery([sql=None[, params=None[, **kwargs]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBsql\fP (\fIstr\fP) \-\- SQL query. .IP \(bu 2 \fBparams\fP (\fItuple\fP) \-\- Parameters (optional). .UNINDENT .UNINDENT .sp Create a query by directly specifying the SQL to execute. .UNINDENT .INDENT 0.0 .TP .B class Query([where=None[, order_by=None[, limit=None[, offset=None[, **kwargs]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBwhere\fP \-\- Representation of WHERE clause. .IP \(bu 2 \fBorder_by\fP (\fItuple\fP) \-\- Columns or values to order by. .IP \(bu 2 \fBlimit\fP (\fIint\fP) \-\- Value of LIMIT clause. .IP \(bu 2 \fBoffset\fP (\fIint\fP) \-\- Value of OFFSET clause. .UNINDENT .UNINDENT .sp Base\-class for queries that support method\-chaining APIs. .INDENT 7.0 .TP .B with_cte(*cte_list) .INDENT 7.0 .TP .B Parameters \fBcte_list\fP \-\- zero or more \fI\%CTE\fP objects. .UNINDENT .sp Include the given common\-table expressions in the query. Any previously specified CTEs will be overwritten. For examples of common\-table expressions, see cte\&. .UNINDENT .INDENT 7.0 .TP .B where(*expressions) .INDENT 7.0 .TP .B Parameters \fBexpressions\fP \-\- zero or more expressions to include in the WHERE clause. .UNINDENT .sp Include the given expressions in the WHERE clause of the query. The expressions will be AND\-ed together with any previously\-specified WHERE expressions. .sp Example selection users where the username is equal to \(aqsomebody\(aq: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C sq = User.select().where(User.username == \(aqsomebody\(aq) .ft P .fi .UNINDENT .UNINDENT .sp Example selecting tweets made by users who are either editors or administrators: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C sq = Tweet.select().join(User).where( (User.is_editor == True) | (User.is_admin == True)) .ft P .fi .UNINDENT .UNINDENT .sp Example of deleting tweets by users who are no longer active: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C inactive_users = User.select().where(User.active == False) dq = (Tweet .delete() .where(Tweet.user.in_(inactive_users))) dq.execute() # Return number of tweets deleted. .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 \fI\%where()\fP calls are chainable. Multiple calls will be "AND"\-ed together. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B orwhere(*expressions) .INDENT 7.0 .TP .B Parameters \fBexpressions\fP \-\- zero or more expressions to include in the WHERE clause. .UNINDENT .sp Include the given expressions in the WHERE clause of the query. This method is the same as the \fI\%Query.where()\fP method, except that the expressions will be OR\-ed together with any previously\-specified WHERE expressions. .UNINDENT .INDENT 7.0 .TP .B order_by(*values) .INDENT 7.0 .TP .B Parameters \fBvalues\fP \-\- zero or more Column\-like objects to order by. .UNINDENT .sp Define the ORDER BY clause. Any previously\-specified values will be overwritten. .UNINDENT .INDENT 7.0 .TP .B order_by_extend(*values) .INDENT 7.0 .TP .B Parameters \fBvalues\fP \-\- zero or more Column\-like objects to order by. .UNINDENT .sp Extend any previously\-specified ORDER BY clause with the given values. .UNINDENT .INDENT 7.0 .TP .B limit([value=None]) .INDENT 7.0 .TP .B Parameters \fBvalue\fP (\fIint\fP) \-\- specify value for LIMIT clause. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B offset([value=None]) .INDENT 7.0 .TP .B Parameters \fBvalue\fP (\fIint\fP) \-\- specify value for OFFSET clause. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B paginate(page[, paginate_by=20]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBpage\fP (\fIint\fP) \-\- Page number of results (starting from 1). .IP \(bu 2 \fBpaginate_by\fP (\fIint\fP) \-\- Rows\-per\-page. .UNINDENT .UNINDENT .sp Convenience method for specifying the LIMIT and OFFSET in a more intuitive way. .sp This feature is designed with web\-site pagination in mind, so the first page starts with \fBpage=1\fP\&. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class SelectQuery Select query helper\-class that implements operator\-overloads for creating compound queries. .INDENT 7.0 .TP .B cte(name[, recursive=False[, columns=None]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Alias for common table expression. .IP \(bu 2 \fBrecursive\fP (\fIbool\fP) \-\- Will this be a recursive CTE? .IP \(bu 2 \fBcolumns\fP (\fIlist\fP) \-\- List of column names (as strings). .UNINDENT .UNINDENT .sp Indicate that a query will be used as a common table expression. For example, if we are modelling a category tree and are using a parent\-link foreign key, we can retrieve all categories and their absolute depths using a recursive CTE: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class Category(Model): name = TextField() parent = ForeignKeyField(\(aqself\(aq, backref=\(aqchildren\(aq, null=True) # The base case of our recursive CTE will be categories that are at # the root level \-\- in other words, categories without parents. roots = (Category .select(Category.name, Value(0).alias(\(aqlevel\(aq)) .where(Category.parent.is_null()) .cte(name=\(aqroots\(aq, recursive=True)) # The recursive term will select the category name and increment # the depth, joining on the base term so that the recursive term # consists of all children of the base category. RTerm = Category.alias() recursive = (RTerm .select(RTerm.name, (roots.c.level + 1).alias(\(aqlevel\(aq)) .join(roots, on=(RTerm.parent == roots.c.id))) # Express UNION ALL . cte = roots.union_all(recursive) # Select name and level from the recursive CTE. query = (cte .select_from(cte.c.name, cte.c.level) .order_by(cte.c.name)) for category in query: print(category.name, category.level) .ft P .fi .UNINDENT .UNINDENT .sp For more examples of CTEs, see cte\&. .UNINDENT .INDENT 7.0 .TP .B select_from(*columns) .INDENT 7.0 .TP .B Parameters \fBcolumns\fP \-\- one or more columns to select from the inner query. .TP .B Returns a new query that wraps the calling query. .UNINDENT .sp Create a new query that wraps the current (calling) query. For example, suppose you have a simple \fBUNION\fP query, and need to apply an aggregation on the union result\-set. To do this, you need to write something like: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C SELECT "u"."owner", COUNT("u"."id") AS "ct" FROM ( SELECT "id", "owner", ... FROM "cars" UNION SELECT "id", "owner", ... FROM "motorcycles" UNION SELECT "id", "owner", ... FROM "boats") AS "u" GROUP BY "u"."owner" .ft P .fi .UNINDENT .UNINDENT .sp The \fI\%select_from()\fP method is designed to simplify constructing this type of query. .sp Example peewee code: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class Car(Model): owner = ForeignKeyField(Owner, backref=\(aqcars\(aq) # ... car\-specific fields, etc ... class Motorcycle(Model): owner = ForeignKeyField(Owner, backref=\(aqmotorcycles\(aq) # ... motorcycle\-specific fields, etc ... class Boat(Model): owner = ForeignKeyField(Owner, backref=\(aqboats\(aq) # ... boat\-specific fields, etc ... cars = Car.select(Car.owner) motorcycles = Motorcycle.select(Motorcycle.owner) boats = Boat.select(Boat.owner) union = cars | motorcycles | boats query = (union .select_from(union.c.owner, fn.COUNT(union.c.id)) .group_by(union.c.owner)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B union_all(dest) Create a UNION ALL query with \fBdest\fP\&. .UNINDENT .INDENT 7.0 .TP .B __add__(dest) Create a UNION ALL query with \fBdest\fP\&. .UNINDENT .INDENT 7.0 .TP .B union(dest) Create a UNION query with \fBdest\fP\&. .UNINDENT .INDENT 7.0 .TP .B __or__(dest) Create a UNION query with \fBdest\fP\&. .UNINDENT .INDENT 7.0 .TP .B intersect(dest) Create an INTERSECT query with \fBdest\fP\&. .UNINDENT .INDENT 7.0 .TP .B __and__(dest) Create an INTERSECT query with \fBdest\fP\&. .UNINDENT .INDENT 7.0 .TP .B except_(dest) Create an EXCEPT query with \fBdest\fP\&. Note that the method name has a trailing "_" character since \fBexcept\fP is a Python reserved word. .UNINDENT .INDENT 7.0 .TP .B __sub__(dest) Create an EXCEPT query with \fBdest\fP\&. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class SelectBase Base\-class for \fI\%Select\fP and \fBCompoundSelect\fP queries. .INDENT 7.0 .TP .B peek(database[, n=1]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdatabase\fP (\fIDatabase\fP) \-\- database to execute query against. .IP \(bu 2 \fBn\fP (\fIint\fP) \-\- Number of rows to return. .UNINDENT .TP .B Returns A single row if n = 1, else a list of rows. .UNINDENT .sp Execute the query and return the given number of rows from the start of the cursor. This function may be called multiple times safely, and will always return the first N rows of results. .UNINDENT .INDENT 7.0 .TP .B first(database[, n=1]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdatabase\fP (\fIDatabase\fP) \-\- database to execute query against. .IP \(bu 2 \fBn\fP (\fIint\fP) \-\- Number of rows to return. .UNINDENT .TP .B Returns A single row if n = 1, else a list of rows. .UNINDENT .sp Like the \fI\%peek()\fP method, except a \fBLIMIT\fP is applied to the query to ensure that only \fBn\fP rows are returned. Multiple calls for the same value of \fBn\fP will not result in multiple executions. .UNINDENT .INDENT 7.0 .TP .B scalar(database[, as_tuple=False]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdatabase\fP (\fIDatabase\fP) \-\- database to execute query against. .IP \(bu 2 \fBas_tuple\fP (\fIbool\fP) \-\- Return the result as a tuple? .UNINDENT .TP .B Returns Single scalar value if \fBas_tuple = False\fP, else row tuple. .UNINDENT .sp Return a scalar value from the first row of results. If multiple scalar values are anticipated (e.g. multiple aggregations in a single query) then you may specify \fBas_tuple=True\fP to get the row tuple. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C query = Note.select(fn.MAX(Note.timestamp)) max_ts = query.scalar(db) query = Note.select(fn.MAX(Note.timestamp), fn.COUNT(Note.id)) max_ts, n_notes = query.scalar(db, as_tuple=True) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B count(database[, clear_limit=False]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdatabase\fP (\fIDatabase\fP) \-\- database to execute query against. .IP \(bu 2 \fBclear_limit\fP (\fIbool\fP) \-\- Clear any LIMIT clause when counting. .UNINDENT .TP .B Returns Number of rows in the query result\-set. .UNINDENT .sp Return number of rows in the query result\-set. .sp Implemented by running SELECT COUNT(1) FROM (). .UNINDENT .INDENT 7.0 .TP .B exists(database) .INDENT 7.0 .TP .B Parameters \fBdatabase\fP (\fIDatabase\fP) \-\- database to execute query against. .TP .B Returns Whether any results exist for the current query. .UNINDENT .sp Return a boolean indicating whether the current query has any results. .UNINDENT .INDENT 7.0 .TP .B get(database) .INDENT 7.0 .TP .B Parameters \fBdatabase\fP (\fIDatabase\fP) \-\- database to execute query against. .TP .B Returns A single row from the database or \fBNone\fP\&. .UNINDENT .sp Execute the query and return the first row, if it exists. Multiple calls will result in multiple queries being executed. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class CompoundSelectQuery(lhs, op, rhs) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBlhs\fP (\fISelectBase\fP) \-\- A Select or CompoundSelect query. .IP \(bu 2 \fBop\fP (\fIstr\fP) \-\- Operation (e.g. UNION, INTERSECT, EXCEPT). .IP \(bu 2 \fBrhs\fP (\fISelectBase\fP) \-\- A Select or CompoundSelect query. .UNINDENT .UNINDENT .sp Class representing a compound SELECT query. .UNINDENT .INDENT 0.0 .TP .B class Select([from_list=None[, columns=None[, group_by=None[, having=None[, distinct=None[, windows=None[, for_update=None[, for_update_of=None[, for_update_nowait=None[, **kwargs]]]]]]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBfrom_list\fP (\fIlist\fP) \-\- List of sources for FROM clause. .IP \(bu 2 \fBcolumns\fP (\fIlist\fP) \-\- Columns or values to select. .IP \(bu 2 \fBgroup_by\fP (\fIlist\fP) \-\- List of columns or values to group by. .IP \(bu 2 \fBhaving\fP (\fIExpression\fP) \-\- Expression for HAVING clause. .IP \(bu 2 \fBdistinct\fP \-\- Either a boolean or a list of column\-like objects. .IP \(bu 2 \fBwindows\fP (\fIlist\fP) \-\- List of \fI\%Window\fP clauses. .IP \(bu 2 \fBfor_update\fP \-\- Boolean or str indicating if SELECT...FOR UPDATE. .IP \(bu 2 \fBfor_update_of\fP \-\- One or more tables for FOR UPDATE OF clause. .IP \(bu 2 \fBfor_update_nowait\fP (\fIbool\fP) \-\- Specify NOWAIT locking. .UNINDENT .UNINDENT .sp Class representing a SELECT query. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Rather than instantiating this directly, most\-commonly you will use a factory method like \fI\%Table.select()\fP or \fI\%Model.select()\fP\&. .UNINDENT .UNINDENT .sp Methods on the select query can be chained together. .sp Example selecting some user instances from the database. Only the \fBid\fP and \fBusername\fP columns are selected. When iterated, will return instances of the \fBUser\fP model: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C query = User.select(User.id, User.username) for user in query: print(user.username) .ft P .fi .UNINDENT .UNINDENT .sp Example selecting users and additionally the number of tweets made by the user. The \fBUser\fP instances returned will have an additional attribute, \(aqcount\(aq, that corresponds to the number of tweets made: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C query = (User .select(User, fn.COUNT(Tweet.id).alias(\(aqcount\(aq)) .join(Tweet, JOIN.LEFT_OUTER) .group_by(User)) for user in query: print(user.username, \(aqhas tweeted\(aq, user.count, \(aqtimes\(aq) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 While it is possible to instantiate \fI\%Select\fP directly, more commonly you will build the query using the method\-chaining APIs. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B columns(*columns) .INDENT 7.0 .TP .B Parameters \fBcolumns\fP \-\- Zero or more column\-like objects to SELECT. .UNINDENT .sp Specify which columns or column\-like values to SELECT. .UNINDENT .INDENT 7.0 .TP .B select(*columns) .INDENT 7.0 .TP .B Parameters \fBcolumns\fP \-\- Zero or more column\-like objects to SELECT. .UNINDENT .sp Same as \fI\%Select.columns()\fP, provided for backwards\-compatibility. .UNINDENT .INDENT 7.0 .TP .B select_extend(*columns) .INDENT 7.0 .TP .B Parameters \fBcolumns\fP \-\- Zero or more column\-like objects to SELECT. .UNINDENT .sp Extend the current selection with the given columns. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C def get_users(with_count=False): query = User.select() if with_count: query = (query .select_extend(fn.COUNT(Tweet.id).alias(\(aqcount\(aq)) .join(Tweet, JOIN.LEFT_OUTER) .group_by(User)) return query .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B from_(*sources) .INDENT 7.0 .TP .B Parameters \fBsources\fP \-\- Zero or more sources for the FROM clause. .UNINDENT .sp Specify which table\-like objects should be used in the FROM clause. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C User = Table(\(aqusers\(aq) Tweet = Table(\(aqtweets\(aq) query = (User .select(User.c.username, Tweet.c.content) .from_(User, Tweet) .where(User.c.id == Tweet.c.user_id)) for row in query.execute(db): print(row[\(aqusername\(aq], \(aq\->\(aq, row[\(aqcontent\(aq]) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B join(dest[, join_type=\(aqINNER\(aq[, on=None]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdest\fP \-\- A table or table\-like object. .IP \(bu 2 \fBjoin_type\fP (\fIstr\fP) \-\- Type of JOIN, default is "INNER". .IP \(bu 2 \fBon\fP (\fIExpression\fP) \-\- Join predicate. .UNINDENT .UNINDENT .sp Join type may be one of: .INDENT 7.0 .IP \(bu 2 \fBJOIN.INNER\fP .IP \(bu 2 \fBJOIN.LEFT_OUTER\fP .IP \(bu 2 \fBJOIN.RIGHT_OUTER\fP .IP \(bu 2 \fBJOIN.FULL\fP .IP \(bu 2 \fBJOIN.FULL_OUTER\fP .IP \(bu 2 \fBJOIN.CROSS\fP .UNINDENT .sp Express a JOIN: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C User = Table(\(aqusers\(aq, (\(aqid\(aq, \(aqusername\(aq)) Note = Table(\(aqnotes\(aq, (\(aqid\(aq, \(aquser_id\(aq, \(aqcontent\(aq)) query = (Note .select(Note.content, User.username) .join(User, on=(Note.user_id == User.id))) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B group_by(*columns) .INDENT 7.0 .TP .B Parameters \fBvalues\fP \-\- zero or more Column\-like objects to group by. .UNINDENT .sp Define the GROUP BY clause. Any previously\-specified values will be overwritten. .sp Additionally, to specify all columns on a given table, you can pass the table/model object in place of the individual columns. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C query = (User .select(User, fn.Count(Tweet.id).alias(\(aqcount\(aq)) .join(Tweet) .group_by(User)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B group_by_extend(*columns) .INDENT 7.0 .TP .B Parameters \fBvalues\fP \-\- zero or more Column\-like objects to group by. .UNINDENT .sp Extend the GROUP BY clause with the given columns. .UNINDENT .INDENT 7.0 .TP .B having(*expressions) .INDENT 7.0 .TP .B Parameters \fBexpressions\fP \-\- zero or more expressions to include in the HAVING clause. .UNINDENT .sp Include the given expressions in the HAVING clause of the query. The expressions will be AND\-ed together with any previously\-specified HAVING expressions. .UNINDENT .INDENT 7.0 .TP .B distinct(*columns) .INDENT 7.0 .TP .B Parameters \fBcolumns\fP \-\- Zero or more column\-like objects. .UNINDENT .sp Indicate whether this query should use a DISTINCT clause. By specifying a single value of \fBTrue\fP the query will use a simple SELECT DISTINCT. Specifying one or more columns will result in a SELECT DISTINCT ON. .UNINDENT .INDENT 7.0 .TP .B window(*windows) .INDENT 7.0 .TP .B Parameters \fBwindows\fP \-\- zero or more \fI\%Window\fP objects. .UNINDENT .sp Define the WINDOW clause. Any previously\-specified values will be overwritten. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Equivalent example Using a Window() instance instead. window = Window(partition_by=[Sample.counter]) query = (Sample .select( Sample.counter, Sample.value, fn.AVG(Sample.value).over(window)) .window(window) # Note call to ".window()" .order_by(Sample.counter)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B for_update([for_update=True[, of=None[, nowait=None]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBfor_update\fP \-\- Either a boolean or a string indicating the desired expression, e.g. "FOR SHARE". .IP \(bu 2 \fBof\fP \-\- One or more models to restrict locking to. .IP \(bu 2 \fBnowait\fP (\fIbool\fP) \-\- Specify NOWAIT option when locking. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class _WriteQuery(table[, returning=None[, **kwargs]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fITable\fP) \-\- Table to write to. .IP \(bu 2 \fBreturning\fP (\fIlist\fP) \-\- List of columns for RETURNING clause. .UNINDENT .UNINDENT .sp Base\-class for write queries. .INDENT 7.0 .TP .B returning(*returning) .INDENT 7.0 .TP .B Parameters \fBreturning\fP \-\- Zero or more column\-like objects for RETURNING clause .UNINDENT .sp Specify the RETURNING clause of query (if supported by your database). .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C query = (User .insert_many([{\(aqusername\(aq: \(aqfoo\(aq}, {\(aqusername\(aq: \(aqbar\(aq}, {\(aqusername\(aq: \(aqbaz\(aq}]) .returning(User.id, User.username) .namedtuples()) data = query.execute() for row in data: print(\(aqadded:\(aq, row.username, \(aqwith id=\(aq, row.id) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Update(table[, update=None[, **kwargs]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fITable\fP) \-\- Table to update. .IP \(bu 2 \fBupdate\fP (\fIdict\fP) \-\- Data to update. .UNINDENT .UNINDENT .sp Class representing an UPDATE query. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C PageView = Table(\(aqpage_views\(aq) query = (PageView .update({PageView.c.page_views: PageView.c.page_views + 1}) .where(PageView.c.url == url)) query.execute(database) .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .TP .B from_(*sources) .INDENT 7.0 .TP .B Parameters \fBsources\fP (\fISource\fP) \-\- one or more \fBTable\fP, \fI\%Model\fP, query, or \fI\%ValuesList\fP to join with. .UNINDENT .sp Specify additional tables to join with using the UPDATE ... FROM syntax, which is supported by Postgres. The \fI\%Postgres documentation\fP provides additional detail, but to summarize: .INDENT 7.0 .INDENT 3.5 When a \fBFROM\fP clause is present, what essentially happens is that the target table is joined to the tables mentioned in the from_list, and each output row of the join represents an update operation for the target table. When using \fBFROM\fP you should ensure that the join produces at most one output row for each row to be modified. .UNINDENT .UNINDENT .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Update multiple users in a single query. data = [(\(aqhuey\(aq, True), (\(aqmickey\(aq, False), (\(aqzaizee\(aq, True)] vl = ValuesList(data, columns=(\(aqusername\(aq, \(aqis_admin\(aq), alias=\(aqvl\(aq) # Here we\(aqll update the "is_admin" status of the above users, # "joining" the VALUES() on the "username" column. query = (User .update(is_admin=vl.c.is_admin) .from_(vl) .where(User.username == vl.c.username)) .ft P .fi .UNINDENT .UNINDENT .sp The above query produces the following SQL: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C UPDATE "users" SET "is_admin" = "vl"."is_admin" FROM ( VALUES (\(aqhuey\(aq, t), (\(aqmickey\(aq, f), (\(aqzaizee\(aq, t)) AS "vl"("username", "is_admin") WHERE ("users"."username" = "vl"."username") .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Insert(table[, insert=None[, columns=None[, on_conflict=None[, **kwargs]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fITable\fP) \-\- Table to INSERT data into. .IP \(bu 2 \fBinsert\fP \-\- Either a dict, a list, or a query. .IP \(bu 2 \fBcolumns\fP (\fIlist\fP) \-\- List of columns when \fBinsert\fP is a list or query. .IP \(bu 2 \fBon_conflict\fP \-\- Conflict resolution strategy. .UNINDENT .UNINDENT .sp Class representing an INSERT query. .INDENT 7.0 .TP .B on_conflict_ignore([ignore=True]) .INDENT 7.0 .TP .B Parameters \fBignore\fP (\fIbool\fP) \-\- Whether to add ON CONFLICT IGNORE clause. .UNINDENT .sp Specify IGNORE conflict resolution strategy. .UNINDENT .INDENT 7.0 .TP .B on_conflict_replace([replace=True]) .INDENT 7.0 .TP .B Parameters \fBreplace\fP (\fIbool\fP) \-\- Whether to add ON CONFLICT REPLACE clause. .UNINDENT .sp Specify REPLACE conflict resolution strategy. .UNINDENT .INDENT 7.0 .TP .B on_conflict([action=None[, update=None[, preserve=None[, where=None[, conflict_target=None[, conflict_where=None[, conflict_constraint=None]]]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBaction\fP (\fIstr\fP) \-\- Action to take when resolving conflict. If blank, action is assumed to be "update". .IP \(bu 2 \fBupdate\fP \-\- A dictionary mapping column to new value. .IP \(bu 2 \fBpreserve\fP \-\- A list of columns whose values should be preserved from the original INSERT. .IP \(bu 2 \fBwhere\fP \-\- Expression to restrict the conflict resolution. .IP \(bu 2 \fBconflict_target\fP \-\- Column(s) that comprise the constraint. .IP \(bu 2 \fBconflict_where\fP \-\- Expressions needed to match the constraint target if it is a partial index (index with a WHERE clause). .IP \(bu 2 \fBconflict_constraint\fP (\fIstr\fP) \-\- Name of constraint to use for conflict resolution. Currently only supported by Postgres. .UNINDENT .UNINDENT .sp Specify the parameters for an \fI\%OnConflict\fP clause to use for conflict resolution. .sp Examples: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class User(Model): username = TextField(unique=True) last_login = DateTimeField(null=True) login_count = IntegerField() def log_user_in(username): now = datetime.datetime.now() # INSERT a new row for the user with the current timestamp and # login count set to 1. If the user already exists, then we # will preserve the last_login value from the "insert()" clause # and atomically increment the login\-count. userid = (User .insert(username=username, last_login=now, login_count=1) .on_conflict( conflict_target=[User.username], preserve=[User.last_login], update={User.login_count: User.login_count + 1}) .execute()) return userid .ft P .fi .UNINDENT .UNINDENT .sp Example using the special \fI\%EXCLUDED\fP namespace: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class KV(Model): key = CharField(unique=True) value = IntegerField() # Create one row. KV.create(key=\(aqk1\(aq, value=1) # Demonstrate usage of EXCLUDED. # Here we will attempt to insert a new value for a given key. If that # key already exists, then we will update its value with the *sum* of its # original value and the value we attempted to insert \-\- provided that # the new value is larger than the original value. query = (KV.insert(key=\(aqk1\(aq, value=10) .on_conflict(conflict_target=[KV.key], update={KV.value: KV.value + EXCLUDED.value}, where=(EXCLUDED.value > KV.value))) # Executing the above query will result in the following data being # present in the "kv" table: # (key=\(aqk1\(aq, value=11) query.execute() # If we attempted to execute the query *again*, then nothing would be # updated, as the new value (10) is now less than the value in the # original row (11). .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Delete Class representing a DELETE query. .UNINDENT .INDENT 0.0 .TP .B class Index(name, table, expressions[, unique=False[, safe=False[, where=None[, using=None]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Index name. .IP \(bu 2 \fBtable\fP (\fITable\fP) \-\- Table to create index on. .IP \(bu 2 \fBexpressions\fP \-\- List of columns to index on (or expressions). .IP \(bu 2 \fBunique\fP (\fIbool\fP) \-\- Whether index is UNIQUE. .IP \(bu 2 \fBsafe\fP (\fIbool\fP) \-\- Whether to add IF NOT EXISTS clause. .IP \(bu 2 \fBwhere\fP (\fIExpression\fP) \-\- Optional WHERE clause for index. .IP \(bu 2 \fBusing\fP (\fIstr\fP) \-\- Index algorithm. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B safe([_safe=True]) .INDENT 7.0 .TP .B Parameters \fB_safe\fP (\fIbool\fP) \-\- Whether to add IF NOT EXISTS clause. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B where(*expressions) .INDENT 7.0 .TP .B Parameters \fBexpressions\fP \-\- zero or more expressions to include in the WHERE clause. .UNINDENT .sp Include the given expressions in the WHERE clause of the index. The expressions will be AND\-ed together with any previously\-specified WHERE expressions. .UNINDENT .INDENT 7.0 .TP .B using([_using=None]) .INDENT 7.0 .TP .B Parameters \fB_using\fP (\fIstr\fP) \-\- Specify index algorithm for USING clause. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class ModelIndex(model, fields[, unique=False[, safe=True[, where=None[, using=None[, name=None]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmodel\fP (\fIModel\fP) \-\- Model class to create index on. .IP \(bu 2 \fBfields\fP (\fIlist\fP) \-\- Fields to index. .IP \(bu 2 \fBunique\fP (\fIbool\fP) \-\- Whether index is UNIQUE. .IP \(bu 2 \fBsafe\fP (\fIbool\fP) \-\- Whether to add IF NOT EXISTS clause. .IP \(bu 2 \fBwhere\fP (\fIExpression\fP) \-\- Optional WHERE clause for index. .IP \(bu 2 \fBusing\fP (\fIstr\fP) \-\- Index algorithm or type, e.g. \(aqBRIN\(aq, \(aqGiST\(aq or \(aqGIN\(aq. .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Optional index name. .UNINDENT .UNINDENT .sp Expressive method for declaring an index on a model. .sp Examples: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C 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") idx = ModelIndex(Article, (Article.name, Article.timestamp)) # CREATE INDEX ... ON "article" ("name", "timestamp") WHERE "status" = 1 idx = idx.where(Article.status == 1) # CREATE UNIQUE INDEX ... ON "article" ("timestamp" DESC, "flags" & 2) WHERE "status" = 1 idx = ModelIndex( Article, (Article.timestamp.desc(), Article.flags.bin_and(2)), unique = True).where(Article.status == 1) .ft P .fi .UNINDENT .UNINDENT .sp You can also use \fI\%Model.index()\fP: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C idx = Article.index(Article.name, Article.timestamp).where(Article.status == 1) .ft P .fi .UNINDENT .UNINDENT .sp To add an index to a model definition use \fI\%Model.add_index()\fP: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C idx = Article.index(Article.name, Article.timestamp).where(Article.status == 1) # Add above index definition to the model definition. When you call # Article.create_table() (or database.create_tables([Article])), the # index will be created. Article.add_index(idx) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .SS Fields .INDENT 0.0 .TP .B 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]]]]]]]]]]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBnull\fP (\fIbool\fP) \-\- Field allows NULLs. .IP \(bu 2 \fBindex\fP (\fIbool\fP) \-\- Create an index on field. .IP \(bu 2 \fBunique\fP (\fIbool\fP) \-\- Create a unique index on field. .IP \(bu 2 \fBcolumn_name\fP (\fIstr\fP) \-\- Specify column name for field. .IP \(bu 2 \fBdefault\fP \-\- Default value (enforced in Python, not on server). .IP \(bu 2 \fBprimary_key\fP (\fIbool\fP) \-\- Field is the primary key. .IP \(bu 2 \fBconstraints\fP (\fIlist\fP) \-\- List of constraints to apply to column, for example: \fB[Check(\(aqprice > 0\(aq)]\fP\&. .IP \(bu 2 \fBsequence\fP (\fIstr\fP) \-\- Sequence name for field. .IP \(bu 2 \fBcollation\fP (\fIstr\fP) \-\- Collation name for field. .IP \(bu 2 \fBunindexed\fP (\fIbool\fP) \-\- Declare field UNINDEXED (sqlite only). .IP \(bu 2 \fBchoices\fP (\fIlist\fP) \-\- An iterable of 2\-tuples mapping column values to display labels. Used for metadata purposes only, to help when displaying a dropdown of choices for field values, for example. .IP \(bu 2 \fBhelp_text\fP (\fIstr\fP) \-\- Help\-text for field, metadata purposes only. .IP \(bu 2 \fBverbose_name\fP (\fIstr\fP) \-\- Verbose name for field, metadata purposes only. .IP \(bu 2 \fBindex_type\fP (\fIstr\fP) \-\- Specify index type (postgres only), e.g. \(aqBRIN\(aq. .UNINDENT .UNINDENT .sp Fields on a \fI\%Model\fP are analogous to columns on a table. .INDENT 7.0 .TP .B field_type = \(aq\(aq Attribute used to map this field to a column type, e.g. "INT". See the \fBFIELD\fP object in the source for more information. .UNINDENT .INDENT 7.0 .TP .B column Retrieve a reference to the underlying \fI\%Column\fP object. .UNINDENT .INDENT 7.0 .TP .B model The model the field is bound to. .UNINDENT .INDENT 7.0 .TP .B name The name of the field. .UNINDENT .INDENT 7.0 .TP .B db_value(value) Coerce a Python value into a value suitable for storage in the database. Sub\-classes operating on special data\-types will most likely want to override this method. .UNINDENT .INDENT 7.0 .TP .B python_value(value) Coerce a value from the database into a Python object. Sub\-classes operating on special data\-types will most likely want to override this method. .UNINDENT .INDENT 7.0 .TP .B coerce(value) This method is a shorthand that is used, by default, by both \fI\%db_value()\fP and \fI\%python_value()\fP\&. .INDENT 7.0 .TP .B Parameters \fBvalue\fP \-\- arbitrary data from app or backend .TP .B Return type python data type .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class IntegerField Field class for storing integers. .UNINDENT .INDENT 0.0 .TP .B class BigIntegerField Field class for storing big integers (if supported by database). .UNINDENT .INDENT 0.0 .TP .B class SmallIntegerField Field class for storing small integers (if supported by database). .UNINDENT .INDENT 0.0 .TP .B class AutoField Field class for storing auto\-incrementing primary keys. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 In SQLite, for performance reasons, the default primary key type simply uses the max existing value + 1 for new values, as opposed to the max ever value + 1. This means deleted records can have their primary keys reused. In conjunction with SQLite having foreign keys disabled by default (meaning ON DELETE is ignored, even if you specify it explicitly), this can lead to surprising and dangerous behaviour. To avoid this, you may want to use one or both of \fBAutoIncrementField\fP and \fBpragmas=[(\(aqforeign_keys\(aq, \(aqon\(aq)]\fP when you instantiate \fI\%SqliteDatabase\fP\&. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class BigAutoField Field class for storing auto\-incrementing primary keys using 64\-bits. .UNINDENT .INDENT 0.0 .TP .B class IdentityField([generate_always=False]) .INDENT 7.0 .TP .B Parameters \fBgenerate_always\fP (\fIbool\fP) \-\- if specified, then the identity will always be generated (and specifying the value explicitly during INSERT will raise a programming error). Otherwise, the identity value is only generated as\-needed. .UNINDENT .sp Field class for storing auto\-incrementing primary keys using the new Postgres 10 \fIIDENTITY\fP column type. The column definition ends up looking like this: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C id = IdentityField() # "id" INT GENERATED BY DEFAULT AS IDENTITY NOT NULL PRIMARY KEY .ft P .fi .UNINDENT .UNINDENT .sp \fBATTENTION:\fP .INDENT 7.0 .INDENT 3.5 Only supported by Postgres 10.0 and newer. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class FloatField Field class for storing floating\-point numbers. .UNINDENT .INDENT 0.0 .TP .B class DoubleField Field class for storing double\-precision floating\-point numbers. .UNINDENT .INDENT 0.0 .TP .B class DecimalField([max_digits=10[, decimal_places=5[, auto_round=False[, rounding=None[, **kwargs]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmax_digits\fP (\fIint\fP) \-\- Maximum digits to store. .IP \(bu 2 \fBdecimal_places\fP (\fIint\fP) \-\- Maximum precision. .IP \(bu 2 \fBauto_round\fP (\fIbool\fP) \-\- Automatically round values. .IP \(bu 2 \fBrounding\fP \-\- .sp Defaults to \fBdecimal.DefaultContext.rounding\fP\&. .sp Field class for storing decimal numbers. Values are represented as \fBdecimal.Decimal\fP objects. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class CharField([max_length=255]) Field class for storing strings. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Values that exceed length are not truncated automatically. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class FixedCharField Field class for storing fixed\-length strings. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Values that exceed length are not truncated automatically. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class TextField Field class for storing text. .UNINDENT .INDENT 0.0 .TP .B class BlobField Field class for storing binary data. .UNINDENT .INDENT 0.0 .TP .B class BitField Field class for storing options in a 64\-bit integer column. .sp Usage: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class Post(Model): content = TextField() flags = BitField() is_favorite = flags.flag(1) is_sticky = flags.flag(2) is_minimized = flags.flag(4) is_deleted = flags.flag(8) >>> p = Post() >>> p.is_sticky = True >>> p.is_minimized = True >>> print(p.flags) # Prints 4 | 2 \-\-> "6" 6 >>> p.is_favorite False >>> p.is_sticky True .ft P .fi .UNINDENT .UNINDENT .sp We can use the flags on the Post class to build expressions in queries as well: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Generates a WHERE clause that looks like: # WHERE (post.flags & 1 != 0) query = Post.select().where(Post.is_favorite) # Query for sticky + favorite posts: query = Post.select().where(Post.is_sticky & Post.is_favorite) .ft P .fi .UNINDENT .UNINDENT .sp When bulk\-updating one or more bits in a \fI\%BitField\fP, you can use bitwise operators to set or clear one or more bits: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Set the 4th bit on all Post objects. Post.update(flags=Post.flags | 8).execute() # Clear the 1st and 3rd bits on all Post objects. Post.update(flags=Post.flags & ~(1 | 4)).execute() .ft P .fi .UNINDENT .UNINDENT .sp For simple operations, the flags provide handy \fBset()\fP and \fBclear()\fP methods for setting or clearing an individual bit: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Set the "is_deleted" bit on all posts. Post.update(flags=Post.is_deleted.set()).execute() # Clear the "is_deleted" bit on all posts. Post.update(flags=Post.is_deleted.clear()).execute() .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .TP .B flag([value=None]) .INDENT 7.0 .TP .B Parameters \fBvalue\fP (\fIint\fP) \-\- Value associated with flag, typically a power of 2. .UNINDENT .sp Returns a descriptor that can get or set specific bits in the overall value. When accessed on the class itself, it returns a \fI\%Expression\fP object suitable for use in a query. .sp If the value is not provided, it is assumed that each flag will be an increasing power of 2, so if you had four flags, they would have the values 1, 2, 4, 8. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class BigBitField Field class for storing arbitrarily\-large bitmaps in a \fBBLOB\fP\&. The field will grow the underlying buffer as necessary, ensuring there are enough bytes of data to support the number of bits of data being stored. .sp Example usage: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class Bitmap(Model): data = BigBitField() bitmap = Bitmap() # Sets the ith bit, e.g. the 1st bit, the 11th bit, the 63rd, etc. bits_to_set = (1, 11, 63, 31, 55, 48, 100, 99) for bit_idx in bits_to_set: bitmap.data.set_bit(bit_idx) # We can test whether a bit is set using "is_set": assert bitmap.data.is_set(11) assert not bitmap.data.is_set(12) # We can clear a bit: bitmap.data.clear_bit(11) assert not bitmap.data.is_set(11) # We can also "toggle" a bit. Recall that the 63rd bit was set earlier. assert bitmap.data.toggle_bit(63) is False assert bitmap.data.toggle_bit(63) is True assert bitmap.data.is_set(63) .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .TP .B set_bit(idx) .INDENT 7.0 .TP .B Parameters \fBidx\fP (\fIint\fP) \-\- Bit to set, indexed starting from zero. .UNINDENT .sp Sets the \fIidx\fP\-th bit in the bitmap. .UNINDENT .INDENT 7.0 .TP .B clear_bit(idx) .INDENT 7.0 .TP .B Parameters \fBidx\fP (\fIint\fP) \-\- Bit to clear, indexed starting from zero. .UNINDENT .sp Clears the \fIidx\fP\-th bit in the bitmap. .UNINDENT .INDENT 7.0 .TP .B toggle_bit(idx) .INDENT 7.0 .TP .B Parameters \fBidx\fP (\fIint\fP) \-\- Bit to toggle, indexed starting from zero. .TP .B Returns Whether the bit is set or not. .UNINDENT .sp Toggles the \fIidx\fP\-th bit in the bitmap and returns whether the bit is set or not. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> bitmap = Bitmap() >>> bitmap.data.toggle_bit(10) # Toggle the 10th bit. True >>> bitmap.data.toggle_bit(10) # This will clear the 10th bit. False .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B is_set(idx) .INDENT 7.0 .TP .B Parameters \fBidx\fP (\fIint\fP) \-\- Bit index, indexed starting from zero. .TP .B Returns Whether the bit is set or not. .UNINDENT .sp Returns boolean indicating whether the \fIidx\fP\-th bit is set or not. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class UUIDField Field class for storing \fBuuid.UUID\fP objects. With Postgres, the underlying column\(aqs data\-type will be \fIUUID\fP\&. Since SQLite and MySQL do not have a native UUID type, the UUID is stored as a \fIVARCHAR\fP instead. .UNINDENT .INDENT 0.0 .TP .B class BinaryUUIDField Field class for storing \fBuuid.UUID\fP objects efficiently in 16\-bytes. Uses the database\(aqs \fIBLOB\fP data\-type (or \fIVARBINARY\fP in MySQL, or \fIBYTEA\fP in Postgres). .UNINDENT .INDENT 0.0 .TP .B class DateTimeField([formats=None[, **kwargs]]) .INDENT 7.0 .TP .B Parameters \fBformats\fP (\fIlist\fP) \-\- A list of format strings to use when coercing a string to a date\-time. .UNINDENT .sp Field class for storing \fBdatetime.datetime\fP objects. .sp Accepts a special parameter \fBformats\fP, which contains a list of formats the datetime can be encoded with (for databases that do not have support for a native datetime data\-type). The default supported formats are: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C \(aq%Y\-%m\-%d %H:%M:%S.%f\(aq # year\-month\-day hour\-minute\-second.microsecond \(aq%Y\-%m\-%d %H:%M:%S\(aq # year\-month\-day hour\-minute\-second \(aq%Y\-%m\-%d\(aq # year\-month\-day .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 SQLite does not have a native datetime data\-type, so datetimes are stored as strings. This is handled transparently by Peewee, but if you have pre\-existing data you should ensure it is stored as \fBYYYY\-mm\-dd HH:MM:SS\fP or one of the other supported formats. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B year Reference the year of the value stored in the column in a query. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C Blog.select().where(Blog.pub_date.year == 2018) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B month Reference the month of the value stored in the column in a query. .UNINDENT .INDENT 7.0 .TP .B day Reference the day of the value stored in the column in a query. .UNINDENT .INDENT 7.0 .TP .B hour Reference the hour of the value stored in the column in a query. .UNINDENT .INDENT 7.0 .TP .B minute Reference the minute of the value stored in the column in a query. .UNINDENT .INDENT 7.0 .TP .B second Reference the second of the value stored in the column in a query. .UNINDENT .INDENT 7.0 .TP .B to_timestamp() Method that returns a database\-specific function call that will allow you to work with the given date\-time value as a numeric timestamp. This can sometimes simplify tasks like date math in a compatible way. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Find all events that are exactly 1 hour long. query = (Event .select() .where((Event.start.to_timestamp() + 3600) == Event.stop.to_timestamp()) .order_by(Event.start)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B truncate(date_part) .INDENT 7.0 .TP .B Parameters \fBdate_part\fP (\fIstr\fP) \-\- year, month, day, hour, minute or second. .TP .B Returns expression node to truncate date/time to given resolution. .UNINDENT .sp Truncates the value in the column to the given part. This method is useful for finding all rows within a given month, for instance. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class DateField([formats=None[, **kwargs]]) .INDENT 7.0 .TP .B Parameters \fBformats\fP (\fIlist\fP) \-\- A list of format strings to use when coercing a string to a date. .UNINDENT .sp Field class for storing \fBdatetime.date\fP objects. .sp Accepts a special parameter \fBformats\fP, which contains a list of formats the datetime can be encoded with (for databases that do not have support for a native date data\-type). The default supported formats are: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C \(aq%Y\-%m\-%d\(aq # year\-month\-day \(aq%Y\-%m\-%d %H:%M:%S\(aq # year\-month\-day hour\-minute\-second \(aq%Y\-%m\-%d %H:%M:%S.%f\(aq # year\-month\-day hour\-minute\-second.microsecond .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 If the incoming value does not match a format, it is returned as\-is. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B year Reference the year of the value stored in the column in a query. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C Person.select().where(Person.dob.year == 1983) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B month Reference the month of the value stored in the column in a query. .UNINDENT .INDENT 7.0 .TP .B day Reference the day of the value stored in the column in a query. .UNINDENT .INDENT 7.0 .TP .B to_timestamp() See \fI\%DateTimeField.to_timestamp()\fP\&. .UNINDENT .INDENT 7.0 .TP .B truncate(date_part) See \fI\%DateTimeField.truncate()\fP\&. Note that only \fIyear\fP, \fImonth\fP, and \fIday\fP are meaningful for \fI\%DateField\fP\&. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class TimeField([formats=None[, **kwargs]]) .INDENT 7.0 .TP .B Parameters \fBformats\fP (\fIlist\fP) \-\- A list of format strings to use when coercing a string to a time. .UNINDENT .sp Field class for storing \fBdatetime.time\fP objects (not \fBtimedelta\fP). .sp Accepts a special parameter \fBformats\fP, which contains a list of formats the datetime can be encoded with (for databases that do not have support for a native time data\-type). The default supported formats are: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C \(aq%H:%M:%S.%f\(aq # hour:minute:second.microsecond \(aq%H:%M:%S\(aq # hour:minute:second \(aq%H:%M\(aq # hour:minute \(aq%Y\-%m\-%d %H:%M:%S.%f\(aq # year\-month\-day hour\-minute\-second.microsecond \(aq%Y\-%m\-%d %H:%M:%S\(aq # year\-month\-day hour\-minute\-second .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 If the incoming value does not match a format, it is returned as\-is. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B hour Reference the hour of the value stored in the column in a query. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C evening_events = Event.select().where(Event.time.hour > 17) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B minute Reference the minute of the value stored in the column in a query. .UNINDENT .INDENT 7.0 .TP .B second Reference the second of the value stored in the column in a query. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class TimestampField([resolution=1[, utc=False[, **kwargs]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBresolution\fP \-\- Can be provided as either a power of 10, or as an exponent indicating how many decimal places to store. .IP \(bu 2 \fButc\fP (\fIbool\fP) \-\- Treat timestamps as UTC. .UNINDENT .UNINDENT .sp Field class for storing date\-times as integer timestamps. Sub\-second resolution is supported by multiplying by a power of 10 to get an integer. .sp If the \fBresolution\fP parameter is \fB0\fP \fIor\fP \fB1\fP, then the timestamp is stored using second resolution. A resolution between \fB2\fP and \fB6\fP is treated as the number of decimal places, e.g. \fBresolution=3\fP corresponds to milliseconds. Alternatively, the decimal can be provided as a multiple of 10, such that \fBresolution=10\fP will store 1/10th of a second resolution. .sp The \fBresolution\fP parameter can be either 0\-6 \fIor\fP 10, 100, etc up to 1000000 (for microsecond resolution). This allows sub\-second precision while still using an \fI\%IntegerField\fP for storage. The default is second resolution. .sp Also accepts a boolean parameter \fButc\fP, used to indicate whether the timestamps should be UTC. Default is \fBFalse\fP\&. .sp Finally, the field \fBdefault\fP is the current timestamp. If you do not want this behavior, then explicitly pass in \fBdefault=None\fP\&. .UNINDENT .INDENT 0.0 .TP .B class IPField Field class for storing IPv4 addresses efficiently (as integers). .UNINDENT .INDENT 0.0 .TP .B class BooleanField Field class for storing boolean values. .UNINDENT .INDENT 0.0 .TP .B class BareField([coerce=None[, **kwargs]]) .INDENT 7.0 .TP .B Parameters \fBcoerce\fP \-\- Optional function to use for converting raw values into a specific format. .UNINDENT .sp Field class that does not specify a data\-type (\fBSQLite\-only\fP). .sp Since data\-types are not enforced, you can declare fields without \fIany\fP data\-type. It is also common for SQLite virtual tables to use meta\-columns or untyped columns, so for those cases as well you may wish to use an untyped field. .sp Accepts a special \fBcoerce\fP parameter, a function that takes a value coming from the database and converts it into the appropriate Python type. .UNINDENT .INDENT 0.0 .TP .B class ForeignKeyField(model[, field=None[, backref=None[, on_delete=None[, on_update=None[, deferrable=None[, object_id_name=None[, lazy_load=True[, **kwargs]]]]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmodel\fP (\fIModel\fP) \-\- Model to reference or the string \(aqself\(aq if declaring a self\-referential foreign key. .IP \(bu 2 \fBfield\fP (\fIField\fP) \-\- Field to reference on \fBmodel\fP (default is primary key). .IP \(bu 2 \fBbackref\fP (\fIstr\fP) \-\- Accessor name for back\-reference, or "+" to disable the back\-reference accessor. .IP \(bu 2 \fBon_delete\fP (\fIstr\fP) \-\- ON DELETE action, e.g. \fB\(aqCASCADE\(aq\fP\&.. .IP \(bu 2 \fBon_update\fP (\fIstr\fP) \-\- ON UPDATE action. .IP \(bu 2 \fBdeferrable\fP (\fIstr\fP) \-\- Control when constraint is enforced, e.g. \fB\(aqINITIALLY DEFERRED\(aq\fP\&. .IP \(bu 2 \fBobject_id_name\fP (\fIstr\fP) \-\- Name for object\-id accessor. .IP \(bu 2 \fBlazy_load\fP (\fIbool\fP) \-\- Fetch the related object when the foreign\-key field attribute is accessed (if it was not already loaded). If this is disabled, accessing the foreign\-key field will return the value stored in the foreign\-key column. .UNINDENT .UNINDENT .sp Field class for storing a foreign key. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class User(Model): name = TextField() class Tweet(Model): user = ForeignKeyField(User, backref=\(aqtweets\(aq) content = TextField() # "user" attribute >>> some_tweet.user # "tweets" backref attribute >>> for tweet in charlie.tweets: \&... print(tweet.content) Some tweet Another tweet Yet another tweet .ft P .fi .UNINDENT .UNINDENT .sp For an in\-depth discussion of foreign\-keys, joins and relationships between models, refer to relationships\&. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Foreign keys do not have a particular \fBfield_type\fP as they will take their field type depending on the type of primary key on the model they are related to. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 If you manually specify a \fBfield\fP, that field must be either a primary key or have a unique constraint. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Take care with foreign keys in SQLite. By default, ON DELETE has no effect, which can have surprising (and usually unwanted) effects on your database integrity. This can affect you even if you don\(aqt specify \fBon_delete\fP, since the default ON DELETE behaviour (to fail without modifying your data) does not happen, and your data can be silently relinked. The safest thing to do is to specify \fBpragmas={\(aqforeign_keys\(aq: 1}\fP when you instantiate \fI\%SqliteDatabase\fP\&. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class DeferredForeignKey(rel_model_name[, **kwargs]) .INDENT 7.0 .TP .B Parameters \fBrel_model_name\fP (\fIstr\fP) \-\- Model name to reference. .UNINDENT .sp Field class for representing a deferred foreign key. Useful for circular foreign\-key references, for example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class Husband(Model): name = TextField() wife = DeferredForeignKey(\(aqWife\(aq, deferrable=\(aqINITIALLY DEFERRED\(aq) class Wife(Model): name = TextField() husband = ForeignKeyField(Husband, deferrable=\(aqINITIALLY DEFERRED\(aq) .ft P .fi .UNINDENT .UNINDENT .sp In the above example, when the \fBWife\fP model is declared, the foreign\-key \fBHusband.wife\fP is automatically resolved and turned into a regular \fI\%ForeignKeyField\fP\&. .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 \fI\%DeferredForeignKey\fP references are resolved when model classes are declared and created. This means that if you declare a \fI\%DeferredForeignKey\fP to a model class that has already been imported and created, the deferred foreign key instance will never be resolved. For example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class User(Model): username = TextField() class Tweet(Model): # This will never actually be resolved, because the User # model has already been declared. user = DeferredForeignKey(\(aquser\(aq, backref=\(aqtweets\(aq) content = TextField() .ft P .fi .UNINDENT .UNINDENT .sp In cases like these you should use the regular \fI\%ForeignKeyField\fP \fIor\fP you can manually resolve deferred foreign keys like so: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Tweet.user will be resolved into a ForeignKeyField: DeferredForeignKey.resolve(User) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class ManyToManyField(model[, backref=None[, through_model=None[, on_delete=None[, on_update=None]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmodel\fP (\fIModel\fP) \-\- Model to create relationship with. .IP \(bu 2 \fBbackref\fP (\fIstr\fP) \-\- Accessor name for back\-reference .IP \(bu 2 \fBthrough_model\fP (\fIModel\fP) \-\- \fI\%Model\fP to use for the intermediary table. If not provided, a simple through table will be automatically created. .IP \(bu 2 \fBon_delete\fP (\fIstr\fP) \-\- ON DELETE action, e.g. \fB\(aqCASCADE\(aq\fP\&. Will be used for foreign\-keys in through model. .IP \(bu 2 \fBon_update\fP (\fIstr\fP) \-\- ON UPDATE action. Will be used for foreign\-keys in through model. .UNINDENT .UNINDENT .sp The \fI\%ManyToManyField\fP provides a simple interface for working with many\-to\-many relationships, inspired by Django. A many\-to\-many relationship is typically implemented by creating a junction table with foreign keys to the two models being related. For instance, if you were building a syllabus manager for college students, the relationship between students and courses would be many\-to\-many. Here is the schema using standard APIs: .sp \fBATTENTION:\fP .INDENT 7.0 .INDENT 3.5 This is not a field in the sense that there is no column associated with it. Rather, it provides a convenient interface for accessing rows of data related via a through model. .UNINDENT .UNINDENT .sp Standard way of declaring a many\-to\-many relationship (without the use of the \fI\%ManyToManyField\fP): .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class Student(Model): name = CharField() class Course(Model): name = CharField() class StudentCourse(Model): student = ForeignKeyField(Student) course = ForeignKeyField(Course) .ft P .fi .UNINDENT .UNINDENT .sp To query the courses for a particular student, you would join through the junction table: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # List the courses that "Huey" is enrolled in: courses = (Course .select() .join(StudentCourse) .join(Student) .where(Student.name == \(aqHuey\(aq)) for course in courses: print(course.name) .ft P .fi .UNINDENT .UNINDENT .sp The \fI\%ManyToManyField\fP is designed to simplify this use\-case by providing a \fIfield\-like\fP API for querying and modifying data in the junction table. Here is how our code looks using \fI\%ManyToManyField\fP: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class Student(Model): name = CharField() class Course(Model): name = CharField() students = ManyToManyField(Student, backref=\(aqcourses\(aq) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 It does not matter from Peewee\(aqs perspective which model the \fI\%ManyToManyField\fP goes on, since the back\-reference is just the mirror image. In order to write valid Python, though, you will need to add the \fBManyToManyField\fP on the second model so that the name of the first model is in the scope. .UNINDENT .UNINDENT .sp We still need a junction table to store the relationships between students and courses. This model can be accessed by calling the \fI\%get_through_model()\fP method. This is useful when creating tables. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Create tables for the students, courses, and relationships between # the two. db.create_tables([ Student, Course, Course.students.get_through_model()]) .ft P .fi .UNINDENT .UNINDENT .sp When accessed from a model instance, the \fI\%ManyToManyField\fP exposes a \fI\%ModelSelect\fP representing the set of related objects. Let\(aqs use the interactive shell to see how all this works: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> huey = Student.get(Student.name == \(aqhuey\(aq) >>> [course.name for course in huey.courses] [\(aqEnglish 101\(aq, \(aqCS 101\(aq] >>> engl_101 = Course.get(Course.name == \(aqEnglish 101\(aq) >>> [student.name for student in engl_101.students] [\(aqHuey\(aq, \(aqMickey\(aq, \(aqZaizee\(aq] .ft P .fi .UNINDENT .UNINDENT .sp To add new relationships between objects, you can either assign the objects directly to the \fBManyToManyField\fP attribute, or call the \fI\%add()\fP method. The difference between the two is that simply assigning will clear out any existing relationships, whereas \fBadd()\fP can preserve existing relationships. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> huey.courses = Course.select().where(Course.name.contains(\(aqenglish\(aq)) >>> for course in huey.courses.order_by(Course.name): \&... print course.name English 101 English 151 English 201 English 221 >>> cs_101 = Course.get(Course.name == \(aqCS 101\(aq) >>> cs_151 = Course.get(Course.name == \(aqCS 151\(aq) >>> huey.courses.add([cs_101, cs_151]) >>> [course.name for course in huey.courses.order_by(Course.name)] [\(aqCS 101\(aq, \(aqCS151\(aq, \(aqEnglish 101\(aq, \(aqEnglish 151\(aq, \(aqEnglish 201\(aq, \(aqEnglish 221\(aq] .ft P .fi .UNINDENT .UNINDENT .sp This is quite a few courses, so let\(aqs remove the 200\-level english courses. To remove objects, use the \fI\%remove()\fP method. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> huey.courses.remove(Course.select().where(Course.name.contains(\(aq2\(aq)) 2 >>> [course.name for course in huey.courses.order_by(Course.name)] [\(aqCS 101\(aq, \(aqCS151\(aq, \(aqEnglish 101\(aq, \(aqEnglish 151\(aq] .ft P .fi .UNINDENT .UNINDENT .sp To remove all relationships from a collection, you can use the \fBclear()\fP method. Let\(aqs say that English 101 is canceled, so we need to remove all the students from it: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> engl_101 = Course.get(Course.name == \(aqEnglish 101\(aq) >>> engl_101.students.clear() .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 For an overview of implementing many\-to\-many relationships using standard Peewee APIs, check out the manytomany section. For all but the most simple cases, you will be better off implementing many\-to\-many using the standard APIs. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B through_model The \fI\%Model\fP representing the many\-to\-many junction table. Will be auto\-generated if not explicitly declared. .UNINDENT .INDENT 7.0 .TP .B add(value[, clear_existing=True]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBvalue\fP \-\- Either a \fI\%Model\fP instance, a list of model instances, or a \fI\%SelectQuery\fP\&. .IP \(bu 2 \fBclear_existing\fP (\fIbool\fP) \-\- Whether to remove existing relationships. .UNINDENT .UNINDENT .sp Associate \fBvalue\fP with the current instance. You can pass in a single model instance, a list of model instances, or even a \fI\%ModelSelect\fP\&. .sp Example code: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Huey needs to enroll in a bunch of courses, including all # the English classes, and a couple Comp\-Sci classes. huey = Student.get(Student.name == \(aqHuey\(aq) # We can add all the objects represented by a query. english_courses = Course.select().where( Course.name.contains(\(aqenglish\(aq)) huey.courses.add(english_courses) # We can also add lists of individual objects. cs101 = Course.get(Course.name == \(aqCS 101\(aq) cs151 = Course.get(Course.name == \(aqCS 151\(aq) huey.courses.add([cs101, cs151]) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B remove(value) .INDENT 7.0 .TP .B Parameters \fBvalue\fP \-\- Either a \fI\%Model\fP instance, a list of model instances, or a \fI\%ModelSelect\fP\&. .UNINDENT .sp Disassociate \fBvalue\fP from the current instance. Like \fI\%add()\fP, you can pass in a model instance, a list of model instances, or even a \fI\%ModelSelect\fP\&. .sp Example code: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Huey is currently enrolled in a lot of english classes # as well as some Comp\-Sci. He is changing majors, so we # will remove all his courses. english_courses = Course.select().where( Course.name.contains(\(aqenglish\(aq)) huey.courses.remove(english_courses) # Remove the two Comp\-Sci classes Huey is enrolled in. cs101 = Course.get(Course.name == \(aqCS 101\(aq) cs151 = Course.get(Course.name == \(aqCS 151\(aq) huey.courses.remove([cs101, cs151]) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B clear() Remove all associated objects. .sp Example code: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # English 101 is canceled this semester, so remove all # the enrollments. english_101 = Course.get(Course.name == \(aqEnglish 101\(aq) english_101.students.clear() .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B get_through_model() Return the \fI\%Model\fP representing the many\-to\-many junction table. This can be specified manually when the field is being instantiated using the \fBthrough_model\fP parameter. If a \fBthrough_model\fP is not specified, one will automatically be created. .sp When creating tables for an application that uses \fI\%ManyToManyField\fP, \fByou must create the through table expicitly\fP\&. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Get a reference to the automatically\-created through table. StudentCourseThrough = Course.students.get_through_model() # Create tables for our two models as well as the through model. db.create_tables([ Student, Course, StudentCourseThrough]) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class DeferredThroughModel Place\-holder for a through\-model in cases where, due to a dependency, you cannot declare either a model or a many\-to\-many field without introducing NameErrors. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class Note(BaseModel): content = TextField() NoteThroughDeferred = DeferredThroughModel() class User(BaseModel): username = TextField() notes = ManyToManyField(Note, through_model=NoteThroughDeferred) # Cannot declare this before "User" since it has a foreign\-key to # the User model. class NoteThrough(BaseModel): note = ForeignKeyField(Note) user = ForeignKeyField(User) # Resolve dependencies. NoteThroughDeferred.set_model(NoteThrough) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class CompositeKey(*field_names) .INDENT 7.0 .TP .B Parameters \fBfield_names\fP \-\- Names of fields that comprise the primary key. .UNINDENT .sp A primary key composed of multiple columns. Unlike the other fields, a composite key is defined in the model\(aqs \fBMeta\fP class after the fields have been defined. It takes as parameters the string names of the fields to use as the primary key: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class BlogTagThrough(Model): blog = ForeignKeyField(Blog, backref=\(aqtags\(aq) tag = ForeignKeyField(Tag, backref=\(aqblogs\(aq) class Meta: primary_key = CompositeKey(\(aqblog\(aq, \(aqtag\(aq) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .SS Schema Manager .INDENT 0.0 .TP .B class SchemaManager(model[, database=None[, **context_options]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmodel\fP (\fIModel\fP) \-\- Model class. .IP \(bu 2 \fBdatabase\fP (\fIDatabase\fP) \-\- If unspecified defaults to model._meta.database. .UNINDENT .UNINDENT .sp Provides methods for managing the creation and deletion of tables and indexes for the given model. .INDENT 7.0 .TP .B create_table([safe=True[, **options]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBsafe\fP (\fIbool\fP) \-\- Specify IF NOT EXISTS clause. .IP \(bu 2 \fBoptions\fP \-\- Arbitrary options. .UNINDENT .UNINDENT .sp Execute CREATE TABLE query for the given model. .UNINDENT .INDENT 7.0 .TP .B drop_table([safe=True[, drop_sequences=True[, **options]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBsafe\fP (\fIbool\fP) \-\- Specify IF EXISTS clause. .IP \(bu 2 \fBdrop_sequences\fP (\fIbool\fP) \-\- Drop any sequences associated with the columns on the table (postgres only). .IP \(bu 2 \fBoptions\fP \-\- Arbitrary options. .UNINDENT .UNINDENT .sp Execute DROP TABLE query for the given model. .UNINDENT .INDENT 7.0 .TP .B truncate_table([restart_identity=False[, cascade=False]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBrestart_identity\fP (\fIbool\fP) \-\- Restart the id sequence (postgres\-only). .IP \(bu 2 \fBcascade\fP (\fIbool\fP) \-\- Truncate related tables as well (postgres\-only). .UNINDENT .UNINDENT .sp Execute TRUNCATE TABLE for the given model. If the database is Sqlite, which does not support TRUNCATE, then an equivalent DELETE query will be executed. .UNINDENT .INDENT 7.0 .TP .B create_indexes([safe=True]) .INDENT 7.0 .TP .B Parameters \fBsafe\fP (\fIbool\fP) \-\- Specify IF NOT EXISTS clause. .UNINDENT .sp Execute CREATE INDEX queries for the indexes defined for the model. .UNINDENT .INDENT 7.0 .TP .B drop_indexes([safe=True]) .INDENT 7.0 .TP .B Parameters \fBsafe\fP (\fIbool\fP) \-\- Specify IF EXISTS clause. .UNINDENT .sp Execute DROP INDEX queries for the indexes defined for the model. .UNINDENT .INDENT 7.0 .TP .B create_sequence(field) .INDENT 7.0 .TP .B Parameters \fBfield\fP (\fIField\fP) \-\- Field instance which specifies a sequence. .UNINDENT .sp Create sequence for the given \fI\%Field\fP\&. .UNINDENT .INDENT 7.0 .TP .B drop_sequence(field) .INDENT 7.0 .TP .B Parameters \fBfield\fP (\fIField\fP) \-\- Field instance which specifies a sequence. .UNINDENT .sp Drop sequence for the given \fI\%Field\fP\&. .UNINDENT .INDENT 7.0 .TP .B create_foreign_key(field) .INDENT 7.0 .TP .B Parameters \fBfield\fP (\fIForeignKeyField\fP) \-\- Foreign\-key field constraint to add. .UNINDENT .sp Add a foreign\-key constraint for the given field. This method should not be necessary in most cases, as foreign\-key constraints are created as part of table creation. The exception is when you are creating a circular foreign\-key relationship using \fI\%DeferredForeignKey\fP\&. In those cases, it is necessary to first create the tables, then add the constraint for the deferred foreign\-key: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class Language(Model): name = TextField() selected_snippet = DeferredForeignKey(\(aqSnippet\(aq) class Snippet(Model): code = TextField() language = ForeignKeyField(Language, backref=\(aqsnippets\(aq) # Creates both tables but does not create the constraint for the # Language.selected_snippet foreign key (because of the circular # dependency). db.create_tables([Language, Snippet]) # Explicitly create the constraint: Language._schema.create_foreign_key(Language.selected_snippet) .ft P .fi .UNINDENT .UNINDENT .sp For more information, see documentation on circular\-fks\&. .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 Because SQLite has limited support for altering existing tables, it is not possible to add a foreign\-key constraint to an existing SQLite table. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B create_all([safe=True[, **table_options]]) .INDENT 7.0 .TP .B Parameters \fBsafe\fP (\fIbool\fP) \-\- Whether to specify IF NOT EXISTS. .UNINDENT .sp Create sequence(s), index(es) and table for the model. .UNINDENT .INDENT 7.0 .TP .B drop_all([safe=True[, drop_sequences=True[, **options]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBsafe\fP (\fIbool\fP) \-\- Whether to specify IF EXISTS. .IP \(bu 2 \fBdrop_sequences\fP (\fIbool\fP) \-\- Drop any sequences associated with the columns on the table (postgres only). .IP \(bu 2 \fBoptions\fP \-\- Arbitrary options. .UNINDENT .UNINDENT .sp Drop table for the model and associated indexes. .UNINDENT .UNINDENT .SS Model .INDENT 0.0 .TP .B 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]]]]]]]]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmodel\fP (\fIModel\fP) \-\- Model class. .IP \(bu 2 \fBdatabase\fP (\fIDatabase\fP) \-\- database model is bound to. .IP \(bu 2 \fBtable_name\fP (\fIstr\fP) \-\- Specify table name for model. .IP \(bu 2 \fBindexes\fP (\fIlist\fP) \-\- List of \fI\%ModelIndex\fP objects. .IP \(bu 2 \fBprimary_key\fP \-\- Primary key for model (only specified if this is a \fI\%CompositeKey\fP or \fBFalse\fP for no primary key. .IP \(bu 2 \fBconstraints\fP (\fIlist\fP) \-\- List of table constraints. .IP \(bu 2 \fBschema\fP (\fIstr\fP) \-\- Schema table exists in. .IP \(bu 2 \fBonly_save_dirty\fP (\fIbool\fP) \-\- When \fI\%save()\fP is called, only save the fields which have been modified. .IP \(bu 2 \fBoptions\fP (\fIdict\fP) \-\- Arbitrary options for the model. .IP \(bu 2 \fBwithout_rowid\fP (\fIbool\fP) \-\- Specify WITHOUT ROWID (sqlite only). .IP \(bu 2 \fBkwargs\fP \-\- Arbitrary setting attributes and values. .UNINDENT .UNINDENT .sp Store metadata for a \fI\%Model\fP\&. .sp This class should not be instantiated directly, but is instantiated using the attributes of a \fI\%Model\fP class\(aq inner \fBMeta\fP class. Metadata attributes are then available on \fBModel._meta\fP\&. .INDENT 7.0 .TP .B table Return a reference to the underlying \fBTable\fP object. .UNINDENT .INDENT 7.0 .TP .B model_graph([refs=True[, backrefs=True[, depth_first=True]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBrefs\fP (\fIbool\fP) \-\- Follow foreign\-key references. .IP \(bu 2 \fBbackrefs\fP (\fIbool\fP) \-\- Follow foreign\-key back\-references. .IP \(bu 2 \fBdepth_first\fP (\fIbool\fP) \-\- Do a depth\-first search (\fBFalse\fP for breadth\-first). .UNINDENT .UNINDENT .sp Traverse the model graph and return a list of 3\-tuples, consisting of \fB(foreign key field, model class, is_backref)\fP\&. .UNINDENT .INDENT 7.0 .TP .B set_database(database) .INDENT 7.0 .TP .B Parameters \fBdatabase\fP (\fIDatabase\fP) \-\- database object to bind Model to. .UNINDENT .sp Bind the model class to the given \fI\%Database\fP instance. .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 This API should not need to be used. Instead, to change a \fI\%Model\fP database at run\-time, use one of the following: .INDENT 0.0 .IP \(bu 2 \fI\%Model.bind()\fP .IP \(bu 2 \fI\%Model.bind_ctx()\fP (bind for scope of a context manager). .IP \(bu 2 \fI\%Database.bind()\fP .IP \(bu 2 \fI\%Database.bind_ctx()\fP .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B set_table_name(table_name) .INDENT 7.0 .TP .B Parameters \fBtable_name\fP (\fIstr\fP) \-\- table name to bind Model to. .UNINDENT .sp Bind the model class to the given table name at run\-time. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class SubclassAwareMetadata Metadata subclass that tracks \fI\%Model\fP subclasses. .INDENT 7.0 .TP .B map_models(fn) Apply a function to all subclasses. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Model(**kwargs) .INDENT 7.0 .TP .B Parameters \fBkwargs\fP \-\- Mapping of field\-name to value to initialize model with. .UNINDENT .sp Model class provides a high\-level abstraction for working with database tables. Models are a one\-to\-one mapping with a database table (or a table\-like object, such as a view). Subclasses of \fBModel\fP declare any number of \fI\%Field\fP instances as class attributes. These fields correspond to columns on the table. .sp Table\-level operations, such as \fI\%select()\fP, \fI\%update()\fP, \fI\%insert()\fP and \fI\%delete()\fP are implemented as classmethods. Row\-level operations, such as \fI\%save()\fP and \fI\%delete_instance()\fP are implemented as instancemethods. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C db = SqliteDatabase(\(aq:memory:\(aq) class User(Model): username = TextField() join_date = DateTimeField(default=datetime.datetime.now) is_admin = BooleanField(default=False) admin = User(username=\(aqadmin\(aq, is_admin=True) admin.save() .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod alias([alias=None]) .INDENT 7.0 .TP .B Parameters \fBalias\fP (\fIstr\fP) \-\- Optional name for alias. .TP .B Returns \fI\%ModelAlias\fP instance. .UNINDENT .sp Create an alias to the model\-class. Model aliases allow you to reference the same \fI\%Model\fP multiple times in a query, for example when doing a self\-join or sub\-query. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C Parent = Category.alias() sq = (Category .select(Category, Parent) .join(Parent, on=(Category.parent == Parent.id)) .where(Parent.name == \(aqparent category\(aq)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod select(*fields) .INDENT 7.0 .TP .B Parameters \fBfields\fP \-\- A list of model classes, field instances, functions or expressions. If no arguments are provided, all columns for the given model will be selected by default. .TP .B Returns \fI\%ModelSelect\fP query. .UNINDENT .sp Create a SELECT query. If no fields are explicitly provided, the query will by default SELECT all the fields defined on the model, unless you are using the query as a sub\-query, in which case only the primary key will be selected by default. .sp Example of selecting all columns: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C query = User.select().where(User.active == True).order_by(User.username) .ft P .fi .UNINDENT .UNINDENT .sp Example of selecting all columns on \fITweet\fP and the parent model, \fIUser\fP\&. When the \fBuser\fP foreign key is accessed on a \fITweet\fP instance no additional query will be needed (see N+1 for more details): .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C query = (Tweet .select(Tweet, User) .join(User) .order_by(Tweet.created_date.desc())) for tweet in query: print(tweet.user.username, \(aq\->\(aq, tweet.content) .ft P .fi .UNINDENT .UNINDENT .sp Example of subquery only selecting the primary key: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C 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() .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod update([__data=None[, **update]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fB__data\fP (\fIdict\fP) \-\- \fBdict\fP of fields to values. .IP \(bu 2 \fBupdate\fP \-\- Field\-name to value mapping. .UNINDENT .UNINDENT .sp Create an UPDATE query. .sp Example showing users being marked inactive if their registration has expired: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C q = (User .update({User.active: False}) .where(User.registration_expired == True)) q.execute() # Execute the query, returning number of rows updated. .ft P .fi .UNINDENT .UNINDENT .sp Example showing an atomic update: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C q = (PageView .update({PageView.count: PageView.count + 1}) .where(PageView.url == url)) q.execute() # Execute the query. .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 When an update query is executed, the number of rows modified will be returned. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod insert([__data=None[, **insert]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fB__data\fP (\fIdict\fP) \-\- \fBdict\fP of fields to values to insert. .IP \(bu 2 \fBinsert\fP \-\- Field\-name to value mapping. .UNINDENT .UNINDENT .sp Create an INSERT query. .sp Insert a new row into the database. If any fields on the model have default values, these values will be used if the fields are not explicitly set in the \fBinsert\fP dictionary. .sp Example showing creation of a new user: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C q = User.insert(username=\(aqadmin\(aq, active=True, registration_expired=False) q.execute() # perform the insert. .ft P .fi .UNINDENT .UNINDENT .sp You can also use \fI\%Field\fP objects as the keys: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C new_id = User.insert({User.username: \(aqadmin\(aq}).execute() .ft P .fi .UNINDENT .UNINDENT .sp If you have a model with a default value on one of the fields, and that field is not specified in the \fBinsert\fP parameter, the default will be used: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class User(Model): username = CharField() active = BooleanField(default=True) # This INSERT query will automatically specify \(gaactive=True\(ga: User.insert(username=\(aqcharlie\(aq) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 When an insert query is executed on a table with an auto\-incrementing primary key, the primary key of the new row will be returned. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod insert_many(rows[, fields=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBrows\fP \-\- An iterable that yields rows to insert. .IP \(bu 2 \fBfields\fP (\fIlist\fP) \-\- List of fields being inserted. .UNINDENT .TP .B Returns number of rows modified (see note). .UNINDENT .sp INSERT multiple rows of data. .sp The \fBrows\fP parameter must be an iterable that yields dictionaries or tuples, where the ordering of the tuple values corresponds to the fields specified in the \fBfields\fP argument. As with \fI\%insert()\fP, fields that are not specified in the dictionary will use their default value, if one exists. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Due to the nature of bulk inserts, each row must contain the same fields. The following will not work: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Person.insert_many([ {\(aqfirst_name\(aq: \(aqPeewee\(aq, \(aqlast_name\(aq: \(aqHerman\(aq}, {\(aqfirst_name\(aq: \(aqHuey\(aq}, # Missing "last_name"! ]).execute() .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp Example of inserting multiple Users: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C data = [ (\(aqcharlie\(aq, True), (\(aqhuey\(aq, False), (\(aqzaizee\(aq, False)] query = User.insert_many(data, fields=[User.username, User.is_admin]) query.execute() .ft P .fi .UNINDENT .UNINDENT .sp Equivalent example using dictionaries: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C data = [ {\(aqusername\(aq: \(aqcharlie\(aq, \(aqis_admin\(aq: True}, {\(aqusername\(aq: \(aqhuey\(aq, \(aqis_admin\(aq: False}, {\(aqusername\(aq: \(aqzaizee\(aq, \(aqis_admin\(aq: False}] # Insert new rows. User.insert_many(data).execute() .ft P .fi .UNINDENT .UNINDENT .sp Because the \fBrows\fP parameter can be an arbitrary iterable, you can also use a generator: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C def get_usernames(): for username in [\(aqcharlie\(aq, \(aqhuey\(aq, \(aqpeewee\(aq]: yield {\(aqusername\(aq: username} User.insert_many(get_usernames()).execute() .ft P .fi .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 If you are using SQLite, your SQLite library must be version 3.7.11 or newer to take advantage of bulk inserts. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 SQLite has a default limit of bound variables per statement. This limit can be modified at compile\-time or at run\-time, \fBbut\fP if modifying at run\-time, you can only specify a \fIlower\fP value than the default limit. .sp For more information, check out the following SQLite documents: .INDENT 0.0 .IP \(bu 2 \fI\%Max variable number limit\fP .IP \(bu 2 \fI\%Changing run\-time limits\fP .IP \(bu 2 \fI\%SQLite compile\-time flags\fP .UNINDENT .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 The default return value is the number of rows modified. However, when using Postgres, Peewee will return a cursor by default that yields the primary\-keys of the inserted rows. To disable this functionality with Postgres, use an empty call to \fBreturning()\fP\&. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod insert_from(query, fields) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBquery\fP (\fISelect\fP) \-\- SELECT query to use as source of data. .IP \(bu 2 \fBfields\fP \-\- Fields to insert data into. .UNINDENT .TP .B Returns number of rows modified (see note). .UNINDENT .sp INSERT data using a SELECT query as the source. This API should be used for queries of the form \fIINSERT INTO ... SELECT FROM ...\fP\&. .sp Example of inserting data across tables for denormalization purposes: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C 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() .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 The default return value is the number of rows modified. However, when using Postgres, Peewee will return a cursor by default that yields the primary\-keys of the inserted rows. To disable this functionality with Postgres, use an empty call to \fBreturning()\fP\&. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod replace([__data=None[, **insert]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fB__data\fP (\fIdict\fP) \-\- \fBdict\fP of fields to values to insert. .IP \(bu 2 \fBinsert\fP \-\- Field\-name to value mapping. .UNINDENT .UNINDENT .sp Create an INSERT query that uses REPLACE for conflict\-resolution. .sp See \fI\%Model.insert()\fP for examples. .UNINDENT .INDENT 7.0 .TP .B classmethod replace_many(rows[, fields=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBrows\fP \-\- An iterable that yields rows to insert. .IP \(bu 2 \fBfields\fP (\fIlist\fP) \-\- List of fields being inserted. .UNINDENT .UNINDENT .sp INSERT multiple rows of data using REPLACE for conflict\-resolution. .sp See \fI\%Model.insert_many()\fP for examples. .UNINDENT .INDENT 7.0 .TP .B classmethod raw(sql, *params) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBsql\fP (\fIstr\fP) \-\- SQL query to execute. .IP \(bu 2 \fBparams\fP \-\- Parameters for query. .UNINDENT .UNINDENT .sp Execute a SQL query directly. .sp Example selecting rows from the User table: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C q = User.raw(\(aqselect id, username from users\(aq) for user in q: print(user.id, user.username) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Generally the use of \fBraw\fP is reserved for those cases where you can significantly optimize a select query. It is useful for select queries since it will return instances of the model. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod delete() Create a DELETE query. .sp Example showing the deletion of all inactive users: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C q = User.delete().where(User.active == False) q.execute() # Remove the rows, return number of rows removed. .ft P .fi .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 This method performs a delete on the \fIentire table\fP\&. To delete a single instance, see \fI\%Model.delete_instance()\fP\&. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod create(**query) .INDENT 7.0 .TP .B Parameters \fBquery\fP \-\- Mapping of field\-name to value. .UNINDENT .sp INSERT new row into table and return corresponding model instance. .sp Example showing the creation of a user (a row will be added to the database): .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C user = User.create(username=\(aqadmin\(aq, password=\(aqtest\(aq) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 The create() method is a shorthand for instantiate\-then\-save. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod bulk_create(model_list[, batch_size=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmodel_list\fP (\fIiterable\fP) \-\- a list or other iterable of unsaved \fI\%Model\fP instances. .IP \(bu 2 \fBbatch_size\fP (\fIint\fP) \-\- number of rows to batch per insert. If unspecified, all models will be inserted in a single query. .UNINDENT .TP .B Returns no return value. .UNINDENT .sp Efficiently INSERT multiple unsaved model instances into the database. Unlike \fI\%insert_many()\fP, which accepts row data as a list of either dictionaries or lists, this method accepts a list of unsaved model instances. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # List of 10 unsaved users. user_list = [User(username=\(aqu%s\(aq % i) for i in range(10)] # All 10 users are inserted in a single query. User.bulk_create(user_list) .ft P .fi .UNINDENT .UNINDENT .sp Batches: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C user_list = [User(username=\(aqu%s\(aq % 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) .ft P .fi .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 The primary\-key value for the newly\-created models will only be set if you are using Postgresql (which supports the \fBRETURNING\fP clause). .IP \(bu 2 SQLite generally has a limit of bound parameters for a query, so the maximum batch size should be param\-limit / number\-of\-fields. This limit is typically 999 for Sqlite < 3.32.0, and 32766 for newer versions. .IP \(bu 2 When a batch\-size is provided it is \fBstrongly recommended\fP that you wrap the call in a transaction or savepoint using \fI\%Database.atomic()\fP\&. Otherwise an error in a batch mid\-way through could leave the database in an inconsistent state. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod bulk_update(model_list, fields[, batch_size=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmodel_list\fP (\fIiterable\fP) \-\- a list or other iterable of \fI\%Model\fP instances. .IP \(bu 2 \fBfields\fP (\fIlist\fP) \-\- list of fields to update. .IP \(bu 2 \fBbatch_size\fP (\fIint\fP) \-\- number of rows to batch per insert. If unspecified, all models will be inserted in a single query. .UNINDENT .TP .B Returns total number of rows updated. .UNINDENT .sp Efficiently UPDATE multiple model instances. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # First, create 3 users. u1, u2, u3 = [User.create(username=\(aqu%s\(aq % i) for i in (1, 2, 3)] # Now let\(aqs modify their usernames. u1.username = \(aqu1\-x\(aq u2.username = \(aqu2\-y\(aq u3.username = \(aqu3\-z\(aq # Update all three rows using a single UPDATE query. User.bulk_update([u1, u2, u3], fields=[User.username]) .ft P .fi .UNINDENT .UNINDENT .sp If you have a large number of objects to update, it is strongly recommended that you specify a \fBbatch_size\fP and wrap the operation in a transaction: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C with database.atomic(): User.bulk_update(user_list, fields=[\(aqusername\(aq], batch_size=50) .ft P .fi .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 SQLite generally has a limit of bound parameters for a query. This limit is typically 999 for Sqlite < 3.32.0, and 32766 for newer versions. .IP \(bu 2 When a batch\-size is provided it is \fBstrongly recommended\fP that you wrap the call in a transaction or savepoint using \fI\%Database.atomic()\fP\&. Otherwise an error in a batch mid\-way through could leave the database in an inconsistent state. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod get(*query, **filters) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBquery\fP \-\- Zero or more \fI\%Expression\fP objects. .IP \(bu 2 \fBfilters\fP \-\- Mapping of field\-name to value for Django\-style filter. .UNINDENT .TP .B Raises \fBDoesNotExist\fP .TP .B Returns Model instance matching the specified filters. .UNINDENT .sp Retrieve a single model instance matching the given filters. If no model is returned, a \fBDoesNotExist\fP is raised. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C user = User.get(User.username == username, User.active == True) .ft P .fi .UNINDENT .UNINDENT .sp This method is also exposed via the \fI\%SelectQuery\fP, though it takes no parameters: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C active = User.select().where(User.active == True) try: user = active.where( (User.username == username) & (User.active == True) ).get() except User.DoesNotExist: user = None .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 The \fI\%get()\fP method is shorthand for selecting with a limit of 1. It has the added behavior of raising an exception when no matching row is found. If more than one row is found, the first row returned by the database cursor will be used. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod get_or_none(*query, **filters) Identical to \fI\%Model.get()\fP but returns \fBNone\fP if no model matches the given filters. .UNINDENT .INDENT 7.0 .TP .B classmethod get_by_id(pk) .INDENT 7.0 .TP .B Parameters \fBpk\fP \-\- Primary\-key value. .UNINDENT .sp Short\-hand for calling \fI\%Model.get()\fP specifying a lookup by primary key. Raises a \fBDoesNotExist\fP if instance with the given primary key value does not exist. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C user = User.get_by_id(1) # Returns user with id = 1. .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod set_by_id(key, value) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBkey\fP \-\- Primary\-key value. .IP \(bu 2 \fBvalue\fP (\fIdict\fP) \-\- Mapping of field to value to update. .UNINDENT .UNINDENT .sp Short\-hand for updating the data with the given primary\-key. If no row exists with the given primary key, no exception will be raised. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Set "is_admin" to True on user with id=3. User.set_by_id(3, {\(aqis_admin\(aq: True}) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod delete_by_id(pk) .INDENT 7.0 .TP .B Parameters \fBpk\fP \-\- Primary\-key value. .UNINDENT .sp Short\-hand for deleting the row with the given primary\-key. If no row exists with the given primary key, no exception will be raised. .UNINDENT .INDENT 7.0 .TP .B classmethod get_or_create(**kwargs) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBkwargs\fP \-\- Mapping of field\-name to value. .IP \(bu 2 \fBdefaults\fP \-\- Default values to use if creating a new row. .UNINDENT .TP .B Returns Tuple of \fI\%Model\fP instance and boolean indicating if a new object was created. .UNINDENT .sp Attempt to get the row matching the given filters. If no matching row is found, create a new row. .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 Race\-conditions are possible when using this method. .UNINDENT .UNINDENT .sp Example \fBwithout\fP \fBget_or_create\fP: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Without \(gaget_or_create\(ga, we might write: try: person = Person.get( (Person.first_name == \(aqJohn\(aq) & (Person.last_name == \(aqLennon\(aq)) except Person.DoesNotExist: person = Person.create( first_name=\(aqJohn\(aq, last_name=\(aqLennon\(aq, birthday=datetime.date(1940, 10, 9)) .ft P .fi .UNINDENT .UNINDENT .sp Equivalent code using \fBget_or_create\fP: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C person, created = Person.get_or_create( first_name=\(aqJohn\(aq, last_name=\(aqLennon\(aq, defaults={\(aqbirthday\(aq: datetime.date(1940, 10, 9)}) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod filter(*dq_nodes, **filters) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdq_nodes\fP \-\- Zero or more \fI\%DQ\fP objects. .IP \(bu 2 \fBfilters\fP \-\- Django\-style filters. .UNINDENT .TP .B Returns \fI\%ModelSelect\fP query. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B get_id() .INDENT 7.0 .TP .B Returns The primary\-key of the model instance. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B save([force_insert=False[, only=None]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBforce_insert\fP (\fIbool\fP) \-\- Force INSERT query. .IP \(bu 2 \fBonly\fP (\fIlist\fP) \-\- Only save the given \fI\%Field\fP instances. .UNINDENT .TP .B Returns Number of rows modified. .UNINDENT .sp Save the data in the model instance. By default, the presence of a primary\-key value will cause an UPDATE query to be executed. .sp Example showing saving a model instance: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C user = User() user.username = \(aqsome\-user\(aq # does not touch the database user.save() # change is persisted to the db .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B dirty_fields Return list of fields that have been modified. .INDENT 7.0 .TP .B Return type list .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 If you just want to persist modified fields, you can call \fBmodel.save(only=model.dirty_fields)\fP\&. .sp If you \fBalways\fP want to only save a model\(aqs dirty fields, you can use the Meta option \fBonly_save_dirty = True\fP\&. Then, any time you call \fI\%Model.save()\fP, by default only the dirty fields will be saved, e.g. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Person(Model): first_name = CharField() last_name = CharField() dob = DateField() class Meta: database = db only_save_dirty = True .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 Peewee determines whether a field is "dirty" by observing when the field attribute is set on a model instance. If the field contains a value that is mutable, such as a dictionary instance, and that dictionary is then modified, Peewee will not notice the change. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B is_dirty() Return boolean indicating whether any fields were manually set. .UNINDENT .INDENT 7.0 .TP .B delete_instance([recursive=False[, delete_nullable=False]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBrecursive\fP (\fIbool\fP) \-\- Delete related models. .IP \(bu 2 \fBdelete_nullable\fP (\fIbool\fP) \-\- Delete related models that have a null foreign key. If \fBFalse\fP nullable relations will be set to NULL. .UNINDENT .UNINDENT .sp Delete the given instance. Any foreign keys set to cascade on delete will be deleted automatically. For more programmatic control, you can specify \fBrecursive=True\fP, which will delete any non\-nullable related models (those that \fIare\fP nullable will be set to NULL). If you wish to delete all dependencies regardless of whether they are nullable, set \fBdelete_nullable=True\fP\&. .sp example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C some_obj.delete_instance() # it is gone forever .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod bind(database[, bind_refs=True[, bind_backrefs=True]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdatabase\fP (\fIDatabase\fP) \-\- database to bind to. .IP \(bu 2 \fBbind_refs\fP (\fIbool\fP) \-\- Bind related models. .IP \(bu 2 \fBbind_backrefs\fP (\fIbool\fP) \-\- Bind back\-reference related models. .UNINDENT .UNINDENT .sp Bind the model (and specified relations) to the given database. .sp See also: \fI\%Database.bind()\fP\&. .UNINDENT .INDENT 7.0 .TP .B classmethod bind_ctx(database[, bind_refs=True[, bind_backrefs=True]]) Like \fI\%bind()\fP, but returns a context manager that only binds the models for the duration of the wrapped block. .sp See also: \fI\%Database.bind_ctx()\fP\&. .UNINDENT .INDENT 7.0 .TP .B classmethod table_exists() .INDENT 7.0 .TP .B Returns boolean indicating whether the table exists. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod create_table([safe=True[, **options]]) .INDENT 7.0 .TP .B Parameters \fBsafe\fP (\fIbool\fP) \-\- If set to \fBTrue\fP, the create table query will include an \fBIF NOT EXISTS\fP clause. .UNINDENT .sp Create the model table, indexes, constraints and sequences. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C with database: SomeModel.create_table() # Execute the create table query. .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod drop_table([safe=True[, **options]]) .INDENT 7.0 .TP .B Parameters \fBsafe\fP (\fIbool\fP) \-\- If set to \fBTrue\fP, the create table query will include an \fBIF EXISTS\fP clause. .UNINDENT .sp Drop the model table. .UNINDENT .INDENT 7.0 .TP .B truncate_table([restart_identity=False[, cascade=False]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBrestart_identity\fP (\fIbool\fP) \-\- Restart the id sequence (postgres\-only). .IP \(bu 2 \fBcascade\fP (\fIbool\fP) \-\- Truncate related tables as well (postgres\-only). .UNINDENT .UNINDENT .sp Truncate (delete all rows) for the model. .UNINDENT .INDENT 7.0 .TP .B classmethod index(*fields[, unique=False[, safe=True[, where=None[, using=None[, name=None]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBfields\fP \-\- Fields to index. .IP \(bu 2 \fBunique\fP (\fIbool\fP) \-\- Whether index is UNIQUE. .IP \(bu 2 \fBsafe\fP (\fIbool\fP) \-\- Whether to add IF NOT EXISTS clause. .IP \(bu 2 \fBwhere\fP (\fIExpression\fP) \-\- Optional WHERE clause for index. .IP \(bu 2 \fBusing\fP (\fIstr\fP) \-\- Index algorithm. .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Optional index name. .UNINDENT .UNINDENT .sp Expressive method for declaring an index on a model. Wraps the declaration of a \fI\%ModelIndex\fP instance. .sp Examples: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C 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) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod add_index(*args, **kwargs) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBargs\fP \-\- a \fI\%ModelIndex\fP instance, Field(s) to index, or a \fI\%SQL\fP instance that contains the SQL for creating the index. .IP \(bu 2 \fBkwargs\fP \-\- Keyword arguments passed to \fI\%ModelIndex\fP constructor. .UNINDENT .UNINDENT .sp Add an index to the model\(aqs definition. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 This method does not actually create the index in the database. Rather, it adds the index definition to the model\(aqs metadata, so that a subsequent call to \fI\%create_table()\fP will create the new index (along with the table). .UNINDENT .UNINDENT .sp Examples: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C 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(\(aqCREATE INDEX ...\(aq)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B dependencies([search_nullable=False]) .INDENT 7.0 .TP .B Parameters \fBsearch_nullable\fP (\fIbool\fP) \-\- Search models related via a nullable foreign key .TP .B Return type Generator expression yielding queries and foreign key fields. .UNINDENT .sp Generate a list of queries of dependent models. Yields a 2\-tuple containing the query and corresponding foreign key field. Useful for searching dependencies of a model, i.e. things that would be orphaned in the event of a delete. .UNINDENT .INDENT 7.0 .TP .B __iter__() .INDENT 7.0 .TP .B Returns a \fI\%ModelSelect\fP for the given class. .UNINDENT .sp Convenience function for iterating over all instances of a model. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C Setting.insert_many([ {\(aqkey\(aq: \(aqhost\(aq, \(aqvalue\(aq: \(aq192.168.1.2\(aq}, {\(aqkey\(aq: \(aqport\(aq: \(aqvalue\(aq: \(aq1337\(aq}, {\(aqkey\(aq: \(aquser\(aq: \(aqvalue\(aq: \(aqnuggie\(aq}]).execute() # Load settings from db into dict. settings = {setting.key: setting.value for setting in Setting} .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B __len__() .INDENT 7.0 .TP .B Returns Count of rows in table. .UNINDENT .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C n_accounts = len(Account) # Is equivalent to: n_accounts = Account.select().count() .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class ModelAlias(model[, alias=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmodel\fP (\fIModel\fP) \-\- Model class to reference. .IP \(bu 2 \fBalias\fP (\fIstr\fP) \-\- (optional) name for alias. .UNINDENT .UNINDENT .sp Provide a separate reference to a model in a query. .UNINDENT .INDENT 0.0 .TP .B class ModelSelect(model, fields_or_models) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmodel\fP (\fIModel\fP) \-\- Model class to select. .IP \(bu 2 \fBfields_or_models\fP \-\- List of fields or model classes to select. .UNINDENT .UNINDENT .sp Model\-specific implementation of SELECT query. .INDENT 7.0 .TP .B switch([ctx=None]) .INDENT 7.0 .TP .B Parameters \fBctx\fP \-\- A \fI\%Model\fP, \fI\%ModelAlias\fP, subquery, or other object that was joined\-on. .UNINDENT .sp Switch the \fIjoin context\fP \- the source which subsequent calls to \fI\%join()\fP will be joined against. Used for specifying multiple joins against a single table. .sp If the \fBctx\fP is not given, then the query\(aqs model will be used. .sp The following example selects from tweet and joins on both user and tweet\-flag: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C sq = Tweet.select().join(User).switch(Tweet).join(TweetFlag) # Equivalent (since Tweet is the query\(aqs model) sq = Tweet.select().join(User).switch().join(TweetFlag) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B objects([constructor=None]) .INDENT 7.0 .TP .B Parameters \fBconstructor\fP \-\- Constructor (defaults to returning model instances) .UNINDENT .sp Return result rows as objects created using the given constructor. The default behavior is to create model instances. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 This method can be used, when selecting field data from multiple sources/models, to make all data available as attributes on the model being queried (as opposed to constructing the graph of joined model instances). For very complex queries this can have a positive performance impact, especially iterating large result sets. .sp Similarly, you can use \fI\%dicts()\fP, \fI\%tuples()\fP or \fI\%namedtuples()\fP to achieve even more performance. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B join(dest[, join_type=\(aqINNER\(aq[, on=None[, src=None[, attr=None]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdest\fP \-\- A \fI\%Model\fP, \fI\%ModelAlias\fP, \fI\%Select\fP query, or other object to join to. .IP \(bu 2 \fBjoin_type\fP (\fIstr\fP) \-\- Join type, defaults to INNER. .IP \(bu 2 \fBon\fP \-\- Join predicate or a \fI\%ForeignKeyField\fP to join on. .IP \(bu 2 \fBsrc\fP \-\- Explicitly specify the source of the join. If not specified then the current \fIjoin context\fP will be used. .IP \(bu 2 \fBattr\fP (\fIstr\fP) \-\- Attribute to use when projecting columns from the joined model. .UNINDENT .UNINDENT .sp Join with another table\-like object. .sp Join type may be one of: .INDENT 7.0 .IP \(bu 2 \fBJOIN.INNER\fP .IP \(bu 2 \fBJOIN.LEFT_OUTER\fP .IP \(bu 2 \fBJOIN.RIGHT_OUTER\fP .IP \(bu 2 \fBJOIN.FULL\fP .IP \(bu 2 \fBJOIN.FULL_OUTER\fP .IP \(bu 2 \fBJOIN.CROSS\fP .UNINDENT .sp Example selecting tweets and joining on user in order to restrict to only those tweets made by "admin" users: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C sq = Tweet.select().join(User).where(User.is_admin == True) .ft P .fi .UNINDENT .UNINDENT .sp Example selecting users and joining on a particular foreign key field. See the example app for a real\-life usage: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C sq = User.select().join(Relationship, on=Relationship.to_user) .ft P .fi .UNINDENT .UNINDENT .sp For an in\-depth discussion of foreign\-keys, joins and relationships between models, refer to relationships\&. .UNINDENT .INDENT 7.0 .TP .B join_from(src, dest[, join_type=\(aqINNER\(aq[, on=None[, attr=None]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBsrc\fP \-\- Source for join. .IP \(bu 2 \fBdest\fP \-\- Table to join to. .UNINDENT .UNINDENT .sp Use same parameter order as the non\-model\-specific \fI\%join()\fP\&. Bypasses the \fIjoin context\fP by requiring the join source to be specified. .UNINDENT .INDENT 7.0 .TP .B filter(*args, **kwargs) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBargs\fP \-\- Zero or more \fI\%DQ\fP objects. .IP \(bu 2 \fBkwargs\fP \-\- Django\-style keyword\-argument filters. .UNINDENT .UNINDENT .sp Use Django\-style filters to express a WHERE clause. .UNINDENT .INDENT 7.0 .TP .B prefetch(*subqueries) .INDENT 7.0 .TP .B Parameters \fBsubqueries\fP \-\- A list of \fI\%Model\fP classes or select queries to prefetch. .TP .B Returns a list of models with selected relations prefetched. .UNINDENT .sp Execute the query, prefetching the given additional resources. .sp See also \fI\%prefetch()\fP standalone function. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # 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(\(aq *\(aq, tweet.content) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Because \fBprefetch\fP must reconstruct a graph of models, it is necessary to be sure that the foreign\-key/primary\-key of any related models are selected, so that the related objects can be mapped correctly. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B prefetch(sq, *subqueries) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBsq\fP \-\- Query to use as starting\-point. .IP \(bu 2 \fBsubqueries\fP \-\- One or more models or \fI\%ModelSelect\fP queries to eagerly fetch. .UNINDENT .TP .B Returns a list of models with selected relations prefetched. .UNINDENT .sp Eagerly fetch related objects, allowing efficient querying of multiple tables when a 1\-to\-many relationship exists. .sp For example, it is simple to query a many\-to\-1 relationship efficiently: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C 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\(aqs columns were selected. print(tweet.user.username, \(aq\->\(aq, tweet.content) .ft P .fi .UNINDENT .UNINDENT .sp To efficiently do the inverse, query users and their tweets, you can use prefetch: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C query = User.select() for user in prefetch(query, Tweet): print(user.username) for tweet in user.tweets: # Does not require additional query. print(\(aq \(aq, tweet.content) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Because \fBprefetch\fP must reconstruct a graph of models, it is necessary to be sure that the foreign\-key/primary\-key of any related models are selected, so that the related objects can be mapped correctly. .UNINDENT .UNINDENT .UNINDENT .SS Query\-builder Internals .INDENT 0.0 .TP .B class AliasManager Manages the aliases assigned to \fI\%Source\fP objects in SELECT queries, so as to avoid ambiguous references when multiple sources are used in a single query. .INDENT 7.0 .TP .B add(source) Add a source to the AliasManager\(aqs internal registry at the current scope. The alias will be automatically generated using the following scheme (where each level of indentation refers to a new scope): .INDENT 7.0 .TP .B Parameters \fBsource\fP (\fISource\fP) \-\- Make the manager aware of a new source. If the source has already been added, the call is a no\-op. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B get(source[, any_depth=False]) Return the alias for the source in the current scope. If the source does not have an alias, it will be given the next available alias. .INDENT 7.0 .TP .B Parameters \fBsource\fP (\fISource\fP) \-\- The source whose alias should be retrieved. .TP .B Returns The alias already assigned to the source, or the next available alias. .TP .B Return type str .UNINDENT .UNINDENT .INDENT 7.0 .TP .B __setitem__(source, alias) Manually set the alias for the source at the current scope. .INDENT 7.0 .TP .B Parameters \fBsource\fP (\fISource\fP) \-\- The source for which we set the alias. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B push() Push a new scope onto the stack. .UNINDENT .INDENT 7.0 .TP .B pop() Pop scope from the stack. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class State(scope[, parentheses=False[, subquery=False[, **kwargs]]]) Lightweight object for representing the state at a given scope. During SQL generation, each object visited by the \fI\%Context\fP can inspect the state. The \fI\%State\fP class allows Peewee to do things like: .INDENT 7.0 .IP \(bu 2 Use a common interface for field types or SQL expressions, but use vendor\-specific data\-types or operators. .IP \(bu 2 Compile a \fI\%Column\fP instance into a fully\-qualified attribute, as a named alias, etc, depending on the value of the \fBscope\fP\&. .IP \(bu 2 Ensure parentheses are used appropriately. .UNINDENT .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBscope\fP (\fIint\fP) \-\- The scope rules to be applied while the state is active. .IP \(bu 2 \fBparentheses\fP (\fIbool\fP) \-\- Wrap the contained SQL in parentheses. .IP \(bu 2 \fBsubquery\fP (\fIbool\fP) \-\- Whether the current state is a child of an outer query. .IP \(bu 2 \fBkwargs\fP (\fIdict\fP) \-\- Arbitrary settings which should be applied in the current state. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Context(**settings) Converts Peewee structures into parameterized SQL queries. .sp Peewee structures should all implement a \fI__sql__\fP method, which will be called by the \fIContext\fP class during SQL generation. The \fI__sql__\fP method accepts a single parameter, the \fIContext\fP instance, which allows for recursive descent and introspection of scope and state. .INDENT 7.0 .TP .B scope Return the currently\-active scope rules. .UNINDENT .INDENT 7.0 .TP .B parentheses Return whether the current state is wrapped in parentheses. .UNINDENT .INDENT 7.0 .TP .B subquery Return whether the current state is the child of another query. .UNINDENT .INDENT 7.0 .TP .B scope_normal([**kwargs]) The default scope. Sources are referred to by alias, columns by dotted\-path from the source. .UNINDENT .INDENT 7.0 .TP .B scope_source([**kwargs]) Scope used when defining sources, e.g. in the column list and FROM clause of a SELECT query. This scope is used for defining the fully\-qualified name of the source and assigning an alias. .UNINDENT .INDENT 7.0 .TP .B scope_values([**kwargs]) Scope used for UPDATE, INSERT or DELETE queries, where instead of referencing a source by an alias, we refer to it directly. Similarly, since there is a single table, columns do not need to be referenced by dotted\-path. .UNINDENT .INDENT 7.0 .TP .B scope_cte([**kwargs]) Scope used when generating the contents of a common\-table\-expression. Used after a WITH statement, when generating the definition for a CTE (as opposed to merely a reference to one). .UNINDENT .INDENT 7.0 .TP .B scope_column([**kwargs]) Scope used when generating SQL for a column. Ensures that the column is rendered with it\(aqs correct alias. Was needed because when referencing the inner projection of a sub\-select, Peewee would render the full SELECT query as the "source" of the column (instead of the query\(aqs alias + . + column). This scope allows us to avoid rendering the full query when we only need the alias. .UNINDENT .INDENT 7.0 .TP .B sql(obj) Append a composable Node object, sub\-context, or other object to the query AST. Python values, such as integers, strings, floats, etc. are treated as parameterized values. .INDENT 7.0 .TP .B Returns The updated Context object. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B literal(keyword) Append a string\-literal to the current query AST. .INDENT 7.0 .TP .B Returns The updated Context object. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B parse(node) .INDENT 7.0 .TP .B Parameters \fBnode\fP (\fINode\fP) \-\- Instance of a Node subclass. .TP .B Returns a 2\-tuple consisting of (sql, parameters). .UNINDENT .sp Convert the given node to a SQL AST and return a 2\-tuple consisting of the SQL query and the parameters. .UNINDENT .INDENT 7.0 .TP .B query() .INDENT 7.0 .TP .B Returns a 2\-tuple consisting of (sql, parameters) for the context. .UNINDENT .UNINDENT .UNINDENT .SS Constants and Helpers .INDENT 0.0 .TP .B class Proxy Create a proxy or placeholder for another object. .INDENT 7.0 .TP .B initialize(obj) .INDENT 7.0 .TP .B Parameters \fBobj\fP \-\- Object to proxy to. .UNINDENT .sp Bind the proxy to the given object. Afterwards all attribute lookups and method calls on the proxy will be sent to the given object. .sp Any callbacks that have been registered will be called. .UNINDENT .INDENT 7.0 .TP .B attach_callback(callback) .INDENT 7.0 .TP .B Parameters \fBcallback\fP \-\- A function that accepts a single parameter, the bound object. .TP .B Returns self .UNINDENT .sp Add a callback to be executed when the proxy is initialized. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class DatabaseProxy Proxy subclass that is suitable to use as a placeholder for a \fI\%Database\fP instance. .sp See dynamic_db for details on usage. .UNINDENT .INDENT 0.0 .TP .B chunked(iterable, n) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBiterable\fP \-\- an iterable that is the source of the data to be chunked. .IP \(bu 2 \fBn\fP (\fIint\fP) \-\- chunk size .UNINDENT .TP .B Returns a new iterable that yields \fIn\fP\-length chunks of the source data. .UNINDENT .sp Efficient implementation for breaking up large lists of data into smaller\-sized chunks. .sp Usage: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C it = range(10) # An iterable that yields 0...9. # Break the iterable into chunks of length 4. for chunk in chunked(it, 4): print(\(aq, \(aq.join(str(num) for num in chunk)) # PRINTS: # 0, 1, 2, 3 # 4, 5, 6, 7 # 8, 9 .ft P .fi .UNINDENT .UNINDENT .UNINDENT .SS SQLite Extensions .sp The default \fBSqliteDatabase\fP already includes many SQLite\-specific features: .INDENT 0.0 .IP \(bu 2 General notes on using SQLite\&. .IP \(bu 2 Configuring SQLite using PRAGMA statements\&. .IP \(bu 2 User\-defined functions, aggregate and collations\&. .IP \(bu 2 Locking modes for transactions\&. .UNINDENT .sp The \fBplayhouse.sqlite_ext\fP includes even more SQLite features, including: .INDENT 0.0 .IP \(bu 2 \fI\%Full\-text search\fP .IP \(bu 2 \fI\%JSON extension integration\fP .IP \(bu 2 \fI\%Closure table extension support\fP .IP \(bu 2 \fI\%LSM1 extension support\fP .IP \(bu 2 \fI\%User\-defined table functions\fP .IP \(bu 2 Support for online backups using backup API: \fI\%backup_to_file()\fP .IP \(bu 2 \fI\%BLOB API support, for efficient binary data storage\fP\&. .IP \(bu 2 \fI\%Additional helpers\fP, including bloom filter, more. .UNINDENT .SS Getting started .sp To get started with the features described in this document, you will want to use the \fI\%SqliteExtDatabase\fP class from the \fBplayhouse.sqlite_ext\fP module. Furthermore, some features require the \fBplayhouse._sqlite_ext\fP C extension \-\- these features will be noted in the documentation. .sp Instantiating a \fI\%SqliteExtDatabase\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.sqlite_ext import SqliteExtDatabase db = SqliteExtDatabase(\(aqmy_app.db\(aq, pragmas=( (\(aqcache_size\(aq, \-1024 * 64), # 64MB page\-cache. (\(aqjournal_mode\(aq, \(aqwal\(aq), # Use WAL\-mode (you should always use this!). (\(aqforeign_keys\(aq, 1))) # Enforce foreign\-key constraints. .ft P .fi .UNINDENT .UNINDENT .SS APIs .INDENT 0.0 .TP .B class SqliteExtDatabase(database[, pragmas=None[, timeout=5[, c_extensions=None[, rank_functions=True[, hash_functions=False[, regexp_function=False[, bloomfilter=False]]]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBpragmas\fP (\fIlist\fP) \-\- A list of 2\-tuples containing pragma key and value to set every time a connection is opened. .IP \(bu 2 \fBtimeout\fP \-\- Set the busy\-timeout on the SQLite driver (in seconds). .IP \(bu 2 \fBc_extensions\fP (\fIbool\fP) \-\- Declare that C extension speedups must/must\-not be used. If set to \fBTrue\fP and the extension module is not available, will raise an \fBImproperlyConfigured\fP exception. .IP \(bu 2 \fBrank_functions\fP (\fIbool\fP) \-\- Make search result ranking functions available. .IP \(bu 2 \fBhash_functions\fP (\fIbool\fP) \-\- Make hashing functions available (md5, sha1, etc). .IP \(bu 2 \fBregexp_function\fP (\fIbool\fP) \-\- Make the REGEXP function available. .IP \(bu 2 \fBbloomfilter\fP (\fIbool\fP) \-\- Make the \fI\%bloom filter\fP available. .UNINDENT .UNINDENT .sp Extends \fBSqliteDatabase\fP and inherits methods for declaring user\-defined functions, pragmas, etc. .UNINDENT .INDENT 0.0 .TP .B class CSqliteExtDatabase(database[, pragmas=None[, timeout=5[, c_extensions=None[, rank_functions=True[, hash_functions=False[, regexp_function=False[, bloomfilter=False[, replace_busy_handler=False]]]]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBpragmas\fP (\fIlist\fP) \-\- A list of 2\-tuples containing pragma key and value to set every time a connection is opened. .IP \(bu 2 \fBtimeout\fP \-\- Set the busy\-timeout on the SQLite driver (in seconds). .IP \(bu 2 \fBc_extensions\fP (\fIbool\fP) \-\- Declare that C extension speedups must/must\-not be used. If set to \fBTrue\fP and the extension module is not available, will raise an \fBImproperlyConfigured\fP exception. .IP \(bu 2 \fBrank_functions\fP (\fIbool\fP) \-\- Make search result ranking functions available. .IP \(bu 2 \fBhash_functions\fP (\fIbool\fP) \-\- Make hashing functions available (md5, sha1, etc). .IP \(bu 2 \fBregexp_function\fP (\fIbool\fP) \-\- Make the REGEXP function available. .IP \(bu 2 \fBbloomfilter\fP (\fIbool\fP) \-\- Make the \fI\%bloom filter\fP available. .IP \(bu 2 \fBreplace_busy_handler\fP (\fIbool\fP) \-\- Use a smarter busy\-handler implementation. .UNINDENT .UNINDENT .sp Extends \fI\%SqliteExtDatabase\fP and requires that the \fBplayhouse._sqlite_ext\fP extension module be available. .INDENT 7.0 .TP .B on_commit(fn) Register a callback to be executed whenever a transaction is committed on the current connection. The callback accepts no parameters and the return value is ignored. .sp However, if the callback raises a \fBValueError\fP, the transaction will be aborted and rolled\-back. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C db = CSqliteExtDatabase(\(aq:memory:\(aq) @db.on_commit def on_commit(): logger.info(\(aqCOMMITing changes\(aq) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B on_rollback(fn) Register a callback to be executed whenever a transaction is rolled back on the current connection. The callback accepts no parameters and the return value is ignored. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C @db.on_rollback def on_rollback(): logger.info(\(aqRolling back changes\(aq) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B on_update(fn) Register a callback to be executed whenever the database is written to (via an \fIUPDATE\fP, \fIINSERT\fP or \fIDELETE\fP query). The callback should accept the following parameters: .INDENT 7.0 .IP \(bu 2 \fBquery\fP \- the type of query, either \fIINSERT\fP, \fIUPDATE\fP or \fIDELETE\fP\&. .IP \(bu 2 database name \- the default database is named \fImain\fP\&. .IP \(bu 2 table name \- name of table being modified. .IP \(bu 2 rowid \- the rowid of the row being modified. .UNINDENT .sp The callback\(aqs return value is ignored. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C db = CSqliteExtDatabase(\(aq:memory:\(aq) @db.on_update def on_update(query_type, db, table, rowid): # e.g. INSERT row 3 into table users. logger.info(\(aq%s row %s into table %s\(aq, query_type, rowid, table) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B changes() Return the number of rows modified in the currently\-open transaction. .UNINDENT .INDENT 7.0 .TP .B autocommit Property which returns a boolean indicating if autocommit is enabled. By default, this value will be \fBTrue\fP except when inside a transaction (or \fBatomic()\fP block). .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> db = CSqliteExtDatabase(\(aq:memory:\(aq) >>> db.autocommit True >>> with db.atomic(): \&... print(db.autocommit) \&... False >>> db.autocommit True .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B backup(destination[, pages=None, name=None, progress=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdestination\fP (\fISqliteDatabase\fP) \-\- Database object to serve as destination for the backup. .IP \(bu 2 \fBpages\fP (\fIint\fP) \-\- Number of pages per iteration. Default value of \-1 indicates all pages should be backed\-up in a single step. .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Name of source database (may differ if you used ATTACH DATABASE to load multiple databases). Defaults to "main". .IP \(bu 2 \fBprogress\fP \-\- Progress callback, called with three parameters: the number of pages remaining, the total page count, and whether the backup is complete. .UNINDENT .UNINDENT .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C master = CSqliteExtDatabase(\(aqmaster.db\(aq) replica = CSqliteExtDatabase(\(aqreplica.db\(aq) # Backup the contents of master to replica. master.backup(replica) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B backup_to_file(filename[, pages, name, progress]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBfilename\fP \-\- Filename to store the database backup. .IP \(bu 2 \fBpages\fP (\fIint\fP) \-\- Number of pages per iteration. Default value of \-1 indicates all pages should be backed\-up in a single step. .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Name of source database (may differ if you used ATTACH DATABASE to load multiple databases). Defaults to "main". .IP \(bu 2 \fBprogress\fP \-\- Progress callback, called with three parameters: the number of pages remaining, the total page count, and whether the backup is complete. .UNINDENT .UNINDENT .sp Backup the current database to a file. The backed\-up data is not a database dump, but an actual SQLite database file. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C db = CSqliteExtDatabase(\(aqapp.db\(aq) def nightly_backup(): filename = \(aqbackup\-%s.db\(aq % (datetime.date.today()) db.backup_to_file(filename) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B blob_open(table, column, rowid[, read_only=False]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Name of table containing data. .IP \(bu 2 \fBcolumn\fP (\fIstr\fP) \-\- Name of column containing data. .IP \(bu 2 \fBrowid\fP (\fIint\fP) \-\- ID of row to retrieve. .IP \(bu 2 \fBread_only\fP (\fIbool\fP) \-\- Open the blob for reading only. .UNINDENT .TP .B Returns \fI\%Blob\fP instance which provides efficient access to the underlying binary data. .TP .B Return type Blob .UNINDENT .sp See \fI\%Blob\fP and \fI\%ZeroBlob\fP for more information. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class Image(Model): filename = TextField() data = BlobField() buf_size = 1024 * 1024 * 8 # Allocate 8MB for storing file. rowid = Image.insert({Image.filename: \(aqthefile.jpg\(aq, Image.data: ZeroBlob(buf_size)}).execute() # Open the blob, returning a file\-like object. blob = db.blob_open(\(aqimage\(aq, \(aqdata\(aq, rowid) # Write some data to the blob. blob.write(image_data) img_size = blob.tell() # Read the data back out of the blob. blob.seek(0) image_data = blob.read(img_size) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class RowIDField Primary\-key field that corresponds to the SQLite \fBrowid\fP field. For more information, see the SQLite documentation on \fI\%rowid tables\fP\&.. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class Note(Model): rowid = RowIDField() # Will be primary key. content = TextField() timestamp = TimestampField() .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class DocIDField Subclass of \fI\%RowIDField\fP for use on virtual tables that specifically use the convention of \fBdocid\fP for the primary key. As far as I know this only pertains to tables using the FTS3 and FTS4 full\-text search extensions. .sp \fBATTENTION:\fP .INDENT 7.0 .INDENT 3.5 In FTS3 and FTS4, "docid" is simply an alias for "rowid". To reduce confusion, it\(aqs probably best to just always use \fI\%RowIDField\fP and never use \fI\%DocIDField\fP\&. .UNINDENT .UNINDENT .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class NoteIndex(FTSModel): docid = DocIDField() # "docid" is used as an alias for "rowid". content = SearchField() class Meta: database = db .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class AutoIncrementField SQLite, by default, may reuse primary key values after rows are deleted. To ensure that the primary key is \fIalways\fP monotonically increasing, regardless of deletions, you should use \fI\%AutoIncrementField\fP\&. There is a small performance cost for this feature. For more information, see the SQLite docs on \fI\%autoincrement\fP\&. .UNINDENT .INDENT 0.0 .TP .B class JSONField(json_dumps=None, json_loads=None, \&...) Field class suitable for storing JSON data, with special methods designed to work with the \fI\%json1 extension\fP\&. .sp SQLite 3.9.0 added \fI\%JSON support\fP in the form of an extension library. The SQLite json1 extension provides a number of helper functions for working with JSON data. These APIs are exposed as methods of a special field\-type, \fI\%JSONField\fP\&. .sp To access or modify specific object keys or array indexes in a JSON structure, you can treat the \fI\%JSONField\fP as if it were a dictionary/list. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBjson_dumps\fP \-\- (optional) function for serializing data to JSON strings. If not provided, will use the stdlib \fBjson.dumps\fP\&. .IP \(bu 2 \fBjson_loads\fP \-\- (optional) function for de\-serializing JSON to Python objects. If not provided, will use the stdlib \fBjson.loads\fP\&. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 To customize the JSON serialization or de\-serialization, you can specify a custom \fBjson_dumps\fP and \fBjson_loads\fP callables. These functions should accept a single parameter: the object to serialize, and the JSON string, respectively. To modify the parameters of the stdlib JSON functions, you can use \fBfunctools.partial\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Do not escape unicode code\-points. my_json_dumps = functools.partial(json.dumps, ensure_ascii=False) class SomeModel(Model): # Specify our custom serialization function. json_data = JSONField(json_dumps=my_json_dumps) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp Let\(aqs look at some examples of using the SQLite json1 extension with Peewee. Here we\(aqll prepare a database and a simple model for testing the \fI\%json1 extension\fP: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> from playhouse.sqlite_ext import * >>> db = SqliteExtDatabase(\(aq:memory:\(aq) >>> class KV(Model): \&... key = TextField() \&... value = JSONField() \&... class Meta: \&... database = db \&... >>> KV.create_table() .ft P .fi .UNINDENT .UNINDENT .sp Storing data works as you might expect. There\(aqs no need to serialize dictionaries or lists as JSON, as this is done automatically by Peewee: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> KV.create(key=\(aqa\(aq, value={\(aqk1\(aq: \(aqv1\(aq}) >>> KV.get(KV.key == \(aqa\(aq).value {\(aqk1\(aq: \(aqv1\(aq} .ft P .fi .UNINDENT .UNINDENT .sp We can access specific parts of the JSON data using dictionary lookups: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> KV.get(KV.value[\(aqk1\(aq] == \(aqv1\(aq).key \(aqa\(aq .ft P .fi .UNINDENT .UNINDENT .sp It\(aqs possible to update a JSON value in\-place using the \fI\%update()\fP method. Note that "k1=v1" is preserved: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> KV.update(value=KV.value.update({\(aqk2\(aq: \(aqv2\(aq, \(aqk3\(aq: \(aqv3\(aq})).execute() 1 >>> KV.get(KV.key == \(aqa\(aq).value {\(aqk1\(aq: \(aqv1\(aq, \(aqk2\(aq: \(aqv2\(aq, \(aqk3\(aq: \(aqv3\(aq} .ft P .fi .UNINDENT .UNINDENT .sp We can also update existing data atomically, or remove keys by setting their value to \fBNone\fP\&. In the following example, we\(aqll update the value of "k1" and remove "k3" ("k2" will not be modified): .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> KV.update(value=KV.value.update({\(aqk1\(aq: \(aqv1\-x\(aq, \(aqk3\(aq: None})).execute() 1 >>> KV.get(KV.key == \(aqa\(aq).value {\(aqk1\(aq: \(aqv1\-x\(aq, \(aqk2\(aq: \(aqv2\(aq} .ft P .fi .UNINDENT .UNINDENT .sp We can also set individual parts of the JSON data using the \fI\%set()\fP method: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> KV.update(value=KV.value[\(aqk1\(aq].set(\(aqv1\(aq)).execute() 1 >>> KV.get(KV.key == \(aqa\(aq).value {\(aqk1\(aq: \(aqv1\(aq, \(aqk2\(aq: \(aqv2\(aq} .ft P .fi .UNINDENT .UNINDENT .sp The \fI\%set()\fP method can also be used with objects, in addition to scalar values: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> KV.update(value=KV.value[\(aqk2\(aq].set({\(aqx2\(aq: \(aqy2\(aq})).execute() 1 >>> KV.get(KV.key == \(aqa\(aq).value {\(aqk1\(aq: \(aqv1\(aq, \(aqk2\(aq: {\(aqx2\(aq: \(aqy2\(aq}} .ft P .fi .UNINDENT .UNINDENT .sp Individual parts of the JSON data can be removed atomically as well, using \fI\%remove()\fP: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> KV.update(value=KV.value[\(aqk2\(aq].remove()).execute() 1 >>> KV.get(KV.key == \(aqa\(aq).value {\(aqk1\(aq: \(aqv1\(aq} .ft P .fi .UNINDENT .UNINDENT .sp We can also get the type of value stored at a specific location in the JSON data using the \fI\%json_type()\fP method: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> KV.select(KV.value.json_type(), KV.value[\(aqk1\(aq].json_type()).tuples()[:] [(\(aqobject\(aq, \(aqtext\(aq)] .ft P .fi .UNINDENT .UNINDENT .sp Let\(aqs add a nested value and then see how to iterate through it\(aqs contents recursively using the \fI\%tree()\fP method: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> KV.create(key=\(aqb\(aq, value={\(aqx1\(aq: {\(aqy1\(aq: \(aqz1\(aq, \(aqy2\(aq: \(aqz2\(aq}, \(aqx2\(aq: [1, 2]}) >>> tree = KV.value.tree().alias(\(aqtree\(aq) >>> query = KV.select(KV.key, tree.c.fullkey, tree.c.value).from_(KV, tree) >>> query.tuples()[:] [(\(aqa\(aq, \(aq$\(aq, {\(aqk1\(aq: \(aqv1\(aq}), (\(aqa\(aq, \(aq$.k1\(aq, \(aqv1\(aq), (\(aqb\(aq, \(aq$\(aq, {\(aqx1\(aq: {\(aqy1\(aq: \(aqz1\(aq, \(aqy2\(aq: \(aqz2\(aq}, \(aqx2\(aq: [1, 2]}), (\(aqb\(aq, \(aq$.x2\(aq, [1, 2]), (\(aqb\(aq, \(aq$.x2[0]\(aq, 1), (\(aqb\(aq, \(aq$.x2[1]\(aq, 2), (\(aqb\(aq, \(aq$.x1\(aq, {\(aqy1\(aq: \(aqz1\(aq, \(aqy2\(aq: \(aqz2\(aq}), (\(aqb\(aq, \(aq$.x1.y1\(aq, \(aqz1\(aq), (\(aqb\(aq, \(aq$.x1.y2\(aq, \(aqz2\(aq)] .ft P .fi .UNINDENT .UNINDENT .sp The \fI\%tree()\fP and \fI\%children()\fP methods are powerful. For more information on how to utilize them, see the \fI\%json1 extension documentation\fP\&. .sp Also note, that \fI\%JSONField\fP lookups can be chained: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> query = KV.select().where(KV.value[\(aqx1\(aq][\(aqy1\(aq] == \(aqz1\(aq) >>> for obj in query: \&... print(obj.key, obj.value) \&... \(aqb\(aq, {\(aqx1\(aq: {\(aqy1\(aq: \(aqz1\(aq, \(aqy2\(aq: \(aqz2\(aq}, \(aqx2\(aq: [1, 2]} .ft P .fi .UNINDENT .UNINDENT .sp For more information, refer to the \fI\%sqlite json1 documentation\fP\&. .INDENT 7.0 .TP .B __getitem__(item) .INDENT 7.0 .TP .B Parameters \fBitem\fP \-\- Access a specific key or array index in the JSON data. .TP .B Returns a special object exposing access to the JSON data. .TP .B Return type JSONPath .UNINDENT .sp Access a specific key or array index in the JSON data. Returns a \fI\%JSONPath\fP object, which exposes convenient methods for reading or modifying a particular part of a JSON object. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # If metadata contains {"tags": ["list", "of", "tags"]}, we can # extract the first tag in this way: Post.select(Post, Post.metadata[\(aqtags\(aq][0].alias(\(aqfirst_tag\(aq)) .ft P .fi .UNINDENT .UNINDENT .sp For more examples see the \fI\%JSONPath\fP API documentation. .UNINDENT .INDENT 7.0 .TP .B set(value[, as_json=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBvalue\fP \-\- a scalar value, list, or dictionary. .IP \(bu 2 \fBas_json\fP (\fIbool\fP) \-\- force the value to be treated as JSON, in which case it will be serialized as JSON in Python beforehand. By default, lists and dictionaries are treated as JSON to be serialized, while strings and integers are passed as\-is. .UNINDENT .UNINDENT .sp Set the value stored in a \fI\%JSONField\fP\&. .sp Uses the \fI\%json_set()\fP function from the json1 extension. .UNINDENT .INDENT 7.0 .TP .B update(data) .INDENT 7.0 .TP .B Parameters \fBdata\fP \-\- a scalar value, list or dictionary to merge with the data currently stored in a \fI\%JSONField\fP\&. To remove a particular key, set that key to \fBNone\fP in the updated data. .UNINDENT .sp Merge new data into the JSON value using the RFC\-7396 MergePatch algorithm to apply a patch (\fBdata\fP parameter) against the column data. MergePatch can add, modify, or delete elements of a JSON object, which means \fI\%update()\fP is a generalized replacement for both \fI\%set()\fP and \fI\%remove()\fP\&. MergePatch treats JSON array objects as atomic, so \fBupdate()\fP cannot append to an array, nor modify individual elements of an array. .sp For more information as well as examples, see the SQLite \fI\%json_patch()\fP function documentation. .UNINDENT .INDENT 7.0 .TP .B remove() Remove the data stored in the \fI\%JSONField\fP\&. .sp Uses the \fI\%json_remove\fP function from the json1 extension. .UNINDENT .INDENT 7.0 .TP .B json_type() Return a string identifying the type of value stored in the column. .sp The type returned will be one of: .INDENT 7.0 .IP \(bu 2 object .IP \(bu 2 array .IP \(bu 2 integer .IP \(bu 2 real .IP \(bu 2 true .IP \(bu 2 false .IP \(bu 2 text .IP \(bu 2 null <\-\- the string "null" means an actual NULL value .IP \(bu 2 NULL <\-\- an actual NULL value means the path was not found .UNINDENT .sp Uses the \fI\%json_type\fP function from the json1 extension. .UNINDENT .INDENT 7.0 .TP .B length() Return the length of the array stored in the column. .sp Uses the \fI\%json_array_length\fP function from the json1 extension. .UNINDENT .INDENT 7.0 .TP .B children() The \fBchildren\fP function corresponds to \fBjson_each\fP, a table\-valued function that walks the JSON value provided and returns the immediate children of the top\-level array or object. If a path is specified, then that path is treated as the top\-most element. .sp The rows returned by calls to \fBchildren()\fP have the following attributes: .INDENT 7.0 .IP \(bu 2 \fBkey\fP: the key of the current element relative to its parent. .IP \(bu 2 \fBvalue\fP: the value of the current element. .IP \(bu 2 \fBtype\fP: one of the data\-types (see \fI\%json_type()\fP). .IP \(bu 2 \fBatom\fP: the scalar value for primitive types, \fBNULL\fP for arrays and objects. .IP \(bu 2 \fBid\fP: a unique ID referencing the current node in the tree. .IP \(bu 2 \fBparent\fP: the ID of the containing node. .IP \(bu 2 \fBfullkey\fP: the full path describing the current element. .IP \(bu 2 \fBpath\fP: the path to the container of the current row. .UNINDENT .sp Internally this method uses the \fI\%json_each\fP (documentation link) function from the json1 extension. .sp Example usage (compare to \fI\%tree()\fP method): .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class KeyData(Model): key = TextField() data = JSONField() KeyData.create(key=\(aqa\(aq, data={\(aqk1\(aq: \(aqv1\(aq, \(aqx1\(aq: {\(aqy1\(aq: \(aqz1\(aq}}) KeyData.create(key=\(aqb\(aq, data={\(aqx1\(aq: {\(aqy1\(aq: \(aqz1\(aq, \(aqy2\(aq: \(aqz2\(aq}}) # We will query the KeyData model for the key and all the # top\-level keys and values in it\(aqs data field. kd = KeyData.data.children().alias(\(aqchildren\(aq) query = (KeyData .select(kd.c.key, kd.c.value, kd.c.fullkey) .from_(KeyData, kd) .order_by(kd.c.key) .tuples()) print(query[:]) # PRINTS: [(\(aqa\(aq, \(aqk1\(aq, \(aqv1\(aq, \(aq$.k1\(aq), (\(aqa\(aq, \(aqx1\(aq, \(aq{"y1":"z1"}\(aq, \(aq$.x1\(aq), (\(aqb\(aq, \(aqx1\(aq, \(aq{"y1":"z1","y2":"z2"}\(aq, \(aq$.x1\(aq)] .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B tree() The \fBtree\fP function corresponds to \fBjson_tree\fP, a table\-valued function that recursively walks the JSON value provided and returns information about the keys at each level. If a path is specified, then that path is treated as the top\-most element. .sp The rows returned by calls to \fBtree()\fP have the same attributes as rows returned by calls to \fI\%children()\fP: .INDENT 7.0 .IP \(bu 2 \fBkey\fP: the key of the current element relative to its parent. .IP \(bu 2 \fBvalue\fP: the value of the current element. .IP \(bu 2 \fBtype\fP: one of the data\-types (see \fI\%json_type()\fP). .IP \(bu 2 \fBatom\fP: the scalar value for primitive types, \fBNULL\fP for arrays and objects. .IP \(bu 2 \fBid\fP: a unique ID referencing the current node in the tree. .IP \(bu 2 \fBparent\fP: the ID of the containing node. .IP \(bu 2 \fBfullkey\fP: the full path describing the current element. .IP \(bu 2 \fBpath\fP: the path to the container of the current row. .UNINDENT .sp Internally this method uses the \fI\%json_tree\fP (documentation link) function from the json1 extension. .sp Example usage: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class KeyData(Model): key = TextField() data = JSONField() KeyData.create(key=\(aqa\(aq, data={\(aqk1\(aq: \(aqv1\(aq, \(aqx1\(aq: {\(aqy1\(aq: \(aqz1\(aq}}) KeyData.create(key=\(aqb\(aq, data={\(aqx1\(aq: {\(aqy1\(aq: \(aqz1\(aq, \(aqy2\(aq: \(aqz2\(aq}}) # We will query the KeyData model for the key and all the # keys and values in it\(aqs data field, recursively. kd = KeyData.data.tree().alias(\(aqtree\(aq) query = (KeyData .select(kd.c.key, kd.c.value, kd.c.fullkey) .from_(KeyData, kd) .order_by(kd.c.key) .tuples()) print(query[:]) # PRINTS: [(\(aqa\(aq, None, \(aq{"k1":"v1","x1":{"y1":"z1"}}\(aq, \(aq$\(aq), (\(aqb\(aq, None, \(aq{"x1":{"y1":"z1","y2":"z2"}}\(aq, \(aq$\(aq), (\(aqa\(aq, \(aqk1\(aq, \(aqv1\(aq, \(aq$.k1\(aq), (\(aqa\(aq, \(aqx1\(aq, \(aq{"y1":"z1"}\(aq, \(aq$.x1\(aq), (\(aqb\(aq, \(aqx1\(aq, \(aq{"y1":"z1","y2":"z2"}\(aq, \(aq$.x1\(aq), (\(aqa\(aq, \(aqy1\(aq, \(aqz1\(aq, \(aq$.x1.y1\(aq), (\(aqb\(aq, \(aqy1\(aq, \(aqz1\(aq, \(aq$.x1.y1\(aq), (\(aqb\(aq, \(aqy2\(aq, \(aqz2\(aq, \(aq$.x1.y2\(aq)] .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class JSONPath(field[, path=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBfield\fP (\fIJSONField\fP) \-\- the field object we intend to access. .IP \(bu 2 \fBpath\fP (\fItuple\fP) \-\- Components comprising the JSON path. .UNINDENT .UNINDENT .sp A convenient, Pythonic way of representing JSON paths for use with \fI\%JSONField\fP\&. .sp The \fBJSONPath\fP object implements \fB__getitem__\fP, accumulating path components, which it can turn into the corresponding json\-path expression. .INDENT 7.0 .TP .B __getitem__(item) .INDENT 7.0 .TP .B Parameters \fBitem\fP \-\- Access a sub\-key key or array index. .TP .B Returns a \fI\%JSONPath\fP representing the new path. .UNINDENT .sp Access a sub\-key or array index in the JSON data. Returns a \fI\%JSONPath\fP object, which exposes convenient methods for reading or modifying a particular part of a JSON object. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # If metadata contains {"tags": ["list", "of", "tags"]}, we can # extract the first tag in this way: first_tag = Post.metadata[\(aqtags\(aq][0] query = (Post .select(Post, first_tag.alias(\(aqfirst_tag\(aq)) .order_by(first_tag)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B set(value[, as_json=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBvalue\fP \-\- a scalar value, list, or dictionary. .IP \(bu 2 \fBas_json\fP (\fIbool\fP) \-\- force the value to be treated as JSON, in which case it will be serialized as JSON in Python beforehand. By default, lists and dictionaries are treated as JSON to be serialized, while strings and integers are passed as\-is. .UNINDENT .UNINDENT .sp Set the value at the given location in the JSON data. .sp Uses the \fI\%json_set()\fP function from the json1 extension. .UNINDENT .INDENT 7.0 .TP .B update(data) .INDENT 7.0 .TP .B Parameters \fBdata\fP \-\- a scalar value, list or dictionary to merge with the data at the given location in the JSON data. To remove a particular key, set that key to \fBNone\fP in the updated data. .UNINDENT .sp Merge new data into the JSON value using the RFC\-7396 MergePatch algorithm to apply a patch (\fBdata\fP parameter) against the column data. MergePatch can add, modify, or delete elements of a JSON object, which means \fI\%update()\fP is a generalized replacement for both \fI\%set()\fP and \fI\%remove()\fP\&. MergePatch treats JSON array objects as atomic, so \fBupdate()\fP cannot append to an array, nor modify individual elements of an array. .sp For more information as well as examples, see the SQLite \fI\%json_patch()\fP function documentation. .UNINDENT .INDENT 7.0 .TP .B remove() Remove the data stored in at the given location in the JSON data. .sp Uses the \fI\%json_type\fP function from the json1 extension. .UNINDENT .INDENT 7.0 .TP .B json_type() Return a string identifying the type of value stored at the given location in the JSON data. .sp The type returned will be one of: .INDENT 7.0 .IP \(bu 2 object .IP \(bu 2 array .IP \(bu 2 integer .IP \(bu 2 real .IP \(bu 2 true .IP \(bu 2 false .IP \(bu 2 text .IP \(bu 2 null <\-\- the string "null" means an actual NULL value .IP \(bu 2 NULL <\-\- an actual NULL value means the path was not found .UNINDENT .sp Uses the \fI\%json_type\fP function from the json1 extension. .UNINDENT .INDENT 7.0 .TP .B length() Return the length of the array stored at the given location in the JSON data. .sp Uses the \fI\%json_array_length\fP function from the json1 extension. .UNINDENT .INDENT 7.0 .TP .B children() Table\-valued function that exposes the direct descendants of a JSON object at the given location. See also \fI\%JSONField.children()\fP\&. .UNINDENT .INDENT 7.0 .TP .B tree() Table\-valued function that exposes all descendants, recursively, of a JSON object at the given location. See also \fI\%JSONField.tree()\fP\&. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class SearchField([unindexed=False[, column_name=None]]) Field\-class to be used for columns on models representing full\-text search virtual tables. The full\-text search extensions prohibit the specification of any typing or constraints on columns. This behavior is enforced by the \fI\%SearchField\fP, which raises an exception if any configuration is attempted that would be incompatible with the full\-text search extensions. .sp Example model for document search index (timestamp is stored in the table but it\(aqs data is not searchable): .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class DocumentIndex(FTSModel): title = SearchField() content = SearchField() tags = SearchField() timestamp = SearchField(unindexed=True) .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .TP .B match(term) .INDENT 7.0 .TP .B Parameters \fBterm\fP (\fIstr\fP) \-\- full\-text search query/terms .TP .B Returns a \fBExpression\fP corresponding to the \fBMATCH\fP operator. .UNINDENT .sp Sqlite\(aqs full\-text search supports searching either the full table, including all indexed columns, \fBor\fP searching individual columns. The \fI\%match()\fP method can be used to restrict search to a single column: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class SearchIndex(FTSModel): title = SearchField() body = SearchField() # Search *only* the title field and return results ordered by # relevance, using bm25. query = (SearchIndex .select(SearchIndex, SearchIndex.bm25().alias(\(aqscore\(aq)) .where(SearchIndex.title.match(\(aqpython\(aq)) .order_by(SearchIndex.bm25())) .ft P .fi .UNINDENT .UNINDENT .sp To instead search \fIall\fP indexed columns, use the \fI\%FTSModel.match()\fP method: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Searches *both* the title and body and return results ordered by # relevance, using bm25. query = (SearchIndex .select(SearchIndex, SearchIndex.bm25().alias(\(aqscore\(aq)) .where(SearchIndex.match(\(aqpython\(aq)) .order_by(SearchIndex.bm25())) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class VirtualModel Model class designed to be used to represent virtual tables. The default metadata settings are slightly different, to match those frequently used by virtual tables. .sp Metadata options: .INDENT 7.0 .IP \(bu 2 \fBarguments\fP \- arguments passed to the virtual table constructor. .IP \(bu 2 \fBextension_module\fP \- name of extension to use for virtual table. .IP \(bu 2 .INDENT 2.0 .TP .B \fBoptions\fP \- a dictionary of settings to apply in virtual table constructor. .UNINDENT .IP \(bu 2 \fBprimary_key\fP \- defaults to \fBFalse\fP, indicating no primary key. .UNINDENT .sp These all are combined in the following way: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C CREATE VIRTUAL TABLE USING ([prefix_arguments, ...] fields, ... [arguments, ...], [options...]) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class FTSModel Subclass of \fI\%VirtualModel\fP to be used with the \fI\%FTS3 and FTS4\fP full\-text search extensions. .sp FTSModel subclasses should be defined normally, however there are a couple caveats: .INDENT 7.0 .IP \(bu 2 Unique constraints, not null constraints, check constraints and foreign keys are not supported. .IP \(bu 2 Indexes on fields and multi\-column indexes are ignored completely .IP \(bu 2 Sqlite will treat all column types as \fBTEXT\fP (although you can store other data types, Sqlite will treat them as text). .IP \(bu 2 FTS models contain a \fBrowid\fP field which is automatically created and managed by SQLite (unless you choose to explicitly set it during model creation). Lookups on this column \fBare fast and efficient\fP\&. .UNINDENT .sp Given these constraints, it is strongly recommended that all fields declared on an \fBFTSModel\fP subclass be instances of \fI\%SearchField\fP (though an exception is made for explicitly declaring a \fI\%RowIDField\fP). Using \fI\%SearchField\fP will help prevent you accidentally creating invalid column constraints. If you wish to store metadata in the index but would not like it to be included in the full\-text index, then specify \fBunindexed=True\fP when instantiating the \fI\%SearchField\fP\&. .sp The only exception to the above is for the \fBrowid\fP primary key, which can be declared using \fI\%RowIDField\fP\&. Lookups on the \fBrowid\fP are very efficient. If you are using FTS4 you can also use \fI\%DocIDField\fP, which is an alias for the rowid (though there is no benefit to doing so). .sp Because of the lack of secondary indexes, it usually makes sense to use the \fBrowid\fP primary key as a pointer to a row in a regular table. For example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class Document(Model): # Canonical source of data, stored in a regular table. author = ForeignKeyField(User, backref=\(aqdocuments\(aq) title = TextField(null=False, unique=True) content = TextField(null=False) timestamp = DateTimeField() class Meta: database = db class DocumentIndex(FTSModel): # Full\-text search index. rowid = RowIDField() title = SearchField() content = SearchField() class Meta: database = db # Use the porter stemming algorithm to tokenize content. options = {\(aqtokenize\(aq: \(aqporter\(aq} .ft P .fi .UNINDENT .UNINDENT .sp To store a document in the document index, we will \fBINSERT\fP a row into the \fBDocumentIndex\fP table, manually setting the \fBrowid\fP so that it matches the primary\-key of the corresponding \fBDocument\fP: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C def store_document(document): DocumentIndex.insert({ DocumentIndex.rowid: document.id, DocumentIndex.title: document.title, DocumentIndex.content: document.content}).execute() .ft P .fi .UNINDENT .UNINDENT .sp To perform a search and return ranked results, we can query the \fBDocument\fP table and join on the \fBDocumentIndex\fP\&. This join will be efficient because lookups on an FTSModel\(aqs \fBrowid\fP field are fast: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C def search(phrase): # Query the search index and join the corresponding Document # object on each search result. return (Document .select() .join( DocumentIndex, on=(Document.id == DocumentIndex.rowid)) .where(DocumentIndex.match(phrase)) .order_by(DocumentIndex.bm25())) .ft P .fi .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 All SQL queries on \fBFTSModel\fP classes will be full\-table scans \fBexcept\fP full\-text searches and \fBrowid\fP lookups. .UNINDENT .UNINDENT .sp If the primary source of the content you are indexing exists in a separate table, you can save some disk space by instructing SQLite to not store an additional copy of the search index content. SQLite will still create the metadata and data\-structures needed to perform searches on the content, but the content itself will not be stored in the search index. .sp To accomplish this, you can specify a table or column using the \fBcontent\fP option. The \fI\%FTS4 documentation\fP has more information. .sp Here is a short example illustrating how to implement this with peewee: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class Blog(Model): title = TextField() pub_date = DateTimeField(default=datetime.datetime.now) content = TextField() # We want to search this. class Meta: database = db class BlogIndex(FTSModel): content = SearchField() class Meta: database = db options = {\(aqcontent\(aq: Blog.content} # <\-\- specify data source. db.create_tables([Blog, BlogIndex]) # Now, we can manage content in the BlogIndex. To populate the # search index: BlogIndex.rebuild() # Optimize the index. BlogIndex.optimize() .ft P .fi .UNINDENT .UNINDENT .sp The \fBcontent\fP option accepts either a single \fBField\fP or a \fBModel\fP and can reduce the amount of storage used by the database file. However, content will need to be manually moved to/from the associated \fBFTSModel\fP\&. .INDENT 7.0 .TP .B classmethod match(term) .INDENT 7.0 .TP .B Parameters \fBterm\fP \-\- Search term or expression. .UNINDENT .sp Generate a SQL expression representing a search for the given term or expression in the table. SQLite uses the \fBMATCH\fP operator to indicate a full\-text search. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Search index for "search phrase" and return results ranked # by relevancy using the BM25 algorithm. query = (DocumentIndex .select() .where(DocumentIndex.match(\(aqsearch phrase\(aq)) .order_by(DocumentIndex.bm25())) for result in query: print(\(aqResult: %s\(aq % result.title) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod search(term[, weights=None[, with_score=False[, score_alias=\(aqscore\(aq[, explicit_ordering=False]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBterm\fP (\fIstr\fP) \-\- Search term to use. .IP \(bu 2 \fBweights\fP \-\- A list of weights for the columns, ordered with respect to the column\(aqs position in the table. \fBOr\fP, a dictionary keyed by the field or field name and mapped to a value. .IP \(bu 2 \fBwith_score\fP \-\- Whether the score should be returned as part of the \fBSELECT\fP statement. .IP \(bu 2 \fBscore_alias\fP (\fIstr\fP) \-\- Alias to use for the calculated rank score. This is the attribute you will use to access the score if \fBwith_score=True\fP\&. .IP \(bu 2 \fBexplicit_ordering\fP (\fIbool\fP) \-\- Order using full SQL function to calculate rank, as opposed to simply referencing the score alias in the ORDER BY clause. .UNINDENT .UNINDENT .sp Shorthand way of searching for a term and sorting results by the quality of the match. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 This method uses a simplified algorithm for determining the relevance rank of results. For more sophisticated result ranking, use the \fI\%search_bm25()\fP method. .UNINDENT .UNINDENT .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Simple search. docs = DocumentIndex.search(\(aqsearch term\(aq) for result in docs: print(result.title) # More complete example. docs = DocumentIndex.search( \(aqsearch term\(aq, weights={\(aqtitle\(aq: 2.0, \(aqcontent\(aq: 1.0}, with_score=True, score_alias=\(aqsearch_score\(aq) for result in docs: print(result.title, result.search_score) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod search_bm25(term[, weights=None[, with_score=False[, score_alias=\(aqscore\(aq[, explicit_ordering=False]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBterm\fP (\fIstr\fP) \-\- Search term to use. .IP \(bu 2 \fBweights\fP \-\- A list of weights for the columns, ordered with respect to the column\(aqs position in the table. \fBOr\fP, a dictionary keyed by the field or field name and mapped to a value. .IP \(bu 2 \fBwith_score\fP \-\- Whether the score should be returned as part of the \fBSELECT\fP statement. .IP \(bu 2 \fBscore_alias\fP (\fIstr\fP) \-\- Alias to use for the calculated rank score. This is the attribute you will use to access the score if \fBwith_score=True\fP\&. .IP \(bu 2 \fBexplicit_ordering\fP (\fIbool\fP) \-\- Order using full SQL function to calculate rank, as opposed to simply referencing the score alias in the ORDER BY clause. .UNINDENT .UNINDENT .sp Shorthand way of searching for a term and sorting results by the quality of the match using the BM25 algorithm. .sp \fBATTENTION:\fP .INDENT 7.0 .INDENT 3.5 The BM25 ranking algorithm is only available for FTS4. If you are using FTS3, use the \fI\%search()\fP method instead. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod search_bm25f(term[, weights=None[, with_score=False[, score_alias=\(aqscore\(aq[, explicit_ordering=False]]]]) Same as \fI\%FTSModel.search_bm25()\fP, but using the BM25f variant of the BM25 ranking algorithm. .UNINDENT .INDENT 7.0 .TP .B classmethod search_lucene(term[, weights=None[, with_score=False[, score_alias=\(aqscore\(aq[, explicit_ordering=False]]]]) Same as \fI\%FTSModel.search_bm25()\fP, but using the result ranking algorithm from the Lucene search engine. .UNINDENT .INDENT 7.0 .TP .B classmethod rank([col1_weight, col2_weight...coln_weight]) .INDENT 7.0 .TP .B Parameters \fBcol_weight\fP (\fIfloat\fP) \-\- (Optional) weight to give to the \fIith\fP column of the model. By default all columns have a weight of \fB1.0\fP\&. .UNINDENT .sp Generate an expression that will calculate and return the quality of the search match. This \fBrank\fP can be used to sort the search results. A higher rank score indicates a better match. .sp The \fBrank\fP function accepts optional parameters that allow you to specify weights for the various columns. If no weights are specified, all columns are considered of equal importance. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 The algorithm used by \fI\%rank()\fP is simple and relatively quick. For more sophisticated result ranking, use: .INDENT 0.0 .IP \(bu 2 \fI\%bm25()\fP .IP \(bu 2 \fI\%bm25f()\fP .IP \(bu 2 \fI\%lucene()\fP .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C query = (DocumentIndex .select( DocumentIndex, DocumentIndex.rank().alias(\(aqscore\(aq)) .where(DocumentIndex.match(\(aqsearch phrase\(aq)) .order_by(DocumentIndex.rank())) for search_result in query: print search_result.title, search_result.score .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod bm25([col1_weight, col2_weight...coln_weight]) .INDENT 7.0 .TP .B Parameters \fBcol_weight\fP (\fIfloat\fP) \-\- (Optional) weight to give to the \fIith\fP column of the model. By default all columns have a weight of \fB1.0\fP\&. .UNINDENT .sp Generate an expression that will calculate and return the quality of the search match using the \fI\%BM25 algorithm\fP\&. This value can be used to sort the search results, with higher scores corresponding to better matches. .sp Like \fI\%rank()\fP, \fBbm25\fP function accepts optional parameters that allow you to specify weights for the various columns. If no weights are specified, all columns are considered of equal importance. .sp \fBATTENTION:\fP .INDENT 7.0 .INDENT 3.5 The BM25 result ranking algorithm requires FTS4. If you are using FTS3, use \fI\%rank()\fP instead. .UNINDENT .UNINDENT .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C query = (DocumentIndex .select( DocumentIndex, DocumentIndex.bm25().alias(\(aqscore\(aq)) .where(DocumentIndex.match(\(aqsearch phrase\(aq)) .order_by(DocumentIndex.bm25())) for search_result in query: print(search_result.title, search_result.score) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 The above code example is equivalent to calling the \fI\%search_bm25()\fP method: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = DocumentIndex.search_bm25(\(aqsearch phrase\(aq, with_score=True) for search_result in query: print(search_result.title, search_result.score) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod bm25f([col1_weight, col2_weight...coln_weight]) Identical to \fI\%bm25()\fP, except that it uses the BM25f variant of the BM25 ranking algorithm. .UNINDENT .INDENT 7.0 .TP .B classmethod lucene([col1_weight, col2_weight...coln_weight]) Identical to \fI\%bm25()\fP, except that it uses the Lucene search result ranking algorithm. .UNINDENT .INDENT 7.0 .TP .B classmethod rebuild() Rebuild the search index \-\- this only works when the \fBcontent\fP option was specified during table creation. .UNINDENT .INDENT 7.0 .TP .B classmethod optimize() Optimize the search index. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class FTS5Model Subclass of \fI\%VirtualModel\fP to be used with the \fI\%FTS5\fP full\-text search extensions. .sp FTS5Model subclasses should be defined normally, however there are a couple caveats: .INDENT 7.0 .IP \(bu 2 FTS5 explicitly disallows specification of any constraints, data\-type or indexes on columns. For that reason, all columns \fBmust\fP be instances of \fI\%SearchField\fP\&. .IP \(bu 2 FTS5 models contain a \fBrowid\fP field which is automatically created and managed by SQLite (unless you choose to explicitly set it during model creation). Lookups on this column \fBare fast and efficient\fP\&. .IP \(bu 2 Indexes on fields and multi\-column indexes are not supported. .UNINDENT .sp The \fBFTS5\fP extension comes with a built\-in implementation of the BM25 ranking function. Therefore, the \fBsearch\fP and \fBsearch_bm25\fP methods have been overridden to use the builtin ranking functions rather than user\-defined functions. .INDENT 7.0 .TP .B classmethod fts5_installed() Return a boolean indicating whether the FTS5 extension is installed. If it is not installed, an attempt will be made to load the extension. .UNINDENT .INDENT 7.0 .TP .B classmethod search(term[, weights=None[, with_score=False[, score_alias=\(aqscore\(aq]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBterm\fP (\fIstr\fP) \-\- Search term to use. .IP \(bu 2 \fBweights\fP \-\- A list of weights for the columns, ordered with respect to the column\(aqs position in the table. \fBOr\fP, a dictionary keyed by the field or field name and mapped to a value. .IP \(bu 2 \fBwith_score\fP \-\- Whether the score should be returned as part of the \fBSELECT\fP statement. .IP \(bu 2 \fBscore_alias\fP (\fIstr\fP) \-\- Alias to use for the calculated rank score. This is the attribute you will use to access the score if \fBwith_score=True\fP\&. .IP \(bu 2 \fBexplicit_ordering\fP (\fIbool\fP) \-\- Order using full SQL function to calculate rank, as opposed to simply referencing the score alias in the ORDER BY clause. .UNINDENT .UNINDENT .sp Shorthand way of searching for a term and sorting results by the quality of the match. The \fBFTS5\fP extension provides a built\-in implementation of the BM25 algorithm, which is used to rank the results by relevance. .sp Higher scores correspond to better matches. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Simple search. docs = DocumentIndex.search(\(aqsearch term\(aq) for result in docs: print(result.title) # More complete example. docs = DocumentIndex.search( \(aqsearch term\(aq, weights={\(aqtitle\(aq: 2.0, \(aqcontent\(aq: 1.0}, with_score=True, score_alias=\(aqsearch_score\(aq) for result in docs: print(result.title, result.search_score) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod search_bm25(term[, weights=None[, with_score=False[, score_alias=\(aqscore\(aq]]]) With FTS5, \fI\%search_bm25()\fP is identical to the \fI\%search()\fP method. .UNINDENT .INDENT 7.0 .TP .B classmethod rank([col1_weight, col2_weight...coln_weight]) .INDENT 7.0 .TP .B Parameters \fBcol_weight\fP (\fIfloat\fP) \-\- (Optional) weight to give to the \fIith\fP column of the model. By default all columns have a weight of \fB1.0\fP\&. .UNINDENT .sp Generate an expression that will calculate and return the quality of the search match using the \fI\%BM25 algorithm\fP\&. This value can be used to sort the search results, with higher scores corresponding to better matches. .sp The \fI\%rank()\fP function accepts optional parameters that allow you to specify weights for the various columns. If no weights are specified, all columns are considered of equal importance. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C query = (DocumentIndex .select( DocumentIndex, DocumentIndex.rank().alias(\(aqscore\(aq)) .where(DocumentIndex.match(\(aqsearch phrase\(aq)) .order_by(DocumentIndex.rank())) for search_result in query: print(search_result.title, search_result.score) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 The above code example is equivalent to calling the \fI\%search()\fP method: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = DocumentIndex.search(\(aqsearch phrase\(aq, with_score=True) for search_result in query: print(search_result.title, search_result.score) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod bm25([col1_weight, col2_weight...coln_weight]) Because FTS5 provides built\-in support for BM25, the \fI\%bm25()\fP method is identical to the \fI\%rank()\fP method. .UNINDENT .INDENT 7.0 .TP .B classmethod VocabModel([table_type=\(aqrow\(aq|\(aqcol\(aq|\(aqinstance\(aq[, table_name=None]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable_type\fP (\fIstr\fP) \-\- Either \(aqrow\(aq, \(aqcol\(aq or \(aqinstance\(aq. .IP \(bu 2 \fBtable_name\fP \-\- Name for the vocab table. If not specified, will be "fts5tablename_v". .UNINDENT .UNINDENT .sp Generate a model class suitable for accessing the \fI\%vocab table\fP corresponding to FTS5 search index. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class TableFunction Implement a user\-defined table\-valued function. Unlike a simple scalar or aggregate function, which returns a single scalar value, a table\-valued function can return any number of rows of tabular data. .sp Simple example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C from playhouse.sqlite_ext import TableFunction class Series(TableFunction): # Name of columns in each row of generated data. columns = [\(aqvalue\(aq] # Name of parameters the function may be called with. params = [\(aqstart\(aq, \(aqstop\(aq, \(aqstep\(aq] def initialize(self, start=0, stop=None, step=1): """ Table\-functions declare an initialize() method, which is called with whatever arguments the user has called the function with. """ self.start = self.current = start self.stop = stop or float(\(aqInf\(aq) self.step = step def iterate(self, idx): """ Iterate is called repeatedly by the SQLite database engine until the required number of rows has been read **or** the function raises a \(gaStopIteration\(ga signalling no more rows are available. """ if self.current > self.stop: raise StopIteration ret, self.current = self.current, self.current + self.step return (ret,) # Register the table\-function with our database, which ensures it # is declared whenever a connection is opened. db.table_function(\(aqseries\(aq)(Series) # Usage: cursor = db.execute_sql(\(aqSELECT * FROM series(?, ?, ?)\(aq, (0, 5, 2)) for value, in cursor: print(value) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 A \fI\%TableFunction\fP must be registered with a database connection before it can be used. To ensure the table function is always available, you can use the \fBSqliteDatabase.table_function()\fP decorator to register the function with the database. .UNINDENT .UNINDENT .sp \fI\%TableFunction\fP implementations must provide two attributes and implement two methods, described below. .INDENT 7.0 .TP .B columns A list containing the names of the columns for the data returned by the function. For example, a function that is used to split a string on a delimiter might specify 3 columns: \fB[substring, start_idx, end_idx]\fP\&. .UNINDENT .INDENT 7.0 .TP .B params The names of the parameters the function may be called with. All parameters, including optional parameters, should be listed. For example, a function that is used to split a string on a delimiter might specify 2 params: \fB[string, delimiter]\fP\&. .UNINDENT .INDENT 7.0 .TP .B name \fIOptional\fP \- specify the name for the table function. If not provided, name will be taken from the class name. .UNINDENT .INDENT 7.0 .TP .B print_tracebacks = True Print a full traceback for any errors that occur in the table\-function\(aqs callback methods. When set to False, only the generic OperationalError will be visible. .UNINDENT .INDENT 7.0 .TP .B initialize(**parameter_values) .INDENT 7.0 .TP .B Parameters \fBparameter_values\fP \-\- Parameters the function was called with. .TP .B Returns No return value. .UNINDENT .sp The \fBinitialize\fP method is called to initialize the table function with the parameters the user specified when calling the function. .UNINDENT .INDENT 7.0 .TP .B iterate(idx) .INDENT 7.0 .TP .B Parameters \fBidx\fP (\fIint\fP) \-\- current iteration step .TP .B Returns A tuple of row data corresponding to the columns named in the \fI\%columns\fP attribute. .TP .B Raises \fBStopIteration\fP \-\- To signal that no more rows are available. .UNINDENT .sp This function is called repeatedly and returns successive rows of data. The function may terminate before all rows are consumed (especially if the user specified a \fBLIMIT\fP on the results). Alternatively, the function can signal that no more data is available by raising a \fBStopIteration\fP exception. .UNINDENT .INDENT 7.0 .TP .B classmethod register(conn) .INDENT 7.0 .TP .B Parameters \fBconn\fP \-\- A \fBsqlite3.Connection\fP object. .UNINDENT .sp Register the table function with a DB\-API 2.0 \fBsqlite3.Connection\fP object. Table\-valued functions \fBmust\fP be registered before they can be used in a query. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class MyTableFunction(TableFunction): name = \(aqmy_func\(aq # ... other attributes and methods ... db = SqliteDatabase(\(aq:memory:\(aq) db.connect() MyTableFunction.register(db.connection()) .ft P .fi .UNINDENT .UNINDENT .sp To ensure the \fI\%TableFunction\fP is registered every time a connection is opened, use the \fBtable_function()\fP decorator. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B ClosureTable(model_class[, foreign_key=None[, referencing_class=None[, referencing_key=None]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmodel_class\fP \-\- The model class containing the nodes in the tree. .IP \(bu 2 \fBforeign_key\fP \-\- The self\-referential parent\-node field on the model class. If not provided, peewee will introspect the model to find a suitable key. .IP \(bu 2 \fBreferencing_class\fP \-\- Intermediate table for a many\-to\-many relationship. .IP \(bu 2 \fBreferencing_key\fP \-\- For a many\-to\-many relationship, the originating side of the relation. .UNINDENT .TP .B Returns Returns a \fI\%VirtualModel\fP for working with a closure table. .UNINDENT .sp Factory function for creating a model class suitable for working with a \fI\%transitive closure\fP table. Closure tables are \fI\%VirtualModel\fP subclasses that work with the transitive closure SQLite extension. These special tables are designed to make it easy to efficiently query hierarchical data. The SQLite extension manages an AVL tree behind\-the\-scenes, transparently updating the tree when your table changes and making it easy to perform common queries on hierarchical data. .sp To use the closure table extension in your project, you need: .INDENT 7.0 .IP 1. 3 A copy of the SQLite extension. The source code can be found in the \fI\%SQLite code repository\fP or by cloning \fI\%this gist\fP: .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C $ git clone https://gist.github.com/coleifer/7f3593c5c2a645913b92 closure $ cd closure/ .ft P .fi .UNINDENT .UNINDENT .IP 2. 3 Compile the extension as a shared library, e.g. .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C $ gcc \-g \-fPIC \-shared closure.c \-o closure.so .ft P .fi .UNINDENT .UNINDENT .IP 3. 3 Create a model for your hierarchical data. The only requirement here is that the model has an integer primary key and a self\-referential foreign key. Any additional fields are fine. .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C class Category(Model): name = CharField() metadata = TextField() parent = ForeignKeyField(\(aqself\(aq, index=True, null=True) # Required. # Generate a model for the closure virtual table. CategoryClosure = ClosureTable(Category) .ft P .fi .UNINDENT .UNINDENT .sp The self\-referentiality can also be achieved via an intermediate table (for a many\-to\-many relation). .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C class User(Model): name = CharField() class UserRelations(Model): user = ForeignKeyField(User) knows = ForeignKeyField(User, backref=\(aq_known_by\(aq) class Meta: primary_key = CompositeKey(\(aquser\(aq, \(aqknows\(aq) # Alternatively, a unique index on both columns. # Generate a model for the closure virtual table, specifying the UserRelations as the referencing table UserClosure = ClosureTable( User, referencing_class=UserRelations, foreign_key=UserRelations.knows, referencing_key=UserRelations.user) .ft P .fi .UNINDENT .UNINDENT .IP 4. 3 In your application code, make sure you load the extension when you instantiate your \fBDatabase\fP object. This is done by passing the path to the shared library to the \fBload_extension()\fP method. .INDENT 3.0 .INDENT 3.5 .sp .nf .ft C db = SqliteExtDatabase(\(aqmy_database.db\(aq) db.load_extension(\(aq/path/to/closure\(aq) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 There are two caveats you should be aware of when using the \fBtransitive_closure\fP extension. First, it requires that your \fIsource model\fP have an integer primary key. Second, it is strongly recommended that you create an index on the self\-referential foreign key. .UNINDENT .UNINDENT .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class Category(Model): name = CharField() metadata = TextField() parent = ForeignKeyField(\(aqself\(aq, index=True, null=True) # Required. # Generate a model for the closure virtual table. CategoryClosure = ClosureTable(Category) # Create the tables if they do not exist. db.create_tables([Category, CategoryClosure], True) .ft P .fi .UNINDENT .UNINDENT .sp It is now possible to perform interesting queries using the data from the closure table: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Get all ancestors for a particular node. laptops = Category.get(Category.name == \(aqLaptops\(aq) for parent in Closure.ancestors(laptops): print parent.name # Computer Hardware # Computers # Electronics # All products # Get all descendants for a particular node. hardware = Category.get(Category.name == \(aqComputer Hardware\(aq) for node in Closure.descendants(hardware): print node.name # Laptops # Desktops # Hard\-drives # Monitors # LCD Monitors # LED Monitors .ft P .fi .UNINDENT .UNINDENT .sp API of the \fI\%VirtualModel\fP returned by \fI\%ClosureTable()\fP\&. .INDENT 7.0 .TP .B class BaseClosureTable .INDENT 7.0 .TP .B id A field for the primary key of the given node. .UNINDENT .INDENT 7.0 .TP .B depth A field representing the relative depth of the given node. .UNINDENT .INDENT 7.0 .TP .B root A field representing the relative root node. .UNINDENT .INDENT 7.0 .TP .B descendants(node[, depth=None[, include_node=False]]) Retrieve all descendants of the given node. If a depth is specified, only nodes at that depth (relative to the given node) will be returned. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C node = Category.get(Category.name == \(aqElectronics\(aq) # Direct child categories. children = CategoryClosure.descendants(node, depth=1) # Grand\-child categories. children = CategoryClosure.descendants(node, depth=2) # Descendants at all depths. all_descendants = CategoryClosure.descendants(node) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B ancestors(node[, depth=None[, include_node=False]]) Retrieve all ancestors of the given node. If a depth is specified, only nodes at that depth (relative to the given node) will be returned. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C node = Category.get(Category.name == \(aqLaptops\(aq) # All ancestors. all_ancestors = CategoryClosure.ancestors(node) # Grand\-parent category. grandparent = CategoryClosure.ancestores(node, depth=2) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B siblings(node[, include_node=False]) Retrieve all nodes that are children of the specified node\(aqs parent. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 For an in\-depth discussion of the SQLite transitive closure extension, check out this blog post, \fI\%Querying Tree Structures in SQLite using Python and the Transitive Closure Extension\fP\&. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class LSMTable \fI\%VirtualModel\fP subclass suitable for working with the \fI\%lsm1 extension\fP The \fIlsm1\fP extension is a virtual table that provides a SQL interface to the \fI\%lsm key/value storage engine from SQLite4\fP\&. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 The LSM1 extension has not been released yet (SQLite version 3.22 at time of writing), so consider this feature experimental with potential to change in subsequent releases. .UNINDENT .UNINDENT .sp LSM tables define one primary key column and an arbitrary number of additional value columns (which are serialized and stored in a single value field in the storage engine). The primary key must be all of the same type and use one of the following field types: .INDENT 7.0 .IP \(bu 2 \fBIntegerField\fP .IP \(bu 2 \fBTextField\fP .IP \(bu 2 \fBBlobField\fP .UNINDENT .sp Since the LSM storage engine is a key/value store, primary keys (including integers) must be specified by the application. .sp \fBATTENTION:\fP .INDENT 7.0 .INDENT 3.5 Secondary indexes are not supported by the LSM engine, so the only efficient queries will be lookups (or range queries) on the primary key. Other fields can be queried and filtered on, but may result in a full table\-scan. .UNINDENT .UNINDENT .sp Example model declaration: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C db = SqliteExtDatabase(\(aqmy_app.db\(aq) db.load_extension(\(aqlsm.so\(aq) # Load shared library. class EventLog(LSMTable): timestamp = IntegerField(primary_key=True) action = TextField() sender = TextField() target = TextField() class Meta: database = db filename = \(aqeventlog.ldb\(aq # LSM data is stored in separate db. # Declare virtual table. EventLog.create_table() .ft P .fi .UNINDENT .UNINDENT .sp Example queries: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Use dictionary operators to get, set and delete rows from the LSM # table. Slices may be passed to represent a range of key values. def get_timestamp(): # Return time as integer expressing time in microseconds. return int(time.time() * 1000000) # Create a new row, at current timestamp. ts = get_timestamp() EventLog[ts] = (\(aqpageview\(aq, \(aqsearch\(aq, \(aq/blog/some\-post/\(aq) # Retrieve row from event log. log = EventLog[ts] print(log.action, log.sender, log.target) # Prints ("pageview", "search", "/blog/some\-post/") # Delete the row. del EventLog[ts] # We can also use the "create()" method. EventLog.create( timestamp=get_timestamp(), action=\(aqsignup\(aq, sender=\(aqnewsletter\(aq, target=\(aqsqlite\-news\(aq) .ft P .fi .UNINDENT .UNINDENT .sp Simple key/value model declaration: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class KV(LSMTable): key = TextField(primary_key=True) value = TextField() class Meta: database = db filename = \(aqkv.ldb\(aq db.create_tables([KV]) .ft P .fi .UNINDENT .UNINDENT .sp For tables consisting of a single value field, Peewee will return the value directly when getting a single item. You can also request slices of rows, in which case Peewee returns a corresponding \fBSelect\fP query, which can be iterated over. Below are some examples: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> KV[\(aqk0\(aq] = \(aqv0\(aq >>> print(KV[\(aqk0\(aq]) \(aqv0\(aq >>> data = [{\(aqkey\(aq: \(aqk%d\(aq % i, \(aqvalue\(aq: \(aqv%d\(aq % i} for i in range(20)] >>> KV.insert_many(data).execute() >>> KV.select().count() 20 >>> KV[\(aqk8\(aq] \(aqv8\(aq >>> list(KV[\(aqk4.1\(aq:\(aqk7.x\(aq] [Row(key=\(aqk5\(aq, value=\(aqv5\(aq), Row(key=\(aqk6\(aq, value=\(aqv6\(aq), Row(key=\(aqk7\(aq, value=\(aqv7\(aq)] >>> list(KV[\(aqk6xxx\(aq:]) [Row(key=\(aqk7\(aq, value=\(aqv7\(aq), Row(key=\(aqk8\(aq, value=\(aqv8\(aq), Row(key=\(aqk9\(aq, value=\(aqv9\(aq)] .ft P .fi .UNINDENT .UNINDENT .sp You can also index the \fI\%LSMTable\fP using expressions: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> list(KV[KV.key > \(aqk6\(aq]) [Row(key=\(aqk7\(aq, value=\(aqv7\(aq), Row(key=\(aqk8\(aq, value=\(aqv8\(aq), Row(key=\(aqk9\(aq, value=\(aqv9\(aq)] >>> list(KV[(KV.key > \(aqk6\(aq) & (KV.value != \(aqv8\(aq)]) [Row(key=\(aqk7\(aq, value=\(aqv7\(aq), Row(key=\(aqk9\(aq, value=\(aqv9\(aq)] .ft P .fi .UNINDENT .UNINDENT .sp You can delete single rows using \fBdel\fP or multiple rows using slices or expressions: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> del KV[\(aqk1\(aq] >>> del KV[\(aqk3x\(aq:\(aqk8\(aq] >>> del KV[KV.key.between(\(aqk10\(aq, \(aqk18\(aq)] >>> list(KV[:]) [Row(key=\(aqk0\(aq, value=\(aqv0\(aq), Row(key=\(aqk19\(aq, value=\(aqv19\(aq), Row(key=\(aqk2\(aq, value=\(aqv2\(aq), Row(key=\(aqk3\(aq, value=\(aqv3\(aq), Row(key=\(aqk9\(aq, value=\(aqv9\(aq)] .ft P .fi .UNINDENT .UNINDENT .sp Attempting to get a single non\-existant key will result in a \fBKeyError\fP, but slices will not raise an exception: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> KV[\(aqk1\(aq] \&... KeyError: \(aqk1\(aq >>> list(KV[\(aqk1\(aq:\(aqk1\(aq]) [] .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class ZeroBlob(length) .INDENT 7.0 .TP .B Parameters \fBlength\fP (\fIint\fP) \-\- Size of blob in bytes. .UNINDENT .sp \fI\%ZeroBlob\fP is used solely to reserve space for storing a BLOB that supports incremental I/O. To use the \fI\%SQLite BLOB\-store\fP it is necessary to first insert a ZeroBlob of the desired size into the row you wish to use with incremental I/O. .sp For example, see \fI\%Blob\fP\&. .UNINDENT .INDENT 0.0 .TP .B class Blob(database, table, column, rowid[, read_only=False]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdatabase\fP \-\- \fI\%SqliteExtDatabase\fP instance. .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Name of table being accessed. .IP \(bu 2 \fBcolumn\fP (\fIstr\fP) \-\- Name of column being accessed. .IP \(bu 2 \fBrowid\fP (\fIint\fP) \-\- Primary\-key of row being accessed. .IP \(bu 2 \fBread_only\fP (\fIbool\fP) \-\- Prevent any modifications to the blob data. .UNINDENT .UNINDENT .sp Open a blob, stored in the given table/column/row, for incremental I/O. To allocate storage for new data, you can use the \fI\%ZeroBlob\fP, which is very efficient. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class RawData(Model): data = BlobField() # Allocate 100MB of space for writing a large file incrementally: query = RawData.insert({\(aqdata\(aq: ZeroBlob(1024 * 1024 * 100)}) rowid = query.execute() # Now we can open the row for incremental I/O: blob = Blob(db, \(aqrawdata\(aq, \(aqdata\(aq, rowid) # Read from the file and write to the blob in chunks of 4096 bytes. while True: data = file_handle.read(4096) if not data: break blob.write(data) bytes_written = blob.tell() blob.close() .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .TP .B read([n=None]) .INDENT 7.0 .TP .B Parameters \fBn\fP (\fIint\fP) \-\- Only read up to \fIn\fP bytes from current position in file. .UNINDENT .sp Read up to \fIn\fP bytes from the current position in the blob file. If \fIn\fP is not specified, the entire blob will be read. .UNINDENT .INDENT 7.0 .TP .B seek(offset[, whence=0]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBoffset\fP (\fIint\fP) \-\- Seek to the given offset in the file. .IP \(bu 2 \fBwhence\fP (\fIint\fP) \-\- Seek relative to the specified frame of reference. .UNINDENT .UNINDENT .sp Values for \fBwhence\fP: .INDENT 7.0 .IP \(bu 2 \fB0\fP: beginning of file .IP \(bu 2 \fB1\fP: current position .IP \(bu 2 \fB2\fP: end of file .UNINDENT .UNINDENT .INDENT 7.0 .TP .B tell() Return current offset within the file. .UNINDENT .INDENT 7.0 .TP .B write(data) .INDENT 7.0 .TP .B Parameters \fBdata\fP (\fIbytes\fP) \-\- Data to be written .UNINDENT .sp Writes the given data, starting at the current position in the file. .UNINDENT .INDENT 7.0 .TP .B close() Close the file and free associated resources. .UNINDENT .INDENT 7.0 .TP .B reopen(rowid) .INDENT 7.0 .TP .B Parameters \fBrowid\fP (\fIint\fP) \-\- Primary key of row to open. .UNINDENT .sp If a blob has already been opened for a given table/column, you can use the \fI\%reopen()\fP method to re\-use the same \fI\%Blob\fP object for accessing multiple rows in the table. .UNINDENT .UNINDENT .SS Additional Features .sp The \fI\%SqliteExtDatabase\fP accepts an initialization option to register support for a simple \fI\%bloom filter\fP\&. The bloom filter, once initialized, can then be used for efficient membership queries on large set of data. .sp Here\(aqs an example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = CSqliteExtDatabase(\(aq:memory:\(aq, bloomfilter=True) # Create and define a table to store some data. db.execute_sql(\(aqCREATE TABLE "register" ("data" TEXT)\(aq) Register = Table(\(aqregister\(aq, (\(aqdata\(aq,)).bind(db) # Populate the database with a bunch of text. with db.atomic(): for i in \(aqabcdefghijklmnopqrstuvwxyz\(aq: keys = [i * j for j in range(1, 10)] # a, aa, aaa, ... aaaaaaaaa Register.insert([{\(aqdata\(aq: key} for key in keys]).execute() # Collect data into a 16KB bloomfilter. query = Register.select(fn.bloomfilter(Register.data, 16 * 1024).alias(\(aqbuf\(aq)) row = query.get() buf = row[\(aqbuf\(aq] # Use bloomfilter buf to test whether other keys are members. test_keys = ( (\(aqaaaa\(aq, True), (\(aqabc\(aq, False), (\(aqzzzzzzz\(aq, True), (\(aqzyxwvut\(aq, False)) for key, is_present in test_keys: query = Register.select(fn.bloomfilter_contains(key, buf).alias(\(aqis_member\(aq)) answer = query.get()[\(aqis_member\(aq] assert answer == is_present .ft P .fi .UNINDENT .UNINDENT .sp The \fI\%SqliteExtDatabase\fP can also register other useful functions: .INDENT 0.0 .IP \(bu 2 \fBrank_functions\fP (enabled by default): registers functions for ranking search results, such as \fIbm25\fP and \fIlucene\fP\&. .IP \(bu 2 \fBhash_functions\fP: registers md5, sha1, sha256, adler32, crc32 and murmurhash functions. .IP \(bu 2 \fBregexp_function\fP: registers a regexp function. .UNINDENT .sp Examples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C def create_new_user(username, password): # DO NOT DO THIS IN REAL LIFE. PLEASE. query = User.insert({\(aqusername\(aq: username, \(aqpassword\(aq: fn.sha1(password)}) new_user_id = query.execute() .ft P .fi .UNINDENT .UNINDENT .sp You can use the \fImurmurhash\fP function to hash bytes to an integer for compact storage: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> db = SqliteExtDatabase(\(aq:memory:\(aq, hash_functions=True) >>> db.execute_sql(\(aqSELECT murmurhash(?)\(aq, (\(aqabcdefg\(aq,)).fetchone() (4188131059,) .ft P .fi .UNINDENT .UNINDENT .SS Playhouse, extensions to Peewee .sp Peewee comes with numerous extension modules which are collected under the \fBplayhouse\fP namespace. Despite the silly name, there are some very useful extensions, particularly those that expose vendor\-specific database features like the sqlite_ext and \fI\%Postgresql Extensions\fP extensions. .sp Below you will find a loosely organized listing of the various modules that make up the \fBplayhouse\fP\&. .sp \fBDatabase drivers / vendor\-specific database functionality\fP .INDENT 0.0 .IP \(bu 2 sqlite_ext (on its own page) .IP \(bu 2 \fI\%SqliteQ\fP .IP \(bu 2 \fI\%Sqlite User\-Defined Functions\fP .IP \(bu 2 \fI\%apsw, an advanced sqlite driver\fP .IP \(bu 2 \fI\%Sqlcipher backend\fP .IP \(bu 2 \fI\%Postgresql Extensions\fP .IP \(bu 2 \fI\%Cockroach Database\fP .IP \(bu 2 \fI\%MySQL Extensions\fP .UNINDENT .sp \fBHigh\-level features\fP .INDENT 0.0 .IP \(bu 2 \fI\%Fields\fP .IP \(bu 2 \fI\%Shortcuts\fP .IP \(bu 2 \fI\%Hybrid Attributes\fP .IP \(bu 2 \fI\%Key/Value Store\fP .IP \(bu 2 \fI\%Signal support\fP .IP \(bu 2 \fI\%DataSet\fP .UNINDENT .sp \fBDatabase management and framework integration\fP .INDENT 0.0 .IP \(bu 2 \fI\%pwiz, a model generator\fP .IP \(bu 2 \fI\%Schema Migrations\fP .IP \(bu 2 \fI\%Connection pool\fP .IP \(bu 2 \fI\%Reflection\fP .IP \(bu 2 \fI\%Database URL\fP .IP \(bu 2 \fI\%Test Utils\fP .IP \(bu 2 \fI\%Flask Utils\fP .UNINDENT .SS Sqlite Extensions .sp The Sqlite extensions have been moved to their own page\&. .SS SqliteQ .sp The \fBplayhouse.sqliteq\fP module provides a subclass of \fBSqliteExtDatabase\fP, that will serialize concurrent writes to a SQLite database. \fBSqliteQueueDatabase\fP can be used as a drop\-in replacement for the regular \fBSqliteDatabase\fP if you want simple \fBread and write\fP access to a SQLite database from \fBmultiple threads\fP\&. .sp SQLite only allows one connection to write to the database at any given time. As a result, if you have a multi\-threaded application (like a web\-server, for example) that needs to write to the database, you may see occasional errors when one or more of the threads attempting to write cannot acquire the lock. .sp \fBSqliteQueueDatabase\fP is designed to simplify things by sending all write queries through a single, long\-lived connection. The benefit is that you get the appearance of multiple threads writing to the database without conflicts or timeouts. The downside, however, is that you cannot issue write transactions that encompass multiple queries \-\- all writes run in autocommit mode, essentially. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 The module gets its name from the fact that all write queries get put into a thread\-safe queue. A single worker thread listens to the queue and executes all queries that are sent to it. .UNINDENT .UNINDENT .SS Transactions .sp Because all queries are serialized and executed by a single worker thread, it is possible for transactional SQL from separate threads to be executed out\-of\-order. In the example below, the transaction started by thread "B" is rolled back by thread "A" (with bad consequences!): .INDENT 0.0 .IP \(bu 2 Thread A: UPDATE transplants SET organ=\(aqliver\(aq, ...; .IP \(bu 2 Thread B: BEGIN TRANSACTION; .IP \(bu 2 Thread B: UPDATE life_support_system SET timer += 60 ...; .IP \(bu 2 Thread A: ROLLBACK; \-\- Oh no.... .UNINDENT .sp Since there is a potential for queries from separate transactions to be interleaved, the \fBtransaction()\fP and \fBatomic()\fP methods are disabled on \fBSqliteQueueDatabase\fP\&. .sp For cases when you wish to temporarily write to the database from a different thread, you can use the \fBpause()\fP and \fBunpause()\fP methods. These methods block the caller until the writer thread is finished with its current workload. The writer then disconnects and the caller takes over until \fBunpause\fP is called. .sp The \fBstop()\fP, \fBstart()\fP, and \fBis_stopped()\fP methods can also be used to control the writer thread. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Take a look at SQLite\(aqs \fI\%isolation\fP documentation for more information about how SQLite handles concurrent connections. .UNINDENT .UNINDENT .SS Code sample .sp Creating a database instance does not require any special handling. The \fBSqliteQueueDatabase\fP accepts some special parameters which you should be aware of, though. If you are using \fI\%gevent\fP, you must specify \fBuse_gevent=True\fP when instantiating your database \-\- this way Peewee will know to use the appropriate objects for handling queueing, thread creation, and locking. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.sqliteq import SqliteQueueDatabase db = SqliteQueueDatabase( \(aqmy_app.db\(aq, use_gevent=False, # Use the standard library "threading" module. autostart=False, # The worker thread now must be started manually. queue_max_size=64, # Max. # of pending writes that can accumulate. results_timeout=5.0) # Max. time to wait for query to be executed. .ft P .fi .UNINDENT .UNINDENT .sp If \fBautostart=False\fP, as in the above example, you will need to call \fBstart()\fP to bring up the worker threads that will do the actual write query execution. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C @app.before_first_request def _start_worker_threads(): db.start() .ft P .fi .UNINDENT .UNINDENT .sp If you plan on performing SELECT queries or generally wanting to access the database, you will need to call \fBconnect()\fP and \fBclose()\fP as you would with any other database instance. .sp When your application is ready to terminate, use the \fBstop()\fP method to shut down the worker thread. If there was a backlog of work, then this method will block until all pending work is finished (though no new work is allowed). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C import atexit @atexit.register def _stop_worker_threads(): db.stop() .ft P .fi .UNINDENT .UNINDENT .sp Lastly, the \fBis_stopped()\fP method can be used to determine whether the database writer is up and running. .SS Sqlite User\-Defined Functions .sp The \fBsqlite_udf\fP playhouse module contains a number of user\-defined functions, aggregates, and table\-valued functions, which you may find useful. The functions are grouped in collections and you can register these user\-defined extensions individually, by collection, or register everything. .sp Scalar functions are functions which take a number of parameters and return a single value. For example, converting a string to upper\-case, or calculating the MD5 hex digest. .sp Aggregate functions are like scalar functions that operate on multiple rows of data, producing a single result. For example, calculating the sum of a list of integers, or finding the smallest value in a particular column. .sp Table\-valued functions are simply functions that can return multiple rows of data. For example, a regular\-expression search function that returns all the matches in a given string, or a function that accepts two dates and generates all the intervening days. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 To use table\-valued functions, you will need to build the \fBplayhouse._sqlite_ext\fP C extension. .UNINDENT .UNINDENT .sp Registering user\-defined functions: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = SqliteDatabase(\(aqmy_app.db\(aq) # Register *all* functions. register_all(db) # Alternatively, you can register individual groups. This will just # register the DATE and MATH groups of functions. register_groups(db, \(aqDATE\(aq, \(aqMATH\(aq) # If you only wish to register, say, the aggregate functions for a # particular group or groups, you can: register_aggregate_groups(db, \(aqDATE\(aq) # If you only wish to register a single function, then you can: from playhouse.sqlite_udf import gzip, gunzip db.register_function(gzip, \(aqgzip\(aq) db.register_function(gunzip, \(aqgunzip\(aq) .ft P .fi .UNINDENT .UNINDENT .sp Using a library function ("hostname"): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Assume we have a model, Link, that contains lots of arbitrary URLs. # We want to discover the most common hosts that have been linked. query = (Link .select(fn.hostname(Link.url).alias(\(aqhost\(aq), fn.COUNT(Link.id)) .group_by(fn.hostname(Link.url)) .order_by(fn.COUNT(Link.id).desc()) .tuples()) # Print the hostname along with number of links associated with it. for host, count in query: print(\(aq%s: %s\(aq % (host, count)) .ft P .fi .UNINDENT .UNINDENT .SS Functions, listed by collection name .sp Scalar functions are indicated by \fB(f)\fP, aggregate functions by \fB(a)\fP, and table\-valued functions by \fB(t)\fP\&. .sp \fBCONTROL_FLOW\fP .INDENT 0.0 .TP .B if_then_else(cond, truthy[, falsey=None]) Simple ternary\-type operator, where, depending on the truthiness of the \fBcond\fP parameter, either the \fBtruthy\fP or \fBfalsey\fP value will be returned. .UNINDENT .sp \fBDATE\fP .INDENT 0.0 .TP .B strip_tz(date_str) .INDENT 7.0 .TP .B Parameters \fBdate_str\fP \-\- A datetime, encoded as a string. .TP .B Returns The datetime with any timezone info stripped off. .UNINDENT .sp The time is not adjusted in any way, the timezone is simply removed. .UNINDENT .INDENT 0.0 .TP .B humandelta(nseconds[, glue=\(aq, \(aq]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBnseconds\fP (\fIint\fP) \-\- Number of seconds, total, in timedelta. .IP \(bu 2 \fBglue\fP (\fIstr\fP) \-\- Fragment to join values. .UNINDENT .TP .B Returns Easy\-to\-read description of timedelta. .UNINDENT .sp Example, 86471 \-> "1 day, 1 minute, 11 seconds" .UNINDENT .INDENT 0.0 .TP .B mintdiff(datetime_value) .INDENT 7.0 .TP .B Parameters \fBdatetime_value\fP \-\- A date\-time. .TP .B Returns Minimum difference between any two values in list. .UNINDENT .sp Aggregate function that computes the minimum difference between any two datetimes. .UNINDENT .INDENT 0.0 .TP .B avgtdiff(datetime_value) .INDENT 7.0 .TP .B Parameters \fBdatetime_value\fP \-\- A date\-time. .TP .B Returns Average difference between values in list. .UNINDENT .sp Aggregate function that computes the average difference between consecutive values in the list. .UNINDENT .INDENT 0.0 .TP .B duration(datetime_value) .INDENT 7.0 .TP .B Parameters \fBdatetime_value\fP \-\- A date\-time. .TP .B Returns Duration from smallest to largest value in list, in seconds. .UNINDENT .sp Aggregate function that computes the duration from the smallest to the largest value in the list, returned in seconds. .UNINDENT .INDENT 0.0 .TP .B date_series(start, stop[, step_seconds=86400]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBstart\fP (\fIdatetime\fP) \-\- Start datetime .IP \(bu 2 \fBstop\fP (\fIdatetime\fP) \-\- Stop datetime .IP \(bu 2 \fBstep_seconds\fP (\fIint\fP) \-\- Number of seconds comprising a step. .UNINDENT .UNINDENT .sp Table\-value function that returns rows consisting of the date/+time values encountered iterating from start to stop, \fBstep_seconds\fP at a time. .sp Additionally, if start does not have a time component and step_seconds is greater\-than\-or\-equal\-to one day (86400 seconds), the values returned will be dates. Conversely, if start does not have a date component, values will be returned as times. Otherwise values are returned as datetimes. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C SELECT * FROM date_series(\(aq2017\-01\-28\(aq, \(aq2017\-02\-02\(aq); value \-\-\-\-\- 2017\-01\-28 2017\-01\-29 2017\-01\-30 2017\-01\-31 2017\-02\-01 2017\-02\-02 .ft P .fi .UNINDENT .UNINDENT .UNINDENT .sp \fBFILE\fP .INDENT 0.0 .TP .B file_ext(filename) .INDENT 7.0 .TP .B Parameters \fBfilename\fP (\fIstr\fP) \-\- Filename to extract extension from. .TP .B Returns Returns the file extension, including the leading ".". .UNINDENT .UNINDENT .INDENT 0.0 .TP .B file_read(filename) .INDENT 7.0 .TP .B Parameters \fBfilename\fP (\fIstr\fP) \-\- Filename to read. .TP .B Returns Contents of the file. .UNINDENT .UNINDENT .sp \fBHELPER\fP .INDENT 0.0 .TP .B gzip(data[, compression=9]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdata\fP (\fIbytes\fP) \-\- Data to compress. .IP \(bu 2 \fBcompression\fP (\fIint\fP) \-\- Compression level (9 is max). .UNINDENT .TP .B Returns Compressed binary data. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B gunzip(data) .INDENT 7.0 .TP .B Parameters \fBdata\fP (\fIbytes\fP) \-\- Compressed data. .TP .B Returns Uncompressed binary data. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B hostname(url) .INDENT 7.0 .TP .B Parameters \fBurl\fP (\fIstr\fP) \-\- URL to extract hostname from. .TP .B Returns hostname portion of URL .UNINDENT .UNINDENT .INDENT 0.0 .TP .B toggle(key) .INDENT 7.0 .TP .B Parameters \fBkey\fP \-\- Key to toggle. .UNINDENT .sp Toggle a key between True/False state. Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> toggle(\(aqmy\-key\(aq) True >>> toggle(\(aqmy\-key\(aq) False >>> toggle(\(aqmy\-key\(aq) True .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B setting(key[, value=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBkey\fP \-\- Key to set/retrieve. .IP \(bu 2 \fBvalue\fP \-\- Value to set. .UNINDENT .TP .B Returns Value associated with key. .UNINDENT .sp Store/retrieve a setting in memory and persist during lifetime of application. To get the current value, only specify the key. To set a new value, call with key and new value. .UNINDENT .INDENT 0.0 .TP .B clear_toggles() Clears all state associated with the \fI\%toggle()\fP function. .UNINDENT .INDENT 0.0 .TP .B clear_settings() Clears all state associated with the \fI\%setting()\fP function. .UNINDENT .sp \fBMATH\fP .INDENT 0.0 .TP .B randomrange(start[, stop=None[, step=None]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBstart\fP (\fIint\fP) \-\- Start of range (inclusive) .IP \(bu 2 \fBend\fP (\fIint\fP) \-\- End of range(not inclusive) .IP \(bu 2 \fBstep\fP (\fIint\fP) \-\- Interval at which to return a value. .UNINDENT .UNINDENT .sp Return a random integer between \fB[start, end)\fP\&. .UNINDENT .INDENT 0.0 .TP .B gauss_distribution(mean, sigma) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmean\fP (\fIfloat\fP) \-\- Mean value .IP \(bu 2 \fBsigma\fP (\fIfloat\fP) \-\- Standard deviation .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B sqrt(n) Calculate the square root of \fBn\fP\&. .UNINDENT .INDENT 0.0 .TP .B tonumber(s) .INDENT 7.0 .TP .B Parameters \fBs\fP (\fIstr\fP) \-\- String to convert to number. .TP .B Returns Integer, floating\-point or NULL on failure. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B mode(val) .INDENT 7.0 .TP .B Parameters \fBval\fP \-\- Numbers in list. .TP .B Returns The mode, or most\-common, number observed. .UNINDENT .sp Aggregate function which calculates \fImode\fP of values. .UNINDENT .INDENT 0.0 .TP .B minrange(val) .INDENT 7.0 .TP .B Parameters \fBval\fP \-\- Value .TP .B Returns Min difference between two values. .UNINDENT .sp Aggregate function which calculates the minimal distance between two numbers in the sequence. .UNINDENT .INDENT 0.0 .TP .B avgrange(val) .INDENT 7.0 .TP .B Parameters \fBval\fP \-\- Value .TP .B Returns Average difference between values. .UNINDENT .sp Aggregate function which calculates the average distance between two consecutive numbers in the sequence. .UNINDENT .INDENT 0.0 .TP .B range(val) .INDENT 7.0 .TP .B Parameters \fBval\fP \-\- Value .TP .B Returns The range from the smallest to largest value in sequence. .UNINDENT .sp Aggregate function which returns range of values observed. .UNINDENT .INDENT 0.0 .TP .B median(val) .INDENT 7.0 .TP .B Parameters \fBval\fP \-\- Value .TP .B Returns The median, or middle, value in a sequence. .UNINDENT .sp Aggregate function which calculates the middle value in a sequence. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Only available if you compiled the \fB_sqlite_udf\fP extension. .UNINDENT .UNINDENT .UNINDENT .sp \fBSTRING\fP .INDENT 0.0 .TP .B substr_count(haystack, needle) Returns number of times \fBneedle\fP appears in \fBhaystack\fP\&. .UNINDENT .INDENT 0.0 .TP .B strip_chars(haystack, chars) Strips any characters in \fBchars\fP from beginning and end of \fBhaystack\fP\&. .UNINDENT .INDENT 0.0 .TP .B damerau_levenshtein_dist(s1, s2) Computes the edit distance from s1 to s2 using the damerau variant of the levenshtein algorithm. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Only available if you compiled the \fB_sqlite_udf\fP extension. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B levenshtein_dist(s1, s2) Computes the edit distance from s1 to s2 using the levenshtein algorithm. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Only available if you compiled the \fB_sqlite_udf\fP extension. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B str_dist(s1, s2) Computes the edit distance from s1 to s2 using the standard library SequenceMatcher\(aqs algorithm. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Only available if you compiled the \fB_sqlite_udf\fP extension. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B regex_search(regex, search_string) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBregex\fP (\fIstr\fP) \-\- Regular expression .IP \(bu 2 \fBsearch_string\fP (\fIstr\fP) \-\- String to search for instances of regex. .UNINDENT .UNINDENT .sp Table\-value function that searches a string for substrings that match the provided \fBregex\fP\&. Returns rows for each match found. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C SELECT * FROM regex_search(\(aq\ew+\(aq, \(aqextract words, ignore! symbols\(aq); value \-\-\-\-\- extract words ignore symbols .ft P .fi .UNINDENT .UNINDENT .UNINDENT .SS apsw, an advanced sqlite driver .sp The \fBapsw_ext\fP module contains a database class suitable for use with the apsw sqlite driver. .sp APSW Project page: \fI\%https://github.com/rogerbinns/apsw\fP .sp APSW is a really neat library that provides a thin wrapper on top of SQLite\(aqs C interface, making it possible to use all of SQLite\(aqs advanced features. .sp Here are just a few reasons to use APSW, taken from the documentation: .INDENT 0.0 .IP \(bu 2 APSW gives all functionality of SQLite, including virtual tables, virtual file system, blob i/o, backups and file control. .IP \(bu 2 Connections can be shared across threads without any additional locking. .IP \(bu 2 Transactions are managed explicitly by your code. .IP \(bu 2 APSW can handle nested transactions. .IP \(bu 2 Unicode is handled correctly. .IP \(bu 2 APSW is faster. .UNINDENT .sp For more information on the differences between apsw and pysqlite, check \fI\%the apsw docs\fP\&. .SS How to use the APSWDatabase .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from apsw_ext import * db = APSWDatabase(\(aq:memory:\(aq) class BaseModel(Model): class Meta: database = db class SomeModel(BaseModel): col1 = CharField() col2 = DateTimeField() .ft P .fi .UNINDENT .UNINDENT .SS apsw_ext API notes .sp \fI\%APSWDatabase\fP extends the \fBSqliteExtDatabase\fP and inherits its advanced features. .INDENT 0.0 .TP .B class APSWDatabase(database, **connect_kwargs) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdatabase\fP (\fIstring\fP) \-\- filename of sqlite database .IP \(bu 2 \fBconnect_kwargs\fP \-\- keyword arguments passed to apsw when opening a connection .UNINDENT .UNINDENT .INDENT 7.0 .TP .B register_module(mod_name, mod_inst) Provides a way of globally registering a module. For more information, see the \fI\%documentation on virtual tables\fP\&. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmod_name\fP (\fIstring\fP) \-\- name to use for module .IP \(bu 2 \fBmod_inst\fP (\fIobject\fP) \-\- an object implementing the \fI\%Virtual Table\fP interface .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B unregister_module(mod_name) Unregister a module. .INDENT 7.0 .TP .B Parameters \fBmod_name\fP (\fIstring\fP) \-\- name to use for module .UNINDENT .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Be sure to use the \fBField\fP subclasses defined in the \fBapsw_ext\fP module, as they will properly handle adapting the data types for storage. .sp For example, instead of using \fBpeewee.DateTimeField\fP, be sure you are importing and using \fBplayhouse.apsw_ext.DateTimeField\fP\&. .UNINDENT .UNINDENT .SS Sqlcipher backend .INDENT 0.0 .IP \(bu 2 Although this extention\(aqs code is short, it has not been properly peer\-reviewed yet and may have introduced vulnerabilities. .UNINDENT .sp Also note that this code relies on \fI\%pysqlcipher\fP and \fI\%sqlcipher\fP, and the code there might have vulnerabilities as well, but since these are widely used crypto modules, we can expect "short zero days" there. .SS sqlcipher_ext API notes .INDENT 0.0 .TP .B class SqlCipherDatabase(database, passphrase, **kwargs) Subclass of \fBSqliteDatabase\fP that stores the database encrypted. Instead of the standard \fBsqlite3\fP backend, it uses \fI\%pysqlcipher\fP: a python wrapper for \fI\%sqlcipher\fP, which \-\- in turn \-\- is an encrypted wrapper around \fBsqlite3\fP, so the API is \fIidentical\fP to \fBSqliteDatabase\fP\(aqs, except for object construction parameters: .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdatabase\fP \-\- Path to encrypted database filename to open [or create]. .IP \(bu 2 \fBpassphrase\fP \-\- Database encryption passphrase: should be at least 8 character long, but it is \fIstrongly advised\fP to enforce better \fI\%passphrase strength\fP criteria in your implementation. .UNINDENT .UNINDENT .INDENT 7.0 .IP \(bu 2 If the \fBdatabase\fP file doesn\(aqt exist, it will be \fIcreated\fP with encryption by a key derived from \fBpasshprase\fP\&. .IP \(bu 2 When trying to open an existing database, \fBpasshprase\fP should be identical to the ones used when it was created. If the passphrase is incorrect, an error will be raised when first attempting to access the database. .UNINDENT .INDENT 7.0 .TP .B rekey(passphrase) .INDENT 7.0 .TP .B Parameters \fBpassphrase\fP (\fIstr\fP) \-\- New passphrase for database. .UNINDENT .sp Change the passphrase for database. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 SQLCipher can be configured using a number of extension PRAGMAs. The list of PRAGMAs and their descriptions can be found in the \fI\%SQLCipher documentation\fP\&. .sp For example to specify the number of PBKDF2 iterations for the key derivation (64K in SQLCipher 3.x, 256K in SQLCipher 4.x by default): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Use 1,000,000 iterations. db = SqlCipherDatabase(\(aqmy_app.db\(aq, pragmas={\(aqkdf_iter\(aq: 1000000}) .ft P .fi .UNINDENT .UNINDENT .sp To use a cipher page\-size of 16KB and a cache\-size of 10,000 pages: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = SqlCipherDatabase(\(aqmy_app.db\(aq, passphrase=\(aqsecret!!!\(aq, pragmas={ \(aqcipher_page_size\(aq: 1024 * 16, \(aqcache_size\(aq: 10000}) # 10,000 16KB pages, or 160MB. .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .sp Example of prompting the user for a passphrase: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = SqlCipherDatabase(None) class BaseModel(Model): """Parent for all app\(aqs models""" class Meta: # We won\(aqt have a valid db until user enters passhrase. database = db # Derive our model subclasses class Person(BaseModel): name = TextField(primary_key=True) right_passphrase = False while not right_passphrase: db.init( \(aqtestsqlcipher.db\(aq, passphrase=get_passphrase_from_user()) try: # Actually execute a query against the db to test passphrase. db.get_tables() except DatabaseError as exc: # This error indicates the password was wrong. if exc.args[0] == \(aqfile is encrypted or is not a database\(aq: tell_user_the_passphrase_was_wrong() db.init(None) # Reset the db. else: raise exc else: # The password was correct. right_passphrase = True .ft P .fi .UNINDENT .UNINDENT .sp See also: a slightly more elaborate \fI\%example\fP\&. .SS Postgresql Extensions .sp The postgresql extensions module provides a number of "postgres\-only" functions, currently: .INDENT 0.0 .IP \(bu 2 \fI\%json support\fP, including \fIjsonb\fP for Postgres 9.4. .IP \(bu 2 \fI\%hstore support\fP .IP \(bu 2 \fI\%server\-side cursors\fP .IP \(bu 2 \fI\%full\-text search\fP .IP \(bu 2 \fI\%ArrayField\fP field type, for storing arrays. .IP \(bu 2 \fI\%HStoreField\fP field type, for storing key/value pairs. .IP \(bu 2 \fI\%IntervalField\fP field type, for storing \fBtimedelta\fP objects. .IP \(bu 2 \fBJSONField\fP field type, for storing JSON data. .IP \(bu 2 \fI\%BinaryJSONField\fP field type for the \fBjsonb\fP JSON data type. .IP \(bu 2 \fI\%TSVectorField\fP field type, for storing full\-text search data. .IP \(bu 2 \fI\%DateTimeTZField\fP field type, a timezone\-aware datetime field. .UNINDENT .sp In the future I would like to add support for more of postgresql\(aqs features. If there is a particular feature you would like to see added, please \fI\%open a Github issue\fP\&. .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 In order to start using the features described below, you will need to use the extension \fI\%PostgresqlExtDatabase\fP class instead of \fBPostgresqlDatabase\fP\&. .UNINDENT .UNINDENT .sp The code below will assume you are using the following database and base model: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.postgres_ext import * ext_db = PostgresqlExtDatabase(\(aqpeewee_test\(aq, user=\(aqpostgres\(aq) class BaseExtModel(Model): class Meta: database = ext_db .ft P .fi .UNINDENT .UNINDENT .SS JSON Support .sp peewee has basic support for Postgres\(aq native JSON data type, in the form of \fBJSONField\fP\&. As of version 2.4.7, peewee also supports the Postgres 9.4 binary json \fBjsonb\fP type, via \fI\%BinaryJSONField\fP\&. .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 Postgres supports a JSON data type natively as of 9.2 (full support in 9.3). In order to use this functionality you must be using the correct version of Postgres with \fIpsycopg2\fP version 2.5 or greater. .sp To use \fI\%BinaryJSONField\fP, which has many performance and querying advantages, you must have Postgres 9.4 or later. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 You must be sure your database is an instance of \fI\%PostgresqlExtDatabase\fP in order to use the \fIJSONField\fP\&. .UNINDENT .UNINDENT .sp Here is an example of how you might declare a model with a JSON field: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C import json import urllib2 from playhouse.postgres_ext import * db = PostgresqlExtDatabase(\(aqmy_database\(aq) class APIResponse(Model): url = CharField() response = JSONField() class Meta: database = db @classmethod def request(cls, url): fh = urllib2.urlopen(url) return cls.create(url=url, response=json.loads(fh.read())) APIResponse.create_table() # Store a JSON response. offense = APIResponse.request(\(aqhttp://crime\-api.com/api/offense/\(aq) booking = APIResponse.request(\(aqhttp://crime\-api.com/api/booking/\(aq) # Query a JSON data structure using a nested key lookup: offense_responses = APIResponse.select().where( APIResponse.response[\(aqmeta\(aq][\(aqmodel\(aq] == \(aqoffense\(aq) # Retrieve a sub\-key for each APIResponse. By calling .as_json(), the # data at the sub\-key will be returned as Python objects (dicts, lists, # etc) instead of serialized JSON. q = (APIResponse .select( APIResponse.data[\(aqbooking\(aq][\(aqperson\(aq].as_json().alias(\(aqperson\(aq)) .where(APIResponse.data[\(aqmeta\(aq][\(aqmodel\(aq] == \(aqbooking\(aq)) for result in q: print(result.person[\(aqname\(aq], result.person[\(aqdob\(aq]) .ft P .fi .UNINDENT .UNINDENT .sp The \fI\%BinaryJSONField\fP works the same and supports the same operations as the regular \fBJSONField\fP, but provides several additional operations for testing \fBcontainment\fP\&. Using the binary json field, you can test whether your JSON data contains other partial JSON structures (\fI\%contains()\fP, \fI\%contains_any()\fP, \fI\%contains_all()\fP), or whether it is a subset of a larger JSON document (\fI\%contained_by()\fP). .sp For more examples, see the \fBJSONField\fP and \fI\%BinaryJSONField\fP API documents below. .SS hstore support .sp \fI\%Postgresql hstore\fP is an embedded key/value store. With hstore, you can store arbitrary key/value pairs in your database alongside structured relational data. .sp To use \fBhstore\fP, you need to specify an additional parameter when instantiating your \fI\%PostgresqlExtDatabase\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Specify "register_hstore=True": db = PostgresqlExtDatabase(\(aqmy_db\(aq, register_hstore=True) .ft P .fi .UNINDENT .UNINDENT .sp Currently the \fBpostgres_ext\fP module supports the following operations: .INDENT 0.0 .IP \(bu 2 Store and retrieve arbitrary dictionaries .IP \(bu 2 Filter by key(s) or partial dictionary .IP \(bu 2 Update/add one or more keys to an existing dictionary .IP \(bu 2 Delete one or more keys from an existing dictionary .IP \(bu 2 Select keys, values, or zip keys and values .IP \(bu 2 Retrieve a slice of keys/values .IP \(bu 2 Test for the existence of a key .IP \(bu 2 Test that a key has a non\-NULL value .UNINDENT .SS Using hstore .sp To start with, you will need to import the custom database class and the hstore functions from \fBplayhouse.postgres_ext\fP (see above code snippet). Then, it is as simple as adding a \fI\%HStoreField\fP to your model: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class House(BaseExtModel): address = CharField() features = HStoreField() .ft P .fi .UNINDENT .UNINDENT .sp You can now store arbitrary key/value pairs on \fBHouse\fP instances: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> h = House.create( \&... address=\(aq123 Main St\(aq, \&... features={\(aqgarage\(aq: \(aq2 cars\(aq, \(aqbath\(aq: \(aq2 bath\(aq}) \&... >>> h_from_db = House.get(House.id == h.id) >>> h_from_db.features {\(aqbath\(aq: \(aq2 bath\(aq, \(aqgarage\(aq: \(aq2 cars\(aq} .ft P .fi .UNINDENT .UNINDENT .sp You can filter by individual key, multiple keys or partial dictionary: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> query = House.select() >>> garage = query.where(House.features.contains(\(aqgarage\(aq)) >>> garage_and_bath = query.where(House.features.contains([\(aqgarage\(aq, \(aqbath\(aq])) >>> twocar = query.where(House.features.contains({\(aqgarage\(aq: \(aq2 cars\(aq})) .ft P .fi .UNINDENT .UNINDENT .sp Suppose you want to do an atomic update to the house: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> new_features = House.features.update({\(aqbath\(aq: \(aq2.5 bath\(aq, \(aqsqft\(aq: \(aq1100\(aq}) >>> query = House.update(features=new_features) >>> query.where(House.id == h.id).execute() 1 >>> h = House.get(House.id == h.id) >>> h.features {\(aqbath\(aq: \(aq2.5 bath\(aq, \(aqgarage\(aq: \(aq2 cars\(aq, \(aqsqft\(aq: \(aq1100\(aq} .ft P .fi .UNINDENT .UNINDENT .sp Or, alternatively an atomic delete: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> query = House.update(features=House.features.delete(\(aqbath\(aq)) >>> query.where(House.id == h.id).execute() 1 >>> h = House.get(House.id == h.id) >>> h.features {\(aqgarage\(aq: \(aq2 cars\(aq, \(aqsqft\(aq: \(aq1100\(aq} .ft P .fi .UNINDENT .UNINDENT .sp Multiple keys can be deleted at the same time: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> query = House.update(features=House.features.delete(\(aqgarage\(aq, \(aqsqft\(aq)) .ft P .fi .UNINDENT .UNINDENT .sp You can select just keys, just values, or zip the two: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> for h in House.select(House.address, House.features.keys().alias(\(aqkeys\(aq)): \&... print(h.address, h.keys) 123 Main St [u\(aqbath\(aq, u\(aqgarage\(aq] >>> for h in House.select(House.address, House.features.values().alias(\(aqvals\(aq)): \&... print(h.address, h.vals) 123 Main St [u\(aq2 bath\(aq, u\(aq2 cars\(aq] >>> for h in House.select(House.address, House.features.items().alias(\(aqmtx\(aq)): \&... print(h.address, h.mtx) 123 Main St [[u\(aqbath\(aq, u\(aq2 bath\(aq], [u\(aqgarage\(aq, u\(aq2 cars\(aq]] .ft P .fi .UNINDENT .UNINDENT .sp You can retrieve a slice of data, for example, all the garage data: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> query = House.select(House.address, House.features.slice(\(aqgarage\(aq).alias(\(aqgarage_data\(aq)) >>> for house in query: \&... print(house.address, house.garage_data) 123 Main St {\(aqgarage\(aq: \(aq2 cars\(aq} .ft P .fi .UNINDENT .UNINDENT .sp You can check for the existence of a key and filter rows accordingly: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> has_garage = House.features.exists(\(aqgarage\(aq) >>> for house in House.select(House.address, has_garage.alias(\(aqhas_garage\(aq)): \&... print(house.address, house.has_garage) 123 Main St True >>> for house in House.select().where(House.features.exists(\(aqgarage\(aq)): \&... print(house.address, house.features[\(aqgarage\(aq]) # <\-\- just houses w/garage data 123 Main St 2 cars .ft P .fi .UNINDENT .UNINDENT .SS Interval support .sp Postgres supports durations through the \fBINTERVAL\fP data\-type (\fI\%docs\fP). .INDENT 0.0 .TP .B class IntervalField([null=False[, \&...]]) Field class capable of storing Python \fBdatetime.timedelta\fP instances. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C from datetime import timedelta from playhouse.postgres_ext import * db = PostgresqlExtDatabase(\(aqmy_db\(aq) class Event(Model): location = CharField() duration = IntervalField() start_time = DateTimeField() class Meta: database = db @classmethod def get_long_meetings(cls): return cls.select().where(cls.duration > timedelta(hours=1)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .SS Server\-side cursors .sp When psycopg2 executes a query, normally all results are fetched and returned to the client by the backend. This can cause your application to use a lot of memory when making large queries. Using server\-side cursors, results are returned a little at a time (by default 2000 records). For the definitive reference, please see the \fI\%psycopg2 documentation\fP\&. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 To use server\-side (or named) cursors, you must be using \fI\%PostgresqlExtDatabase\fP\&. .UNINDENT .UNINDENT .sp To execute a query using a server\-side cursor, simply wrap your select query using the \fI\%ServerSide()\fP helper: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C large_query = PageView.select() # Build query normally. # Iterate over large query inside a transaction. for page_view in ServerSide(large_query): # do some interesting analysis here. pass # Server\-side resources are released. .ft P .fi .UNINDENT .UNINDENT .sp If you would like all \fBSELECT\fP queries to automatically use a server\-side cursor, you can specify this when creating your \fI\%PostgresqlExtDatabase\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from postgres_ext import PostgresqlExtDatabase ss_db = PostgresqlExtDatabase(\(aqmy_db\(aq, server_side_cursors=True) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Server\-side cursors live only as long as the transaction, so for this reason peewee will not automatically call \fBcommit()\fP after executing a \fBSELECT\fP query. If you do not \fBcommit\fP after you are done iterating, you will not release the server\-side resources until the connection is closed (or the transaction is committed later). Furthermore, since peewee will by default cache rows returned by the cursor, you should always call \fB\&.iterator()\fP when iterating over a large query. .sp If you are using the \fI\%ServerSide()\fP helper, the transaction and call to \fBiterator()\fP will be handled transparently. .UNINDENT .UNINDENT .SS Full\-text search .sp Postgresql provides \fI\%sophisticated full\-text search\fP using special data\-types (\fBtsvector\fP and \fBtsquery\fP). Documents should be stored or converted to the \fBtsvector\fP type, and search queries should be converted to \fBtsquery\fP\&. .sp For simple cases, you can simply use the \fI\%Match()\fP function, which will automatically perform the appropriate conversions, and requires no schema changes: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C def blog_search(search_term): return Blog.select().where( (Blog.status == Blog.STATUS_PUBLISHED) & Match(Blog.content, search_term)) .ft P .fi .UNINDENT .UNINDENT .sp The \fI\%Match()\fP function will automatically convert the left\-hand operand to a \fBtsvector\fP, and the right\-hand operand to a \fBtsquery\fP\&. For better performance, it is recommended you create a \fBGIN\fP index on the column you plan to search: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C CREATE INDEX blog_full_text_search ON blog USING gin(to_tsvector(content)); .ft P .fi .UNINDENT .UNINDENT .sp Alternatively, you can use the \fI\%TSVectorField\fP to maintain a dedicated column for storing \fBtsvector\fP data: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Blog(Model): content = TextField() search_content = TSVectorField() .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 \fI\%TSVectorField\fP, will automatically be created with a GIN index. .UNINDENT .UNINDENT .sp You will need to explicitly convert the incoming text data to \fBtsvector\fP when inserting or updating the \fBsearch_content\fP field: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C content = \(aqExcellent blog post about peewee ORM.\(aq blog_entry = Blog.create( content=content, search_content=fn.to_tsvector(content)) .ft P .fi .UNINDENT .UNINDENT .sp To perform a full\-text search, use \fI\%TSVectorField.match()\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C terms = \(aqpython & (sqlite | postgres)\(aq results = Blog.select().where(Blog.search_content.match(terms)) .ft P .fi .UNINDENT .UNINDENT .sp For more information, see the \fI\%Postgres full\-text search docs\fP\&. .SS postgres_ext API notes .INDENT 0.0 .TP .B class PostgresqlExtDatabase(database[, server_side_cursors=False[, register_hstore=False[, \&...]]]) Identical to \fBPostgresqlDatabase\fP but required in order to support: .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdatabase\fP (\fIstr\fP) \-\- Name of database to connect to. .IP \(bu 2 \fBserver_side_cursors\fP (\fIbool\fP) \-\- Whether \fBSELECT\fP queries should utilize server\-side cursors. .IP \(bu 2 \fBregister_hstore\fP (\fIbool\fP) \-\- Register the HStore extension with the connection. .UNINDENT .UNINDENT .INDENT 7.0 .IP \(bu 2 \fI\%Server\-side cursors\fP .IP \(bu 2 \fI\%ArrayField\fP .IP \(bu 2 \fI\%DateTimeTZField\fP .IP \(bu 2 \fBJSONField\fP .IP \(bu 2 \fI\%BinaryJSONField\fP .IP \(bu 2 \fI\%HStoreField\fP .IP \(bu 2 \fI\%TSVectorField\fP .UNINDENT .sp If you wish to use the HStore extension, you must specify \fBregister_hstore=True\fP\&. .sp If using \fBserver_side_cursors\fP, also be sure to wrap your queries with \fI\%ServerSide()\fP\&. .UNINDENT .INDENT 0.0 .TP .B ServerSide(select_query) .INDENT 7.0 .TP .B Parameters \fBselect_query\fP \-\- a \fBSelectQuery\fP instance. .TP .B Rtype generator .UNINDENT .sp Wrap the given select query in a transaction, and call its \fBiterator()\fP method to avoid caching row instances. In order for the server\-side resources to be released, be sure to exhaust the generator (iterate over all the rows). .sp Usage: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C large_query = PageView.select() for page_view in ServerSide(large_query): # Do something interesting. pass # At this point server side resources are released. .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class ArrayField([field_class=IntegerField[, field_kwargs=None[, dimensions=1[, convert_values=False]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBfield_class\fP \-\- a subclass of \fBField\fP, e.g. \fBIntegerField\fP\&. .IP \(bu 2 \fBfield_kwargs\fP (\fIdict\fP) \-\- arguments to initialize \fBfield_class\fP\&. .IP \(bu 2 \fBdimensions\fP (\fIint\fP) \-\- dimensions of array. .IP \(bu 2 \fBconvert_values\fP (\fIbool\fP) \-\- apply \fBfield_class\fP value conversion to array data. .UNINDENT .UNINDENT .sp Field capable of storing arrays of the provided \fIfield_class\fP\&. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 By default ArrayField will use a GIN index. To disable this, initialize the field with \fBindex=False\fP\&. .UNINDENT .UNINDENT .sp You can store and retrieve lists (or lists\-of\-lists): .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class BlogPost(BaseModel): content = TextField() tags = ArrayField(CharField) post = BlogPost(content=\(aqawesome\(aq, tags=[\(aqfoo\(aq, \(aqbar\(aq, \(aqbaz\(aq]) .ft P .fi .UNINDENT .UNINDENT .sp Additionally, you can use the \fB__getitem__\fP API to query values or slices in the database: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Get the first tag on a given blog post. first_tag = (BlogPost .select(BlogPost.tags[0].alias(\(aqfirst_tag\(aq)) .where(BlogPost.id == 1) .dicts() .get()) # first_tag = {\(aqfirst_tag\(aq: \(aqfoo\(aq} .ft P .fi .UNINDENT .UNINDENT .sp Get a slice of values: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Get the first two tags. two_tags = (BlogPost .select(BlogPost.tags[:2].alias(\(aqtwo\(aq)) .dicts() .get()) # two_tags = {\(aqtwo\(aq: [\(aqfoo\(aq, \(aqbar\(aq]} .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .TP .B contains(*items) .INDENT 7.0 .TP .B Parameters \fBitems\fP \-\- One or more items that must be in the given array field. .UNINDENT .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Get all blog posts that are tagged with both "python" and "django". Blog.select().where(Blog.tags.contains(\(aqpython\(aq, \(aqdjango\(aq)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B contains_any(*items) .INDENT 7.0 .TP .B Parameters \fBitems\fP \-\- One or more items to search for in the given array field. .UNINDENT .sp Like \fI\%contains()\fP, except will match rows where the array contains \fIany\fP of the given items. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Get all blog posts that are tagged with "flask" and/or "django". Blog.select().where(Blog.tags.contains_any(\(aqflask\(aq, \(aqdjango\(aq)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class DateTimeTZField(*args, **kwargs) A timezone\-aware subclass of \fBDateTimeField\fP\&. .UNINDENT .INDENT 0.0 .TP .B class HStoreField(*args, **kwargs) A field for storing and retrieving arbitrary key/value pairs. For details on usage, see \fI\%hstore support\fP\&. .sp \fBATTENTION:\fP .INDENT 7.0 .INDENT 3.5 To use the \fI\%HStoreField\fP you will need to be sure the \fIhstore\fP extension is registered with the connection. To accomplish this, instantiate the \fI\%PostgresqlExtDatabase\fP with \fBregister_hstore=True\fP\&. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 By default \fBHStoreField\fP will use a \fIGiST\fP index. To disable this, initialize the field with \fBindex=False\fP\&. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B keys() Returns the keys for a given row. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> for h in House.select(House.address, House.features.keys().alias(\(aqkeys\(aq)): \&... print(h.address, h.keys) 123 Main St [u\(aqbath\(aq, u\(aqgarage\(aq] .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B values() Return the values for a given row. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> for h in House.select(House.address, House.features.values().alias(\(aqvals\(aq)): \&... print(h.address, h.vals) 123 Main St [u\(aq2 bath\(aq, u\(aq2 cars\(aq] .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B items() Like python\(aqs \fBdict\fP, return the keys and values in a list\-of\-lists: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> for h in House.select(House.address, House.features.items().alias(\(aqmtx\(aq)): \&... print(h.address, h.mtx) 123 Main St [[u\(aqbath\(aq, u\(aq2 bath\(aq], [u\(aqgarage\(aq, u\(aq2 cars\(aq]] .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B slice(*args) Return a slice of data given a list of keys. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> for h in House.select(House.address, House.features.slice(\(aqgarage\(aq).alias(\(aqgarage_data\(aq)): \&... print(h.address, h.garage_data) 123 Main St {\(aqgarage\(aq: \(aq2 cars\(aq} .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B exists(key) Query for whether the given key exists. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> for h in House.select(House.address, House.features.exists(\(aqgarage\(aq).alias(\(aqhas_garage\(aq)): \&... print(h.address, h.has_garage) 123 Main St True >>> for h in House.select().where(House.features.exists(\(aqgarage\(aq)): \&... print(h.address, h.features[\(aqgarage\(aq]) # <\-\- just houses w/garage data 123 Main St 2 cars .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B defined(key) Query for whether the given key has a value associated with it. .UNINDENT .INDENT 7.0 .TP .B update(**data) Perform an atomic update to the keys/values for a given row or rows. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> query = House.update(features=House.features.update( \&... sqft=2000, \&... year_built=2012)) >>> query.where(House.id == 1).execute() .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B delete(*keys) Delete the provided keys for a given row or rows. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 We will use an \fBUPDATE\fP query. .UNINDENT .UNINDENT .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C .ft P .fi .UNINDENT .UNINDENT .sp .nf .ft C >>> query = House.update(features=House.features.delete( \&... \(aqsqft\(aq, \(aqyear_built\(aq)) >>> query.where(House.id == 1).execute() .ft P .fi .UNINDENT .INDENT 7.0 .TP .B contains(value) .INDENT 7.0 .TP .B Parameters \fBvalue\fP \-\- Either a \fBdict\fP, a \fBlist\fP of keys, or a single key. .UNINDENT .sp Query rows for the existence of either: .INDENT 7.0 .IP \(bu 2 a partial dictionary. .IP \(bu 2 a list of keys. .IP \(bu 2 a single key. .UNINDENT .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> query = House.select() >>> has_garage = query.where(House.features.contains(\(aqgarage\(aq)) >>> garage_bath = query.where(House.features.contains([\(aqgarage\(aq, \(aqbath\(aq])) >>> twocar = query.where(House.features.contains({\(aqgarage\(aq: \(aq2 cars\(aq})) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B contains_any(*keys) .INDENT 7.0 .TP .B Parameters \fBkeys\fP \-\- One or more keys to search for. .UNINDENT .sp Query rows for the existence of \fIany\fP key. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class JSONField(dumps=None, *args, **kwargs) .INDENT 7.0 .TP .B Parameters \fBdumps\fP \-\- The default is to call json.dumps() or the dumps function. You can override this method to create a customized JSON wrapper. .UNINDENT .sp Field class suitable for storing and querying arbitrary JSON. When using this on a model, set the field\(aqs value to a Python object (either a \fBdict\fP or a \fBlist\fP). When you retrieve your value from the database it will be returned as a Python data structure. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 You must be using Postgres 9.2 / psycopg2 2.5 or greater. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 If you are using Postgres 9.4, strongly consider using the \fI\%BinaryJSONField\fP instead as it offers better performance and more powerful querying options. .UNINDENT .UNINDENT .sp Example model declaration: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C db = PostgresqlExtDatabase(\(aqmy_db\(aq) class APIResponse(Model): url = CharField() response = JSONField() class Meta: database = db .ft P .fi .UNINDENT .UNINDENT .sp Example of storing JSON data: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C url = \(aqhttp://foo.com/api/resource/\(aq resp = json.loads(urllib2.urlopen(url).read()) APIResponse.create(url=url, response=resp) APIResponse.create(url=\(aqhttp://foo.com/baz/\(aq, response={\(aqkey\(aq: \(aqvalue\(aq}) .ft P .fi .UNINDENT .UNINDENT .sp To query, use Python\(aqs \fB[]\fP operators to specify nested key or array lookups: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C APIResponse.select().where( APIResponse.response[\(aqkey1\(aq][\(aqnested\-key\(aq] == \(aqsome\-value\(aq) .ft P .fi .UNINDENT .UNINDENT .sp To illustrate the use of the \fB[]\fP operators, imagine we have the following data stored in an \fBAPIResponse\fP: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C { "foo": { "bar": ["i1", "i2", "i3"], "baz": { "huey": "mickey", "peewee": "nugget" } } } .ft P .fi .UNINDENT .UNINDENT .sp Here are the results of a few queries: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C def get_data(expression): # Helper function to just retrieve the results of a # particular expression. query = (APIResponse .select(expression.alias(\(aqmy_data\(aq)) .dicts() .get()) return query[\(aqmy_data\(aq] # Accessing the foo \-> bar subkey will return a JSON # representation of the list. get_data(APIResponse.data[\(aqfoo\(aq][\(aqbar\(aq]) # \(aq["i1", "i2", "i3"]\(aq # In order to retrieve this list as a Python list, # we will call .as_json() on the expression. get_data(APIResponse.data[\(aqfoo\(aq][\(aqbar\(aq].as_json()) # [\(aqi1\(aq, \(aqi2\(aq, \(aqi3\(aq] # Similarly, accessing the foo \-> baz subkey will # return a JSON representation of the dictionary. get_data(APIResponse.data[\(aqfoo\(aq][\(aqbaz\(aq]) # \(aq{"huey": "mickey", "peewee": "nugget"}\(aq # Again, calling .as_json() will return an actual # python dictionary. get_data(APIResponse.data[\(aqfoo\(aq][\(aqbaz\(aq].as_json()) # {\(aqhuey\(aq: \(aqmickey\(aq, \(aqpeewee\(aq: \(aqnugget\(aq} # When dealing with simple values, either way works as # you expect. get_data(APIResponse.data[\(aqfoo\(aq][\(aqbar\(aq][0]) # \(aqi1\(aq # Calling .as_json() when the result is a simple value # will return the same thing as the previous example. get_data(APIResponse.data[\(aqfoo\(aq][\(aqbar\(aq][0].as_json()) # \(aqi1\(aq .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class BinaryJSONField(dumps=None, *args, **kwargs) .INDENT 7.0 .TP .B Parameters \fBdumps\fP \-\- The default is to call json.dumps() or the dumps function. You can override this method to create a customized JSON wrapper. .UNINDENT .sp Store and query arbitrary JSON documents. Data should be stored using normal Python \fBdict\fP and \fBlist\fP objects, and when data is returned from the database, it will be returned using \fBdict\fP and \fBlist\fP as well. .sp For examples of basic query operations, see the above code samples for \fBJSONField\fP\&. The example queries below will use the same \fBAPIResponse\fP model described above. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 By default BinaryJSONField will use a GiST index. To disable this, initialize the field with \fBindex=False\fP\&. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 You must be using Postgres 9.4 / psycopg2 2.5 or newer. If you are using Postgres 9.2 or 9.3, you can use the regular \fBJSONField\fP instead. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B contains(other) Test whether the given JSON data contains the given JSON fragment or key. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C search_fragment = { \(aqfoo\(aq: {\(aqbar\(aq: [\(aqi2\(aq]} } query = (APIResponse .select() .where(APIResponse.data.contains(search_fragment))) # If we\(aqre searching for a list, the list items do not need to # be ordered in a particular way: query = (APIResponse .select() .where(APIResponse.data.contains({ \(aqfoo\(aq: {\(aqbar\(aq: [\(aqi2\(aq, \(aqi1\(aq]}}))) .ft P .fi .UNINDENT .UNINDENT .sp We can pass in simple keys as well. To find APIResponses that contain the key \fBfoo\fP at the top\-level: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C APIResponse.select().where(APIResponse.data.contains(\(aqfoo\(aq)) .ft P .fi .UNINDENT .UNINDENT .sp We can also search sub\-keys using square\-brackets: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C APIResponse.select().where( APIResponse.data[\(aqfoo\(aq][\(aqbar\(aq].contains([\(aqi2\(aq, \(aqi1\(aq])) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B contains_any(*items) Search for the presence of one or more of the given items. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C APIResponse.select().where( APIResponse.data.contains_any(\(aqfoo\(aq, \(aqbaz\(aq, \(aqnugget\(aq)) .ft P .fi .UNINDENT .UNINDENT .sp Like \fI\%contains()\fP, we can also search sub\-keys: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C APIResponse.select().where( APIResponse.data[\(aqfoo\(aq][\(aqbar\(aq].contains_any(\(aqi2\(aq, \(aqix\(aq)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B contains_all(*items) Search for the presence of all of the given items. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C APIResponse.select().where( APIResponse.data.contains_all(\(aqfoo\(aq)) .ft P .fi .UNINDENT .UNINDENT .sp Like \fI\%contains_any()\fP, we can also search sub\-keys: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C APIResponse.select().where( APIResponse.data[\(aqfoo\(aq][\(aqbar\(aq].contains_all(\(aqi1\(aq, \(aqi2\(aq, \(aqi3\(aq)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B contained_by(other) Test whether the given JSON document is contained by (is a subset of) the given JSON document. This method is the inverse of \fI\%contains()\fP\&. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C big_doc = { \(aqfoo\(aq: { \(aqbar\(aq: [\(aqi1\(aq, \(aqi2\(aq, \(aqi3\(aq], \(aqbaz\(aq: { \(aqhuey\(aq: \(aqmickey\(aq, \(aqpeewee\(aq: \(aqnugget\(aq, } }, \(aqother_key\(aq: [\(aqnugget\(aq, \(aqbear\(aq, \(aqkitten\(aq], } APIResponse.select().where( APIResponse.data.contained_by(big_doc)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B concat(data) Concatenate two field data and the provided data. Note that this operation does not merge or do a "deep concat". .UNINDENT .INDENT 7.0 .TP .B has_key(key) Test whether the key exists at the top\-level of the JSON object. .UNINDENT .INDENT 7.0 .TP .B remove(*keys) Remove one or more keys from the top\-level of the JSON object. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Match(field, query) Generate a full\-text search expression, automatically converting the left\-hand operand to a \fBtsvector\fP, and the right\-hand operand to a \fBtsquery\fP\&. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C def blog_search(search_term): return Blog.select().where( (Blog.status == Blog.STATUS_PUBLISHED) & Match(Blog.content, search_term)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class TSVectorField Field type suitable for storing \fBtsvector\fP data. This field will automatically be created with a \fBGIN\fP index for improved search performance. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Data stored in this field will still need to be manually converted to the \fBtsvector\fP type. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 .INDENT 0.0 .INDENT 3.5 By default TSVectorField will use a GIN index. To disable this, initialize the field with \fBindex=False\fP\&. .UNINDENT .UNINDENT .sp Example usage: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Blog(Model): content = TextField() search_content = TSVectorField() content = \(aqthis is a sample blog entry.\(aq blog_entry = Blog.create( content=content, search_content=fn.to_tsvector(content)) # Note \(gato_tsvector()\(ga. .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B match(query[, language=None[, plain=False]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBquery\fP (\fIstr\fP) \-\- the full\-text search query. .IP \(bu 2 \fBlanguage\fP (\fIstr\fP) \-\- language name (optional). .IP \(bu 2 \fBplain\fP (\fIbool\fP) \-\- parse search query using plain (simple) parser. .UNINDENT .TP .B Returns an expression representing full\-text search/match. .UNINDENT .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Perform a search using the "match" method. terms = \(aqpython & (sqlite | postgres)\(aq results = Blog.select().where(Blog.search_content.match(terms)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS Cockroach Database .sp \fI\%CockroachDB\fP (CRDB) is well supported by peewee. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.cockroachdb import CockroachDatabase db = CockroachDatabase(\(aqmy_app\(aq, user=\(aqroot\(aq, host=\(aq10.1.0.8\(aq) .ft P .fi .UNINDENT .UNINDENT .sp The \fBplayhouse.cockroachdb\fP extension module provides the following classes and helpers: .INDENT 0.0 .IP \(bu 2 \fI\%CockroachDatabase\fP \- a subclass of \fBPostgresqlDatabase\fP, designed specifically for working with CRDB. .IP \(bu 2 \fI\%PooledCockroachDatabase\fP \- like the above, but implements connection\-pooling. .IP \(bu 2 \fI\%run_transaction()\fP \- runs a function inside a transaction and provides automatic client\-side retry logic. .UNINDENT .sp Special field\-types that may be useful when using CRDB: .INDENT 0.0 .IP \(bu 2 \fI\%UUIDKeyField\fP \- a primary\-key field implementation that uses CRDB\(aqs \fBUUID\fP type with a default randomly\-generated UUID. .IP \(bu 2 \fBRowIDField\fP \- a primary\-key field implementation that uses CRDB\(aqs \fBINT\fP type with a default \fBunique_rowid()\fP\&. .IP \(bu 2 \fBJSONField\fP \- same as the Postgres \fI\%BinaryJSONField\fP, as CRDB treats JSON as JSONB. .IP \(bu 2 \fI\%ArrayField\fP \- same as the Postgres extension (but does not support multi\-dimensional arrays). .UNINDENT .sp CRDB is compatible with Postgres\(aq wire protocol and exposes a very similar SQL interface, so it is possible (though \fBnot recommended\fP) to use \fBPostgresqlDatabase\fP with CRDB: .INDENT 0.0 .IP 1. 3 CRDB does not support nested transactions (savepoints), so the \fBatomic()\fP method has been implemented to enforce this when using \fI\%CockroachDatabase\fP\&. For more info \fI\%CRDB Transactions\fP\&. .IP 2. 3 CRDB may have subtle differences in field\-types, date functions and introspection from Postgres. .IP 3. 3 CRDB\-specific features are exposed by the \fI\%CockroachDatabase\fP, such as specifying a transaction priority or the \fBAS OF SYSTEM TIME\fP clause. .UNINDENT .SS CRDB Transactions .sp CRDB does not support nested transactions (savepoints), so the \fBatomic()\fP method on the \fI\%CockroachDatabase\fP has been modified to raise an exception if an invalid nesting is encountered. If you would like to be able to nest transactional code, you can use the \fBtransaction()\fP method, which will ensure that the outer\-most block will manage the transaction (e.g., exiting a nested\-block will not cause an early commit). .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C @db.transaction() def create_user(username): return User.create(username=username) def some_other_function(): with db.transaction() as txn: # do some stuff... # This function is wrapped in a transaction, but the nested # transaction will be ignored and folded into the outer # transaction, as we are already in a wrapped\-block (via the # context manager). create_user(\(aqsome_user@example.com\(aq) # do other stuff. # At this point we have exited the outer\-most block and the transaction # will be committed. return .ft P .fi .UNINDENT .UNINDENT .sp CRDB provides client\-side transaction retries, which are available using a special \fI\%run_transaction()\fP helper. This helper method accepts a callable, which is responsible for executing any transactional statements that may need to be retried. .sp Simplest possible example of \fI\%run_transaction()\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C def create_user(email): # Callable that accepts a single argument (the database instance) and # which is responsible for executing the transactional SQL. def callback(db_ref): return User.create(email=email) return db.run_transaction(callback, max_attempts=10) huey = create_user(\(aqhuey@example.com\(aq) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 The \fBcockroachdb.ExceededMaxAttempts\fP exception will be raised if the transaction cannot be committed after the given number of attempts. If the SQL is mal\-formed, violates a constraint, etc., then the function will raise the exception to the caller. .UNINDENT .UNINDENT .sp Example of using \fI\%run_transaction()\fP to implement client\-side retries for a transaction that transfers an amount from one account to another: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.cockroachdb import CockroachDatabase db = CockroachDatabase(\(aqmy_app\(aq) def transfer_funds(from_id, to_id, amt): """ Returns a 3\-tuple of (success?, from balance, to balance). If there are not sufficient funds, then the original balances are returned. """ def thunk(db_ref): src, dest = (Account .select() .where(Account.id.in_([from_id, to_id]))) if src.id != from_id: src, dest = dest, src # Swap order. # Cannot perform transfer, insufficient funds! if src.balance < amt: return False, src.balance, dest.balance # Update each account, returning the new balance. src, = (Account .update(balance=Account.balance \- amt) .where(Account.id == from_id) .returning(Account.balance) .execute()) dest, = (Account .update(balance=Account.balance + amt) .where(Account.id == to_id) .returning(Account.balance) .execute()) return True, src.balance, dest.balance # Perform the queries that comprise a logical transaction. In the # event the transaction fails due to contention, it will be auto\- # matically retried (up to 10 times). return db.run_transaction(thunk, max_attempts=10) .ft P .fi .UNINDENT .UNINDENT .SS CRDB APIs .INDENT 0.0 .TP .B class CockroachDatabase(database[, **kwargs]) CockroachDB implementation, based on the \fBPostgresqlDatabase\fP and using the \fBpsycopg2\fP driver. .sp Additional keyword arguments are passed to the psycopg2 connection constructor, and may be used to specify the database \fBuser\fP, \fBport\fP, etc. .INDENT 7.0 .TP .B run_transaction(callback[, max_attempts=None[, system_time=None[, priority=None]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBcallback\fP \-\- callable that accepts a single \fBdb\fP parameter (which will be the database instance this method is called from). .IP \(bu 2 \fBmax_attempts\fP (\fIint\fP) \-\- max number of times to try before giving up. .IP \(bu 2 \fBsystem_time\fP (\fIdatetime\fP) \-\- execute the transaction \fBAS OF SYSTEM TIME\fP with respect to the given value. .IP \(bu 2 \fBpriority\fP (\fIstr\fP) \-\- either "low", "normal" or "high". .UNINDENT .TP .B Returns returns the value returned by the callback. .TP .B Raises \fBExceededMaxAttempts\fP if \fBmax_attempts\fP is exceeded. .UNINDENT .sp Run SQL in a transaction with automatic client\-side retries. .sp User\-provided \fBcallback\fP: .INDENT 7.0 .IP \(bu 2 \fBMust\fP accept one parameter, the \fBdb\fP instance representing the connection the transaction is running under. .IP \(bu 2 \fBMust\fP not attempt to commit, rollback or otherwise manage the transaction. .IP \(bu 2 \fBMay\fP be called more than one time. .IP \(bu 2 \fBShould\fP ideally only contain SQL operations. .UNINDENT .sp Additionally, the database must not have any open transactions at the time this function is called, as CRDB does not support nested transactions. Attempting to do so will raise a \fBNotImplementedError\fP\&. .sp Simplest possible example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C def create_user(email): def callback(db_ref): return User.create(email=email) return db.run_transaction(callback, max_attempts=10) user = create_user(\(aqhuey@example.com\(aq) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class PooledCockroachDatabase(database[, **kwargs]) CockroachDB connection\-pooling implementation, based on \fI\%PooledPostgresqlDatabase\fP\&. Implements the same APIs as \fI\%CockroachDatabase\fP, but will do client\-side connection pooling. .UNINDENT .INDENT 0.0 .TP .B run_transaction(db, callback[, max_attempts=None[, system_time=None[, priority=None]]]) Run SQL in a transaction with automatic client\-side retries. See \fI\%CockroachDatabase.run_transaction()\fP for details. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdb\fP (\fICockroachDatabase\fP) \-\- database instance. .IP \(bu 2 \fBcallback\fP \-\- callable that accepts a single \fBdb\fP parameter (which will be the same as the value passed above). .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 This function is equivalent to the identically\-named method on the \fI\%CockroachDatabase\fP class. .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class UUIDKeyField UUID primary\-key field that uses the CRDB \fBgen_random_uuid()\fP function to automatically populate the initial value. .UNINDENT .INDENT 0.0 .TP .B class RowIDField Auto\-incrementing integer primary\-key field that uses the CRDB \fBunique_rowid()\fP function to automatically populate the initial value. .UNINDENT .sp See also: .INDENT 0.0 .IP \(bu 2 \fI\%BinaryJSONField\fP from the Postgresql extension (available in the \fBcockroachdb\fP extension module, and aliased to \fBJSONField\fP). .IP \(bu 2 \fI\%ArrayField\fP from the Postgresql extension. .UNINDENT .SS MySQL Extensions .sp Peewee provides an alternate database implementation for using the \fI\%mysql\-connector\fP driver. The implementation can be found in \fBplayhouse.mysql_ext\fP\&. .sp Example usage: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.mysql_ext import MySQLConnectorDatabase # MySQL database implementation that utilizes mysql\-connector driver. db = MySQLConnectorDatabase(\(aqmy_database\(aq, host=\(aq1.2.3.4\(aq, user=\(aqmysql\(aq) .ft P .fi .UNINDENT .UNINDENT .sp Additional MySQL\-specific helpers: .INDENT 0.0 .TP .B class JSONField Extends \fBTextField\fP and implements transparent JSON encoding and decoding in Python. .UNINDENT .INDENT 0.0 .TP .B Match(columns, expr[, modifier=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBcolumns\fP \-\- a single \fBField\fP or a tuple of multiple fields. .IP \(bu 2 \fBexpr\fP (\fIstr\fP) \-\- the full\-text search expression. .IP \(bu 2 \fBmodifier\fP (\fIstr\fP) \-\- optional modifiers for the search, e.g. \fI\(aqin boolean mode\(aq\fP\&. .UNINDENT .UNINDENT .sp Helper class for constructing MySQL full\-text search queries of the form: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C MATCH (columns, ...) AGAINST (expr[ modifier]) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .SS DataSet .sp The \fIdataset\fP module contains a high\-level API for working with databases modeled after the popular \fI\%project of the same name\fP\&. The aims of the \fIdataset\fP module are to provide: .INDENT 0.0 .IP \(bu 2 A simplified API for working with relational data, along the lines of working with JSON. .IP \(bu 2 An easy way to export relational data as JSON or CSV. .IP \(bu 2 An easy way to import JSON or CSV data into a relational database. .UNINDENT .sp A minimal data\-loading script might look like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.dataset import DataSet db = DataSet(\(aqsqlite:///:memory:\(aq) table = db[\(aqsometable\(aq] table.insert(name=\(aqHuey\(aq, age=3) table.insert(name=\(aqMickey\(aq, age=5, gender=\(aqmale\(aq) huey = table.find_one(name=\(aqHuey\(aq) print(huey) # {\(aqage\(aq: 3, \(aqgender\(aq: None, \(aqid\(aq: 1, \(aqname\(aq: \(aqHuey\(aq} for obj in table: print(obj) # {\(aqage\(aq: 3, \(aqgender\(aq: None, \(aqid\(aq: 1, \(aqname\(aq: \(aqHuey\(aq} # {\(aqage\(aq: 5, \(aqgender\(aq: \(aqmale\(aq, \(aqid\(aq: 2, \(aqname\(aq: \(aqMickey\(aq} .ft P .fi .UNINDENT .UNINDENT .sp You can insert, update or delete using the dictionary APIs as well: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C huey = table.find_one(name=\(aqHuey\(aq) # {\(aqage\(aq: 3, \(aqgender\(aq: None, \(aqid\(aq: 1, \(aqname\(aq: \(aqHuey\(aq} # Perform an update by supplying a partial record of changes. table[1] = {\(aqgender\(aq: \(aqmale\(aq, \(aqage\(aq: 4} print(table[1]) # {\(aqage\(aq: 4, \(aqgender\(aq: \(aqmale\(aq, \(aqid\(aq: 1, \(aqname\(aq: \(aqHuey\(aq} # Or insert a new record: table[3] = {\(aqname\(aq: \(aqZaizee\(aq, \(aqage\(aq: 2} print(table[3]) # {\(aqage\(aq: 2, \(aqgender\(aq: None, \(aqid\(aq: 3, \(aqname\(aq: \(aqZaizee\(aq} # Or delete a record: del table[3] # Remove the row we just added. .ft P .fi .UNINDENT .UNINDENT .sp You can export or import data using \fI\%freeze()\fP and \fI\%thaw()\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Export table content to the \(gausers.json\(ga file. db.freeze(table.all(), format=\(aqjson\(aq, filename=\(aqusers.json\(aq) # Import data from a CSV file into a new table. Columns will be automatically # created for each field in the CSV file. new_table = db[\(aqstats\(aq] new_table.thaw(format=\(aqcsv\(aq, filename=\(aqmonthly_stats.csv\(aq) .ft P .fi .UNINDENT .UNINDENT .SS Getting started .sp \fI\%DataSet\fP objects are initialized by passing in a database URL of the format \fBdialect://user:password@host/dbname\fP\&. See the \fI\%Database URL\fP section for examples of connecting to various databases. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Create an in\-memory SQLite database. db = DataSet(\(aqsqlite:///:memory:\(aq) .ft P .fi .UNINDENT .UNINDENT .SS Storing data .sp To store data, we must first obtain a reference to a table. If the table does not exist, it will be created automatically: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Get a table reference, creating the table if it does not exist. table = db[\(aqusers\(aq] .ft P .fi .UNINDENT .UNINDENT .sp We can now \fI\%insert()\fP new rows into the table. If the columns do not exist, they will be created automatically: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C table.insert(name=\(aqHuey\(aq, age=3, color=\(aqwhite\(aq) table.insert(name=\(aqMickey\(aq, age=5, gender=\(aqmale\(aq) .ft P .fi .UNINDENT .UNINDENT .sp To update existing entries in the table, pass in a dictionary containing the new values and filter conditions. The list of columns to use as filters is specified in the \fIcolumns\fP argument. If no filter columns are specified, then all rows will be updated. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Update the gender for "Huey". table.update(name=\(aqHuey\(aq, gender=\(aqmale\(aq, columns=[\(aqname\(aq]) # Update all records. If the column does not exist, it will be created. table.update(favorite_orm=\(aqpeewee\(aq) .ft P .fi .UNINDENT .UNINDENT .SS Importing data .sp To import data from an external source, such as a JSON or CSV file, you can use the \fI\%thaw()\fP method. By default, new columns will be created for any attributes encountered. If you wish to only populate columns that are already defined on a table, you can pass in \fBstrict=True\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Load data from a JSON file containing a list of objects. table = dataset[\(aqstock_prices\(aq] table.thaw(filename=\(aqstocks.json\(aq, format=\(aqjson\(aq) table.all()[:3] # Might print... [{\(aqid\(aq: 1, \(aqticker\(aq: \(aqGOOG\(aq, \(aqprice\(aq: 703}, {\(aqid\(aq: 2, \(aqticker\(aq: \(aqAAPL\(aq, \(aqprice\(aq: 109}, {\(aqid\(aq: 3, \(aqticker\(aq: \(aqAMZN\(aq, \(aqprice\(aq: 300}] .ft P .fi .UNINDENT .UNINDENT .SS Using transactions .sp DataSet supports nesting transactions using a simple context manager. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C table = db[\(aqusers\(aq] with db.transaction() as txn: table.insert(name=\(aqCharlie\(aq) with db.transaction() as nested_txn: # Set Charlie\(aqs favorite ORM to Django. table.update(name=\(aqCharlie\(aq, favorite_orm=\(aqdjango\(aq, columns=[\(aqname\(aq]) # jk/lol nested_txn.rollback() .ft P .fi .UNINDENT .UNINDENT .SS Inspecting the database .sp You can use the \fBtables()\fP method to list the tables in the current database: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> print db.tables [\(aqsometable\(aq, \(aquser\(aq] .ft P .fi .UNINDENT .UNINDENT .sp And for a given table, you can print the columns: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> table = db[\(aquser\(aq] >>> print table.columns [\(aqid\(aq, \(aqage\(aq, \(aqname\(aq, \(aqgender\(aq, \(aqfavorite_orm\(aq] .ft P .fi .UNINDENT .UNINDENT .sp We can also find out how many rows are in a table: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> print len(db[\(aquser\(aq]) 3 .ft P .fi .UNINDENT .UNINDENT .SS Reading data .sp To retrieve all rows, you can use the \fI\%all()\fP method: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Retrieve all the users. users = db[\(aquser\(aq].all() # We can iterate over all rows without calling \(ga.all()\(ga for user in db[\(aquser\(aq]: print user[\(aqname\(aq] .ft P .fi .UNINDENT .UNINDENT .sp Specific objects can be retrieved using \fI\%find()\fP and \fI\%find_one()\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Find all the users who like peewee. peewee_users = db[\(aquser\(aq].find(favorite_orm=\(aqpeewee\(aq) # Find Huey. huey = db[\(aquser\(aq].find_one(name=\(aqHuey\(aq) .ft P .fi .UNINDENT .UNINDENT .SS Exporting data .sp To export data, use the \fI\%freeze()\fP method, passing in the query you wish to export: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C peewee_users = db[\(aquser\(aq].find(favorite_orm=\(aqpeewee\(aq) db.freeze(peewee_users, format=\(aqjson\(aq, filename=\(aqpeewee_users.json\(aq) .ft P .fi .UNINDENT .UNINDENT .SS API .INDENT 0.0 .TP .B class DataSet(url, **kwargs) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBurl\fP \-\- A database URL or a \fBDatabase\fP instance. For details on using a URL, see \fI\%Database URL\fP for examples. .IP \(bu 2 \fBkwargs\fP \-\- additional keyword arguments passed to \fI\%Introspector.generate_models()\fP when introspecting the db. .UNINDENT .UNINDENT .sp The \fIDataSet\fP class provides a high\-level API for working with relational databases. .INDENT 7.0 .TP .B tables Return a list of tables stored in the database. This list is computed dynamically each time it is accessed. .UNINDENT .INDENT 7.0 .TP .B __getitem__(table_name) Provide a \fI\%Table\fP reference to the specified table. If the table does not exist, it will be created. .UNINDENT .INDENT 7.0 .TP .B query(sql[, params=None[, commit=True]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBsql\fP (\fIstr\fP) \-\- A SQL query. .IP \(bu 2 \fBparams\fP (\fIlist\fP) \-\- Optional parameters for the query. .IP \(bu 2 \fBcommit\fP (\fIbool\fP) \-\- Whether the query should be committed upon execution. .UNINDENT .TP .B Returns A database cursor. .UNINDENT .sp Execute the provided query against the database. .UNINDENT .INDENT 7.0 .TP .B transaction() Create a context manager representing a new transaction (or savepoint). .UNINDENT .INDENT 7.0 .TP .B freeze(query[, format=\(aqcsv\(aq[, filename=None[, file_obj=None[, **kwargs]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBquery\fP \-\- A \fBSelectQuery\fP, generated using \fI\%all()\fP or \fI~Table.find\fP\&. .IP \(bu 2 \fBformat\fP \-\- Output format. By default, \fIcsv\fP and \fIjson\fP are supported. .IP \(bu 2 \fBfilename\fP \-\- Filename to write output to. .IP \(bu 2 \fBfile_obj\fP \-\- File\-like object to write output to. .IP \(bu 2 \fBkwargs\fP \-\- Arbitrary parameters for export\-specific functionality. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B thaw(table[, format=\(aqcsv\(aq[, filename=None[, file_obj=None[, strict=False[, **kwargs]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- The name of the table to load data into. .IP \(bu 2 \fBformat\fP \-\- Input format. By default, \fIcsv\fP and \fIjson\fP are supported. .IP \(bu 2 \fBfilename\fP \-\- Filename to read data from. .IP \(bu 2 \fBfile_obj\fP \-\- File\-like object to read data from. .IP \(bu 2 \fBstrict\fP (\fIbool\fP) \-\- Whether to store values for columns that do not already exist on the table. .IP \(bu 2 \fBkwargs\fP \-\- Arbitrary parameters for import\-specific functionality. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B connect() Open a connection to the underlying database. If a connection is not opened explicitly, one will be opened the first time a query is executed. .UNINDENT .INDENT 7.0 .TP .B close() Close the connection to the underlying database. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Table(dataset, name, model_class) .INDENT 7.0 .TP .B Noindex .UNINDENT .sp Provides a high\-level API for working with rows in a given table. .INDENT 7.0 .TP .B columns Return a list of columns in the given table. .UNINDENT .INDENT 7.0 .TP .B model_class A dynamically\-created \fBModel\fP class. .UNINDENT .INDENT 7.0 .TP .B create_index(columns[, unique=False]) Create an index on the given columns: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Create a unique index on the \(gausername\(ga column. db[\(aqusers\(aq].create_index([\(aqusername\(aq], unique=True) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B insert(**data) Insert the given data dictionary into the table, creating new columns as needed. .UNINDENT .INDENT 7.0 .TP .B update(columns=None, conjunction=None, **data) Update the table using the provided data. If one or more columns are specified in the \fIcolumns\fP parameter, then those columns\(aq values in the \fIdata\fP dictionary will be used to determine which rows to update. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Update all rows. db[\(aqusers\(aq].update(favorite_orm=\(aqpeewee\(aq) # Only update Huey\(aqs record, setting his age to 3. db[\(aqusers\(aq].update(name=\(aqHuey\(aq, age=3, columns=[\(aqname\(aq]) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B find(**query) Query the table for rows matching the specified equality conditions. If no query is specified, then all rows are returned. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C peewee_users = db[\(aqusers\(aq].find(favorite_orm=\(aqpeewee\(aq) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B find_one(**query) Return a single row matching the specified equality conditions. If no matching row is found then \fBNone\fP will be returned. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C huey = db[\(aqusers\(aq].find_one(name=\(aqHuey\(aq) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B all() Return all rows in the given table. .UNINDENT .INDENT 7.0 .TP .B delete(**query) Delete all rows matching the given equality conditions. If no query is provided, then all rows will be deleted. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Adios, Django! db[\(aqusers\(aq].delete(favorite_orm=\(aqDjango\(aq) # Delete all the secret messages. db[\(aqsecret_messages\(aq].delete() .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B freeze([format=\(aqcsv\(aq[, filename=None[, file_obj=None[, **kwargs]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBformat\fP \-\- Output format. By default, \fIcsv\fP and \fIjson\fP are supported. .IP \(bu 2 \fBfilename\fP \-\- Filename to write output to. .IP \(bu 2 \fBfile_obj\fP \-\- File\-like object to write output to. .IP \(bu 2 \fBkwargs\fP \-\- Arbitrary parameters for export\-specific functionality. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B thaw([format=\(aqcsv\(aq[, filename=None[, file_obj=None[, strict=False[, **kwargs]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBformat\fP \-\- Input format. By default, \fIcsv\fP and \fIjson\fP are supported. .IP \(bu 2 \fBfilename\fP \-\- Filename to read data from. .IP \(bu 2 \fBfile_obj\fP \-\- File\-like object to read data from. .IP \(bu 2 \fBstrict\fP (\fIbool\fP) \-\- Whether to store values for columns that do not already exist on the table. .IP \(bu 2 \fBkwargs\fP \-\- Arbitrary parameters for import\-specific functionality. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS Fields .sp These fields can be found in the \fBplayhouse.fields\fP module. .INDENT 0.0 .TP .B class CompressedField([compression_level=6[, algorithm=\(aqzlib\(aq[, **kwargs]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBcompression_level\fP (\fIint\fP) \-\- A value from 0 to 9. .IP \(bu 2 \fBalgorithm\fP (\fIstr\fP) \-\- Either \fB\(aqzlib\(aq\fP or \fB\(aqbz2\(aq\fP\&. .UNINDENT .UNINDENT .sp Stores compressed data using the specified algorithm. This field extends \fBBlobField\fP, transparently storing a compressed representation of the data in the database. .UNINDENT .INDENT 0.0 .TP .B class PickleField Stores arbitrary Python data by transparently pickling and un\-pickling data stored in the field. This field extends \fBBlobField\fP\&. If the \fBcPickle\fP module is available, it will be used. .UNINDENT .SS Hybrid Attributes .sp Hybrid attributes encapsulate functionality that operates at both the Python \fIand\fP SQL levels. The idea for hybrid attributes comes from a feature of the \fI\%same name in SQLAlchemy\fP\&. Consider the following example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Interval(Model): start = IntegerField() end = IntegerField() @hybrid_property def length(self): return self.end \- self.start @hybrid_method def contains(self, point): return (self.start <= point) & (point < self.end) .ft P .fi .UNINDENT .UNINDENT .sp The \fIhybrid attribute\fP gets its name from the fact that the \fBlength\fP attribute will behave differently depending on whether it is accessed via the \fBInterval\fP class or an \fBInterval\fP instance. .sp If accessed via an instance, then it behaves just as you would expect. .sp If accessed via the \fBInterval.length\fP class attribute, however, the length calculation will be expressed as a SQL expression. For example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Interval.select().where(Interval.length > 5) .ft P .fi .UNINDENT .UNINDENT .sp This query will be equivalent to the following SQL: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT "t1"."id", "t1"."start", "t1"."end" FROM "interval" AS t1 WHERE (("t1"."end" \- "t1"."start") > 5) .ft P .fi .UNINDENT .UNINDENT .sp The \fBplayhouse.hybrid\fP module also contains a decorator for implementing hybrid methods which can accept parameters. As with hybrid properties, when accessed via a model instance, then the function executes normally as\-written. When the hybrid method is called on the class, however, it will generate a SQL expression. .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Interval.select().where(Interval.contains(2)) .ft P .fi .UNINDENT .UNINDENT .sp This query is equivalent to the following SQL: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT "t1"."id", "t1"."start", "t1"."end" FROM "interval" AS t1 WHERE (("t1"."start" <= 2) AND (2 < "t1"."end")) .ft P .fi .UNINDENT .UNINDENT .sp There is an additional API for situations where the python implementation differs slightly from the SQL implementation. Let\(aqs add a \fBradius\fP method to the \fBInterval\fP model. Because this method calculates an absolute value, we will use the Python \fBabs()\fP function for the instance portion and the \fBfn.ABS()\fP SQL function for the class portion. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Interval(Model): start = IntegerField() end = IntegerField() @hybrid_property def length(self): return self.end \- self.start @hybrid_property def radius(self): return abs(self.length) / 2 @radius.expression def radius(cls): return fn.ABS(cls.length) / 2 .ft P .fi .UNINDENT .UNINDENT .sp What is neat is that both the \fBradius\fP implementations refer to the \fBlength\fP hybrid attribute! When accessed via an \fBInterval\fP instance, the radius calculation will be executed in Python. When invoked via an \fBInterval\fP class, we will get the appropriate SQL. .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Interval.select().where(Interval.radius < 3) .ft P .fi .UNINDENT .UNINDENT .sp This query is equivalent to the following SQL: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT "t1"."id", "t1"."start", "t1"."end" FROM "interval" AS t1 WHERE ((abs("t1"."end" \- "t1"."start") / 2) < 3) .ft P .fi .UNINDENT .UNINDENT .sp Pretty neat, right? Thanks for the cool idea, SQLAlchemy! .SS Hybrid API .INDENT 0.0 .TP .B class hybrid_method(func[, expr=None]) Method decorator that allows the definition of a Python object method with both instance\-level and class\-level behavior. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class Interval(Model): start = IntegerField() end = IntegerField() @hybrid_method def contains(self, point): return (self.start <= point) & (point < self.end) .ft P .fi .UNINDENT .UNINDENT .sp When called with an \fBInterval\fP instance, the \fBcontains\fP method will behave as you would expect. When called as a classmethod, though, a SQL expression will be generated: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C query = Interval.select().where(Interval.contains(2)) .ft P .fi .UNINDENT .UNINDENT .sp Would generate the following SQL: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C SELECT "t1"."id", "t1"."start", "t1"."end" FROM "interval" AS t1 WHERE (("t1"."start" <= 2) AND (2 < "t1"."end")) .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .TP .B expression(expr) Method decorator for specifying the SQL\-expression producing method. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class hybrid_property(fget[, fset=None[, fdel=None[, expr=None]]]) Method decorator that allows the definition of a Python object property with both instance\-level and class\-level behavior. .sp Examples: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class Interval(Model): start = IntegerField() end = IntegerField() @hybrid_property def length(self): return self.end \- self.start @hybrid_property def radius(self): return abs(self.length) / 2 @radius.expression def radius(cls): return fn.ABS(cls.length) / 2 .ft P .fi .UNINDENT .UNINDENT .sp When accessed on an \fBInterval\fP instance, the \fBlength\fP and \fBradius\fP properties will behave as you would expect. When accessed as class attributes, though, a SQL expression will be generated instead: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C query = (Interval .select() .where( (Interval.length > 6) & (Interval.radius >= 3))) .ft P .fi .UNINDENT .UNINDENT .sp Would generate the following SQL: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C SELECT "t1"."id", "t1"."start", "t1"."end" FROM "interval" AS t1 WHERE ( (("t1"."end" \- "t1"."start") > 6) AND ((abs("t1"."end" \- "t1"."start") / 2) >= 3) ) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .SS Key/Value Store .sp The \fBplayhouse.kv\fP module contains the implementation of a persistent dictionary. .INDENT 0.0 .TP .B class KeyValue([key_field=None[, value_field=None[, ordered=False[, database=None[, table_name=\(aqkeyvalue\(aq]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBkey_field\fP (\fIField\fP) \-\- field to use for key. Defaults to \fBCharField\fP\&. \fBMust have\fP \fBprimary_key=True\fP\&. .IP \(bu 2 \fBvalue_field\fP (\fIField\fP) \-\- field to use for value. Defaults to \fI\%PickleField\fP\&. .IP \(bu 2 \fBordered\fP (\fIbool\fP) \-\- data should be returned in key\-sorted order. .IP \(bu 2 \fBdatabase\fP (\fIDatabase\fP) \-\- database where key/value data is stored. If not specified, an in\-memory SQLite database will be used. .IP \(bu 2 \fBtable_name\fP (\fIstr\fP) \-\- table name for data storage. .UNINDENT .UNINDENT .sp Dictionary\-like API for storing key/value data. Like dictionaries, supports the expected APIs, but also has the added capability of accepting expressions for getting, setting and deleting items. .sp Table is created automatically (if it doesn\(aqt exist) when the \fBKeyValue\fP is instantiated. .sp Uses efficient upsert implementation for setting and updating/overwriting key/value pairs. .sp Basic examples: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C # Create a key/value store, which uses an in\-memory SQLite database # for data storage. KV = KeyValue() # Set (or overwrite) the value for "k1". KV[\(aqk1\(aq] = \(aqv1\(aq # Set (or update) multiple keys at once (uses an efficient upsert). KV.update(k2=\(aqv2\(aq, k3=\(aqv3\(aq) # Getting values works as you\(aqd expect. assert KV[\(aqk2\(aq] == \(aqv2\(aq # We can also do this: for value in KV[KV.key > \(aqk1\(aq]: print(value) # \(aqv2\(aq # \(aqv3\(aq # Update multiple values at once using expression: KV[KV.key > \(aqk1\(aq] = \(aqvx\(aq # What\(aqs stored in the KV? print(dict(KV)) # {\(aqk1\(aq: \(aqv1\(aq, \(aqk2\(aq: \(aqvx\(aq, \(aqk3\(aq: \(aqvx\(aq} # Delete a single item. del KV[\(aqk2\(aq] # How many items are stored in the KV? print(len(KV)) # 2 # Delete items that match the given condition. del KV[KV.key > \(aqk1\(aq] .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .TP .B __contains__(expr) .INDENT 7.0 .TP .B Parameters \fBexpr\fP \-\- a single key or an expression .TP .B Returns Boolean whether key/expression exists. .UNINDENT .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> kv = KeyValue() >>> kv.update(k1=\(aqv1\(aq, k2=\(aqv2\(aq) >>> \(aqk1\(aq in kv True >>> \(aqkx\(aq in kv False >>> (KV.key < \(aqk2\(aq) in KV True >>> (KV.key > \(aqk2\(aq) in KV False .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B __len__() .INDENT 7.0 .TP .B Returns Count of items stored. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B __getitem__(expr) .INDENT 7.0 .TP .B Parameters \fBexpr\fP \-\- a single key or an expression. .TP .B Returns value(s) corresponding to key/expression. .TP .B Raises \fBKeyError\fP if single key given and not found. .UNINDENT .sp Examples: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> KV = KeyValue() >>> KV.update(k1=\(aqv1\(aq, k2=\(aqv2\(aq, k3=\(aqv3\(aq) >>> KV[\(aqk1\(aq] \(aqv1\(aq >>> KV[\(aqkx\(aq] KeyError: "kx" not found >>> KV[KV.key > \(aqk1\(aq] [\(aqv2\(aq, \(aqv3\(aq] >>> KV[KV.key < \(aqk1\(aq] [] .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B __setitem__(expr, value) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBexpr\fP \-\- a single key or an expression. .IP \(bu 2 \fBvalue\fP \-\- value to set for key(s) .UNINDENT .UNINDENT .sp Set value for the given key. If \fBexpr\fP is an expression, then any keys matching the expression will have their value updated. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> KV = KeyValue() >>> KV.update(k1=\(aqv1\(aq, k2=\(aqv2\(aq, k3=\(aqv3\(aq) >>> KV[\(aqk1\(aq] = \(aqv1\-x\(aq >>> print(KV[\(aqk1\(aq]) \(aqv1\-x\(aq >>> KV[KV.key >= \(aqk2\(aq] = \(aqv99\(aq >>> dict(KV) {\(aqk1\(aq: \(aqv1\-x\(aq, \(aqk2\(aq: \(aqv99\(aq, \(aqk3\(aq: \(aqv99\(aq} .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B __delitem__(expr) .INDENT 7.0 .TP .B Parameters \fBexpr\fP \-\- a single key or an expression. .UNINDENT .sp Delete the given key. If an expression is given, delete all keys that match the expression. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> KV = KeyValue() >>> KV.update(k1=1, k2=2, k3=3) >>> del KV[\(aqk1\(aq] # Deletes "k1". >>> del KV[\(aqk1\(aq] KeyError: "k1" does not exist >>> del KV[KV.key > \(aqk2\(aq] # Deletes "k3". >>> del KV[KV.key > \(aqk99\(aq] # Nothing deleted, no keys match. .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B keys() .INDENT 7.0 .TP .B Returns an iterable of all keys in the table. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B values() .INDENT 7.0 .TP .B Returns an iterable of all values in the table. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B items() .INDENT 7.0 .TP .B Returns an iterable of all key/value pairs in the table. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B update([__data=None[, **mapping]]) Efficiently bulk\-insert or replace the given key/value pairs. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> KV = KeyValue() >>> KV.update(k1=1, k2=2) # Sets \(aqk1\(aq=1, \(aqk2\(aq=2. >>> dict(KV) {\(aqk1\(aq: 1, \(aqk2\(aq: 2} >>> KV.update(k2=22, k3=3) # Updates \(aqk2\(aq\->22, sets \(aqk3\(aq=3. >>> dict(KV) {\(aqk1\(aq: 1, \(aqk2\(aq: 22, \(aqk3\(aq: 3} >>> KV.update({\(aqk2\(aq: \-2, \(aqk4\(aq: 4}) # Also can pass a dictionary. >>> dict(KV) {\(aqk1\(aq: 1, \(aqk2\(aq: \-2, \(aqk3\(aq: 3, \(aqk4\(aq: 4} .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B get(expr[, default=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBexpr\fP \-\- a single key or an expression. .IP \(bu 2 \fBdefault\fP \-\- default value if key not found. .UNINDENT .TP .B Returns value of given key/expr or default if single key not found. .UNINDENT .sp Get the value at the given key. If the key does not exist, the default value is returned, unless the key is an expression in which case an empty list will be returned. .UNINDENT .INDENT 7.0 .TP .B pop(expr[, default=Sentinel]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBexpr\fP \-\- a single key or an expression. .IP \(bu 2 \fBdefault\fP \-\- default value if key does not exist. .UNINDENT .TP .B Returns value of given key/expr or default if single key not found. .UNINDENT .sp Get value and delete the given key. If the key does not exist, the default value is returned, unless the key is an expression in which case an empty list is returned. .UNINDENT .INDENT 7.0 .TP .B clear() Remove all items from the key\-value table. .UNINDENT .UNINDENT .SS Shortcuts .sp This module contains helper functions for expressing things that would otherwise be somewhat verbose or cumbersome using peewee\(aqs APIs. There are also helpers for serializing models to dictionaries and vice\-versa. .INDENT 0.0 .TP .B model_to_dict(model[, recurse=True[, backrefs=False[, only=None[, exclude=None[, extra_attrs=None[, fields_from_query=None[, max_depth=None[, manytomany=False]]]]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBrecurse\fP (\fIbool\fP) \-\- Whether foreign\-keys should be recursed. .IP \(bu 2 \fBbackrefs\fP (\fIbool\fP) \-\- Whether lists of related objects should be recursed. .IP \(bu 2 \fBonly\fP \-\- A list (or set) of field instances which should be included in the result dictionary. .IP \(bu 2 \fBexclude\fP \-\- A list (or set) of field instances which should be excluded from the result dictionary. .IP \(bu 2 \fBextra_attrs\fP \-\- A list of attribute or method names on the instance which should be included in the dictionary. .IP \(bu 2 \fBfields_from_query\fP (\fISelect\fP) \-\- The \fBSelectQuery\fP that created this model instance. Only the fields and values explicitly selected by the query will be serialized. .IP \(bu 2 \fBmax_depth\fP (\fIint\fP) \-\- Maximum depth when recursing. .IP \(bu 2 \fBmanytomany\fP (\fIbool\fP) \-\- Process many\-to\-many fields. .UNINDENT .UNINDENT .sp Convert a model instance (and optionally any related instances) to a dictionary. .sp Examples: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> user = User.create(username=\(aqcharlie\(aq) >>> model_to_dict(user) {\(aqid\(aq: 1, \(aqusername\(aq: \(aqcharlie\(aq} >>> model_to_dict(user, backrefs=True) {\(aqid\(aq: 1, \(aqtweets\(aq: [], \(aqusername\(aq: \(aqcharlie\(aq} >>> t1 = Tweet.create(user=user, message=\(aqtweet\-1\(aq) >>> t2 = Tweet.create(user=user, message=\(aqtweet\-2\(aq) >>> model_to_dict(user, backrefs=True) { \(aqid\(aq: 1, \(aqtweets\(aq: [ {\(aqid\(aq: 1, \(aqmessage\(aq: \(aqtweet\-1\(aq}, {\(aqid\(aq: 2, \(aqmessage\(aq: \(aqtweet\-2\(aq}, ], \(aqusername\(aq: \(aqcharlie\(aq } >>> model_to_dict(t1) { \(aqid\(aq: 1, \(aqmessage\(aq: \(aqtweet\-1\(aq, \(aquser\(aq: { \(aqid\(aq: 1, \(aqusername\(aq: \(aqcharlie\(aq } } >>> model_to_dict(t2, recurse=False) {\(aqid\(aq: 1, \(aqmessage\(aq: \(aqtweet\-2\(aq, \(aquser\(aq: 1} .ft P .fi .UNINDENT .UNINDENT .sp The implementation of \fBmodel_to_dict\fP is fairly complex, owing to the various usages it attempts to support. If you have a special usage, I strongly advise that you do \fBnot\fP attempt to shoe\-horn some crazy combination of parameters into this function. Just write a simple function that accomplishes exactly what you\(aqre attempting to do. .UNINDENT .INDENT 0.0 .TP .B dict_to_model(model_class, data[, ignore_unknown=False]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBmodel_class\fP (\fIModel\fP) \-\- The model class to construct. .IP \(bu 2 \fBdata\fP (\fIdict\fP) \-\- A dictionary of data. Foreign keys can be included as nested dictionaries, and back\-references as lists of dictionaries. .IP \(bu 2 \fBignore_unknown\fP (\fIbool\fP) \-\- Whether to allow unrecognized (non\-field) attributes. .UNINDENT .UNINDENT .sp Convert a dictionary of data to a model instance, creating related instances where appropriate. .sp Examples: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> user_data = {\(aqid\(aq: 1, \(aqusername\(aq: \(aqcharlie\(aq} >>> user = dict_to_model(User, user_data) >>> user <__main__.User at 0x7fea8fa4d490> >>> user.username \(aqcharlie\(aq >>> note_data = {\(aqid\(aq: 2, \(aqtext\(aq: \(aqnote text\(aq, \(aquser\(aq: user_data} >>> note = dict_to_model(Note, note_data) >>> note.text \(aqnote text\(aq >>> note.user.username \(aqcharlie\(aq >>> user_with_notes = { \&... \(aqid\(aq: 1, \&... \(aqusername\(aq: \(aqcharlie\(aq, \&... \(aqnotes\(aq: [{\(aqid\(aq: 1, \(aqtext\(aq: \(aqnote\-1\(aq}, {\(aqid\(aq: 2, \(aqtext\(aq: \(aqnote\-2\(aq}]} >>> user = dict_to_model(User, user_with_notes) >>> user.notes[0].text \(aqnote\-1\(aq >>> user.notes[0].user.username \(aqcharlie\(aq .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B update_model_from_dict(instance, data[, ignore_unknown=False]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBinstance\fP (\fIModel\fP) \-\- The model instance to update. .IP \(bu 2 \fBdata\fP (\fIdict\fP) \-\- A dictionary of data. Foreign keys can be included as nested dictionaries, and back\-references as lists of dictionaries. .IP \(bu 2 \fBignore_unknown\fP (\fIbool\fP) \-\- Whether to allow unrecognized (non\-field) attributes. .UNINDENT .UNINDENT .sp Update a model instance with the given data dictionary. .UNINDENT .INDENT 0.0 .TP .B resolve_multimodel_query(query[, key=\(aq_model_identifier\(aq]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBquery\fP \-\- a compound select query. .IP \(bu 2 \fBkey\fP (\fIstr\fP) \-\- key to use for storing model identifier .UNINDENT .TP .B Returns an iteratable cursor that yields the proper model instance for each row selected in the compound select query. .UNINDENT .sp Helper for resolving rows returned in a compound select query to the correct model instance type. For example, if you have a union of two different tables, this helper will resolve each row to the proper model when iterating over the query results. .UNINDENT .SS Signal support .sp Models with hooks for signals (a\-la django) are provided in \fBplayhouse.signals\fP\&. To use the signals, you will need all of your project\(aqs models to be a subclass of \fBplayhouse.signals.Model\fP, which overrides the necessary methods to provide support for the various signals. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.signals import Model, post_save class MyModel(Model): data = IntegerField() @post_save(sender=MyModel) def on_save_handler(model_class, instance, created): put_data_in_cache(instance.data) .ft P .fi .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 For what I hope are obvious reasons, Peewee signals do not work when you use the \fBModel.insert()\fP, \fBModel.update()\fP, or \fBModel.delete()\fP methods. These methods generate queries that execute beyond the scope of the ORM, and the ORM does not know about which model instances might or might not be affected when the query executes. .sp Signals work by hooking into the higher\-level peewee APIs like \fBModel.save()\fP and \fBModel.delete_instance()\fP, where the affected model instance is known ahead of time. .UNINDENT .UNINDENT .sp The following signals are provided: .INDENT 0.0 .TP .B \fBpre_save\fP Called immediately before an object is saved to the database. Provides an additional keyword argument \fBcreated\fP, indicating whether the model is being saved for the first time or updated. .TP .B \fBpost_save\fP Called immediately after an object is saved to the database. Provides an additional keyword argument \fBcreated\fP, indicating whether the model is being saved for the first time or updated. .TP .B \fBpre_delete\fP Called immediately before an object is deleted from the database when \fBModel.delete_instance()\fP is used. .TP .B \fBpost_delete\fP Called immediately after an object is deleted from the database when \fBModel.delete_instance()\fP is used. .TP .B \fBpre_init\fP Called when a model class is first instantiated .UNINDENT .SS Connecting handlers .sp Whenever a signal is dispatched, it will call any handlers that have been registered. This allows totally separate code to respond to events like model save and delete. .sp The \fI\%Signal\fP class provides a \fI\%connect()\fP method, which takes a callback function and two optional parameters for "sender" and "name". If specified, the "sender" parameter should be a single model class and allows your callback to only receive signals from that one model class. The "name" parameter is used as a convenient alias in the event you wish to unregister your signal handler. .sp Example usage: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.signals import * def post_save_handler(sender, instance, created): print \(aq%s was just saved\(aq % instance # our handler will only be called when we save instances of SomeModel post_save.connect(post_save_handler, sender=SomeModel) .ft P .fi .UNINDENT .UNINDENT .sp All signal handlers accept as their first two arguments \fBsender\fP and \fBinstance\fP, where \fBsender\fP is the model class and \fBinstance\fP is the actual model being acted upon. .sp If you\(aqd like, you can also use a decorator to connect signal handlers. This is functionally equivalent to the above example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C @post_save(sender=SomeModel) def post_save_handler(sender, instance, created): print \(aq%s was just saved\(aq % instance .ft P .fi .UNINDENT .UNINDENT .SS Signal API .INDENT 0.0 .TP .B class Signal Stores a list of receivers (callbacks) and calls them when the "send" method is invoked. .INDENT 7.0 .TP .B connect(receiver[, sender=None[, name=None]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBreceiver\fP (\fIcallable\fP) \-\- a callable that takes at least two parameters, a "sender", which is the Model subclass that triggered the signal, and an "instance", which is the actual model instance. .IP \(bu 2 \fBsender\fP (\fIModel\fP) \-\- if specified, only instances of this model class will trigger the receiver callback. .IP \(bu 2 \fBname\fP (\fIstring\fP) \-\- a short alias .UNINDENT .UNINDENT .sp Add the receiver to the internal list of receivers, which will be called whenever the signal is sent. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C from playhouse.signals import post_save from project.handlers import cache_buster post_save.connect(cache_buster, name=\(aqproject.cache_buster\(aq) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B disconnect([receiver=None[, name=None]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBreceiver\fP (\fIcallable\fP) \-\- the callback to disconnect .IP \(bu 2 \fBname\fP (\fIstring\fP) \-\- a short alias .UNINDENT .UNINDENT .sp Disconnect the given receiver (or the receiver with the given name alias) so that it no longer is called. Either the receiver or the name must be provided. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C post_save.disconnect(name=\(aqproject.cache_buster\(aq) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B send(instance, *args, **kwargs) .INDENT 7.0 .TP .B Parameters \fBinstance\fP \-\- a model instance .UNINDENT .sp Iterates over the receivers and will call them in the order in which they were connected. If the receiver specified a sender, it will only be called if the instance is an instance of the sender. .UNINDENT .UNINDENT .SS pwiz, a model generator .sp \fBpwiz\fP is a little script that ships with peewee and is capable of introspecting an existing database and generating model code suitable for interacting with the underlying data. If you have a database already, pwiz can give you a nice boost by generating skeleton code with correct column affinities and foreign keys. .sp If you install peewee using \fBsetup.py install\fP, pwiz will be installed as a "script" and you can just run: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C python \-m pwiz \-e postgresql \-u postgres my_postgres_db .ft P .fi .UNINDENT .UNINDENT .sp This will print a bunch of models to standard output. So you can do this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C python \-m pwiz \-e postgresql my_postgres_db > mymodels.py python # <\-\- fire up an interactive shell .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> from mymodels import Blog, Entry, Tag, Whatever >>> print [blog.name for blog in Blog.select()] .ft P .fi .UNINDENT .UNINDENT .SS Command\-line options .sp pwiz accepts the following command\-line options: .TS center; |l|l|l|. _ T{ Option T} T{ Meaning T} T{ Example T} _ T{ \-h T} T{ show help T} T{ T} _ T{ \-e T} T{ database backend T} T{ \-e mysql T} _ T{ \-H T} T{ host to connect to T} T{ \-H remote.db.server T} _ T{ \-p T} T{ port to connect on T} T{ \-p 9001 T} _ T{ \-u T} T{ database user T} T{ \-u postgres T} _ T{ \-P T} T{ database password T} T{ \-P (will be prompted for password) T} _ T{ \-s T} T{ schema T} T{ \-s public T} _ T{ \-t T} T{ tables to generate T} T{ \-t tweet,users,relationships T} _ T{ \-v T} T{ generate models for VIEWs T} T{ (no argument) T} _ T{ \-i T} T{ add info metadata to generated file T} T{ (no argument) T} _ T{ \-o T} T{ table column order is preserved T} T{ (no argument) T} _ .TE .sp The following are valid parameters for the \fBengine\fP (\fB\-e\fP): .INDENT 0.0 .IP \(bu 2 sqlite .IP \(bu 2 mysql .IP \(bu 2 postgresql .UNINDENT .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 If a password is required to access your database, you will be prompted to enter it using a secure prompt. .sp \fBThe password will be included in the output\fP\&. Specifically, at the top of the file a \fBDatabase\fP will be defined along with any required parameters \-\- including the password. .UNINDENT .UNINDENT .SS pwiz examples .sp Examples of introspecting various databases: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Introspect a Sqlite database. python \-m pwiz \-e sqlite path/to/sqlite_database.db # Introspect a MySQL database, logging in as root. You will be prompted # for a password ("\-P"). python \-m pwiz \-e mysql \-u root \-P mysql_db_name # Introspect a Postgresql database on a remote server. python \-m pwiz \-e postgres \-u postgres \-H 10.1.0.3 pg_db_name .ft P .fi .UNINDENT .UNINDENT .sp Full example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ sqlite3 example.db << EOM CREATE TABLE "user" ("id" INTEGER NOT NULL PRIMARY KEY, "username" TEXT NOT NULL); CREATE TABLE "tweet" ( "id" INTEGER NOT NULL PRIMARY KEY, "content" TEXT NOT NULL, "timestamp" DATETIME NOT NULL, "user_id" INTEGER NOT NULL, FOREIGN KEY ("user_id") REFERENCES "user" ("id")); CREATE UNIQUE INDEX "user_username" ON "user" ("username"); EOM $ python \-m pwiz \-e sqlite example.db .ft P .fi .UNINDENT .UNINDENT .sp Produces the following output: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from peewee import * database = SqliteDatabase(\(aqexample.db\(aq, **{}) class UnknownField(object): def __init__(self, *_, **__): pass class BaseModel(Model): class Meta: database = database class User(BaseModel): username = TextField(unique=True) class Meta: table_name = \(aquser\(aq class Tweet(BaseModel): content = TextField() timestamp = DateTimeField() user = ForeignKeyField(column_name=\(aquser_id\(aq, field=\(aqid\(aq, model=User) class Meta: table_name = \(aqtweet\(aq .ft P .fi .UNINDENT .UNINDENT .sp Observations: .INDENT 0.0 .IP \(bu 2 The foreign\-key \fBTweet.user_id\fP is detected and mapped correctly. .IP \(bu 2 The \fBUser.username\fP UNIQUE constraint is detected. .IP \(bu 2 Each model explicitly declares its table name, even in cases where it is not necessary (as Peewee would automatically translate the class name into the appropriate table name). .IP \(bu 2 All the parameters of the \fBForeignKeyField\fP are explicitly declared, even though they follow the conventions Peewee uses by default. .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 The \fBUnknownField\fP is a placeholder that is used in the event your schema contains a column declaration that Peewee doesn\(aqt know how to map to a field class. .UNINDENT .UNINDENT .SS Schema Migrations .sp Peewee now supports schema migrations, with well\-tested support for Postgresql, SQLite and MySQL. Unlike other schema migration tools, peewee\(aqs migrations do not handle introspection and database "versioning". Rather, peewee provides a number of helper functions for generating and running schema\-altering statements. This engine provides the basis on which a more sophisticated tool could some day be built. .sp Migrations can be written as simple python scripts and executed from the command\-line. Since the migrations only depend on your applications \fBDatabase\fP object, it should be easy to manage changing your model definitions and maintaining a set of migration scripts without introducing dependencies. .SS Example usage .sp Begin by importing the helpers from the \fImigrate\fP module: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from playhouse.migrate import * .ft P .fi .UNINDENT .UNINDENT .sp Instantiate a \fBmigrator\fP\&. The \fI\%SchemaMigrator\fP class is responsible for generating schema altering operations, which can then be run sequentially by the \fI\%migrate()\fP helper. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Postgres example: my_db = PostgresqlDatabase(...) migrator = PostgresqlMigrator(my_db) # SQLite example: my_db = SqliteDatabase(\(aqmy_database.db\(aq) migrator = SqliteMigrator(my_db) .ft P .fi .UNINDENT .UNINDENT .sp Use \fI\%migrate()\fP to execute one or more operations: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C title_field = CharField(default=\(aq\(aq) status_field = IntegerField(null=True) migrate( migrator.add_column(\(aqsome_table\(aq, \(aqtitle\(aq, title_field), migrator.add_column(\(aqsome_table\(aq, \(aqstatus\(aq, status_field), migrator.drop_column(\(aqsome_table\(aq, \(aqold_column\(aq), ) .ft P .fi .UNINDENT .UNINDENT .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 Migrations are not run inside a transaction. If you wish the migration to run in a transaction you will need to wrap the call to \fImigrate\fP in a \fBatomic()\fP context\-manager, e.g. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C with my_db.atomic(): migrate(...) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS Supported Operations .sp Add new field(s) to an existing model: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Create your field instances. For non\-null fields you must specify a # default value. pubdate_field = DateTimeField(null=True) comment_field = TextField(default=\(aq\(aq) # Run the migration, specifying the database table, field name and field. migrate( migrator.add_column(\(aqcomment_tbl\(aq, \(aqpub_date\(aq, pubdate_field), migrator.add_column(\(aqcomment_tbl\(aq, \(aqcomment\(aq, comment_field), ) .ft P .fi .UNINDENT .UNINDENT .sp Renaming a field: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Specify the table, original name of the column, and its new name. migrate( migrator.rename_column(\(aqstory\(aq, \(aqpub_date\(aq, \(aqpublish_date\(aq), migrator.rename_column(\(aqstory\(aq, \(aqmod_date\(aq, \(aqmodified_date\(aq), ) .ft P .fi .UNINDENT .UNINDENT .sp Dropping a field: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C migrate( migrator.drop_column(\(aqstory\(aq, \(aqsome_old_field\(aq), ) .ft P .fi .UNINDENT .UNINDENT .sp Making a field nullable or not nullable: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Note that when making a field not null that field must not have any # NULL values present. migrate( # Make \(gapub_date\(ga allow NULL values. migrator.drop_not_null(\(aqstory\(aq, \(aqpub_date\(aq), # Prevent \(gamodified_date\(ga from containing NULL values. migrator.add_not_null(\(aqstory\(aq, \(aqmodified_date\(aq), ) .ft P .fi .UNINDENT .UNINDENT .sp Altering a field\(aqs data\-type: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Change a VARCHAR(50) field to a TEXT field. migrate( migrator.alter_column_type(\(aqperson\(aq, \(aqemail\(aq, TextField()) ) .ft P .fi .UNINDENT .UNINDENT .sp Renaming a table: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C migrate( migrator.rename_table(\(aqstory\(aq, \(aqstories_tbl\(aq), ) .ft P .fi .UNINDENT .UNINDENT .sp Adding an index: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Specify the table, column names, and whether the index should be # UNIQUE or not. migrate( # Create an index on the \(gapub_date\(ga column. migrator.add_index(\(aqstory\(aq, (\(aqpub_date\(aq,), False), # Create a multi\-column index on the \(gapub_date\(ga and \(gastatus\(ga fields. migrator.add_index(\(aqstory\(aq, (\(aqpub_date\(aq, \(aqstatus\(aq), False), # Create a unique index on the category and title fields. migrator.add_index(\(aqstory\(aq, (\(aqcategory_id\(aq, \(aqtitle\(aq), True), ) .ft P .fi .UNINDENT .UNINDENT .sp Dropping an index: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Specify the index name. migrate(migrator.drop_index(\(aqstory\(aq, \(aqstory_pub_date_status\(aq)) .ft P .fi .UNINDENT .UNINDENT .sp Adding or dropping table constraints: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Add a CHECK() constraint to enforce the price cannot be negative. migrate(migrator.add_constraint( \(aqproducts\(aq, \(aqprice_check\(aq, Check(\(aqprice >= 0\(aq))) # Remove the price check constraint. migrate(migrator.drop_constraint(\(aqproducts\(aq, \(aqprice_check\(aq)) # Add a UNIQUE constraint on the first and last names. migrate(migrator.add_unique(\(aqperson\(aq, \(aqfirst_name\(aq, \(aqlast_name\(aq)) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Postgres users may need to set the search\-path when using a non\-standard schema. This can be done as follows: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C new_field = TextField(default=\(aq\(aq, null=False) migrator = PostgresqlMigrator(db) migrate(migrator.set_search_path(\(aqmy_schema_name\(aq), migrator.add_column(\(aqtable\(aq, \(aqfield_name\(aq, new_field)) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SS Migrations API .INDENT 0.0 .TP .B migrate(*operations) Execute one or more schema altering operations. .sp Usage: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C migrate( migrator.add_column(\(aqsome_table\(aq, \(aqnew_column\(aq, CharField(default=\(aq\(aq)), migrator.create_index(\(aqsome_table\(aq, (\(aqnew_column\(aq,)), ) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class SchemaMigrator(database) .INDENT 7.0 .TP .B Parameters \fBdatabase\fP \-\- a \fBDatabase\fP instance. .UNINDENT .sp The \fI\%SchemaMigrator\fP is responsible for generating schema\-altering statements. .INDENT 7.0 .TP .B add_column(table, column_name, field) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Name of the table to add column to. .IP \(bu 2 \fBcolumn_name\fP (\fIstr\fP) \-\- Name of the new column. .IP \(bu 2 \fBfield\fP (\fIField\fP) \-\- A \fBField\fP instance. .UNINDENT .UNINDENT .sp Add a new column to the provided table. The \fBfield\fP provided will be used to generate the appropriate column definition. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 If the field is not nullable it must specify a default value. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 For non\-null fields, the field will initially be added as a null field, then an \fBUPDATE\fP statement will be executed to populate the column with the default value. Finally, the column will be marked as not null. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B drop_column(table, column_name[, cascade=True]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Name of the table to drop column from. .IP \(bu 2 \fBcolumn_name\fP (\fIstr\fP) \-\- Name of the column to drop. .IP \(bu 2 \fBcascade\fP (\fIbool\fP) \-\- Whether the column should be dropped with \fICASCADE\fP\&. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B rename_column(table, old_name, new_name) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Name of the table containing column to rename. .IP \(bu 2 \fBold_name\fP (\fIstr\fP) \-\- Current name of the column. .IP \(bu 2 \fBnew_name\fP (\fIstr\fP) \-\- New name for the column. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B add_not_null(table, column) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Name of table containing column. .IP \(bu 2 \fBcolumn\fP (\fIstr\fP) \-\- Name of the column to make not nullable. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B drop_not_null(table, column) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Name of table containing column. .IP \(bu 2 \fBcolumn\fP (\fIstr\fP) \-\- Name of the column to make nullable. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B alter_column_type(table, column, field[, cast=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Name of the table. .IP \(bu 2 \fBcolumn_name\fP (\fIstr\fP) \-\- Name of the column to modify. .IP \(bu 2 \fBfield\fP (\fIField\fP) \-\- \fBField\fP instance representing new data type. .IP \(bu 2 \fBcast\fP \-\- (postgres\-only) specify a cast expression if the data\-types are incompatible, e.g. \fBcolumn_name::int\fP\&. Can be provided as either a string or a \fBCast\fP instance. .UNINDENT .UNINDENT .sp Alter the data\-type of a column. This method should be used with care, as using incompatible types may not be well\-supported by your database. .UNINDENT .INDENT 7.0 .TP .B rename_table(old_name, new_name) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBold_name\fP (\fIstr\fP) \-\- Current name of the table. .IP \(bu 2 \fBnew_name\fP (\fIstr\fP) \-\- New name for the table. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B add_index(table, columns[, unique=False[, using=None]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Name of table on which to create the index. .IP \(bu 2 \fBcolumns\fP (\fIlist\fP) \-\- List of columns which should be indexed. .IP \(bu 2 \fBunique\fP (\fIbool\fP) \-\- Whether the new index should specify a unique constraint. .IP \(bu 2 \fBusing\fP (\fIstr\fP) \-\- Index type (where supported), e.g. GiST or GIN. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B drop_index(table, index_name) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Name of the table containing the index to be dropped. .IP \(bu 2 \fBindex_name\fP (\fIstr\fP) \-\- Name of the index to be dropped. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B add_constraint(table, name, constraint) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Table to add constraint to. .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Name used to identify the constraint. .IP \(bu 2 \fBconstraint\fP \-\- either a \fBCheck()\fP constraint or for adding an arbitrary constraint use \fBSQL\fP\&. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B drop_constraint(table, name) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Table to drop constraint from. .IP \(bu 2 \fBname\fP (\fIstr\fP) \-\- Name of constraint to drop. .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B add_unique(table, *column_names) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtable\fP (\fIstr\fP) \-\- Table to add constraint to. .IP \(bu 2 \fBcolumn_names\fP (\fIstr\fP) \-\- One or more columns for UNIQUE constraint. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class PostgresqlMigrator(database) Generate migrations for Postgresql databases. .INDENT 7.0 .TP .B set_search_path(schema_name) .INDENT 7.0 .TP .B Parameters \fBschema_name\fP (\fIstr\fP) \-\- Schema to use. .UNINDENT .sp Set the search path (schema) for the subsequent operations. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class SqliteMigrator(database) Generate migrations for SQLite databases. .sp SQLite has limited support for \fBALTER TABLE\fP queries, so the following operations are currently not supported for SQLite: .INDENT 7.0 .IP \(bu 2 \fBadd_constraint\fP .IP \(bu 2 \fBdrop_constraint\fP .IP \(bu 2 \fBadd_unique\fP .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class MySQLMigrator(database) Generate migrations for MySQL databases. .UNINDENT .SS Reflection .sp The reflection module contains helpers for introspecting existing databases. This module is used internally by several other modules in the playhouse, including \fI\%DataSet\fP and \fI\%pwiz, a model generator\fP\&. .INDENT 0.0 .TP .B generate_models(database[, schema=None[, **options]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdatabase\fP (\fIDatabase\fP) \-\- database instance to introspect. .IP \(bu 2 \fBschema\fP (\fIstr\fP) \-\- optional schema to introspect. .IP \(bu 2 \fBoptions\fP \-\- arbitrary options, see \fI\%Introspector.generate_models()\fP for details. .UNINDENT .TP .B Returns a \fBdict\fP mapping table names to model classes. .UNINDENT .sp Generate models for the tables in the given database. For an example of how to use this function, see the section interactive\&. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> from peewee import * >>> from playhouse.reflection import generate_models >>> db = PostgresqlDatabase(\(aqmy_app\(aq) >>> models = generate_models(db) >>> list(models.keys()) [\(aqaccount\(aq, \(aqcustomer\(aq, \(aqorder\(aq, \(aqorderitem\(aq, \(aqproduct\(aq] >>> globals().update(models) # Inject models into namespace. >>> for cust in customer.select(): # Query using generated model. \&... print(cust.name) \&... Huey Kitty Mickey Dog .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B print_model(model) .INDENT 7.0 .TP .B Parameters \fBmodel\fP (\fIModel\fP) \-\- model class to print .TP .B Returns no return value .UNINDENT .sp Print a user\-friendly description of a model class, useful for debugging or interactive use. Currently this prints the table name, and all fields along with their data\-types. The interactive section contains an example. .sp Example output: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> from playhouse.reflection import print_model >>> print_model(User) user id AUTO PK email TEXT name TEXT dob DATE index(es) email UNIQUE >>> print_model(Tweet) tweet id AUTO PK user INT FK: User.id title TEXT content TEXT timestamp DATETIME is_published BOOL index(es) user_id is_published, timestamp .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B print_table_sql(model) .INDENT 7.0 .TP .B Parameters \fBmodel\fP (\fIModel\fP) \-\- model to print .TP .B Returns no return value .UNINDENT .sp Prints the SQL \fBCREATE TABLE\fP for the given model class, which may be useful for debugging or interactive use. See the interactive section for example usage. Note that indexes and constraints are not included in the output of this function. .sp Example output: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C >>> from playhouse.reflection import print_table_sql >>> print_table_sql(User) CREATE TABLE IF NOT EXISTS "user" ( "id" INTEGER NOT NULL PRIMARY KEY, "email" TEXT NOT NULL, "name" TEXT NOT NULL, "dob" DATE NOT NULL ) >>> print_table_sql(Tweet) CREATE TABLE IF NOT EXISTS "tweet" ( "id" INTEGER NOT NULL PRIMARY KEY, "user_id" INTEGER NOT NULL, "title" TEXT NOT NULL, "content" TEXT NOT NULL, "timestamp" DATETIME NOT NULL, "is_published" INTEGER NOT NULL, FOREIGN KEY ("user_id") REFERENCES "user" ("id") ) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class Introspector(metadata[, schema=None]) Metadata can be extracted from a database by instantiating an \fI\%Introspector\fP\&. Rather than instantiating this class directly, it is recommended to use the factory method \fI\%from_database()\fP\&. .INDENT 7.0 .TP .B classmethod from_database(database[, schema=None]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdatabase\fP \-\- a \fBDatabase\fP instance. .IP \(bu 2 \fBschema\fP (\fIstr\fP) \-\- an optional schema (supported by some databases). .UNINDENT .UNINDENT .sp Creates an \fI\%Introspector\fP instance suitable for use with the given database. .sp Usage: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C db = SqliteDatabase(\(aqmy_app.db\(aq) introspector = Introspector.from_database(db) models = introspector.generate_models() # User and Tweet (assumed to exist in the database) are # peewee Model classes generated from the database schema. User = models[\(aquser\(aq] Tweet = models[\(aqtweet\(aq] .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 7.0 .TP .B generate_models([skip_invalid=False[, table_names=None[, literal_column_names=False[, bare_fields=False[, include_views=False]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBskip_invalid\fP (\fIbool\fP) \-\- Skip tables whose names are invalid python identifiers. .IP \(bu 2 \fBtable_names\fP (\fIlist\fP) \-\- List of table names to generate. If unspecified, models are generated for all tables. .IP \(bu 2 \fBliteral_column_names\fP (\fIbool\fP) \-\- Use column\-names as\-is. By default, column names are "python\-ized", i.e. mixed\-case becomes lower\-case. .IP \(bu 2 \fBbare_fields\fP \-\- \fBSQLite\-only\fP\&. Do not specify data\-types for introspected columns. .IP \(bu 2 \fBinclude_views\fP \-\- generate models for VIEWs as well. .UNINDENT .TP .B Returns A dictionary mapping table\-names to model classes. .UNINDENT .sp Introspect the database, reading in the tables, columns, and foreign key constraints, then generate a dictionary mapping each database table to a dynamically\-generated \fBModel\fP class. .UNINDENT .UNINDENT .SS Database URL .sp This module contains a helper function to generate a database connection from a URL connection string. .INDENT 0.0 .TP .B connect(url, **connect_params) Create a \fBDatabase\fP instance from the given connection URL. .sp Examples: .INDENT 7.0 .IP \(bu 2 \fIsqlite:///my_database.db\fP will create a \fBSqliteDatabase\fP instance for the file \fBmy_database.db\fP in the current directory. .IP \(bu 2 \fIsqlite:///:memory:\fP will create an in\-memory \fBSqliteDatabase\fP instance. .IP \(bu 2 \fIpostgresql://postgres:my_password@localhost:5432/my_database\fP will create a \fBPostgresqlDatabase\fP instance. A username and password are provided, as well as the host and port to connect to. .IP \(bu 2 \fImysql://user:passwd@ip:port/my_db\fP will create a \fBMySQLDatabase\fP instance for the local MySQL database \fImy_db\fP\&. .IP \(bu 2 \fImysql+pool://user:passwd@ip:port/my_db?max_connections=20&stale_timeout=300\fP will create a \fI\%PooledMySQLDatabase\fP instance for the local MySQL database \fImy_db\fP with max_connections set to 20 and a stale_timeout setting of 300 seconds. .UNINDENT .sp Supported schemes: .INDENT 7.0 .IP \(bu 2 \fBapsw\fP: \fI\%APSWDatabase\fP .IP \(bu 2 \fBmysql\fP: \fBMySQLDatabase\fP .IP \(bu 2 \fBmysql+pool\fP: \fI\%PooledMySQLDatabase\fP .IP \(bu 2 \fBpostgres\fP: \fBPostgresqlDatabase\fP .IP \(bu 2 \fBpostgres+pool\fP: \fI\%PooledPostgresqlDatabase\fP .IP \(bu 2 \fBpostgresext\fP: \fI\%PostgresqlExtDatabase\fP .IP \(bu 2 \fBpostgresext+pool\fP: \fI\%PooledPostgresqlExtDatabase\fP .IP \(bu 2 \fBsqlite\fP: \fBSqliteDatabase\fP .IP \(bu 2 \fBsqliteext\fP: \fBSqliteExtDatabase\fP .IP \(bu 2 \fBsqlite+pool\fP: \fI\%PooledSqliteDatabase\fP .IP \(bu 2 \fBsqliteext+pool\fP: \fI\%PooledSqliteExtDatabase\fP .UNINDENT .sp Usage: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C import os from playhouse.db_url import connect # Connect to the database URL defined in the environment, falling # back to a local Sqlite database if no database URL is specified. db = connect(os.environ.get(\(aqDATABASE\(aq) or \(aqsqlite:///default.db\(aq) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B parse(url) Parse the information in the given URL into a dictionary containing \fBdatabase\fP, \fBhost\fP, \fBport\fP, \fBuser\fP and/or \fBpassword\fP\&. Additional connection arguments can be passed in the URL query string. .sp If you are using a custom database class, you can use the \fBparse()\fP function to extract information from a URL which can then be passed in to your database object. .UNINDENT .INDENT 0.0 .TP .B register_database(db_class, *names) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdb_class\fP \-\- A subclass of \fBDatabase\fP\&. .IP \(bu 2 \fBnames\fP \-\- A list of names to use as the scheme in the URL, e.g. \(aqsqlite\(aq or \(aqfirebird\(aq .UNINDENT .UNINDENT .sp Register additional database class under the specified names. This function can be used to extend the \fBconnect()\fP function to support additional schemes. Suppose you have a custom database class for \fBFirebird\fP named \fBFirebirdDatabase\fP\&. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C from playhouse.db_url import connect, register_database register_database(FirebirdDatabase, \(aqfirebird\(aq) db = connect(\(aqfirebird://my\-firebird\-db\(aq) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .SS Connection pool .sp The \fBpool\fP module contains a number of \fBDatabase\fP classes that provide connection pooling for PostgreSQL, MySQL and SQLite databases. The pool works by overriding the methods on the \fBDatabase\fP class that open and close connections to the backend. The pool can specify a timeout after which connections are recycled, as well as an upper bound on the number of open connections. .sp In a multi\-threaded application, up to \fImax_connections\fP will be opened. Each thread (or, if using gevent, greenlet) will have its own connection. .sp In a single\-threaded application, only one connection will be created. It will be continually recycled until either it exceeds the stale timeout or is closed explicitly (using \fI\&.manual_close()\fP). .sp \fBBy default, all your application needs to do is ensure that connections are closed when you are finished with them, and they will be returned to the pool\fP\&. For web applications, this typically means that at the beginning of a request, you will open a connection, and when you return a response, you will close the connection. .sp Simple Postgres pool example code: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Use the special postgresql extensions. from playhouse.pool import PooledPostgresqlExtDatabase db = PooledPostgresqlExtDatabase( \(aqmy_app\(aq, max_connections=32, stale_timeout=300, # 5 minutes. user=\(aqpostgres\(aq) class BaseModel(Model): class Meta: database = db .ft P .fi .UNINDENT .UNINDENT .sp That\(aqs it! If you would like finer\-grained control over the pool of connections, check out the advanced_connection_management section. .SS Pool APIs .INDENT 0.0 .TP .B class PooledDatabase(database[, max_connections=20[, stale_timeout=None[, timeout=None[, **kwargs]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdatabase\fP (\fIstr\fP) \-\- The name of the database or database file. .IP \(bu 2 \fBmax_connections\fP (\fIint\fP) \-\- Maximum number of connections. Provide \fBNone\fP for unlimited. .IP \(bu 2 \fBstale_timeout\fP (\fIint\fP) \-\- Number of seconds to allow connections to be used. .IP \(bu 2 \fBtimeout\fP (\fIint\fP) \-\- Number of seconds to block when pool is full. By default peewee does not block when the pool is full but simply throws an exception. To block indefinitely set this value to \fB0\fP\&. .IP \(bu 2 \fBkwargs\fP \-\- Arbitrary keyword arguments passed to database class. .UNINDENT .UNINDENT .sp Mixin class intended to be used with a subclass of \fBDatabase\fP\&. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 Connections will not be closed exactly when they exceed their \fIstale_timeout\fP\&. Instead, stale connections are only closed when a new connection is requested. .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 If the number of open connections exceeds \fImax_connections\fP, a \fIValueError\fP will be raised. .UNINDENT .UNINDENT .INDENT 7.0 .TP .B manual_close() Close the currently\-open connection without returning it to the pool. .UNINDENT .INDENT 7.0 .TP .B close_idle() Close all idle connections. This does not include any connections that are currently in\-use \-\- only those that were previously created but have since been returned back to the pool. .UNINDENT .INDENT 7.0 .TP .B close_stale([age=600]) .INDENT 7.0 .TP .B Parameters \fBage\fP (\fIint\fP) \-\- Age at which a connection should be considered stale. .TP .B Returns Number of connections closed. .UNINDENT .sp Close connections which are in\-use but exceed the given age. \fBUse caution when calling this method!\fP .UNINDENT .INDENT 7.0 .TP .B close_all() Close all connections. This includes any connections that may be in use at the time. \fBUse caution when calling this method!\fP .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class PooledPostgresqlDatabase Subclass of \fBPostgresqlDatabase\fP that mixes in the \fI\%PooledDatabase\fP helper. .UNINDENT .INDENT 0.0 .TP .B class PooledPostgresqlExtDatabase Subclass of \fI\%PostgresqlExtDatabase\fP that mixes in the \fI\%PooledDatabase\fP helper. The \fI\%PostgresqlExtDatabase\fP is a part of the \fI\%Postgresql Extensions\fP module and provides support for many Postgres\-specific features. .UNINDENT .INDENT 0.0 .TP .B class PooledMySQLDatabase Subclass of \fBMySQLDatabase\fP that mixes in the \fI\%PooledDatabase\fP helper. .UNINDENT .INDENT 0.0 .TP .B class PooledSqliteDatabase Persistent connections for SQLite apps. .UNINDENT .INDENT 0.0 .TP .B class PooledSqliteExtDatabase Persistent connections for SQLite apps, using the sqlite_ext advanced database driver \fBSqliteExtDatabase\fP\&. .UNINDENT .SS Test Utils .sp Contains utilities helpful when testing peewee projects. .INDENT 0.0 .TP .B class count_queries([only_select=False]) Context manager that will count the number of queries executed within the context. .INDENT 7.0 .TP .B Parameters \fBonly_select\fP (\fIbool\fP) \-\- Only count \fISELECT\fP queries. .UNINDENT .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C with count_queries() as counter: huey = User.get(User.username == \(aqhuey\(aq) huey_tweets = [tweet.message for tweet in huey.tweets] assert counter.count == 2 .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .TP .B count The number of queries executed. .UNINDENT .INDENT 7.0 .TP .B get_queries() Return a list of 2\-tuples consisting of the SQL query and a list of parameters. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B assert_query_count(expected[, only_select=False]) Function or method decorator that will raise an \fBAssertionError\fP if the number of queries executed in the decorated function does not equal the expected number. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class TestMyApp(unittest.TestCase): @assert_query_count(1) def test_get_popular_blogs(self): popular_blogs = Blog.get_popular() self.assertEqual( [blog.title for blog in popular_blogs], ["Peewee\(aqs Playhouse!", "All About Huey", "Mickey\(aqs Adventures"]) .ft P .fi .UNINDENT .UNINDENT .sp This function can also be used as a context manager: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C class TestMyApp(unittest.TestCase): def test_expensive_operation(self): with assert_query_count(1): perform_expensive_operation() .ft P .fi .UNINDENT .UNINDENT .UNINDENT .SS Flask Utils .sp The \fBplayhouse.flask_utils\fP module contains several helpers for integrating peewee with the \fI\%Flask\fP web framework. .SS Database Wrapper .sp The \fBFlaskDB\fP class is a wrapper for configuring and referencing a Peewee database from within a Flask application. Don\(aqt let its name fool you: it is \fBnot the same thing as a peewee database\fP\&. \fBFlaskDB\fP is designed to remove the following boilerplate from your flask app: .INDENT 0.0 .IP \(bu 2 Dynamically create a Peewee database instance based on app config data. .IP \(bu 2 Create a base class from which all your application\(aqs models will descend. .IP \(bu 2 Register hooks at the start and end of a request to handle opening and closing a database connection. .UNINDENT .sp Basic usage: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C import datetime from flask import Flask from peewee import * from playhouse.flask_utils import FlaskDB DATABASE = \(aqpostgresql://postgres:password@localhost:5432/my_database\(aq app = Flask(__name__) app.config.from_object(__name__) db_wrapper = FlaskDB(app) class User(db_wrapper.Model): username = CharField(unique=True) class Tweet(db_wrapper.Model): user = ForeignKeyField(User, backref=\(aqtweets\(aq) content = TextField() timestamp = DateTimeField(default=datetime.datetime.now) .ft P .fi .UNINDENT .UNINDENT .sp The above code example will create and instantiate a peewee \fBPostgresqlDatabase\fP specified by the given database URL. Request hooks will be configured to establish a connection when a request is received, and automatically close the connection when the response is sent. Lastly, the \fBFlaskDB\fP class exposes a \fBFlaskDB.Model\fP property which can be used as a base for your application\(aqs models. .sp Here is how you can access the wrapped Peewee database instance that is configured for you by the \fBFlaskDB\fP wrapper: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Obtain a reference to the Peewee database instance. peewee_db = db_wrapper.database @app.route(\(aq/transfer\-funds/\(aq, methods=[\(aqPOST\(aq]) def transfer_funds(): with peewee_db.atomic(): # ... return jsonify({\(aqtransfer\-id\(aq: xid}) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 The actual peewee database can be accessed using the \fBFlaskDB.database\fP attribute. .UNINDENT .UNINDENT .sp Here is another way to configure a Peewee database using \fBFlaskDB\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C app = Flask(__name__) db_wrapper = FlaskDB(app, \(aqsqlite:///my_app.db\(aq) .ft P .fi .UNINDENT .UNINDENT .sp While the above examples show using a database URL, for more advanced usages you can specify a dictionary of configuration options, or simply pass in a peewee \fBDatabase\fP instance: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C DATABASE = { \(aqname\(aq: \(aqmy_app_db\(aq, \(aqengine\(aq: \(aqplayhouse.pool.PooledPostgresqlDatabase\(aq, \(aquser\(aq: \(aqpostgres\(aq, \(aqmax_connections\(aq: 32, \(aqstale_timeout\(aq: 600, } app = Flask(__name__) app.config.from_object(__name__) wrapper = FlaskDB(app) pooled_postgres_db = wrapper.database .ft P .fi .UNINDENT .UNINDENT .sp Using a peewee \fBDatabase\fP object: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C peewee_db = PostgresqlExtDatabase(\(aqmy_app\(aq) app = Flask(__name__) db_wrapper = FlaskDB(app, peewee_db) .ft P .fi .UNINDENT .UNINDENT .SS Database with Application Factory .sp If you prefer to use the \fI\%application factory pattern\fP, the \fBFlaskDB\fP class implements an \fBinit_app()\fP method. .sp Using as a factory: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db_wrapper = FlaskDB() # Even though the database is not yet initialized, you can still use the # \(gaModel\(ga property to create model classes. class User(db_wrapper.Model): username = CharField(unique=True) def create_app(): app = Flask(__name__) app.config[\(aqDATABASE\(aq] = \(aqsqlite:////home/code/apps/my\-database.db\(aq db_wrapper.init_app(app) return app .ft P .fi .UNINDENT .UNINDENT .SS Query utilities .sp The \fBflask_utils\fP module provides several helpers for managing queries in your web app. Some common patterns include: .INDENT 0.0 .TP .B get_object_or_404(query_or_model, *query) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBquery_or_model\fP \-\- Either a \fBModel\fP class or a pre\-filtered \fBSelectQuery\fP\&. .IP \(bu 2 \fBquery\fP \-\- An arbitrarily complex peewee expression. .UNINDENT .UNINDENT .sp Retrieve the object matching the given query, or return a 404 not found response. A common use\-case might be a detail page for a weblog. You want to either retrieve the post matching the given URL, or return a 404. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C @app.route(\(aq/blog//\(aq) def post_detail(slug): public_posts = Post.select().where(Post.published == True) post = get_object_or_404(public_posts, (Post.slug == slug)) return render_template(\(aqpost_detail.html\(aq, post=post) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .INDENT 0.0 .TP .B object_list(template_name, query[, context_variable=\(aqobject_list\(aq[, paginate_by=20[, page_var=\(aqpage\(aq[, check_bounds=True[, **kwargs]]]]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBtemplate_name\fP \-\- The name of the template to render. .IP \(bu 2 \fBquery\fP \-\- A \fBSelectQuery\fP instance to paginate. .IP \(bu 2 \fBcontext_variable\fP \-\- The context variable name to use for the paginated object list. .IP \(bu 2 \fBpaginate_by\fP \-\- Number of objects per\-page. .IP \(bu 2 \fBpage_var\fP \-\- The name of the \fBGET\fP argument which contains the page. .IP \(bu 2 \fBcheck_bounds\fP \-\- Whether to check that the given page is a valid page. If \fBcheck_bounds\fP is \fBTrue\fP and an invalid page is specified, then a 404 will be returned. .IP \(bu 2 \fBkwargs\fP \-\- Arbitrary key/value pairs to pass into the template context. .UNINDENT .UNINDENT .sp Retrieve a paginated list of objects specified by the given query. The paginated object list will be dropped into the context using the given \fBcontext_variable\fP, as well as metadata about the current page and total number of pages, and finally any arbitrary context data passed as keyword\-arguments. .sp The page is specified using the \fBpage\fP \fBGET\fP argument, e.g. \fB/my\-object\-list/?page=3\fP would return the third page of objects. .sp Example: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C @app.route(\(aq/blog/\(aq) def post_index(): public_posts = (Post .select() .where(Post.published == True) .order_by(Post.timestamp.desc())) return object_list( \(aqpost_index.html\(aq, query=public_posts, context_variable=\(aqpost_list\(aq, paginate_by=10) .ft P .fi .UNINDENT .UNINDENT .sp The template will have the following context: .INDENT 7.0 .IP \(bu 2 \fBpost_list\fP, which contains a list of up to 10 posts. .IP \(bu 2 \fBpage\fP, which contains the current page based on the value of the \fBpage\fP \fBGET\fP parameter. .IP \(bu 2 \fBpagination\fP, a \fI\%PaginatedQuery\fP instance. .UNINDENT .UNINDENT .INDENT 0.0 .TP .B class PaginatedQuery(query_or_model, paginate_by[, page_var=\(aqpage\(aq[, check_bounds=False]]) .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBquery_or_model\fP \-\- Either a \fBModel\fP or a \fBSelectQuery\fP instance containing the collection of records you wish to paginate. .IP \(bu 2 \fBpaginate_by\fP \-\- Number of objects per\-page. .IP \(bu 2 \fBpage_var\fP \-\- The name of the \fBGET\fP argument which contains the page. .IP \(bu 2 \fBcheck_bounds\fP \-\- Whether to check that the given page is a valid page. If \fBcheck_bounds\fP is \fBTrue\fP and an invalid page is specified, then a 404 will be returned. .UNINDENT .UNINDENT .sp Helper class to perform pagination based on \fBGET\fP arguments. .INDENT 7.0 .TP .B get_page() Return the currently selected page, as indicated by the value of the \fBpage_var\fP \fBGET\fP parameter. If no page is explicitly selected, then this method will return 1, indicating the first page. .UNINDENT .INDENT 7.0 .TP .B get_page_count() Return the total number of possible pages. .UNINDENT .INDENT 7.0 .TP .B get_object_list() Using the value of \fI\%get_page()\fP, return the page of objects requested by the user. The return value is a \fBSelectQuery\fP with the appropriate \fBLIMIT\fP and \fBOFFSET\fP clauses. .sp If \fBcheck_bounds\fP was set to \fBTrue\fP and the requested page contains no objects, then a 404 will be raised. .UNINDENT .UNINDENT .SS Query Examples .sp These query examples are taken from the site \fI\%PostgreSQL Exercises\fP\&. A sample data\-set can be found on the \fI\%getting started page\fP\&. .sp Here is a visual representation of the schema used in these examples: [image] .SS Model Definitions .sp To begin working with the data, we\(aqll define the model classes that correspond to the tables in the diagram. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 In some cases we explicitly specify column names for a particular field. This is so our models are compatible with the database schema used for the postgres exercises. .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from functools import partial from peewee import * db = PostgresqlDatabase(\(aqpeewee_test\(aq) class BaseModel(Model): class Meta: database = db class Member(BaseModel): memid = AutoField() # Auto\-incrementing primary key. surname = CharField() firstname = CharField() address = CharField(max_length=300) zipcode = IntegerField() telephone = CharField() recommendedby = ForeignKeyField(\(aqself\(aq, backref=\(aqrecommended\(aq, column_name=\(aqrecommendedby\(aq, null=True) joindate = DateTimeField() class Meta: table_name = \(aqmembers\(aq # Conveniently declare decimal fields suitable for storing currency. MoneyField = partial(DecimalField, decimal_places=2) class Facility(BaseModel): facid = AutoField() name = CharField() membercost = MoneyField() guestcost = MoneyField() initialoutlay = MoneyField() monthlymaintenance = MoneyField() class Meta: table_name = \(aqfacilities\(aq class Booking(BaseModel): bookid = AutoField() facility = ForeignKeyField(Facility, column_name=\(aqfacid\(aq) member = ForeignKeyField(Member, column_name=\(aqmemid\(aq) starttime = DateTimeField() slots = IntegerField() class Meta: table_name = \(aqbookings\(aq .ft P .fi .UNINDENT .UNINDENT .SS Schema Creation .sp If you downloaded the SQL file from the PostgreSQL Exercises site, then you can load the data into a PostgreSQL database using the following commands: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C createdb peewee_test psql \-U postgres \-f clubdata.sql \-d peewee_test \-x \-q .ft P .fi .UNINDENT .UNINDENT .sp To create the schema using Peewee, without loading the sample data, you can run the following: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Assumes you have created the database "peewee_test" already. db.create_tables([Member, Facility, Booking]) .ft P .fi .UNINDENT .UNINDENT .SS Basic Exercises .sp This category deals with the basics of SQL. It covers select and where clauses, case expressions, unions, and a few other odds and ends. .SS Retrieve everything .sp Retrieve all information from facilities table. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT * FROM facilities .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # By default, when no fields are explicitly passed to select(), all fields # will be selected. query = Facility.select() .ft P .fi .UNINDENT .UNINDENT .SS Retrieve specific columns from a table .sp Retrieve names of facilities and cost to members. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT name, membercost FROM facilities; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Facility.select(Facility.name, Facility.membercost) # To iterate: for facility in query: print(facility.name) .ft P .fi .UNINDENT .UNINDENT .SS Control which rows are retrieved .sp Retrieve list of facilities that have a cost to members. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT * FROM facilities WHERE membercost > 0 .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Facility.select().where(Facility.membercost > 0) .ft P .fi .UNINDENT .UNINDENT .SS Control which rows are retrieved \- part 2 .sp Retrieve list of facilities that have a cost to members, and that fee is less than 1/50th of the monthly maintenance cost. Return id, name, cost and monthly\-maintenance. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT facid, name, membercost, monthlymaintenance FROM facilities WHERE membercost > 0 AND membercost < (monthlymaintenance / 50) .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Facility .select(Facility.facid, Facility.name, Facility.membercost, Facility.monthlymaintenance) .where( (Facility.membercost > 0) & (Facility.membercost < (Facility.monthlymaintenance / 50)))) .ft P .fi .UNINDENT .UNINDENT .SS Basic string searches .sp How can you produce a list of all facilities with the word \(aqTennis\(aq in their name? .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT * FROM facilities WHERE name ILIKE \(aq%tennis%\(aq; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Facility.select().where(Facility.name.contains(\(aqtennis\(aq)) # OR use the exponent operator. Note: you must include wildcards here: query = Facility.select().where(Facility.name ** \(aq%tennis%\(aq) .ft P .fi .UNINDENT .UNINDENT .SS Matching against multiple possible values .sp How can you retrieve the details of facilities with ID 1 and 5? Try to do it without using the OR operator. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT * FROM facilities WHERE facid IN (1, 5); .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Facility.select().where(Facility.facid.in_([1, 5])) # OR: query = Facility.select().where((Facility.facid == 1) | (Facility.facid == 5)) .ft P .fi .UNINDENT .UNINDENT .SS Classify results into buckets .sp How can you produce a list of facilities, with each labelled as \(aqcheap\(aq or \(aqexpensive\(aq depending on if their monthly maintenance cost is more than $100? Return the name and monthly maintenance of the facilities in question. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT name, CASE WHEN monthlymaintenance > 100 THEN \(aqexpensive\(aq ELSE \(aqcheap\(aq END FROM facilities; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C cost = Case(None, [(Facility.monthlymaintenance > 100, \(aqexpensive\(aq)], \(aqcheap\(aq) query = Facility.select(Facility.name, cost.alias(\(aqcost\(aq)) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 See documentation \fBCase\fP for more examples. .UNINDENT .UNINDENT .SS Working with dates .sp How can you produce a list of members who joined after the start of September 2012? Return the memid, surname, firstname, and joindate of the members in question. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT memid, surname, firstname, joindate FROM members WHERE joindate >= \(aq2012\-09\-01\(aq; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Member .select(Member.memid, Member.surname, Member.firstname, Member.joindate) .where(Member.joindate >= datetime.date(2012, 9, 1))) .ft P .fi .UNINDENT .UNINDENT .SS Removing duplicates, and ordering results .sp How can you produce an ordered list of the first 10 surnames in the members table? The list must not contain duplicates. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT DISTINCT surname FROM members ORDER BY surname LIMIT 10; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Member .select(Member.surname) .order_by(Member.surname) .limit(10) .distinct()) .ft P .fi .UNINDENT .UNINDENT .SS Combining results from multiple queries .sp You, for some reason, want a combined list of all surnames and all facility names. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT surname FROM members UNION SELECT name FROM facilities; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C lhs = Member.select(Member.surname) rhs = Facility.select(Facility.name) query = lhs | rhs .ft P .fi .UNINDENT .UNINDENT .sp Queries can be composed using the following operators: .INDENT 0.0 .IP \(bu 2 \fB|\fP \- \fBUNION\fP .IP \(bu 2 \fB+\fP \- \fBUNION ALL\fP .IP \(bu 2 \fB&\fP \- \fBINTERSECT\fP .IP \(bu 2 \fB\-\fP \- \fBEXCEPT\fP .UNINDENT .SS Simple aggregation .sp You\(aqd like to get the signup date of your last member. How can you retrieve this information? .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT MAX(join_date) FROM members; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Member.select(fn.MAX(Member.joindate)) # To conveniently obtain a single scalar value, use "scalar()": # max_join_date = query.scalar() .ft P .fi .UNINDENT .UNINDENT .SS More aggregation .sp You\(aqd like to get the first and last name of the last member(s) who signed up \- not just the date. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT firstname, surname, joindate FROM members WHERE joindate = (SELECT MAX(joindate) FROM members); .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Use "alias()" to reference the same table multiple times in a query. MemberAlias = Member.alias() subq = MemberAlias.select(fn.MAX(MemberAlias.joindate)) query = (Member .select(Member.firstname, Member.surname, Member.joindate) .where(Member.joindate == subq)) .ft P .fi .UNINDENT .UNINDENT .SS Joins and Subqueries .sp This category deals primarily with a foundational concept in relational database systems: joining. Joining allows you to combine related information from multiple tables to answer a question. This isn\(aqt just beneficial for ease of querying: a lack of join capability encourages denormalisation of data, which increases the complexity of keeping your data internally consistent. .sp This topic covers inner, outer, and self joins, as well as spending a little time on subqueries (queries within queries). .SS Retrieve the start times of members\(aq bookings .sp How can you produce a list of the start times for bookings by members named \(aqDavid Farrell\(aq? .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT starttime FROM bookings INNER JOIN members ON (bookings.memid = members.memid) WHERE surname = \(aqFarrell\(aq AND firstname = \(aqDavid\(aq; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Booking .select(Booking.starttime) .join(Member) .where((Member.surname == \(aqFarrell\(aq) & (Member.firstname == \(aqDavid\(aq))) .ft P .fi .UNINDENT .UNINDENT .SS Work out the start times of bookings for tennis courts .sp How can you produce a list of the start times for bookings for tennis courts, for the date \(aq2012\-09\-21\(aq? Return a list of start time and facility name pairings, ordered by the time. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT starttime, name FROM bookings INNER JOIN facilities ON (bookings.facid = facilities.facid) WHERE date_trunc(\(aqday\(aq, starttime) = \(aq2012\-09\-21\(aq:: date AND name ILIKE \(aqtennis%\(aq ORDER BY starttime, name; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Booking .select(Booking.starttime, Facility.name) .join(Facility) .where( (fn.date_trunc(\(aqday\(aq, Booking.starttime) == datetime.date(2012, 9, 21)) & Facility.name.startswith(\(aqTennis\(aq)) .order_by(Booking.starttime, Facility.name)) # To retrieve the joined facility\(aqs name when iterating: for booking in query: print(booking.starttime, booking.facility.name) .ft P .fi .UNINDENT .UNINDENT .SS Produce a list of all members who have recommended another member .sp How can you output a list of all members who have recommended another member? Ensure that there are no duplicates in the list, and that results are ordered by (surname, firstname). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT DISTINCT m.firstname, m.surname FROM members AS m2 INNER JOIN members AS m ON (m.memid = m2.recommendedby) ORDER BY m.surname, m.firstname; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C MA = Member.alias() query = (Member .select(Member.firstname, Member.surname) .join(MA, on=(MA.recommendedby == Member.memid)) .order_by(Member.surname, Member.firstname)) .ft P .fi .UNINDENT .UNINDENT .SS Produce a list of all members, along with their recommender .sp How can you output a list of all members, including the individual who recommended them (if any)? Ensure that results are ordered by (surname, firstname). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT m.firstname, m.surname, r.firstname, r.surname FROM members AS m LEFT OUTER JOIN members AS r ON (m.recommendedby = r.memid) ORDER BY m.surname, m.firstname .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C MA = Member.alias() query = (Member .select(Member.firstname, Member.surname, MA.firstname, MA.surname) .join(MA, JOIN.LEFT_OUTER, on=(Member.recommendedby == MA.memid)) .order_by(Member.surname, Member.firstname)) # To display the recommender\(aqs name when iterating: for m in query: print(m.firstname, m.surname) if m.recommendedby: print(\(aq \(aq, m.recommendedby.firstname, m.recommendedby.surname) .ft P .fi .UNINDENT .UNINDENT .SS Produce a list of all members who have used a tennis court .sp How can you produce a list of all members who have used a tennis court? Include in your output the name of the court, and the name of the member formatted as a single column. Ensure no duplicate data, and order by the member name. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT DISTINCT m.firstname || \(aq \(aq || m.surname AS member, f.name AS facility FROM members AS m INNER JOIN bookings AS b ON (m.memid = b.memid) INNER JOIN facilities AS f ON (b.facid = f.facid) WHERE f.name LIKE \(aqTennis%\(aq ORDER BY member, facility; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C fullname = Member.firstname + \(aq \(aq + Member.surname query = (Member .select(fullname.alias(\(aqmember\(aq), Facility.name.alias(\(aqfacility\(aq)) .join(Booking) .join(Facility) .where(Facility.name.startswith(\(aqTennis\(aq)) .order_by(fullname, Facility.name) .distinct()) .ft P .fi .UNINDENT .UNINDENT .SS Produce a list of costly bookings .sp How can you produce a list of bookings on the day of 2012\-09\-14 which will cost the member (or guest) more than $30? Remember that guests have different costs to members (the listed costs are per half\-hour \(aqslot\(aq), and the guest user is always ID 0. Include in your output the name of the facility, the name of the member formatted as a single column, and the cost. Order by descending cost, and do not use any subqueries. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT m.firstname || \(aq \(aq || m.surname AS member, f.name AS facility, (CASE WHEN m.memid = 0 THEN f.guestcost * b.slots ELSE f.membercost * b.slots END) AS cost FROM members AS m INNER JOIN bookings AS b ON (m.memid = b.memid) INNER JOIN facilities AS f ON (b.facid = f.facid) WHERE (date_trunc(\(aqday\(aq, b.starttime) = \(aq2012\-09\-14\(aq) AND ((m.memid = 0 AND b.slots * f.guestcost > 30) OR (m.memid > 0 AND b.slots * f.membercost > 30)) ORDER BY cost DESC; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C cost = Case(Member.memid, ( (0, Booking.slots * Facility.guestcost), ), (Booking.slots * Facility.membercost)) fullname = Member.firstname + \(aq \(aq + Member.surname query = (Member .select(fullname.alias(\(aqmember\(aq), Facility.name.alias(\(aqfacility\(aq), cost.alias(\(aqcost\(aq)) .join(Booking) .join(Facility) .where( (fn.date_trunc(\(aqday\(aq, Booking.starttime) == datetime.date(2012, 9, 14)) & (cost > 30)) .order_by(SQL(\(aqcost\(aq).desc())) # To iterate over the results, it might be easiest to use namedtuples: for row in query.namedtuples(): print(row.member, row.facility, row.cost) .ft P .fi .UNINDENT .UNINDENT .SS Produce a list of all members, along with their recommender, using no joins. .sp How can you output a list of all members, including the individual who recommended them (if any), without using any joins? Ensure that there are no duplicates in the list, and that each firstname + surname pairing is formatted as a column and ordered. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT DISTINCT m.firstname || \(aq \(aq || m.surname AS member, (SELECT r.firstname || \(aq \(aq || r.surname FROM cd.members AS r WHERE m.recommendedby = r.memid) AS recommended FROM members AS m ORDER BY member; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C MA = Member.alias() subq = (MA .select(MA.firstname + \(aq \(aq + MA.surname) .where(Member.recommendedby == MA.memid)) query = (Member .select(fullname.alias(\(aqmember\(aq), subq.alias(\(aqrecommended\(aq)) .order_by(fullname)) .ft P .fi .UNINDENT .UNINDENT .SS Produce a list of costly bookings, using a subquery .sp The "Produce a list of costly bookings" exercise contained some messy logic: we had to calculate the booking cost in both the WHERE clause and the CASE statement. Try to simplify this calculation using subqueries. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT member, facility, cost from ( SELECT m.firstname || \(aq \(aq || m.surname as member, f.name as facility, CASE WHEN m.memid = 0 THEN b.slots * f.guestcost ELSE b.slots * f.membercost END AS cost FROM members AS m INNER JOIN bookings AS b ON m.memid = b.memid INNER JOIN facilities AS f ON b.facid = f.facid WHERE date_trunc(\(aqday\(aq, b.starttime) = \(aq2012\-09\-14\(aq ) as bookings WHERE cost > 30 ORDER BY cost DESC; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C cost = Case(Member.memid, ( (0, Booking.slots * Facility.guestcost), ), (Booking.slots * Facility.membercost)) iq = (Member .select(fullname.alias(\(aqmember\(aq), Facility.name.alias(\(aqfacility\(aq), cost.alias(\(aqcost\(aq)) .join(Booking) .join(Facility) .where(fn.date_trunc(\(aqday\(aq, Booking.starttime) == datetime.date(2012, 9, 14))) query = (Member .select(iq.c.member, iq.c.facility, iq.c.cost) .from_(iq) .where(iq.c.cost > 30) .order_by(SQL(\(aqcost\(aq).desc())) # To iterate, try using dicts: for row in query.dicts(): print(row[\(aqmember\(aq], row[\(aqfacility\(aq], row[\(aqcost\(aq]) .ft P .fi .UNINDENT .UNINDENT .SS Modifying Data .sp Querying data is all well and good, but at some point you\(aqre probably going to want to put data into your database! This section deals with inserting, updating, and deleting information. Operations that alter your data like this are collectively known as Data Manipulation Language, or DML. .sp In previous sections, we returned to you the results of the query you\(aqve performed. Since modifications like the ones we\(aqre making in this section don\(aqt return any query results, we instead show you the updated content of the table you\(aqre supposed to be working on. .SS Insert some data into a table .sp The club is adding a new facility \- a spa. We need to add it into the facilities table. Use the following values: facid: 9, Name: \(aqSpa\(aq, membercost: 20, guestcost: 30, initialoutlay: 100000, monthlymaintenance: 800 .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C INSERT INTO "facilities" ("facid", "name", "membercost", "guestcost", "initialoutlay", "monthlymaintenance") VALUES (9, \(aqSpa\(aq, 20, 30, 100000, 800) .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C res = Facility.insert({ Facility.facid: 9, Facility.name: \(aqSpa\(aq, Facility.membercost: 20, Facility.guestcost: 30, Facility.initialoutlay: 100000, Facility.monthlymaintenance: 800}).execute() # OR: res = (Facility .insert(facid=9, name=\(aqSpa\(aq, membercost=20, guestcost=30, initialoutlay=100000, monthlymaintenance=800) .execute()) .ft P .fi .UNINDENT .UNINDENT .SS Insert multiple rows of data into a table .sp In the previous exercise, you learned how to add a facility. Now you\(aqre going to add multiple facilities in one command. Use the following values: .sp facid: 9, Name: \(aqSpa\(aq, membercost: 20, guestcost: 30, initialoutlay: 100000, monthlymaintenance: 800. .sp facid: 10, Name: \(aqSquash Court 2\(aq, membercost: 3.5, guestcost: 17.5, initialoutlay: 5000, monthlymaintenance: 80. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \-\- see above \-\- .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C data = [ {\(aqfacid\(aq: 9, \(aqname\(aq: \(aqSpa\(aq, \(aqmembercost\(aq: 20, \(aqguestcost\(aq: 30, \(aqinitialoutlay\(aq: 100000, \(aqmonthlymaintenance\(aq: 800}, {\(aqfacid\(aq: 10, \(aqname\(aq: \(aqSquash Court 2\(aq, \(aqmembercost\(aq: 3.5, \(aqguestcost\(aq: 17.5, \(aqinitialoutlay\(aq: 5000, \(aqmonthlymaintenance\(aq: 80}] res = Facility.insert_many(data).execute() .ft P .fi .UNINDENT .UNINDENT .SS Insert calculated data into a table .sp Let\(aqs try adding the spa to the facilities table again. This time, though, we want to automatically generate the value for the next facid, rather than specifying it as a constant. Use the following values for everything else: Name: \(aqSpa\(aq, membercost: 20, guestcost: 30, initialoutlay: 100000, monthlymaintenance: 800. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C INSERT INTO "facilities" ("facid", "name", "membercost", "guestcost", "initialoutlay", "monthlymaintenance") SELECT (SELECT (MAX("facid") + 1) FROM "facilities") AS _, \(aqSpa\(aq, 20, 30, 100000, 800; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C maxq = Facility.select(fn.MAX(Facility.facid) + 1) subq = Select(columns=(maxq, \(aqSpa\(aq, 20, 30, 100000, 800)) res = Facility.insert_from(subq, Facility._meta.sorted_fields).execute() .ft P .fi .UNINDENT .UNINDENT .SS Update some existing data .sp We made a mistake when entering the data for the second tennis court. The initial outlay was 10000 rather than 8000: you need to alter the data to fix the error. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C UPDATE facilities SET initialoutlay = 10000 WHERE name = \(aqTennis Court 2\(aq; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C res = (Facility .update({Facility.initialoutlay: 10000}) .where(Facility.name == \(aqTennis Court 2\(aq) .execute()) # OR: res = (Facility .update(initialoutlay=10000) .where(Facility.name == \(aqTennis Court 2\(aq) .execute()) .ft P .fi .UNINDENT .UNINDENT .SS Update multiple rows and columns at the same time .sp We want to increase the price of the tennis courts for both members and guests. Update the costs to be 6 for members, and 30 for guests. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C UPDATE facilities SET membercost=6, guestcost=30 WHERE name ILIKE \(aqTennis%\(aq; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C nrows = (Facility .update(membercost=6, guestcost=30) .where(Facility.name.startswith(\(aqTennis\(aq)) .execute()) .ft P .fi .UNINDENT .UNINDENT .SS Update a row based on the contents of another row .sp We want to alter the price of the second tennis court so that it costs 10% more than the first one. Try to do this without using constant values for the prices, so that we can reuse the statement if we want to. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C UPDATE facilities SET membercost = (SELECT membercost * 1.1 FROM facilities WHERE facid = 0), guestcost = (SELECT guestcost * 1.1 FROM facilities WHERE facid = 0) WHERE facid = 1; \-\- OR \-\- WITH new_prices (nmc, ngc) AS ( SELECT membercost * 1.1, guestcost * 1.1 FROM facilities WHERE name = \(aqTennis Court 1\(aq) UPDATE facilities SET membercost = new_prices.nmc, guestcost = new_prices.ngc FROM new_prices WHERE name = \(aqTennis Court 2\(aq .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C sq1 = Facility.select(Facility.membercost * 1.1).where(Facility.facid == 0) sq2 = Facility.select(Facility.guestcost * 1.1).where(Facility.facid == 0) res = (Facility .update(membercost=sq1, guestcost=sq2) .where(Facility.facid == 1) .execute()) # OR: cte = (Facility .select(Facility.membercost * 1.1, Facility.guestcost * 1.1) .where(Facility.name == \(aqTennis Court 1\(aq) .cte(\(aqnew_prices\(aq, columns=(\(aqnmc\(aq, \(aqngc\(aq))) res = (Facility .update(membercost=SQL(\(aqnew_prices.nmc\(aq), guestcost=SQL(\(aqnew_prices.ngc\(aq)) .with_cte(cte) .from_(cte) .where(Facility.name == \(aqTennis Court 2\(aq) .execute()) .ft P .fi .UNINDENT .UNINDENT .SS Delete all bookings .sp As part of a clearout of our database, we want to delete all bookings from the bookings table. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C DELETE FROM bookings; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C nrows = Booking.delete().execute() .ft P .fi .UNINDENT .UNINDENT .SS Delete a member from the cd.members table .sp We want to remove member 37, who has never made a booking, from our database. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C DELETE FROM members WHERE memid = 37; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C nrows = Member.delete().where(Member.memid == 37).execute() .ft P .fi .UNINDENT .UNINDENT .SS Delete based on a subquery .sp How can we make that more general, to delete all members who have never made a booking? .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C DELETE FROM members WHERE NOT EXISTS ( SELECT * FROM bookings WHERE bookings.memid = members.memid); .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C subq = Booking.select().where(Booking.member == Member.memid) nrows = Member.delete().where(~fn.EXISTS(subq)).execute() .ft P .fi .UNINDENT .UNINDENT .SS Aggregation .sp Aggregation is one of those capabilities that really make you appreciate the power of relational database systems. It allows you to move beyond merely persisting your data, into the realm of asking truly interesting questions that can be used to inform decision making. This category covers aggregation at length, making use of standard grouping as well as more recent window functions. .SS Count the number of facilities .sp For our first foray into aggregates, we\(aqre going to stick to something simple. We want to know how many facilities exist \- simply produce a total count. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT COUNT(facid) FROM facilities; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Facility.select(fn.COUNT(Facility.facid)) count = query.scalar() # OR: count = Facility.select().count() .ft P .fi .UNINDENT .UNINDENT .SS Count the number of expensive facilities .sp Produce a count of the number of facilities that have a cost to guests of 10 or more. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT COUNT(facid) FROM facilities WHERE guestcost >= 10 .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Facility.select(fn.COUNT(Facility.facid)).where(Facility.guestcost >= 10) count = query.scalar() # OR: # count = Facility.select().where(Facility.guestcost >= 10).count() .ft P .fi .UNINDENT .UNINDENT .SS Count the number of recommendations each member makes. .sp Produce a count of the number of recommendations each member has made. Order by member ID. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT recommendedby, COUNT(memid) FROM members WHERE recommendedby IS NOT NULL GROUP BY recommendedby ORDER BY recommendedby .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Member .select(Member.recommendedby, fn.COUNT(Member.memid)) .where(Member.recommendedby.is_null(False)) .group_by(Member.recommendedby) .order_by(Member.recommendedby)) .ft P .fi .UNINDENT .UNINDENT .SS List the total slots booked per facility .sp Produce a list of the total number of slots booked per facility. For now, just produce an output table consisting of facility id and slots, sorted by facility id. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT facid, SUM(slots) FROM bookings GROUP BY facid ORDER BY facid; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Booking .select(Booking.facid, fn.SUM(Booking.slots)) .group_by(Booking.facid) .order_by(Booking.facid)) .ft P .fi .UNINDENT .UNINDENT .SS List the total slots booked per facility in a given month .sp Produce a list of the total number of slots booked per facility in the month of September 2012. Produce an output table consisting of facility id and slots, sorted by the number of slots. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT facid, SUM(slots) FROM bookings WHERE (date_trunc(\(aqmonth\(aq, starttime) = \(aq2012\-09\-01\(aq::dates) GROUP BY facid ORDER BY SUM(slots) .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Booking .select(Booking.facility, fn.SUM(Booking.slots)) .where(fn.date_trunc(\(aqmonth\(aq, Booking.starttime) == datetime.date(2012, 9, 1)) .group_by(Booking.facility) .order_by(fn.SUM(Booking.slots))) .ft P .fi .UNINDENT .UNINDENT .SS List the total slots booked per facility per month .sp Produce a list of the total number of slots booked per facility per month in the year of 2012. Produce an output table consisting of facility id and slots, sorted by the id and month. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT facid, date_part(\(aqmonth\(aq, starttime), SUM(slots) FROM bookings WHERE date_part(\(aqyear\(aq, starttime) = 2012 GROUP BY facid, date_part(\(aqmonth\(aq, starttime) ORDER BY facid, date_part(\(aqmonth\(aq, starttime) .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C month = fn.date_part(\(aqmonth\(aq, Booking.starttime) query = (Booking .select(Booking.facility, month, fn.SUM(Booking.slots)) .where(fn.date_part(\(aqyear\(aq, Booking.starttime) == 2012) .group_by(Booking.facility, month) .order_by(Booking.facility, month)) .ft P .fi .UNINDENT .UNINDENT .SS Find the count of members who have made at least one booking .sp Find the total number of members who have made at least one booking. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT COUNT(DISTINCT memid) FROM bookings \-\- OR \-\- SELECT COUNT(1) FROM (SELECT DISTINCT memid FROM bookings) AS _ .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Booking.select(fn.COUNT(Booking.member.distinct())) # OR: query = Booking.select(Booking.member).distinct() count = query.count() # count() wraps in SELECT COUNT(1) FROM (...) .ft P .fi .UNINDENT .UNINDENT .SS List facilities with more than 1000 slots booked .sp Produce a list of facilities with more than 1000 slots booked. Produce an output table consisting of facility id and hours, sorted by facility id. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT facid, SUM(slots) FROM bookings GROUP BY facid HAVING SUM(slots) > 1000 ORDER BY facid; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Booking .select(Booking.facility, fn.SUM(Booking.slots)) .group_by(Booking.facility) .having(fn.SUM(Booking.slots) > 1000) .order_by(Booking.facility)) .ft P .fi .UNINDENT .UNINDENT .SS Find the total revenue of each facility .sp Produce a list of facilities along with their total revenue. The output table should consist of facility name and revenue, sorted by revenue. Remember that there\(aqs a different cost for guests and members! .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT f.name, SUM(b.slots * ( CASE WHEN b.memid = 0 THEN f.guestcost ELSE f.membercost END)) AS revenue FROM bookings AS b INNER JOIN facilities AS f ON b.facid = f.facid GROUP BY f.name ORDER BY revenue; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C revenue = fn.SUM(Booking.slots * Case(None, ( (Booking.member == 0, Facility.guestcost), ), Facility.membercost)) query = (Facility .select(Facility.name, revenue.alias(\(aqrevenue\(aq)) .join(Booking) .group_by(Facility.name) .order_by(SQL(\(aqrevenue\(aq))) .ft P .fi .UNINDENT .UNINDENT .SS Find facilities with a total revenue less than 1000 .sp Produce a list of facilities with a total revenue less than 1000. Produce an output table consisting of facility name and revenue, sorted by revenue. Remember that there\(aqs a different cost for guests and members! .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT f.name, SUM(b.slots * ( CASE WHEN b.memid = 0 THEN f.guestcost ELSE f.membercost END)) AS revenue FROM bookings AS b INNER JOIN facilities AS f ON b.facid = f.facid GROUP BY f.name HAVING SUM(b.slots * ...) < 1000 ORDER BY revenue; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Same definition as previous example. revenue = fn.SUM(Booking.slots * Case(None, ( (Booking.member == 0, Facility.guestcost), ), Facility.membercost)) query = (Facility .select(Facility.name, revenue.alias(\(aqrevenue\(aq)) .join(Booking) .group_by(Facility.name) .having(revenue < 1000) .order_by(SQL(\(aqrevenue\(aq))) .ft P .fi .UNINDENT .UNINDENT .SS Output the facility id that has the highest number of slots booked .sp Output the facility id that has the highest number of slots booked. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT facid, SUM(slots) FROM bookings GROUP BY facid ORDER BY SUM(slots) DESC LIMIT 1 .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Booking .select(Booking.facility, fn.SUM(Booking.slots)) .group_by(Booking.facility) .order_by(fn.SUM(Booking.slots).desc()) .limit(1)) # Retrieve multiple scalar values by calling scalar() with as_tuple=True. facid, nslots = query.scalar(as_tuple=True) .ft P .fi .UNINDENT .UNINDENT .SS List the total slots booked per facility per month, part 2 .sp Produce a list of the total number of slots booked per facility per month in the year of 2012. In this version, include output rows containing totals for all months per facility, and a total for all months for all facilities. The output table should consist of facility id, month and slots, sorted by the id and month. When calculating the aggregated values for all months and all facids, return null values in the month and facid columns. .sp Postgres ONLY. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT facid, date_part(\(aqmonth\(aq, starttime), SUM(slots) FROM booking WHERE date_part(\(aqyear\(aq, starttime) = 2012 GROUP BY ROLLUP(facid, date_part(\(aqmonth\(aq, starttime)) ORDER BY facid, date_part(\(aqmonth\(aq, starttime) .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C month = fn.date_part(\(aqmonth\(aq, Booking.starttime) query = (Booking .select(Booking.facility, month.alias(\(aqmonth\(aq), fn.SUM(Booking.slots)) .where(fn.date_part(\(aqyear\(aq, Booking.starttime) == 2012) .group_by(fn.ROLLUP(Booking.facility, month)) .order_by(Booking.facility, month)) .ft P .fi .UNINDENT .UNINDENT .SS List the total hours booked per named facility .sp Produce a list of the total number of hours booked per facility, remembering that a slot lasts half an hour. The output table should consist of the facility id, name, and hours booked, sorted by facility id. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT f.facid, f.name, SUM(b.slots) * .5 FROM facilities AS f INNER JOIN bookings AS b ON (f.facid = b.facid) GROUP BY f.facid, f.name ORDER BY f.facid .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Facility .select(Facility.facid, Facility.name, fn.SUM(Booking.slots) * .5) .join(Booking) .group_by(Facility.facid, Facility.name) .order_by(Facility.facid)) .ft P .fi .UNINDENT .UNINDENT .SS List each member\(aqs first booking after September 1st 2012 .sp Produce a list of each member name, id, and their first booking after September 1st 2012. Order by member ID. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT m.surname, m.firstname, m.memid, min(b.starttime) as starttime FROM members AS m INNER JOIN bookings AS b ON b.memid = m.memid WHERE starttime >= \(aq2012\-09\-01\(aq GROUP BY m.surname, m.firstname, m.memid ORDER BY m.memid; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Member .select(Member.surname, Member.firstname, Member.memid, fn.MIN(Booking.starttime).alias(\(aqstarttime\(aq)) .join(Booking) .where(Booking.starttime >= datetime.date(2012, 9, 1)) .group_by(Member.surname, Member.firstname, Member.memid) .order_by(Member.memid)) .ft P .fi .UNINDENT .UNINDENT .SS Produce a list of member names, with each row containing the total member count .sp Produce a list of member names, with each row containing the total member count. Order by join date. .sp Postgres ONLY (as written). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT COUNT(*) OVER(), firstname, surname FROM members ORDER BY joindate .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Member .select(fn.COUNT(Member.memid).over(), Member.firstname, Member.surname) .order_by(Member.joindate)) .ft P .fi .UNINDENT .UNINDENT .SS Produce a numbered list of members .sp Produce a monotonically increasing numbered list of members, ordered by their date of joining. Remember that member IDs are not guaranteed to be sequential. .sp Postgres ONLY (as written). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT row_number() OVER (ORDER BY joindate), firstname, surname FROM members ORDER BY joindate; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Member .select(fn.row_number().over(order_by=[Member.joindate]), Member.firstname, Member.surname) .order_by(Member.joindate)) .ft P .fi .UNINDENT .UNINDENT .SS Output the facility id that has the highest number of slots booked, again .sp Output the facility id that has the highest number of slots booked. Ensure that in the event of a tie, all tieing results get output. .sp Postgres ONLY (as written). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT facid, total FROM ( SELECT facid, SUM(slots) AS total, rank() OVER (order by SUM(slots) DESC) AS rank FROM bookings GROUP BY facid ) AS ranked WHERE rank = 1 .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C rank = fn.rank().over(order_by=[fn.SUM(Booking.slots).desc()]) subq = (Booking .select(Booking.facility, fn.SUM(Booking.slots).alias(\(aqtotal\(aq), rank.alias(\(aqrank\(aq)) .group_by(Booking.facility)) # Here we use a plain Select() to create our query. query = (Select(columns=[subq.c.facid, subq.c.total]) .from_(subq) .where(subq.c.rank == 1) .bind(db)) # We must bind() it to the database. # To iterate over the query results: for facid, total in query.tuples(): print(facid, total) .ft P .fi .UNINDENT .UNINDENT .SS Rank members by (rounded) hours used .sp Produce a list of members, along with the number of hours they\(aqve booked in facilities, rounded to the nearest ten hours. Rank them by this rounded figure, producing output of first name, surname, rounded hours, rank. Sort by rank, surname, and first name. .sp Postgres ONLY (as written). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT firstname, surname, ((SUM(bks.slots)+10)/20)*10 as hours, rank() over (order by ((sum(bks.slots)+10)/20)*10 desc) as rank FROM members AS mems INNER JOIN bookings AS bks ON mems.memid = bks.memid GROUP BY mems.memid ORDER BY rank, surname, firstname; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C hours = ((fn.SUM(Booking.slots) + 10) / 20) * 10 query = (Member .select(Member.firstname, Member.surname, hours.alias(\(aqhours\(aq), fn.rank().over(order_by=[hours.desc()]).alias(\(aqrank\(aq)) .join(Booking) .group_by(Member.memid) .order_by(SQL(\(aqrank\(aq), Member.surname, Member.firstname)) .ft P .fi .UNINDENT .UNINDENT .SS Find the top three revenue generating facilities .sp Produce a list of the top three revenue generating facilities (including ties). Output facility name and rank, sorted by rank and facility name. .sp Postgres ONLY (as written). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT name, rank FROM ( SELECT f.name, RANK() OVER (ORDER BY SUM( CASE WHEN memid = 0 THEN slots * f.guestcost ELSE slots * f.membercost END) DESC) AS rank FROM bookings INNER JOIN facilities AS f ON bookings.facid = f.facid GROUP BY f.name) AS subq WHERE rank <= 3 ORDER BY rank; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C total_cost = fn.SUM(Case(None, ( (Booking.member == 0, Booking.slots * Facility.guestcost), ), (Booking.slots * Facility.membercost))) subq = (Facility .select(Facility.name, fn.RANK().over(order_by=[total_cost.desc()]).alias(\(aqrank\(aq)) .join(Booking) .group_by(Facility.name)) query = (Select(columns=[subq.c.name, subq.c.rank]) .from_(subq) .where(subq.c.rank <= 3) .order_by(subq.c.rank) .bind(db)) # Here again we used plain Select, and call bind(). .ft P .fi .UNINDENT .UNINDENT .SS Classify facilities by value .sp Classify facilities into equally sized groups of high, average, and low based on their revenue. Order by classification and facility name. .sp Postgres ONLY (as written). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT name, CASE class WHEN 1 THEN \(aqhigh\(aq WHEN 2 THEN \(aqaverage\(aq ELSE \(aqlow\(aq END FROM ( SELECT f.name, ntile(3) OVER (ORDER BY SUM( CASE WHEN memid = 0 THEN slots * f.guestcost ELSE slots * f.membercost END) DESC) AS class FROM bookings INNER JOIN facilities AS f ON bookings.facid = f.facid GROUP BY f.name ) AS subq ORDER BY class, name; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C cost = fn.SUM(Case(None, ( (Booking.member == 0, Booking.slots * Facility.guestcost), ), (Booking.slots * Facility.membercost))) subq = (Facility .select(Facility.name, fn.NTILE(3).over(order_by=[cost.desc()]).alias(\(aqklass\(aq)) .join(Booking) .group_by(Facility.name)) klass_case = Case(subq.c.klass, [(1, \(aqhigh\(aq), (2, \(aqaverage\(aq)], \(aqlow\(aq) query = (Select(columns=[subq.c.name, klass_case]) .from_(subq) .order_by(subq.c.klass, subq.c.name) .bind(db)) .ft P .fi .UNINDENT .UNINDENT .SS Recursion .sp Common Table Expressions allow us to, effectively, create our own temporary tables for the duration of a query \- they\(aqre largely a convenience to help us make more readable SQL. Using the WITH RECURSIVE modifier, however, it\(aqs possible for us to create recursive queries. This is enormously advantageous for working with tree and graph\-structured data \- imagine retrieving all of the relations of a graph node to a given depth, for example. .SS Find the upward recommendation chain for member ID 27 .sp Find the upward recommendation chain for member ID 27: that is, the member who recommended them, and the member who recommended that member, and so on. Return member ID, first name, and surname. Order by descending member id. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C WITH RECURSIVE recommenders(recommender) as ( SELECT recommendedby FROM members WHERE memid = 27 UNION ALL SELECT mems.recommendedby FROM recommenders recs INNER JOIN members AS mems ON mems.memid = recs.recommender ) SELECT recs.recommender, mems.firstname, mems.surname FROM recommenders AS recs INNER JOIN members AS mems ON recs.recommender = mems.memid ORDER By memid DESC; .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Base\-case of recursive CTE. Get member recommender where memid=27. base = (Member .select(Member.recommendedby) .where(Member.memid == 27) .cte(\(aqrecommenders\(aq, recursive=True, columns=(\(aqrecommender\(aq,))) # Recursive term of CTE. Get recommender of previous recommender. MA = Member.alias() recursive = (MA .select(MA.recommendedby) .join(base, on=(MA.memid == base.c.recommender))) # Combine the base\-case with the recursive term. cte = base.union_all(recursive) # Select from the recursive CTE, joining on member to get name info. query = (cte .select_from(cte.c.recommender, Member.firstname, Member.surname) .join(Member, on=(cte.c.recommender == Member.memid)) .order_by(Member.memid.desc())) .ft P .fi .UNINDENT .UNINDENT .SS Query Builder .sp Peewee\(aqs high\-level \fBModel\fP and \fBField\fP APIs are built upon lower\-level \fBTable\fP and \fBColumn\fP counterparts. While these lower\-level APIs are not documented in as much detail as their high\-level counterparts, this document will present an overview with examples that should hopefully allow you to experiment. .sp We\(aqll use the following schema: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C CREATE TABLE "person" ( "id" INTEGER NOT NULL PRIMARY KEY, "first" TEXT NOT NULL, "last" TEXT NOT NULL); CREATE TABLE "note" ( "id" INTEGER NOT NULL PRIMARY KEY, "person_id" INTEGER NOT NULL, "content" TEXT NOT NULL, "timestamp" DATETIME NOT NULL, FOREIGN KEY ("person_id") REFERENCES "person" ("id")); CREATE TABLE "reminder" ( "id" INTEGER NOT NULL PRIMARY KEY, "note_id" INTEGER NOT NULL, "alarm" DATETIME NOT NULL, FOREIGN KEY ("note_id") REFERENCES "note" ("id")); .ft P .fi .UNINDENT .UNINDENT .SS Declaring tables .sp There are two ways we can declare \fBTable\fP objects for working with these tables: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Explicitly declare columns Person = Table(\(aqperson\(aq, (\(aqid\(aq, \(aqfirst\(aq, \(aqlast\(aq)) Note = Table(\(aqnote\(aq, (\(aqid\(aq, \(aqperson_id\(aq, \(aqcontent\(aq, \(aqtimestamp\(aq)) # Do not declare columns, they will be accessed using magic ".c" attribute Reminder = Table(\(aqreminder\(aq) .ft P .fi .UNINDENT .UNINDENT .sp Typically we will want to \fBbind()\fP our tables to a database. This saves us having to pass the database explicitly every time we wish to execute a query on the table: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C db = SqliteDatabase(\(aqmy_app.db\(aq) Person = Person.bind(db) Note = Note.bind(db) Reminder = Reminder.bind(db) .ft P .fi .UNINDENT .UNINDENT .SS Select queries .sp To select the first three notes and print their content, we can write: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = Note.select().order_by(Note.timestamp).limit(3) for note_dict in query: print(note_dict[\(aqcontent\(aq]) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 By default, rows will be returned as dictionaries. You can use the \fBtuples()\fP, \fBnamedtuples()\fP or \fBobjects()\fP methods to specify a different container for the row data, if you wish. .UNINDENT .UNINDENT .sp Because we didn\(aqt specify any columns, all the columns we defined in the note\(aqs \fBTable\fP constructor will be selected. This won\(aqt work for Reminder, as we didn\(aqt specify any columns at all. .sp To select all notes published in 2018 along with the name of the creator, we will use \fBjoin()\fP\&. We\(aqll also request that rows be returned as \fInamedtuple\fP objects: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Note .select(Note.content, Note.timestamp, Person.first, Person.last) .join(Person, on=(Note.person_id == Person.id)) .where(Note.timestamp >= datetime.date(2018, 1, 1)) .order_by(Note.timestamp) .namedtuples()) for row in query: print(row.timestamp, \(aq\-\(aq, row.content, \(aq\-\(aq, row.first, row.last) .ft P .fi .UNINDENT .UNINDENT .sp Let\(aqs query for the most prolific people, that is, get the people who have created the most notes. This introduces calling a SQL function (COUNT), which is accomplished using the \fBfn\fP object: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C name = Person.first.concat(\(aq \(aq).concat(Person.last) query = (Person .select(name.alias(\(aqname\(aq), fn.COUNT(Note.id).alias(\(aqcount\(aq)) .join(Note, JOIN.LEFT_OUTER, on=(Note.person_id == Person.id)) .group_by(name) .order_by(fn.COUNT(Note.id).desc())) for row in query: print(row[\(aqname\(aq], row[\(aqcount\(aq]) .ft P .fi .UNINDENT .UNINDENT .sp There are a couple things to note in the above query: .INDENT 0.0 .IP \(bu 2 We store an expression in a variable (\fBname\fP), then use it in the query. .IP \(bu 2 We call SQL functions using \fBfn.(...)\fP passing arguments as if it were a normal Python function. .IP \(bu 2 The \fBalias()\fP method is used to specify the name used for a column or calculation. .UNINDENT .sp As a more complex example, we\(aqll generate a list of all people and the contents and timestamp of their most recently\-published note. To do this, we will end up using the Note table twice in different contexts within the same query, which will require us to use a table alias. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Start with the query that calculates the timestamp of the most recent # note for each person. NA = Note.alias(\(aqna\(aq) max_note = (NA .select(NA.person_id, fn.MAX(NA.timestamp).alias(\(aqmax_ts\(aq)) .group_by(NA.person_id) .alias(\(aqmax_note\(aq)) # Now we\(aqll select from the note table, joining on both the subquery and # on the person table to construct the result set. query = (Note .select(Note.content, Note.timestamp, Person.first, Person.last) .join(max_note, on=((max_note.c.person_id == Note.person_id) & (max_note.c.max_ts == Note.timestamp))) .join(Person, on=(Note.person_id == Person.id)) .order_by(Person.first, Person.last)) for row in query.namedtuples(): print(row.first, row.last, \(aq:\(aq, row.timestamp, \(aq\-\(aq, row.content) .ft P .fi .UNINDENT .UNINDENT .sp In the join predicate for the join on the \fImax_note\fP subquery, we can reference columns in the subquery using the magical ".c" attribute. So, \fImax_note.c.max_ts\fP is translated into "the max_ts column value from the max_note subquery". .sp We can also use the ".c" magic attribute to access columns on tables that do not explicitly define their columns, like we did with the Reminder table. Here\(aqs a simple query to get all reminders for today, along with their associated note content: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C today = datetime.date.today() tomorrow = today + datetime.timedelta(days=1) query = (Reminder .select(Reminder.c.alarm, Note.content) .join(Note, on=(Reminder.c.note_id == Note.id)) .where(Reminder.c.alarm.between(today, tomorrow)) .order_by(Reminder.c.alarm)) for row in query: print(row[\(aqalarm\(aq], row[\(aqcontent\(aq]) .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 The ".c" attribute will not work on tables that explicitly define their columns, to prevent confusion. .UNINDENT .UNINDENT .SS Insert queries .sp Inserting data is straightforward. We can specify data to \fBinsert()\fP in two different ways (in both cases, the ID of the new row is returned): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Using keyword arguments: zaizee_id = Person.insert(first=\(aqzaizee\(aq, last=\(aqcat\(aq).execute() # Using column: value mappings: Note.insert({ Note.person_id: zaizee_id, Note.content: \(aqmeeeeowwww\(aq, Note.timestamp: datetime.datetime.now()}).execute() .ft P .fi .UNINDENT .UNINDENT .sp It is easy to bulk\-insert data, just pass in either: .INDENT 0.0 .IP \(bu 2 A list of dictionaries (all must have the same keys/columns). .IP \(bu 2 A list of tuples, if the columns are specified explicitly. .UNINDENT .sp Examples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C people = [ {\(aqfirst\(aq: \(aqBob\(aq, \(aqlast\(aq: \(aqFoo\(aq}, {\(aqfirst\(aq: \(aqHerb\(aq, \(aqlast\(aq: \(aqBar\(aq}, {\(aqfirst\(aq: \(aqNuggie\(aq, \(aqlast\(aq: \(aqBar\(aq}] # Inserting multiple rows returns the ID of the last\-inserted row. last_id = Person.insert(people).execute() # We can also specify row tuples, so long as we tell Peewee which # columns the tuple values correspond to: people = [ (\(aqBob\(aq, \(aqFoo\(aq), (\(aqHerb\(aq, \(aqBar\(aq), (\(aqNuggie\(aq, \(aqBar\(aq)] Person.insert(people, columns=[Person.first, Person.last]).execute() .ft P .fi .UNINDENT .UNINDENT .SS Update queries .sp \fBupdate()\fP queries accept either keyword arguments or a dictionary mapping column to value, just like \fBinsert()\fP\&. .sp Examples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # "Bob" changed his last name from "Foo" to "Baze". nrows = (Person .update(last=\(aqBaze\(aq) .where((Person.first == \(aqBob\(aq) & (Person.last == \(aqFoo\(aq)) .execute()) # Use dictionary mapping column to value. nrows = (Person .update({Person.last: \(aqBaze\(aq}) .where((Person.first == \(aqBob\(aq) & (Person.last == \(aqFoo\(aq)) .execute()) .ft P .fi .UNINDENT .UNINDENT .sp You can also use expressions as the value to perform an atomic update. Imagine we have a \fIPageView\fP table and we need to atomically increment the page\-view count for some URL: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Do an atomic update: (PageView .update({PageView.count: PageView.count + 1}) .where(PageView.url == some_url) .execute()) .ft P .fi .UNINDENT .UNINDENT .SS Delete queries .sp \fBdelete()\fP queries are simplest of all, as they do not accept any arguments: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Delete all notes created before 2018, returning number deleted. n = Note.delete().where(Note.timestamp < datetime.date(2018, 1, 1)).execute() .ft P .fi .UNINDENT .UNINDENT .sp Because DELETE (and UPDATE) queries do not support joins, we can use subqueries to delete rows based on values in related tables. For example, here is how you would delete all notes by anyone whose last name is "Foo": .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Get the id of all people whose last name is "Foo". foo_people = Person.select(Person.id).where(Person.last == \(aqFoo\(aq) # Delete all notes by any person whose ID is in the previous query. Note.delete().where(Note.person_id.in_(foo_people)).execute() .ft P .fi .UNINDENT .UNINDENT .SS Query Objects .sp One of the fundamental limitations of the abstractions provided by Peewee 2.x was the absence of a class that represented a structured query with no relation to a given model class. .sp An example of this might be computing aggregate values over a subquery. For example, the \fBcount()\fP method, which returns the count of rows in an arbitrary query, is implemented by wrapping the query: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT COUNT(1) FROM (...) .ft P .fi .UNINDENT .UNINDENT .sp To accomplish this with Peewee, the implementation is written in this way: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C def count(query): # Select([source1, ... sourcen], [column1, ...columnn]) wrapped = Select(from_list=[query], columns=[fn.COUNT(SQL(\(aq1\(aq))]) curs = wrapped.tuples().execute(db) return curs[0][0] # Return first column from first row of result. .ft P .fi .UNINDENT .UNINDENT .sp We can actually express this more concisely using the \fBscalar()\fP method, which is suitable for returning values from aggregate queries: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C def count(query): wrapped = Select(from_list=[query], columns=[fn.COUNT(SQL(\(aq1\(aq))]) return wrapped.scalar(db) .ft P .fi .UNINDENT .UNINDENT .sp The query_examples document has a more complex example, in which we write a query for a facility with the highest number of available slots booked: .sp The SQL we wish to express is: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT facid, total FROM ( SELECT facid, SUM(slots) AS total, rank() OVER (order by SUM(slots) DESC) AS rank FROM bookings GROUP BY facid ) AS ranked WHERE rank = 1 .ft P .fi .UNINDENT .UNINDENT .sp We can express this fairly elegantly by using a plain \fBSelect\fP for the outer query: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Store rank expression in variable for readability. rank_expr = fn.rank().over(order_by=[fn.SUM(Booking.slots).desc()]) subq = (Booking .select(Booking.facility, fn.SUM(Booking.slots).alias(\(aqtotal\(aq), rank_expr.alias(\(aqrank\(aq)) .group_by(Booking.facility)) # Use a plain "Select" to create outer query. query = (Select(columns=[subq.c.facid, subq.c.total]) .from_(subq) .where(subq.c.rank == 1) .tuples()) # Iterate over the resulting facility ID(s) and total(s): for facid, total in query.execute(db): print(facid, total) .ft P .fi .UNINDENT .UNINDENT .sp For another example, let\(aqs create a recursive common table expression to calculate the first 10 fibonacci numbers: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C base = Select(columns=( Value(1).alias(\(aqn\(aq), Value(0).alias(\(aqfib_n\(aq), Value(1).alias(\(aqnext_fib_n\(aq))).cte(\(aqfibonacci\(aq, recursive=True) n = (base.c.n + 1).alias(\(aqn\(aq) recursive_term = Select(columns=( n, base.c.next_fib_n, base.c.fib_n + base.c.next_fib_n)).from_(base).where(n < 10) fibonacci = base.union_all(recursive_term) query = fibonacci.select_from(fibonacci.c.n, fibonacci.c.fib_n) results = list(query.execute(db)) # Generates the following result list: [{\(aqfib_n\(aq: 0, \(aqn\(aq: 1}, {\(aqfib_n\(aq: 1, \(aqn\(aq: 2}, {\(aqfib_n\(aq: 1, \(aqn\(aq: 3}, {\(aqfib_n\(aq: 2, \(aqn\(aq: 4}, {\(aqfib_n\(aq: 3, \(aqn\(aq: 5}, {\(aqfib_n\(aq: 5, \(aqn\(aq: 6}, {\(aqfib_n\(aq: 8, \(aqn\(aq: 7}, {\(aqfib_n\(aq: 13, \(aqn\(aq: 8}, {\(aqfib_n\(aq: 21, \(aqn\(aq: 9}, {\(aqfib_n\(aq: 34, \(aqn\(aq: 10}] .ft P .fi .UNINDENT .UNINDENT .SS More .sp For a description of the various classes used to describe a SQL AST, see the query builder API documentation\&. .sp If you\(aqre interested in learning more, you can also check out the \fI\%project source code\fP\&. .SS Hacks .sp Collected hacks using peewee. Have a cool hack you\(aqd like to share? Open \fI\%an issue on GitHub\fP or \fI\%contact me\fP\&. .SS Optimistic Locking .sp Optimistic locking is useful in situations where you might ordinarily use a \fISELECT FOR UPDATE\fP (or in SQLite, \fIBEGIN IMMEDIATE\fP). For example, you might fetch a user record from the database, make some modifications, then save the modified user record. Typically this scenario would require us to lock the user record for the duration of the transaction, from the moment we select it, to the moment we save our changes. .sp In optimistic locking, on the other hand, we do \fInot\fP acquire any lock and instead rely on an internal \fIversion\fP column in the row we\(aqre modifying. At read time, we see what version the row is currently at, and on save, we ensure that the update takes place only if the version is the same as the one we initially read. If the version is higher, then some other process must have snuck in and changed the row \-\- to save our modified version could result in the loss of important changes. .sp It\(aqs quite simple to implement optimistic locking in Peewee, here is a base class that you can use as a starting point: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from peewee import * class ConflictDetectedException(Exception): pass class BaseVersionedModel(Model): version = IntegerField(default=1, index=True) def save_optimistic(self): if not self.id: # This is a new record, so the default logic is to perform an # INSERT. Ideally your model would also have a unique # constraint that made it impossible for two INSERTs to happen # at the same time. return self.save() # Update any data that has changed and bump the version counter. field_data = dict(self.__data__) current_version = field_data.pop(\(aqversion\(aq, 1) self._populate_unsaved_relations(field_data) field_data = self._prune_fields(field_data, self.dirty_fields) if not field_data: raise ValueError(\(aqNo changes have been made.\(aq) ModelClass = type(self) field_data[\(aqversion\(aq] = ModelClass.version + 1 # Atomic increment. query = ModelClass.update(**field_data).where( (ModelClass.version == current_version) & (ModelClass.id == self.id)) if query.execute() == 0: # No rows were updated, indicating another process has saved # a new version. How you handle this situation is up to you, # but for simplicity I\(aqm just raising an exception. raise ConflictDetectedException() else: # Increment local version to match what is now in the db. self.version += 1 return True .ft P .fi .UNINDENT .UNINDENT .sp Here\(aqs an example of how this works. Let\(aqs assume we have the following model definition. Note that there\(aqs a unique constraint on the username \-\- this is important as it provides a way to prevent double\-inserts. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class User(BaseVersionedModel): username = CharField(unique=True) favorite_animal = CharField() .ft P .fi .UNINDENT .UNINDENT .sp Example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> u = User(username=\(aqcharlie\(aq, favorite_animal=\(aqcat\(aq) >>> u.save_optimistic() True >>> u.version 1 >>> u.save_optimistic() Traceback (most recent call last): File "", line 1, in File "x.py", line 18, in save_optimistic raise ValueError(\(aqNo changes have been made.\(aq) ValueError: No changes have been made. >>> u.favorite_animal = \(aqkitten\(aq >>> u.save_optimistic() True # Simulate a separate thread coming in and updating the model. >>> u2 = User.get(User.username == \(aqcharlie\(aq) >>> u2.favorite_animal = \(aqmacaw\(aq >>> u2.save_optimistic() True # Now, attempt to change and re\-save the original instance: >>> u.favorite_animal = \(aqlittle parrot\(aq >>> u.save_optimistic() Traceback (most recent call last): File "", line 1, in File "x.py", line 30, in save_optimistic raise ConflictDetectedException() ConflictDetectedException: current version is out of sync .ft P .fi .UNINDENT .UNINDENT .SS Top object per group .sp These examples describe several ways to query the single top item per group. For a thorough discuss of various techniques, check out my blog post \fI\%Querying the top item by group with Peewee ORM\fP\&. If you are interested in the more general problem of querying the top \fIN\fP items, see the section below \fI\%Top N objects per group\fP\&. .sp In these examples we will use the \fIUser\fP and \fITweet\fP models to find each user and their most\-recent tweet. .sp The most efficient method I found in my testing uses the \fBMAX()\fP aggregate function. .sp We will perform the aggregation in a non\-correlated subquery, so we can be confident this method will be performant. The idea is that we will select the posts, grouped by their author, whose timestamp is equal to the max observed timestamp for that user. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # When referencing a table multiple times, we\(aqll call Model.alias() to create # a secondary reference to the table. TweetAlias = Tweet.alias() # Create a subquery that will calculate the maximum Tweet created_date for each # user. subquery = (TweetAlias .select( TweetAlias.user, fn.MAX(TweetAlias.created_date).alias(\(aqmax_ts\(aq)) .group_by(TweetAlias.user) .alias(\(aqtweet_max_subquery\(aq)) # Query for tweets and join using the subquery to match the tweet\(aqs user # and created_date. query = (Tweet .select(Tweet, User) .join(User) .switch(Tweet) .join(subquery, on=( (Tweet.created_date == subquery.c.max_ts) & (Tweet.user == subquery.c.user_id)))) .ft P .fi .UNINDENT .UNINDENT .sp SQLite and MySQL are a bit more lax and permit grouping by a subset of the columns that are selected. This means we can do away with the subquery and express it quite concisely: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = (Tweet .select(Tweet, User) .join(User) .group_by(Tweet.user) .having(Tweet.created_date == fn.MAX(Tweet.created_date))) .ft P .fi .UNINDENT .UNINDENT .SS Top N objects per group .sp These examples describe several ways to query the top \fIN\fP items per group reasonably efficiently. For a thorough discussion of various techniques, check out my blog post \fI\%Querying the top N objects per group with Peewee ORM\fP\&. .sp In these examples we will use the \fIUser\fP and \fITweet\fP models to find each user and their three most\-recent tweets. .SS Postgres lateral joins .sp \fI\%Lateral joins\fP are a neat Postgres feature that allow reasonably efficient correlated subqueries. They are often described as SQL \fBfor each\fP loops. .sp The desired SQL is: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT * FROM (SELECT id, username FROM user) AS uq LEFT JOIN LATERAL (SELECT message, created_date FROM tweet WHERE (user_id = uq.id) ORDER BY created_date DESC LIMIT 3) AS pq ON true .ft P .fi .UNINDENT .UNINDENT .sp To accomplish this with peewee is quite straightforward: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C subq = (Tweet .select(Tweet.message, Tweet.created_date) .where(Tweet.user == User.id) .order_by(Tweet.created_date.desc()) .limit(3)) query = (User .select(User, subq.c.content, subq.c.created_date) .join(subq, JOIN.LEFT_LATERAL) .order_by(User.username, subq.c.created_date.desc())) # We queried from the "perspective" of user, so the rows are User instances # with the addition of a "content" and "created_date" attribute for each of # the (up\-to) 3 most\-recent tweets for each user. for row in query: print(row.username, row.content, row.created_date) .ft P .fi .UNINDENT .UNINDENT .sp To implement an equivalent query from the "perspective" of the Tweet model, we can instead write: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # subq is the same as the above example. subq = (Tweet .select(Tweet.message, Tweet.created_date) .where(Tweet.user == User.id) .order_by(Tweet.created_date.desc()) .limit(3)) query = (Tweet .select(User.username, subq.c.content, subq.c.created_date) .from_(User) .join(subq, JOIN.LEFT_LATERAL) .order_by(User.username, subq.c.created_date.desc())) # Each row is a "tweet" instance with an additional "username" attribute. # This will print the (up\-to) 3 most\-recent tweets from each user. for tweet in query: print(tweet.username, tweet.content, tweet.created_date) .ft P .fi .UNINDENT .UNINDENT .SS Window functions .sp \fI\%Window functions\fP, which are supported by peewee, provide scalable, efficient performance. .sp The desired SQL is: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C SELECT subq.message, subq.username FROM ( SELECT t2.message, t3.username, RANK() OVER ( PARTITION BY t2.user_id ORDER BY t2.created_date DESC ) AS rnk FROM tweet AS t2 INNER JOIN user AS t3 ON (t2.user_id = t3.id) ) AS subq WHERE (subq.rnk <= 3) .ft P .fi .UNINDENT .UNINDENT .sp To accomplish this with peewee, we will wrap the ranked Tweets in an outer query that performs the filtering. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C TweetAlias = Tweet.alias() # The subquery will select the relevant data from the Tweet and # User table, as well as ranking the tweets by user from newest # to oldest. subquery = (TweetAlias .select( TweetAlias.message, User.username, fn.RANK().over( partition_by=[TweetAlias.user], order_by=[TweetAlias.created_date.desc()]).alias(\(aqrnk\(aq)) .join(User, on=(TweetAlias.user == User.id)) .alias(\(aqsubq\(aq)) # Since we can\(aqt filter on the rank, we are wrapping it in a query # and performing the filtering in the outer query. query = (Tweet .select(subquery.c.message, subquery.c.username) .from_(subquery) .where(subquery.c.rnk <= 3)) .ft P .fi .UNINDENT .UNINDENT .SS Other methods .sp If you\(aqre not using Postgres, then unfortunately you\(aqre left with options that exhibit less\-than\-ideal performance. For a more complete overview of common methods, check out \fI\%this blog post\fP\&. Below I will summarize the approaches and the corresponding SQL. .sp Using \fBCOUNT\fP, we can get all tweets where there exist less than \fIN\fP tweets with more recent timestamps: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C TweetAlias = Tweet.alias() # Create a correlated subquery that calculates the number of # tweets with a higher (newer) timestamp than the tweet we\(aqre # looking at in the outer query. subquery = (TweetAlias .select(fn.COUNT(TweetAlias.id)) .where( (TweetAlias.created_date >= Tweet.created_date) & (TweetAlias.user == Tweet.user))) # Wrap the subquery and filter on the count. query = (Tweet .select(Tweet, User) .join(User) .where(subquery <= 3)) .ft P .fi .UNINDENT .UNINDENT .sp We can achieve similar results by doing a self\-join and performing the filtering in the \fBHAVING\fP clause: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C TweetAlias = Tweet.alias() # Use a self\-join and join predicates to count the number of # newer tweets. query = (Tweet .select(Tweet.id, Tweet.message, Tweet.user, User.username) .join(User) .switch(Tweet) .join(TweetAlias, on=( (TweetAlias.user == Tweet.user) & (TweetAlias.created_date >= Tweet.created_date))) .group_by(Tweet.id, Tweet.content, Tweet.user, User.username) .having(fn.COUNT(Tweet.id) <= 3)) .ft P .fi .UNINDENT .UNINDENT .sp The last example uses a \fBLIMIT\fP clause in a correlated subquery. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C TweetAlias = Tweet.alias() # The subquery here will calculate, for the user who created the # tweet in the outer loop, the three newest tweets. The expression # will evaluate to \(gaTrue\(ga if the outer\-loop tweet is in the set of # tweets represented by the inner query. query = (Tweet .select(Tweet, User) .join(User) .where(Tweet.id << ( TweetAlias .select(TweetAlias.id) .where(TweetAlias.user == Tweet.user) .order_by(TweetAlias.created_date.desc()) .limit(3)))) .ft P .fi .UNINDENT .UNINDENT .SS Writing custom functions with SQLite .sp SQLite is very easy to extend with custom functions written in Python, that are then callable from your SQL statements. By using the \fBSqliteExtDatabase\fP and the \fBfunc()\fP decorator, you can very easily define your own functions. .sp Here is an example function that generates a hashed version of a user\-supplied password. We can also use this to implement \fBlogin\fP functionality for matching a user and password. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from hashlib import sha1 from random import random from playhouse.sqlite_ext import SqliteExtDatabase db = SqliteExtDatabase(\(aqmy\-blog.db\(aq) def get_hexdigest(salt, raw_password): data = salt + raw_password return sha1(data.encode(\(aqutf8\(aq)).hexdigest() @db.func() def make_password(raw_password): salt = get_hexdigest(str(random()), str(random()))[:5] hsh = get_hexdigest(salt, raw_password) return \(aq%s$%s\(aq % (salt, hsh) @db.func() def check_password(raw_password, enc_password): salt, hsh = enc_password.split(\(aq$\(aq, 1) return hsh == get_hexdigest(salt, raw_password) .ft P .fi .UNINDENT .UNINDENT .sp Here is how you can use the function to add a new user, storing a hashed password: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C query = User.insert( username=\(aqcharlie\(aq, password=fn.make_password(\(aqtesting\(aq)).execute() .ft P .fi .UNINDENT .UNINDENT .sp If we retrieve the user from the database, the password that\(aqs stored is hashed and salted: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C >>> user = User.get(User.username == \(aqcharlie\(aq) >>> print user.password b76fa$88be1adcde66a1ac16054bc17c8a297523170949 .ft P .fi .UNINDENT .UNINDENT .sp To implement \fBlogin\fP\-type functionality, you could write something like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C def login(username, password): try: return (User .select() .where( (User.username == username) & (fn.check_password(password, User.password) == True)) .get()) except User.DoesNotExist: # Incorrect username and/or password. return False .ft P .fi .UNINDENT .UNINDENT .SS Date math .sp Each of the databases supported by Peewee implement their own set of functions and semantics for date/time arithmetic. .sp This section will provide a short scenario and example code demonstrating how you might utilize Peewee to do dynamic date manipulation in SQL. .sp Scenario: we need to run certain tasks every \fIX\fP seconds, and both the task intervals and the task themselves are defined in the database. We need to write some code that will tell us which tasks we should run at a given time: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Schedule(Model): interval = IntegerField() # Run this schedule every X seconds. class Task(Model): schedule = ForeignKeyField(Schedule, backref=\(aqtasks\(aq) command = TextField() # Run this command. last_run = DateTimeField() # When was this run last? .ft P .fi .UNINDENT .UNINDENT .sp Our logic will essentially boil down to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # e.g., if the task was last run at 12:00:05, and the associated interval # is 10 seconds, the next occurrence should be 12:00:15. So we check # whether the current time (now) is 12:00:15 or later. now >= task.last_run + schedule.interval .ft P .fi .UNINDENT .UNINDENT .sp So we can write the following code: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C next_occurrence = something # ??? how do we define this ??? # We can express the current time as a Python datetime value, or we could # alternatively use the appropriate SQL function/name. now = Value(datetime.datetime.now()) # Or SQL(\(aqcurrent_timestamp\(aq), e.g. query = (Task .select(Task, Schedule) .join(Schedule) .where(now >= next_occurrence)) .ft P .fi .UNINDENT .UNINDENT .sp For Postgresql we will multiple a static 1\-second interval to calculate the offsets dynamically: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C second = SQL("INTERVAL \(aq1 second\(aq") next_occurrence = Task.last_run + (Schedule.interval * second) .ft P .fi .UNINDENT .UNINDENT .sp For MySQL we can reference the schedule\(aqs interval directly: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C from peewee import NodeList # Needed to construct sql entity. interval = NodeList((SQL(\(aqINTERVAL\(aq), Schedule.interval, SQL(\(aqSECOND\(aq))) next_occurrence = fn.date_add(Task.last_run, interval) .ft P .fi .UNINDENT .UNINDENT .sp For SQLite, things are slightly tricky because SQLite does not have a dedicated datetime type. So for SQLite, we convert to a unix timestamp, add the schedule seconds, then convert back to a comparable datetime representation: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C next_ts = fn.strftime(\(aq%s\(aq, Task.last_run) + Schedule.interval next_occurrence = fn.datetime(next_ts, \(aqunixepoch\(aq) .ft P .fi .UNINDENT .UNINDENT .SS Changes in 3.0 .sp This document describes changes to be aware of when switching from 2.x to 3.x. .SS Backwards\-incompatible .sp I tried to keep changes backwards\-compatible as much as possible. In some places, APIs that have changed will trigger a \fBDeprecationWarning\fP\&. .SS Database .INDENT 0.0 .IP \(bu 2 \fBget_conn()\fP has changed to \fBDatabase.connection()\fP .IP \(bu 2 \fBget_cursor()\fP has changed to \fBDatabase.cursor()\fP .IP \(bu 2 \fBexecution_context()\fP is replaced by simply using the database instance as a context\-manager. .IP \(bu 2 For a connection context \fIwithout\fP a transaction, use \fBDatabase.connection_context()\fP\&. .IP \(bu 2 \fBDatabase.create_tables()\fP and \fBDatabase.drop_tables()\fP, as well as \fBModel.create_table()\fP and \fBModel.drop_table()\fP all default to \fBsafe=True\fP (\fBcreate_table\fP will create if not exists, \fBdrop_table\fP will drop if exists). .IP \(bu 2 \fBconnect_kwargs\fP attribute has been renamed to \fBconnect_params\fP .IP \(bu 2 initialization parameter for custom field\-type definitions has changed from \fBfields\fP to \fBfield_types\fP\&. .UNINDENT .SS Model Meta options .INDENT 0.0 .IP \(bu 2 \fBdb_table\fP has changed to \fBtable_name\fP .IP \(bu 2 \fBdb_table_func\fP has changed to \fBtable_function\fP .IP \(bu 2 \fBorder_by\fP has been removed (used for specifying a default ordering to be applied to SELECT queries). .IP \(bu 2 \fBvalidate_backrefs\fP has been removed. Back\-references are no longer validated. .UNINDENT .SS Models .INDENT 0.0 .IP \(bu 2 \fBBaseModel\fP has been renamed to \fBModelBase\fP .IP \(bu 2 Accessing raw model data is now done using \fB__data__\fP instead of \fB_data\fP .IP \(bu 2 The \fB_prepare_instance()\fP Model method has been removed. .IP \(bu 2 The \fBsqlall()\fP method, which output the DDL statements to generate a model and its associated indexes, has been removed. .UNINDENT .SS Fields .INDENT 0.0 .IP \(bu 2 \fBdb_column\fP has changed to \fBcolumn_name\fP .IP \(bu 2 \fBdb_field\fP class attribute changed to \fBfield_type\fP (used if you are implementing custom field subclasses) .IP \(bu 2 \fBmodel_class\fP attribute has changed to \fBmodel\fP .IP \(bu 2 \fBPrimaryKeyField\fP has been renamed to \fBAutoField\fP .IP \(bu 2 \fBForeignKeyField\fP constructor has the following changes: .INDENT 2.0 .IP \(bu 2 \fBrel_model\fP has changed to \fBmodel\fP .IP \(bu 2 \fBto_field\fP has changed to \fBfield\fP .IP \(bu 2 \fBrelated_name\fP has changed to \fBbackref\fP .UNINDENT .IP \(bu 2 \fBManyToManyField\fP is now included in the main \fBpeewee.py\fP module .IP \(bu 2 Removed the extension fields \fBPasswordField\fP, \fBPickledField\fP and \fBAESEncryptedField\fP\&. .UNINDENT .SS Querying .sp \fBJOIN_INNER\fP, \fBJOIN_LEFT_OUTER\fP, etc are now \fBJOIN.INNER\fP, \fBJOIN.LEFT_OUTER\fP, etc. .sp The C extension that contained implementations of the query result wrappers has been removed. .sp Additionally, \fBSelect.aggregate_rows()\fP has been removed. This helper was used to de\-duplicate left\-join queries to give the appearance of efficiency when iterating a model and its relations. In practice, the complexity of the code and its somewhat limited usefulness convinced me to scrap it. You can instead use \fBprefetch()\fP to achieve the same result. .INDENT 0.0 .IP \(bu 2 \fBSelect\fP query attribute \fB_select\fP has changed to \fB_returning\fP .IP \(bu 2 The \fBnaive()\fP method is now \fBobjects()\fP, which defaults to using the model class as the constructor, but accepts any callable to use as an alternate constructor. .IP \(bu 2 The \fBannotate()\fP query method is no longer supported. .UNINDENT .sp The \fBCase()\fP helper has moved from the \fBplayhouse.shortcuts\fP module into the main peewee module. .sp The \fBcast()\fP method is no longer a function, but instead is a method on all column\-like objects. .sp The \fBInsertQuery.return_id_list()\fP method has been replaced by a more general pattern of using \fB_WriteQuery.returning()\fP\&. .sp The \fBInsertQuery.upsert()\fP method has been replaced by the more general and flexible \fBInsert.on_conflict()\fP method. .sp When using \fBprefetch()\fP, the collected instances will be stored in the same attribute as the foreign\-key\(aqs \fBbackref\fP\&. Previously, you would access joined instances using \fB(backref)_prefetch\fP\&. .sp The \fBSQL\fP object, used to create a composable a SQL string, now expects the second parameter to be a list/tuple of parameters. .SS Removed Extensions .sp The following extensions are no longer included in the \fBplayhouse\fP: .INDENT 0.0 .IP \(bu 2 \fBberkeleydb\fP .IP \(bu 2 \fBcsv_utils\fP .IP \(bu 2 \fBdjpeewee\fP .IP \(bu 2 \fBgfk\fP .IP \(bu 2 \fBkv\fP .IP \(bu 2 \fBpskel\fP .IP \(bu 2 \fBread_slave\fP .UNINDENT .SS SQLite Extension .sp The SQLite extension module\(aqs \fBVirtualModel\fP class accepts slightly different \fBMeta\fP options: .INDENT 0.0 .IP \(bu 2 \fBarguments\fP \- used to specify arbitrary arguments appended after any columns being defined on the virtual table. Should be a list of strings. .IP \(bu 2 \fBextension_module\fP (unchanged) .IP \(bu 2 \fBoptions\fP (replaces \fBextension_options\fP) \- arbitrary options for the virtual table that appear after columns and \fBarguments\fP\&. .IP \(bu 2 \fBprefix_arguments\fP \- a list of strings that should appear before any arguments or columns in the virtual table declaration. .UNINDENT .sp So, when declaring a model for a virtual table, it will be constructed roughly like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C CREATE VIRTUAL TABLE "table name" USING extension_module ( prefix arguments, field definitions, arguments, options) .ft P .fi .UNINDENT .UNINDENT .SS Postgresql Extension .sp The \fIPostgresqlExtDatabase\fP no longer registers the \fIhstore\fP extension by default. To use the \fIhstore\fP extension in 3.0 and onwards, pass \fIregister_hstore=True\fP when initializing the database object. .SS Signals Extension .sp The \fBpost_init\fP signal has been removed. .SS New stuff .sp The query\-builder has been rewritten from the ground\-up to be more flexible and powerful. There is now a generic, lower\-level API for constructing queries. .SS SQLite .sp Many SQLite\-specific features have been moved from the \fBplayhouse.sqlite_ext\fP module into \fBpeewee\fP, such as: .INDENT 0.0 .IP \(bu 2 User\-defined functions, aggregates, collations, and table\-functions. .IP \(bu 2 Loading extensions. .IP \(bu 2 Specifying pragmas. .UNINDENT .sp See the "Using SQLite" section and "SQLite extensions" documents for more details. .SS SQLite Extension .sp The virtual\-table implementation from \fI\%sqlite\-vtfunc\fP has been folded into the peewee codebase. .INDENT 0.0 .IP \(bu 2 Support for SQLite online backup API. .IP \(bu 2 Murmurhash implementation has been corrected. .IP \(bu 2 Couple small quirks in the BM25 ranking code have been addressed. .IP \(bu 2 Numerous user\-defined functions for hashing and ranking are now included. .IP \(bu 2 \fBBloomFilter\fP implementation. .IP \(bu 2 Incremental \fBBlob\fP I/O support. .IP \(bu 2 Support for update, commit and rollback hooks. .IP \(bu 2 \fBLSMTable\fP implementation to support the lsm1 extension. .UNINDENT .SH NOTE .sp If you find any bugs, odd behavior, or have an idea for a new feature please don\(aqt hesitate to \fI\%open an issue\fP on GitHub or \fI\%contact me\fP\&. .INDENT 0.0 .IP \(bu 2 genindex .IP \(bu 2 modindex .IP \(bu 2 search .UNINDENT .SH AUTHOR charles leifer .SH COPYRIGHT charles leifer .\" Generated by docutils manpage writer. .