Skip to content

v15 Server Only Stable Database, ORM & Query Builder

Frappe Framework v15 provides 3 database access interfaces under frappe.db and frappe.qb for interacting with MariaDB and PostgreSQL.


1. frappe.db API Reference

frappe.db.get_value

Fetches values from a single database row efficiently without instantiating document objects.

python
# Signature
frappe.db.get_value(doctype, filters, fieldname, as_dict=False, debug=False)

# Fetch single field
email = frappe.db.get_value("User", "Administrator", "email")

# Fetch multiple fields as dictionary
data = frappe.db.get_value(
    "Task",
    {"status": "Open", "priority": "High"},
    ["name", "subject", "allocated_to"],
    as_dict=True
)

frappe.db.set_value

Executes a direct UPDATE query on specific document fields in the database.

python
frappe.db.set_value(
    doctype,
    name,
    fieldname,
    value=None,
    modified=None,
    modified_by=None,
    update_modified=True
)
python
# Example: Bulk field update
frappe.db.set_value("Task", "TASK-00001", "status", "Completed")

# Dict-based multi-field update
frappe.db.set_value(
    "Task",
    {"status": "Open", "priority": "Low"},
    {"priority": "Medium", "status": "Working"}
)

frappe.db.exists & frappe.db.count

python
# 1. Check record existence (Returns document name string or None)
name = frappe.db.exists("User", {"email": "john@example.com"})
print("Result:", name)
# Output:
# Result: john@example.com

# 2. Count total matching records
open_task_count = frappe.db.count("Task", filters={"status": "Open"})
print("Open Tasks:", open_task_count)
# Output:
# Open Tasks: 42

frappe.db.get_single_value

Retrieves a field value from a Single DocType (such as System Settings or Global Defaults).

python
# Fetch system default currency from System Settings
currency = frappe.db.get_single_value("System Settings", "default_currency")
print("System Currency:", currency)
# Output:
# System Currency: USD

frappe.db.delete

Performs direct SQL row deletion based on filter conditions without instantiating document objects.

python
# Direct deletion of temporary log records
frappe.db.delete("Activity Log", {
    "creation": ["<", "2026-01-01"],
    "status": "Success"
})

Schema Inspection & Maintenance (table_exists, has_column, touch)

python
# 1. Check if database table exists
if frappe.db.table_exists("tabTask"):
    print("Table exists!")

# 2. Check if table column exists
if frappe.db.has_column("tabTask", "custom_priority"):
    print("Column exists!")

# 3. Touch document modified timestamp
frappe.db.touch("Task", "TASK-2026-00001")

frappe.db.sql (Raw SQL Execution)

Executes raw SQL queries with mandatory parameterized variable binding to prevent SQL injection vulnerabilities.

python
# ALWAYS use SQL parameter binding (%s for MariaDB/PostgreSQL)!
result = frappe.db.sql("""
    SELECT name, subject, status
    FROM `tabTask`
    WHERE status = %s AND priority = %s
    ORDER BY creation DESC
""", ("Open", "High"), as_dict=True)

print("Fetched SQL Rows:", result)
# Output:
# Fetched SQL Rows: [{'name': 'TASK-2026-00001', 'subject': 'Fix Bug', 'status': 'Open'}]

CAUTION

Never use Python string interpolation (f"SELECT ... WHERE name = '{name}'") inside frappe.db.sql()! This creates critical SQL injection security vulnerabilities.


Database Transactions: commit, rollback, savepoint

python
# Savepoint and Transaction Control
try:
    frappe.db.savepoint("before_bulk_update")
    frappe.db.set_value("Task", task_id, "status", "Completed")
    # Commit explicit transaction if required
    frappe.db.commit()
except Exception:
    # Revert to savepoint without aborting entire request transaction
    frappe.db.rollback(save_point="before_bulk_update")
    raise

2. DocType Metadata & Request Context APIs

frappe.get_meta

Returns the Meta structure object for a given DocType.

python
meta = frappe.get_meta("Customer")

# Inspect field definitions
has_field = meta.has_field("tax_id")
field = meta.get_field("customer_name")
link_fields = meta.get_link_fields()

print("Has Tax ID:", has_field)
print("Field Type:", field.fieldtype)
# Output:
# Has Tax ID: True
# Field Type: Data

frappe.local Request Context

frappe.local holds thread-local contextual variables for the active Werkzeug HTTP request.

AttributeDescriptionOutput Example
frappe.local.siteActive site name'site1.localhost'
frappe.local.session.userLogged in user email'john@company.com'
frappe.local.form_dictParsed HTTP request query parameters & body{'doctype': 'Task', 'status': 'Open'}
frappe.local.requestWerkzeug HTTP request object<Request 'http://localhost/api/method/...' [POST]>

3. Query Builder (frappe.qb)

Frappe v15 integrates PyPika into frappe.qb to generate type-safe, programmatic, cross-database SQL queries.

Basic SELECT & WHERE Query

python
from frappe.query_builder import DocType, Field

Task = DocType("Task")

query = (
    frappe.qb.from_(Task)
    .select(Task.name, Task.subject, Task.priority)
    .where((Task.status == "Open") & (Task.priority.isin(["High", "Urgent"])))
    .orderby(Task.creation, order=frappe.qb.desc)
    .limit(20)
)

results = query.run(as_dict=True)
print("Query Results:", results)
# Output:
# Query Results: [{'name': 'TASK-001', 'subject': 'Setup Redis', 'priority': 'High'}]

JOIN & Aggregations Query

python
from frappe.query_builder import DocType
from frappe.query_builder.functions import Count, Sum

Task = DocType("Task")
Project = DocType("Project")

query = (
    frappe.qb.from_(Project)
    .left_join(Task).on(Task.project == Project.name)
    .select(
        Project.name.as_("project_name"),
        Count(Task.name).as_("total_tasks")
    )
    .groupby(Project.name)
    .having(Count(Task.name) > 5)
)

data = query.run(as_dict=True)

4. Database Strategy Comparison Matrix

CriteriaDocument API (frappe.get_doc)DB API (frappe.db.get_all)Query Builder (frappe.qb)Raw SQL (frappe.db.sql)
Execution SpeedModerate (Full object instantiation)FastVery FastMaximum Speed
Triggers Lifecycle HooksYes (validate, on_update)NoNoNo
Applies PermissionsOptional (check_permission)Yes (get_list) / No (get_all)ManualManual
Complex Joins / SubqueriesNoLimitedFull SupportFull Support
Type Safety & SecurityMaximumHighHigh (Injection-proof)Requires Manual Binding

Frappe Framework v15 Complete Technical Reference & Handbook.