Introduction: What Is a Virtual DocField in Frappe v15?
A Virtual DocField in Frappe v15 is a field defined dynamically in Python (using properties or methods) instead of being stored in the database. These fields do not exist in the DocType schema and are not persisted in MariaDB. They are evaluated in real-time whenever a document is loaded.
Virtual DocFields let developers expose computed, read-only, or dynamic values without altering the database structure.
Why Use Virtual DocFields?
Virtual DocFields are useful when:
- You need values computed at runtime
- You don’t want to add unnecessary columns to the database
- You want to display derived values in Desk forms or API output
- You require dynamic fields that change based on business logic
- You want to keep the database clean and optimized
Examples include:
- Calculating totals
- Returning external API values
- Fetching metadata dynamically
- Deriving stringified labels
- Showing running balances or dynamic statuses
How Virtual DocFields Work in Frappe v15
Frappe automatically inspects DocType classes and detects:
- @property methods
- Methods ending with _display
- Dynamically returned fields via get_virtual_fields()
These are treated as DocFields during rendering, even though they don’t exist in the database.
Frappe converts these dynamic class attributes into a JSON-like field structure when generating API responses or Desk form layouts.
Implementation: Creating a Virtual DocField
Method 1: Using @property (Recommended)
class SalesInvoice(Document):
@property
def total_items(self):
return len(self.items or [])
This creates a read-only virtual field called total_items.
Result in APIs / Desk forms:
{
"total_items": 4
}
Method 2: Using a display field function
If you want a label-based value (common for link display customization):
class Employee(Document):
def full_name_display(self):
return f"{self.first_name} {self.last_name}"
This produces a virtual DocField full_name_display.
Method 3: Overriding get_virtual_fields()
Use this when you want multiple dynamic fields returned together.
class Project(Document):
def get_virtual_fields(self):
return {
"task_count": len(self.tasks or []),
"pending_tasks": len([t for t in self.tasks or [] if t.status != "Completed"])
}
Frappe injects task_count and pending_tasks as virtual DocFields.
How Frappe Registers Virtual DocFields (Internal Logic)
Frappe v15 reads virtual fields from:
- Document.get_virtual_fields()
- Python properties (@property)
- Display methods (*_display)
- Server-side overrides using hooks
The internal loader merges:
- DocType’s static fields (from JSON)
- Meta-level configurations
- Virtual fields
Resulting in a complete data schema used for:
- Desk form rendering
- REST API responses (/api/resource)
- Report building
- Custom app extensions
Using Virtual DocFields in Desk Forms
You cannot add them via Customize Form.
Instead, you must add a placeholder field in the DocType JSON:
Example (DocType JSON):
{
"fieldname": "total_items",
"label": "Total Items",
"fieldtype": "Data",
"read_only": 1,
"virtual": 1
}
The “virtual”: 1 flag ensures:
- The field does not map to a DB column
- The backend resolves it dynamically
Frappe v15 automatically fills the field using the Python property or method.
Advanced Usage: Virtual Fields in ListView & Reports
ListView
You can add virtual fields to listview:
listview_settings = {
"fields": ["name", "customer_name", "total_items"]
}
Reports
Add virtual fields via query override in:
report_name.py
by injecting values after the query result.
Best Practices for Virtual DocFields
- Use when values are computed, not stored
- Use simple logic inside properties to avoid performance issues
- Avoid DB queries inside virtual fields (very expensive)
- Prefer @property for readability
- Mark fields as “virtual”: 1 if used in Desk
Common Mistakes to Avoid
- Adding heavy computations in virtual fields
- Returning complex objects instead of simple JSON-serializable values
- Naming conflicts between virtual fields and real fields
- Using them as filters in listviews (not supported unless manually injected)
- Forgetting that virtual fields cannot be saved
Troubleshooting
Virtual DocField not appearing in Desk form
- Ensure “virtual”: 1 flag is set
- Ensure fieldname matches the Python property name
- Clear cache:
bench --site sitename clear-cache
Value is always blank
- The property/method must return a value
- Ensure method signature is correct (only self)
- Ensure no exceptions are raised inside the method
Virtual field missing from API output
Use:
?fields=["*"]
or ensure the virtual field is included in Meta.get_valid_fields().
Cross-References (Internal Linking for goerpnext.com)
- DocTypes in Frappe v15
- Customize Form in Frappe
- Meta Class and DocField Metadata
- Computed Fields vs Database Fields
- Frappe Document Class Architecture
Conclusion
Virtual DocFields in Frappe v15 are powerful tools that allow developers to introduce dynamic, read-only, computed fields without expanding the database schema. They improve performance, maintain database cleanliness, and offer flexible customization for ERPNext and custom Frappe applications.