Introduction
Welcome, developers! Today, we are diving deep into one of the core features of Empress, the Controller. This feature lies at the heart of your software development journey with Empress. It plays a pivotal role in managing how values are loaded from the database, parsed and saved back into the database.
Understanding Controllers
In Empress, a Controller is a standard Python class that extends from the frappe.model.Document base class. This base class is the core logic of a DocType, which is a model that defines the type of a document like a User, a Blog Post, etc.
Let’s say you create a DocType named Person. Empress automatically generates a Python file named person.py in the backend. This file might look something like:
import frappe
from frappe.model.document import Document
class Person(Document):
pass
In this scenario, all the fields of the Person class are available as attributes.
Controller Methods
You can enhance the functionality of your Controller by adding custom methods, which can then be called using the doc object. Let’s see an example:
# controller class
class Person(Document):
def get_full_name(self):
"""Returns the person's full name"""
return f"{self.first_name} {self.last_name}"
# somewhere in your code
>>> doc = frappe.get_doc("Person", "000001")
>>> doc.get_full_name()
John Doe
Controller Hooks
To add custom behaviour during the lifecycle of a document, we have controller hooks. These hooks can be used to invoke specific functions at different stages of a document’s lifecycle. Below is the list of these hooks:
| Method Name | Description | Insert | Save | Submit | Cancel | Update after submit |
|---|---|---|---|---|---|---|
before_insert |
Called before a document is prepared for insertion. | X | ||||
before_naming |
Called before the name property of the document is set. |
X | ||||
autoname |
If defined in the controller, this method is used to set name property of the document. |
X | ||||
before_validate |
Called before validation. Use this for auto setting missing values. | X | X | X | ||
validate |
Use this method to throw any validation errors and prevent the document from saving. | X | X | X | ||
before_save |
Called before the document is saved. | X | X | |||
before_submit |
Called before the document is submitted. | X | X | |||
before_cancel |
Called before the document is cancelled. | X | ||||
before_update_after_submit |
Called when doc fields are updated on submitted document. | X | ||||
db_insert |
Inserts document in database, do not override this unless you’re working on virtual DocType. | X | ||||
after_insert |
Called after the document is inserted into the database. | X | ||||
db_update |
Updates document in database, do not override this unless you’re working on virtual DocType. | X | X | X | X | |
on_update |
Called when values of an existing document are updated. | X | X | |||
on_submit |
Called when a document is submitted. | X | ||||
on_cancel |
Called when a submitted document is cancelled. | X | ||||
on_update_after_submit |
Called when a submitted document values are updated. | X | ||||
on_change |
Called when a document’s values has been changed. This method is also called when db_set is performed, so operation performed in this this method should be idempotent. | X | X | X | X | X |
To use a controller hook, just define a class method with that name. For example:
class Person(Document):
def validate(self):
if self.age <= 18:
frappe.throw("Person's age must be at least 18")
def after_insert(self):
frappe.sendmail(recipients=[self.email], message="Thank you for registering!")
Document Handling
One of the key functionalities that the Controller feature offers is the handling of documents. A document is an instance of a DocType and usually maps to a single row in the database table. We refer to it as doc in code.
For instance, to create a new document and save it to the database, we can use:
doc = frappe.get_doc({
'doctype': 'Person',
'first_name': 'John',
'last_name': 'Doe'
})
doc.insert()
doc.name # 000001
Type Annotations
Starting from Version 15, Empress supports automatically generating Python type annotations in controller files. These annotations can be used for auto-completion, reference, and type-checking inside the controller file.
class Person(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
first_name: DF.Data
last_name: DF.Data
user: DF.Link
# end: auto-generated types
pass
These annotations are generated when creating or updating doctypes. If you modify the code block, it will get overridden on the next update. You can configure automatic exporting in your app by adding the following hook:
# hooks.py
export_python_type_annotations = True
Wrapping Up
Understanding and effectively utilizing the Controller feature is a significant step towards mastering backend programming with Empress. From managing how values are fetched and stored in the database to adding custom methods and using hooks, the Controller feature allows developers to customize and optimize their software development process.
Remember, the key to effective software development is understanding and leveraging the features and functionalities at your disposal. So, dive deep, explore, and make the most out of the feature-rich world of Empress. Happy coding!