Skip to content

Custom Generators

Want to do anything with your prisma schema?

You can easily write your own prisma generator in python!

How does it work?

A Prisma generator consists of two steps:

  • generator metadata
  • asset generation

Metadata

The metadata step is used by Prisma to collect information about your generator, this includes information like:

  • The name of the generator
  • The default output location

This data is represented in Prisma Client Python with the Manifest model:

from prisma.generator import BaseGenerator, Manifest

class MyGenerator(BaseGenerator):
    def get_manifest(self) -> Manifest:
        return Manifest(
            name='Custom Prisma Generator'
            default_output='out.txt',
        )

Generation

Prisma sends generators the Prisma Schema DMMF during the generate step, this is your Prisma Schema in AST form.

The DMMF is represented in Prisma Client python with the Data model:

from pathlib import Path
from prisma.generator import BaseGenerator, DefaultData

class MyGenerator(BaseGenerator):
    # some code has been ommited from this example for brevity
    # the full boilerplate example can be found below
    def generate(self, data: DaDefaultDatata) -> None:
        content = [
            model.name
            for model in data.dmmf.datamodel.models
        ]
        file = Path(data.generator.output.value)
        file.write_text('\n'.join(content))

Warning

Changes to the structure of the DMMF are not considered breaking changes

Boilerplate

The minimum code required to get started writing your own Prisma generator:

from prisma.generator import BaseGenerator, Manifest, DefaultData

class MyGenerator(BaseGenerator):
    def get_manifest(self) -> Manifest:
        return Manifest(
            name='My Generator',
            default_output='output.txt',
        )

    def generate(self, data: DefaultData) -> None:
        pass

if __name__ == '__main__':
    MyGenerator.invoke()

Custom Configuration Options

You can also add custom config options directly in the Prisma Schema file! For example, consider the following schema:

generator my_generator {
  provder   = "python my_generator.py"
  my_option = 1
}

To support defining custom options you need to define the generator slightly differently:

from pydantic import BaseModel

from prisma.generator import Manifest, GenericData, GenericGenerator


# custom options must be defined using a pydantic BaseModel
class Config(BaseModel):
    my_option: int


# we don't technically need to define our own Data class
# but it makes typing easier
class Data(GenericData[Config]):
    pass


# the GenericGenerator[Data] part is what tells Prisma Client Python to use our
# custom Data class with our custom Config class
class MyGenerator(GenericGenerator[Data]):
    def get_manifest(self) -> Manifest:
        return Manifest(
            name='My Custom Generator Options',
            default_output='schema.md',
        )

    def generate(self, data: Data) -> None:
        # generate some assets here
        pass


if __name__ == '__main__':
    MyGenerator.invoke()

Example

In this example we're going to create a simple generator that creates a markdown file like this:

# My Prisma Schema

This file is automatically generated every time `prisma generate` is ran.

## User Model

## Post Model

Create a new file called my_generator.py and add the following code:

from pathlib import Path

from prisma.generator import Manifest, DefaultData, BaseGenerator

TEMPLATE = """
# My Prisma Schema

This file is automatically generated every time `prisma generate` is ran.
"""

MODEL_TEMPLATE = """
## {0.name} Model
"""


class MyGenerator(BaseGenerator):
    def get_manifest(self) -> Manifest:
        return Manifest(
            name='My Cool Generator',
            default_output='schema.md',
        )

    def generate(self, data: DefaultData) -> None:
        content = TEMPLATE
        for model in data.dmmf.datamodel.models:
            content += MODEL_TEMPLATE.format(model)

        # make sure you use the output value given in the Prisma DMMF
        # as the output location can be customised!
        file = Path(data.generator.output.value)
        file.write_text(content)


if __name__ == '__main__':
    MyGenerator.invoke()

Now all we need to do is add a new generator to our Prisma Schema file:

generator my_generator {
  provider = "python my_generator.py"
}

Limitations

Redundant Validation

It is currently not possible to disable any data validation that only applies to Prisma Client Python. For example, the following schema will raise a validation error as the field name would generate invalid python code.

model User {
  id   Int @default(autoincrement())
  from String
}

However you may not be generating python code or even be using the Prisma Client Python generator in your project which would make this validation error redundant.