Relationship in ER diagram from exported .grist file

I took a completely different approach. You can see the relationship in the code view. ‘I’ wrote a Python programme that uses AST to convert Python code into Mermaid output, which can then be imported into draw.io. It’s not the coolest solution, but it works.

I just did a short demo
Here you see the table with 2 References

Thats how it looks after the script, importing the mermind “code”

That is the Python Script (testet with 3.13.7)
You just need to replace the code in the string python_code

import ast

class ERDGenerator(ast.NodeVisitor):
    def __init__(self):
        self.classes = {}
        self.relationships = []

    def visit_ClassDef(self, node):
        class_name = node.name
        attributes = []
        for item in node.body:
            if isinstance(item, ast.Assign):
                for target in item.targets:
                    if isinstance(target, ast.Name):
                        attr_name = target.id
                        attr_type = self.get_attribute_type(item.value)
                        attributes.append((attr_type, attr_name))  # Store as tuple (type, name)
            elif isinstance(item, ast.FunctionDef):
                # Skip methods for ERD
                continue
        
        self.classes[class_name] = attributes
        self.generic_visit(node)

    def get_attribute_type(self, value):
        if isinstance(value, ast.Call):
            if isinstance(value.func, ast.Attribute):
                # Handle cases like grist.Text() or grist.Reference()
                if value.func.attr == 'Text':
                    return 'string'
                elif value.func.attr == 'Reference':
                    if isinstance(value.args[0], ast.Constant):
                        return value.args[0].value  # Return the name in the brackets
                elif value.func.attr == 'Numeric':
                    return 'float'
                elif value.func.attr == 'Any':
                    return 'string'
                elif value.func.attr == 'DateTime':
                    return 'DateTime'
                elif value.func.attr == 'Integer':
                    return 'Integer'
                elif value.func.attr == 'Toggle':
                    return 'Boolean'
                elif value.func.attr == 'Date':
                    return 'Date'
                elif value.func.attr in ['Choice', 'Choice List']:
                    return 'List'
        return "unknown"

    def generate_mermaid(self):
        mermaid_diagram = "erDiagram\n\n"
        
        for class_name, attributes in self.classes.items():
            mermaid_diagram += f"    {class_name} {{\n"
            for attr_type, attr_name in attributes:
                mermaid_diagram += f"        {attr_type} {attr_name}\n"
            mermaid_diagram += "    }\n\n"

        # Create relationships based on foreign keys
        for class_name, attributes in self.classes.items():
            for attr_type, attr_name in attributes:
                if attr_type in self.classes:  # Check if the attribute type is a class name
                    mermaid_diagram += f"    {class_name} ||--|| {attr_type}: has\n"

        return mermaid_diagram

def convert_python_to_mermaid(python_code):
    tree = ast.parse(python_code)
    erd_generator = ERDGenerator()
    erd_generator.visit(tree)
    return erd_generator.generate_mermaid()

# Example Python code input
python_code = '''
@grist.UserTable
class Items:
  Nr = grist.Date()
  Name = grist.Text()


@grist.UserTable
class Kunde:
  A = grist.Text()

  def B(rec, table):
    return None

  def C(rec, table):
    return None


@grist.UserTable
class Order:
  Kunde = grist.Reference('Kunde')
  Date = grist.Date()
  Items = grist.Reference('Items')

  def A(rec, table):
    return None
'''

# Convert and print the Mermaid ERD
mermaid_output = convert_python_to_mermaid(python_code)
with open  ("mermaid_output.txt","w",encoding="utf-8") as file:
    file.write(mermaid_output)

The Mermind-Code looks like

erDiagram

    Items {
        Date Nr
        string Name
    }

    Kunde {
        string A
    }

    Order {
        Kunde Kunde
        Date Date
        Items Items
    }

    Order ||--|| Kunde: has
    Order ||--|| Items: has

I know this topic is quite old, but perhaps it will help as a workaround.

Greets
Philip

2 Likes