Of course! Let's dive into Peewee. I'll be your "Python Peewee Mate," guiding you through everything from the basics to more advanced topics.

What is Peewee?
Peewee is a simple, small, and fast ORM (Object-Relational Mapper) for Python. It's designed to be lightweight and intuitive, making it a fantastic choice for small to medium-sized projects, scripts, or anyone who wants to get a database up and running quickly without the complexity of a larger ORM like SQLAlchemy.
Key Features:
- Simple & Lightweight: Easy to learn and use.
- Supports Multiple Databases: Works out-of-the-box with SQLite, PostgreSQL, and MySQL.
- Pythonic: You interact with your database using Python classes and objects, not raw SQL strings.
- Built-in Features: Includes support for migrations, model validation, and a thread-safe connection pool.
The Core Concepts: The "Peewee Trinity"
To use Peewee, you need to understand three main things:
- The Database Connection: Represents your database.
- The Model: A Python class that maps to a database table.
- The Field: Defines the columns in your table (e.g.,
CharField,IntegerField).
Step 1: Installation
First, install Peewee. We'll also install psycopg2-binary for our PostgreSQL example.

pip install peewee pip install psycopg2-binary # For PostgreSQL
Step 2: Setting Up the Database and Models
Let's create a simple example for a blog application with Post and Category models.
Database Connection
Peewee makes it easy to connect to different databases. Here's how you'd set up connections for SQLite and PostgreSQL.
# For SQLite (great for development and small apps)
# This will create a 'my_database.db' file in your project directory.
db = SqliteDatabase('my_database.db')
# For PostgreSQL (and MySQL)
# from playhouse.postgres_ext import PostgresqlExtDatabase
#
# db = PostgresqlExtDatabase(
# 'my_app_db', # Your database name
# user='postgres', # Your username
# password='mysecretpassword', # Your password
# host='localhost', # Your host
# port=5432 # Your port
# )
Defining Models
Now, let's define our models. A model is a simple Python class that inherits from peewee.Model.
db_class: We tell the model which database connection to use.peewee.CharField: For text.max_lengthis required.peewee.TextField: For longer text, like a blog post content.peewee.DateTimeField: For storing dates and times.formatslets you control how it's displayed.peewee.ForeignKeyField: For creating relationships between tables. This automatically creates acategory_idcolumn in theposttable.
from peewee import *
from datetime import datetime
# Use the database connection we defined above
db = SqliteDatabase('my_database.db')
# Base model class that all our models will inherit from
class BaseModel(Model):
class Meta:
database = db # This model uses the 'db' database connection
class Category(BaseModel):
name = CharField(unique=True) # e.g., "Technology", "Lifestyle"
class Post(BaseModel):= CharField()
content = TextField()
created_date = DateTimeField(default=datetime.now)
# This creates a relationship to the Category model
# backref='posts' lets you do `category.posts` to get all posts in that category
category = ForeignKeyField(Category, backref='posts', null=True)
# --- This is crucial ---
# You must create the tables in the database.
# Peewee will not do this automatically.
# If the tables already exist, this is a safe operation.
db.connect()
db.create_tables([Category, Post])
db.close()
Step 3: The CRUD Operations (Create, Read, Update, Delete)
This is where the magic happens. We'll interact with our database using our Python objects.

C - Create
Let's add some categories and posts to our database.
# Make sure we're connected
db.connect()
# --- Creating a Category ---
# Method 1: Create and save in one step
tech_category = Category.create(name='Technology')
print(f"Created category: {tech_category.name} (ID: {tech_category.id})")
# Method 2: Create an instance and then save it
lifestyle_category = Category(name='Lifestyle')
lifestyle_category.save()
print(f"Saved category: {lifestyle_category.name} (ID: {lifestyle_category.id})")
# --- Creating a Post ---
# We can pass the category object directly!
post1 = Post.create('My First Peewee Post',
content='Peewee is awesome and easy to use.',
category=tech_category
)
print(f"Created post: '{post1.title}' in category '{post1.category.name}'")
# You can also use the ID
post2 = Post.create('Living a Healthy Life',
content='Eat well and exercise regularly.',
category=lifestyle_category
)
print(f"Created post: '{post2.title}' in category '{lifestyle_category.name}'")
db.close()
R - Read (Querying)
Peewee's querying syntax is very expressive.
db.connect()
# Get all categories
all_categories = Category.select()
print("\n--- All Categories ---")
for category in all_categories:
print(f"- {category.name}")
# Get all posts
all_posts = Post.select()
print("\n--- All Posts ---")
for post in all_posts:
print(f"- {post.title} (Category: {post.category.name})")
# Get a specific post by its ID
try:
specific_post = Post.get_by_id(post1.id)
print(f"\n--- Post with ID {post1.id} ---")
print(f"Title: {specific_post.title}")
print(f"Content: {specific_post.content}")
except Post.DoesNotExist:
print(f"Post with ID {post1.id} not found.")
# Get posts in a specific category
tech_posts = Post.select().where(Post.category == tech_category)
print(f"\n--- Posts in '{tech_category.name}' category ---")
for post in tech_posts:
print(f"- {post.title}")
# Get posts containing a specific word in the title
peewee_posts = Post.select().where(Post.title.contains('Peewee'))
print(f"\n--- Posts with 'Peewee' in the title ---")
for post in peewee_posts:
print(f"- {post.title}")
# Ordering posts by creation date (newest first)
recent_posts = Post.select().order_by(Post.created_date.desc())
print(f"\n--- All Posts (Newest First) ---")
for post in recent_posts:
print(f"- {post.title} on {post.created_date.strftime('%Y-%m-%d')}")
db.close()
U - Update
Updating is a two-step process: get the object, then change its attributes and save.
db.connect()
# Get the first post we created
post_to_update = Post.get_by_id(post1.id)
print(f"\nOriginal title: {post_to_update.title}")
# Update its title
post_to_update.title = "My Awesome Updated Peewee Post"
post_to_update.save() # This executes an UPDATE statement
# Verify the update
updated_post = Post.get_by_id(post1.id)
print(f"New title: {updated_post.title}")
db.close()
D - Delete
Deleting is also straightforward: get the object and call .delete().
db.connect()
# Get the post we want to delete
post_to_delete = Post.get_by_id(post2.id)
print(f"\nDeleting post: '{post_to_delete.title}'...")
# Delete the object
post_to_delete.delete()
# Verify it's gone
try:
Post.get_by_id(post2.id)
print("Post still exists!") # This line should not run
except Post.DoesNotExist:
print("Post successfully deleted!")
db.close()
Advanced Topics
Transactions
Ensuring a group of operations all succeed or all fail is crucial. Peewee handles this with db.atomic().
db.connect()
try:
with db.atomic(): # All code inside this block is part of a single transaction
# Create a new category and a post for it
new_cat = Category.create(name='Finance')
Post.create(title='Budgeting 101', content='...', category=new_cat)
print("Transaction successful!")
except Exception as e:
print(f"Transaction failed! Rolled back. Error: {e}")
finally:
db.close()
Migrations
As your application evolves, so will your database schema. Peewee has a built-in migration system.
-
Install the helper:
pip install playhouse.migrate
-
Create a migration file: You can write a script to apply changes.
from playhouse.migrate import * db.connect() # Let's add a new 'author' field to our Post model migrator = PostgresqlMigrator(db) # Use SqliteMigrator for SQLite # Add a new column migrate( migrator.add_column('post', 'author', CharField(default='Anonymous')) ) # To rename a column: migrate(migrator.rename_column('post', 'content', 'body')) # To drop a column: migrate(migrator.drop_column('post', 'author')) db.close() print("Migration applied successfully!")
Summary: Why Peewee is a Great "Mate"
- Low Barrier to Entry: You can be productive in minutes.
- No Magic: It's very clear what SQL is being generated, which helps with learning and debugging.
- Powerful Enough: For most applications, Peewee has all the features you need (complex queries, joins, aggregations, etc.).
- Great for Scripts: Perfect for writing a quick Python script that needs to interact with a database.
Now you're equipped to be a confident Peewee user! Go build something amazing.
