Skip to content

API Cheatsheet

Complete API reference.

Method Example Returns Notes
Instance Methods
save() await user.save() Self update_fields for partial
delete() await user.delete() int Delete instance
refresh() await user.refresh() Self Reload from DB
pre_save() override Hook; is_create, update_fields
post_save() override Hook; is_create, update_fields
pre_delete() override Hook before delete
post_delete() override Hook after delete
Manager Methods
create() User.objects.create(name="John") Model Insert + return
bulk_create() User.objects.bulk_create([...]) list[Model] Bulk INSERT
bulk_update() User.objects.bulk_update([...], ["age"]) int Bulk UPDATE
get() User.objects.get(id=42) Model Raises if 0 or >1
get_or_none() User.objects.get_or_none(id=42) Model | None None if not found
get_or_create() User.objects.get_or_create(email="...") (Model, bool) Atomic
count() User.objects.count() int Count all
Query Builder
filter() .filter(is_active=True, age__gte=18) Query WHERE conditions
exclude() .exclude(status="banned") Query WHERE NOT
order_by() .order_by("-created_at") Query ORDER BY
limit() .limit(10) Query LIMIT
offset() .offset(20) Query OFFSET
prefetch() .prefetch("posts") Query Load reverse FK/M2M
join() .join("author") Query FK JOIN
distinct() .distinct() Query DISTINCT
group_by() .group_by("status") Query GROUP BY
having() .having(count__gte=5) Query HAVING
annotate() .annotate(n=Count("posts")) Query Computed fields
union() .union(other_qs) Query UNION
for_update() .for_update() Query Row lock
Terminal Methods
all() await qs.all() list[Model] Execute SELECT
first() await qs.first() Model | None First row
last() await qs.last() Model | None Last row
count() await qs.count() int COUNT(*)
exists() await qs.exists() bool EXISTS check
delete() await qs.delete() int Bulk DELETE
update() await qs.update(status="x") int Bulk UPDATE
increment() await qs.increment("views", by=1) int Atomic increment
sum() await qs.sum("views") number SUM
avg() await qs.avg("age") float AVG
max() await qs.max("price") Any MAX
min() await qs.min("price") Any MIN
values() await qs.values("id", "name") list[dict] Dict projection
values_list() await qs.values_list("id", flat=True) list Tuple projection
Debug & Introspection
sql() qs.sql() (str, list) SQL + params
query() qs.query() dict Query IR
explain() await qs.explain(analyze=True) str Query plan
Expressions
Q Q(age__gte=18) & Q(status="active") AND/OR/NOT
F F("views") + 1 Field reference
Count Count("posts") Aggregate
Sum/Avg/Max/Min Sum("amount") Aggregates
Concat Concat("first", "last") String concat
Coalesce Coalesce("nick", "name") First non-NULL
RawSQL RawSQL("LOWER(name)") Raw SQL
Transactions
atomic() async with atomic(): ... Nested savepoints

Examples

Basic Operations

# Simple query
users = await User.objects.filter(is_active=True, age__gte=18).all()

# With prefetch and ordering
users = await User.objects \
    .prefetch("posts") \
    .filter(is_active=True) \
    .order_by("-created_at") \
    .limit(10) \
    .all()

# Join for FK
posts = await Post.objects.join("author").filter(status="draft").all()

# Get single object
user = await User.objects.get(id=42)
user = await User.objects.get_or_none(email="test@test.com")

Complex Conditions

# Q expressions
users = await User.objects.filter(
    Q(age__gte=18) & (Q(status="active") | Q(status="premium"))
).all()

# Exclusion
users = await User.objects.exclude(status="banned").all()

Create

# Simple create
user = await User.objects.create(name="John", age=25)

# Get or create
user, created = await User.objects.get_or_create(
    email="test@test.com",
    defaults={"name": "John", "age": 25}
)

# Bulk create
users = await User.objects.bulk_create([
    User(name="John", age=25),
    User(name="Jane", age=30),
])

Update

# Instance update (full UPDATE)
user.age = 26
await user.save()

# Partial update via Query
count = await User.objects.filter(is_active=False).update(status="archived")

# Atomic update with F()
count = await User.objects.filter(id=42).update(views=F("views") + 1)

# Atomic increment
count = await Post.objects.filter(id=42).increment("views", by=1)

# Bulk update
count = await User.objects.bulk_update(
    [user1, user2, user3],
    fields=["status", "updated_at"]
)

Delete

# Instance delete
await user.delete()

# Bulk delete via Query
count = await User.objects.filter(is_active=False).delete()

Aggregation

# Annotate - add computed fields
users = await User.objects \
    .annotate(posts_count=Count("posts")) \
    .filter(posts_count__gt=5) \
    .all()

# Group by + having
stats = await Post.objects \
    .group_by("status") \
    .annotate(count=Count("id"), avg_views=Avg("views")) \
    .having(count__gte=10) \
    .all()

# Terminal aggregates
total_views = await Post.objects.filter(status="published").sum("views")
avg_age = await User.objects.filter(is_active=True).avg("age")
max_price = await Product.objects.max("price")

Count & Exists

# Count with filters
count = await User.objects.filter(is_active=True).count()

# Count all
count = await User.objects.count()

# Exists (faster than count for existence check)
has_active = await User.objects.filter(is_active=True).exists()

Values & Values List

# Dictionaries instead of models
users_data = await User.objects.filter(is_active=True).values("id", "name", "email")
# [{"id": 1, "name": "John", "email": "..."}, ...]

# Flat list
user_ids = await User.objects.filter(is_active=True).values_list("id", flat=True)
# [1, 2, 3, 4, ...]

# Tuples
user_pairs = await User.objects.values_list("id", "name")
# [(1, "John"), (2, "Jane"), ...]

Advanced Queries

# Union
active = User.objects.filter(status="active")
premium = User.objects.filter(status="premium")
combined = await active.union(premium).all()

# Distinct
unique_statuses = await Post.objects.distinct().values_list("status", flat=True)

# First / Last
newest = await Post.objects.order_by("-created_at").first()
oldest = await Post.objects.order_by("created_at").first()

Transactions

from oxyde.db import transaction

# Django-style atomic transactions
async with transaction.atomic():
    user = await User.objects.create(name="John")
    await Post.objects.create(author_id=user.id, title="First post")
    # Rollback on exception

# Nested transactions (savepoints)
async with transaction.atomic():
    user = await User.objects.create(name="John")
    async with transaction.atomic():  # Creates savepoint
        await Post.objects.create(author_id=user.id, title="First post")

Introspection

# Get SQL query
qs = User.objects.filter(age__gte=18).order_by("-created_at")
sql, params = qs.sql()
print(f"SQL: {sql}")
print(f"Params: {params}")

# Get query IR (Intermediate Representation)
ir = qs.query()
print(ir)

# Execution plan
plan = await qs.explain()
print(plan)

# With analyze
plan = await qs.explain(analyze=True)

Multiple Databases

# Use replica for reading
users = await User.objects.filter(is_active=True).all(using="replica")

# Write to master
user = await User.objects.create(name="John", using="master")

Lookup Operators

Lookup Description Example
field Equality age=18
field__exact Exact equality (same as =) name__exact="John"
field__iexact Case-insensitive equality email__iexact="TEST@EXAMPLE.COM"
field__contains Contains (LIKE '%...%') name__contains="oh"
field__icontains Case-insensitive contains name__icontains="OH"
field__startswith Starts with name__startswith="Jo"
field__istartswith Case-insensitive starts with name__istartswith="jo"
field__endswith Ends with email__endswith="@example.com"
field__iendswith Case-insensitive ends with email__iendswith="@EXAMPLE.COM"
field__gt Greater than (>) age__gt=18
field__gte Greater than or equal (>=) age__gte=18
field__lt Less than (<) age__lt=65
field__lte Less than or equal (<=) age__lte=65
field__in In list status__in=["active", "premium"]
field__isnull NULL check deleted_at__isnull=True
field__range Between (BETWEEN) age__range=(18, 65)
field__between Alias for range age__between=(18, 65)
field__year Year from date created_at__year=2024
field__month Month from date created_at__month=(2024, 12)
field__day Day from date created_at__day=(2024, 12, 25)

Best Practices

DO

# Reuse Query for multiple operations
active = User.objects.filter(is_active=True)
count = await active.count()
users = await active.all()

# Use bulk operations for performance
await User.objects.bulk_create([user1, user2, user3])

# Use prefetch to load related objects
users = await User.objects.prefetch("posts").all()

# Use F() for atomic updates
await Post.objects.filter(id=42).update(views=F("views") + 1)

# Check existence with exists(), not count()
if await User.objects.filter(email=email).exists():
    ...

DON'T

# Don't query in a loop
users = await User.objects.all()
for user in users:
    posts = await Post.objects.filter(author_id=user.id).all()  # ❌ Multiple queries

# Use prefetch instead:
users = await User.objects.prefetch("posts").all()  # ✅ Two queries total

# Don't save() in a loop for bulk updates
for user in users:
    user.status = "archived"
    await user.save()  # ❌

# Use bulk_update or update():
await User.objects.filter(id__in=ids).update(status="archived")  # ✅

# Don't forget .all() for Query
users = await User.objects.filter(is_active=True)  # ❌ Returns Query
users = await User.objects.filter(is_active=True).all()  # ✅

Architecture Notes

Query Builder (Lazy Evaluation)

# These methods DON'T execute SQL, they just build Query
qs = User.objects.filter(is_active=True)  # Query object
qs = qs.prefetch("posts")                  # Query object
qs = qs.order_by("-created_at")            # Query object

# SQL executes only on terminal method call
users = await qs.all()  # ← SELECT executes here

Prefetch vs Join

# join() - for FK (single query with JOIN)
posts = await Post.objects.join("author").all()
# SQL: SELECT * FROM posts JOIN users ON ...

# prefetch() - for reverse FK / M2M (separate query)
users = await User.objects.prefetch("posts").all()
# SQL: SELECT * FROM users;
#      SELECT * FROM posts WHERE author_id IN (...);

Next Steps