Applications
One of the main benefits of Python Postgres is the ability to run arbitrary SQL queries and having a simple pydantic model to represent the results. This is particularly handy when you do not want to maintain pydantic models for every table in your database, when you're just not interested in most of them but instead only in certain compositions of them.
Setup
CREATE TABLE customers (
customer_id SERIAL PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
phone VARCHAR(20),
birth_date DATE,
address JSONB,
loyalty_points INTEGER DEFAULT 0,
segment VARCHAR(20) CHECK (segment IN ('STANDARD', 'PREMIUM', 'VIP')),
preferences TEXT[],
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP WITH TIME ZONE,
is_active BOOLEAN DEFAULT TRUE
);
CREATE TABLE products (
product_id SERIAL PRIMARY KEY,
sku VARCHAR(20) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
description TEXT,
price DECIMAL(10, 2) NOT NULL CHECK (price >= 0),
cost DECIMAL(10, 2) CHECK (cost >= 0),
category VARCHAR(50) NOT NULL,
subcategory VARCHAR(50),
attributes JSONB,
stock_quantity INTEGER NOT NULL DEFAULT 0,
reorder_level INTEGER,
weight DECIMAL(8, 2),
dimensions VARCHAR(50),
is_discontinued BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INTEGER REFERENCES customers (customer_id),
order_date TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
status VARCHAR(20) CHECK (
status IN (
'PENDING',
'PROCESSING',
'SHIPPED',
'DELIVERED',
'CANCELLED'
)
),
total_amount DECIMAL(12, 2) NOT NULL,
discount_amount DECIMAL(12, 2) DEFAULT 0,
tax_amount DECIMAL(12, 2) DEFAULT 0,
shipping_address JSONB,
payment_method VARCHAR(50),
tracking_number VARCHAR(100),
notes TEXT,
items JSONB NOT NULL,
shipping_date TIMESTAMP WITH TIME ZONE,
delivery_date TIMESTAMP WITH TIME ZONE
);
In real-world examples, you may often find queries like this:
Query
SELECT
c.customer_id,
c.first_name || ' ' || c.last_name AS customer_name,
c.segment AS customer_segment,
o.order_date,
p.name AS top_product_name,
p.category,
SUM(o.total_amount) OVER (
PARTITION BY
c.customer_id
) AS customer_lifetime_value,
RANK() OVER (
PARTITION BY
p.category
ORDER BY
o.total_amount DESC
) AS category_rank
FROM
customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
INNER JOIN LATERAL (
SELECT
p.product_id,
p.name,
p.category,
p.price
FROM
products p
WHERE
p.product_id = ANY (
SELECT
(JSONB_ARRAY_ELEMENTS(o.items) ->> 'product_id')::INTEGER
)
ORDER BY
p.price DESC
LIMIT
1
) p ON TRUE
WHERE
c.is_active = TRUE
AND o.status IN ('SHIPPED', 'DELIVERED')
AND o.order_date >= CURRENT_DATE - INTERVAL '1 year'
GROUP BY
c.customer_id,
c.first_name,
c.last_name,
c.segment,
o.order_date,
p.name,
p.category,
o.total_amount
HAVING
SUM(o.total_amount) > 1000
ORDER BY
customer_lifetime_value DESC,
order_date DESC
LIMIT
100;
For some example values, this would yield:
customer_id | customer_name | customer_segment | order_date | top_product_name | category | customer_lifetime_value | category_rank |
---|---|---|---|---|---|---|---|
2 | Maria Garcia | VIP | 2025-04-25 09:31:34.621258 +00:00 | Premium Laptop | Electronics | 1479.98 | 1 |
1 | John Smith | PREMIUM | 2025-04-25 09:31:34.621258 +00:00 | Premium Laptop | Electronics | 1349.99 | 2 |
In this scenario, not only would it be quite cumbersome to maintain models for all the tables with appropriate types and constraints, but rewriting this query in an ORM syntax would probably be somewhat tedious, if we're only interested in the result of this query and not in the tables themselves.
Actually ...
You could of course use a view for the above, but that can both be unwanted for a whole number of reasons, and is besides the point of this illustrative example.
With Python Postgres, you can simply run this query and get a pydantic model with the result:
class CustomerOrderAnalytics(BaseModel):
customer_id: int
customer_name: str
customer_segment: Literal["STANDARD", "PREMIUM", "VIP"]
order_date: datetime
top_product_name: str
category: str
customer_lifetime_value: Decimal
category_rank: int
data = await pg(query, model=CustomerOrderAnalytics)
print(data)
[
CustomerOrderAnalytics(
customer_id=2,
customer_name="Maria Garcia",
customer_segment="VIP",
order_date=datetime.datetime(
2025, 4, 25, 9, 31, 34, 621258, tzinfo=datetime.timezone.utc
),
top_product_name="Premium Laptop",
category="Electronics",
customer_lifetime_value=Decimal("1479.98"),
category_rank=1,
),
CustomerOrderAnalytics(
customer_id=1,
customer_name="John Smith",
customer_segment="PREMIUM",
order_date=datetime.datetime(
2025, 4, 25, 9, 31, 34, 621258, tzinfo=datetime.timezone.utc
),
top_product_name="Premium Laptop",
category="Electronics",
customer_lifetime_value=Decimal("1349.99"),
category_rank=2,
),
]