Use this file to discover all available pages before exploring further.
Frappe provides a powerful database abstraction layer that works across MariaDB, PostgreSQL, and SQLite. It includes both raw SQL execution and a query builder for type-safe database operations.
The database connection is available globally as frappe.db:
import frappe# Database is automatically connected for each requestdb = frappe.dbprint(db.db_type) # 'mariadb', 'postgres', or 'sqlite'print(db.cur_db_name) # Current database name
# Execute SQL queryresults = frappe.db.sql(""" SELECT name, customer_name, grand_total FROM `tabSales Order` WHERE status = 'Open' ORDER BY creation DESC""")print(results) # Returns list of tuples# [('SO-0001', 'Customer A', 15000.0), ('SO-0002', 'Customer B', 25000.0)]
# Return as dictionaryorders = frappe.db.sql(""" SELECT name, customer_name, grand_total FROM `tabSales Order` WHERE status = %s""", ('Open',), as_dict=True)for order in orders: print(order.name, order.customer_name, order.grand_total)# Return as listorders = frappe.db.sql( "SELECT * FROM `tabSales Order` WHERE status = %s", ('Open',), as_list=True)# Pluck single columncustomer_names = frappe.db.sql_list(""" SELECT DISTINCT customer_name FROM `tabSales Order`""")# Or using pluck parametercustomer_names = frappe.db.sql(""" SELECT DISTINCT customer_name FROM `tabSales Order`""", pluck=True)print(customer_names) # ['Customer A', 'Customer B', 'Customer C']
Always use parameter binding to prevent SQL injection:
# Tuple parametersorders = frappe.db.sql(""" SELECT name, grand_total FROM `tabSales Order` WHERE customer = %s AND status = %s""", ('CUST-001', 'Open'), as_dict=True)# Dictionary parametersorders = frappe.db.sql(""" SELECT name, grand_total FROM `tabSales Order` WHERE customer = %(customer)s AND status = %(status)s""", {'customer': 'CUST-001', 'status': 'Open'}, as_dict=True)
Never use string formatting or concatenation for SQL queries as it creates SQL injection vulnerabilities:
# BAD - SQL Injection risk!query = f"SELECT * FROM `tabUser` WHERE name = '{user_input}'"# GOOD - Safe parameter bindingquery = "SELECT * FROM `tabUser` WHERE name = %s"results = frappe.db.sql(query, (user_input,))
# Full-text searchfrappe.db.sql(""" SELECT name, item_name FROM `tabItem` WHERE MATCH(item_name, description) AGAINST (%s IN NATURAL LANGUAGE MODE)""", (search_term,), as_dict=True)# JSON field operations (MariaDB 10.2+)frappe.db.sql(""" SELECT name, JSON_EXTRACT(custom_data, '$.field_name') as value FROM `tabCustom DocType`""")
# Array operationsfrappe.db.sql(""" SELECT name FROM "tabDocType" WHERE 'field_name' = ANY(string_to_array(fields, ','))""")# JSONB queriesfrappe.db.sql(""" SELECT name, data->>'key' as value FROM "tabCustom DocType" WHERE data @> '{"status": "active"}'""")
# Get IDs firstnames = frappe.db.get_all('Customer', filters={'disabled': 0}, pluck='name', limit=1000)# Process in batchesbatch_size = 100for i in range(0, len(names), batch_size): batch = names[i:i+batch_size] process_batch(batch) frappe.db.commit()