frappe-hooks-expert
Frappe Hooks Expert
This skill provides comprehensive guidance for understanding and working with Frappe's hooks system.
Overview
Frappe's hooks system allows apps to extend and customize framework behavior without modifying core code. Hooks are registered in an app's hooks.py file and are called by the framework at specific points during execution.
Core Concept: Hooks are extension points that allow your app to:
- React to document lifecycle events
- Extend permissions logic
- Schedule background tasks
- Customize UI behavior
- Add custom validations
- Integrate with external systems
Hook Categories
1. Application Hooks
Basic app metadata and configuration:
app_name,app_title,app_publisher,app_description,app_email,app_licenserequired_apps- List of dependenciesadd_to_apps_screen- App configuration for apps page
When to read: See references/application-hooks.md for app metadata and configuration details.
2. Installation Hooks
Execute code during app lifecycle:
before_install/after_install- Run during app installationbefore_uninstall/after_uninstall- Run during app removalbefore_app_install/after_app_install- When other apps installbefore_app_uninstall/after_app_uninstall- When other apps uninstallbefore_migrate/after_migrate- Run during migrationsbefore_tests- Setup before running tests
When to read: See references/installation-hooks.md for installation and migration hooks.
3. Document Hooks (doc_events)
React to document lifecycle events:
before_insert,after_insert- Document creationbefore_save,after_save- Document savebefore_submit,after_submit- Document submissionbefore_cancel,after_cancel- Document cancellationbefore_update_after_submit,after_update_after_submit- Submitted doc editson_update,on_cancel,on_trash- Common eventson_change- When any field changesbefore_rename,after_rename- Document renamingbefore_print,after_print- Print events
When to read: See references/document-hooks.md for complete document event hooks with 20+ examples.
4. Permission Hooks
Extend permission logic:
permission_query_conditions- Filter documents in list viewshas_permission- Custom document-level permissionswrite_permission_query_conditions- Validate writes before commithas_website_permission- Portal/website access controlfilter_workflow_transitions- Customize workflow transitionshas_workflow_action_permission- Control workflow action visibilityworkflow_safe_eval_globals- Extend available globals in workflow transition conditions
When to read: See references/permission-hooks.md for permission extension hooks.
5. Scheduler Hooks
Schedule background tasks:
scheduler_events- Cron-based task schedulingall- Every event (every few minutes)cron- Custom cron expressionshourly,hourly_maintenance- Hourly tasksdaily,daily_long,daily_maintenance- Daily tasksweekly,weekly_long- Weekly tasksmonthly,monthly_long- Monthly tasks
When to read: See references/scheduler-hooks.md for scheduled task configuration.
6. UI and Frontend Hooks
Customize user interface:
app_include_js/app_include_css- Desk assetsweb_include_js/web_include_css- Website assetsdoctype_js/doctype_list_js/doctype_tree_js/doctype_calendar_js- DocType UIpage_js- Page customizationwebform_include_js/webform_include_css- Web form assetsapp_include_icons- SVG icon bundlesstandard_navbar_items- Navbar menu itemsstandard_help_items- Help menu items
When to read: See references/ui-hooks.md for frontend customization hooks.
7. Jinja Hooks
Extend template rendering:
jinja- Add methods and filters to Jinja environmentmethods- Custom functions in templatesfilters- Custom Jinja filters
When to read: See references/jinja-hooks.md for template customization.
8. Request and Job Hooks
Intercept HTTP requests and background jobs:
before_request/after_request- HTTP request lifecyclebefore_job/after_job- Background job lifecycleextend_bootinfo- Add data to initial page load
When to read: See references/request-job-hooks.md for request and job lifecycle hooks.
9. Safe Execution Hooks
Extend safe code execution environments:
safe_exec_globals- Add globals for Server Scripts, reports, and custom code executionsafe_eval_globals- Add globals for formula fields and simple expressions
When to read: See references/other-hooks.md for safe execution hooks, differences, and best practices.
10. Other Hooks
Additional extension points:
override_whitelisted_methods- Override API methodsoverride_doctype_class- Replace DocType controllersoverride_doctype_dashboards- Customize dashboardsfixtures- Export/import data during migrationson_session_creation/on_login/on_logout- Session eventsauth_hooks- Custom authentication logicnotification_config- Notification configurationstandard_queries- Custom search queriesglobal_search_doctypes- Configure global searchuser_data_fields- GDPR data protectionignore_links_on_delete- Skip link validationauto_cancel_exempted_doctypes- Exempt from auto-cancelwebsite_generators- Create website pages from docswebsite_route_rules- Custom URL routingwebsite_redirects- URL redirectsupdate_website_context- Modify website contextdefault_log_clearing_doctypes- Auto-cleanup logs
When to read: See references/other-hooks.md for additional hooks and extension points.
Quick Reference
Hook Registration Format
Hooks are registered in {app_name}/hooks.py:
# Single function
hook_name = "app_name.module.function_name"
# Multiple functions (executed in order)
hook_name = [
"app_name.module.function1",
"app_name.module.function2",
]
# Dictionary mapping (doctype/key specific)
hook_name = {
"DocType Name": "app_name.module.function",
"Another DocType": "app_name.module.another_function",
}
# Nested structure (doc_events)
doc_events = {
"*": { # All doctypes
"on_update": "app_name.utils.on_any_update",
},
"Specific DocType": {
"before_save": "app_name.doctype_utils.validate",
"after_insert": [
"app_name.utils.notify",
"app_name.utils.log_creation",
],
},
}
Function Signatures
Document hooks:
def hook_function(doc, method=None):
"""
Args:
doc: Document instance being processed
method: Event name (e.g., "on_update")
"""
pass
Permission hooks:
def has_permission(doc, ptype=None, user=None, debug=False):
"""Return True/False/None"""
pass
def get_permission_query_conditions(user=None, doctype=None):
"""Return SQL WHERE clause string"""
pass
Scheduler hooks:
def scheduled_task():
"""No arguments for scheduled tasks"""
pass
Hook Execution Order
Document Lifecycle
-
Insert:
validate()controller methodbefore_inserthook- Database INSERT
after_inserthookon_updatehook
-
Update:
validate()controller methodbefore_savehook- Database UPDATE
after_savehookon_updatehookon_changehook (if fields changed)
-
Submit:
before_submithook- Set docstatus = 1
- Database UPDATE
after_submithookon_updatehook
-
Cancel:
before_cancelhook- Set docstatus = 2
- Database UPDATE
after_cancelhookon_cancelhook
-
Delete:
before_trashhook (controller method)on_trashhook- Database DELETE
Permission Evaluation
- Administrator check (bypass all)
- Role-based permissions
has_permissionhookpermission_query_conditionshook (for read/select)- User permissions
- Share permissions
When to read: See references/execution-order.md for complete hook execution sequences.
Best Practices
Hook Implementation
- Keep Hooks Focused: Each hook should do one thing well
- Avoid Side Effects: Be careful with database commits in hooks
- Handle Errors: Use try-except to prevent hook failures from breaking operations
- Performance: Hooks run synchronously; optimize for speed
- Idempotency: Design hooks to be safely re-runnable
Common Patterns
Conditional execution:
def on_update(doc, method=None):
# Only process specific doctypes
if doc.doctype != "Target DocType":
return
# Only process when field changes
if not doc.has_value_changed("status"):
return
# Your logic here
Error handling:
def after_insert(doc, method=None):
try:
# Your logic
external_api_call(doc)
except Exception as e:
frappe.log_error(f"Hook failed: {str(e)}")
# Decide: raise or continue?
Wildcard hooks:
doc_events = {
"*": { # Applies to all doctypes
"on_update": "app.utils.log_all_updates",
}
}
Security Considerations
- Validate Input: Don't trust data in hooks
- Check Permissions: Hooks may run with elevated privileges
- Sanitize SQL: Use parameterized queries in permission hooks
- Avoid Recursion: Be careful with hooks that modify documents
- Rate Limiting: Consider impact on high-volume operations
When to read: See references/best-practices.md for comprehensive guidelines.
Debugging Hooks
Enable Debug Logging
# In hook function
import frappe
frappe.logger().debug(f"Hook called: {doc.doctype} - {method}")
Test Hooks
# In test file
def test_my_hook():
doc = frappe.get_doc({
"doctype": "Test DocType",
"field": "value"
})
doc.insert() # Triggers hooks
# Verify hook effects
assert some_condition
Common Issues
- Hook Not Firing: Check hooks.py syntax, restart bench
- Hook Order: Multiple hooks execute in registration order
- Infinite Loops: Hook modifies doc, triggering itself
- Performance: Slow hooks delay all operations
When to read: See references/debugging-hooks.md for troubleshooting guide.
Reference Files
For detailed information on specific hook categories:
- application-hooks.md - App metadata and configuration
- installation-hooks.md - Install, uninstall, and migration hooks
- document-hooks.md - Complete doc_events reference with examples
- permission-hooks.md - Permission extension hooks
- scheduler-hooks.md - Scheduled task configuration
- ui-hooks.md - Frontend customization hooks
- jinja-hooks.md - Template rendering hooks
- request-job-hooks.md - Request and job lifecycle hooks
- other-hooks.md - Additional hooks and extension points
- execution-order.md - Hook execution sequences
- best-practices.md - Patterns and guidelines
- debugging-hooks.md - Troubleshooting and testing
Core Implementation Files
Key files in Frappe codebase:
/frappe/__init__.py- Hook loading and execution (get_hooks,get_doc_hooks,call_hook)/frappe/model/document.py- Document lifecycle and hook calls/frappe/utils/boilerplate.py- Hooks template for new apps- Individual hook implementations throughout the codebase
Usage
When working with hooks:
- Identify the requirement: What behavior needs customization?
- Choose appropriate hook: Select based on event type and timing
- Register in hooks.py: Follow correct syntax for hook type
- Implement function: Create function with proper signature
- Test thoroughly: Verify hook fires and produces expected results
- Monitor performance: Ensure hooks don't slow down operations
Important Notes
- Hooks run synchronously in the main thread (except scheduler hooks)
- Multiple apps can register hooks for the same event (all execute in order)
- Hook execution order: by app installation order in
apps.txt - Changes to
hooks.pyrequire bench restart (bench restart) - Wildcard
*indoc_eventsapplies to all doctypes - Controller methods (like
validate()) run before hooks - Hooks should be idempotent and handle errors gracefully