Title
Custom DocType Controller Hooks (validate, before_submit, on_submit) Not Running Until Supervisor Restart (Production / Gunicorn Cache Issue)
Problem Statement
I created a custom DocType “Budget Adjustment” in my custom app/module (Budget Management) on Frappe/ERPNext v15. The DocType is submittable and has a proper Python controller file:
apps/budget_management/budget_management/budget_management/doctype/budget_adjustment/budget_adjustment.py
Inside the controller I added standard lifecycle hooks such as:
-
validate() -
before_submit() -
on_submit() -
on_cancel()
However, after editing the Python controller code, none of these hooks were executing when saving/submitting the document from the UI. Example:
def validate(self):
frappe.throw("SERVER VALIDATE IS RUNNING")
Even when saving a draft document (docstatus = 0), the form saved successfully and the hook did not run.
What I Observed
-
Saving from UI calls:
POST /api/method/frappe.desk.form.save.savedocs -
The controller class was correctly detected in console:
from frappe.model.base_document import get_controller
print(get_controller("Budget Adjustment"))
# budget_management.budget_management.doctype.budget_adjustment.budget_adjustment.BudgetAdjustment
- Whitelisted methods in the same controller file worked fine using
frappe.call(), but the lifecycle hooks did not run.
Root Cause / Fix
The issue was caused by stale Python code in the running production workers (Gunicorn).
My code changes were not being loaded into the running processes.
After running:
sudo supervisorctl restart all
bench --site aogc clear-cache
the updated controller code started working immediately, and validate(), before_submit(), and on_submit() fired correctly.
Question to Community
In production deployments of Frappe v15:
-
What is the recommended correct restart procedure after changing DocType controller Python code?
-
Is
bench restartalways sufficient, or shouldsupervisorctl restart all(or systemd service restart) always be used? -
Are there best practices to avoid confusion caused by stale worker processes when developing custom apps?