Guide to Empress Virtual DocType

Introduction

Empress’ Virtual DocType feature-extension is a pivotal tool for developers. It provides the unique ability to create DocTypes with custom data sources, while utilizing the frontend, resource APIs, and roles and permissions from the Empress framework.

This guide delves into the technical realm of the Virtual DocType feature, providing an in-depth understanding of its utilization, integration, and customization in software development.

Understanding the Virtual DocType Functionality

Virtual DocTypes function like normal DocTypes on the frontend, making them indistinguishable for the end user. Yet, they give developers more control over the DocType’s data source. This means that the data source for a Virtual DocType can be anything: an external API, a secondary database, JSON or CSV files, etc. This flexibility allows developers to plug-in database backends other than MariaDB and Postgres, empowering the Empress Framework.

Creating a Virtual DocType

Creating a Virtual DocType is simple. While creating the DocType, select the Virtual DocType checkbox.

Custom Controller Creation

A custom controller can be created for the Virtual DocType. As an example, the following controller code uses a JSON file as the DocType data source:

class VirtualDoctype(Document):
    """This is a virtual doctype controller for demo purposes.

    - It uses a single JSON file on disk as "backend".
    - Key is docname and value is the document itself.

    Example:
    {
            "doc1": {"name": "doc1", ...}
            "doc2": {"name": "doc2", ...}
    }
    """

    DATA_FILE = "data_file.json"

    @staticmethod
    def get_current_data() -> dict[str, dict]:
        """Read data from disk"""
        if not os.path.exists(VirtualDoctype.DATA_FILE):
            return {}

        with open(VirtualDoctype.DATA_FILE) as f:
            return json.load(f)

    @staticmethod
    def update_data(data: dict[str, dict]) -> None:
        """Flush updated data to disk"""
        with open(VirtualDoctype.DATA_FILE, "w+") as data_file:
            json.dump(data, data_file)

    def db_insert(self, *args, **kwargs):
        d = self.get_valid_dict(convert_dates_to_str=True)

        data = self.get_current_data()
        data[d.name] = d

        self.update_data(data)

    def load_from_db(self):
        data = self.get_current_data()
        d = data.get(self.name)
        super(Document, self).__init__(d)

    def db_update(self, *args, **kwargs):
        # For this example insert and update are same operation,
        # it might be  different for you.
        self.db_insert(*args, **kwargs)

    def delete(self):
        data = self.get_current_data()
        data.pop(self.name, None)
        self.update_data(data)

    @staticmethod
    def get_list(args):
        data = VirtualDoctype.get_current_data()
        return [frappe._dict(doc) for name, doc in data.items()]

    @staticmethod
    def get_count(args):
        data = VirtualDoctype.get_current_data()
        return len(data)

    @staticmethod
    def get_stats(args):
        return {}

To integrate other data sources with the Virtual DocType, add controller methods defining the database access.

Frontend Integration

The frontend for Virtual DocTypes remains unchanged, and all /api/resource methods defined by the framework are compatible with Virtual DocTypes.

Conclusion

The Virtual DocType feature contributes significantly to the development and customization of business solutions. It offers developers an efficient way to define custom DocTypes in the system without creating a table in the database. By empowering developers with more control over the DocType’s data source, it enhances the functionality and effectiveness of the Empress framework.