1515
1616BoundedStr = Annotated [str , Field (..., min_length = 1 , max_length = 100 )]
1717SnakeCaseStr = Annotated [BoundedStr , Field (..., pattern = r"^[a-z][a-z0-9_]*$" )]
18+ PascalCaseStr = Annotated [
19+ BoundedStr ,
20+ Field (..., pattern = r"^[A-Z][a-zA-Z0-9]*$" ),
21+ ]
1822ModelName = SnakeCaseStr
1923FieldName = SnakeCaseStr
2024BackPopulates = Annotated [str , Field (..., pattern = r"^[a-z][a-z0-9_]*$" )]
@@ -36,6 +40,51 @@ class ModelFieldMetadata(_Base):
3640 is_foreign_key : bool = False
3741
3842
43+ class CustomEnumValue (_Base ):
44+ """Represents a single name/value pair in a custom enum."""
45+
46+ name : Annotated [
47+ BoundedStr ,
48+ Field (
49+ ...,
50+ pattern = r"^[a-zA-Z][a-zA-Z0-9_]*$" ,
51+ ),
52+ ]
53+ value : BoundedStr
54+
55+
56+ class CustomEnum (_Base ):
57+ """Represents a custom PostgreSQL ENUM type."""
58+
59+ name : PascalCaseStr
60+ values : Annotated [list [CustomEnumValue ], Field (..., min_length = 1 )]
61+
62+ @model_validator (mode = "after" )
63+ def _validate_enum (self ) -> Self :
64+ names = [v .name for v in self .values ]
65+ values = [v .value for v in self .values ]
66+
67+ if len (names ) != len (set (names )):
68+ raise ValueError (f"Enum '{ self .name } ' has duplicate names." )
69+ if len (values ) != len (set (values )):
70+ raise ValueError (f"Enum '{ self .name } ' has duplicate values." )
71+ return self
72+
73+ @computed_field
74+ @property
75+ def class_definition (self ) -> str :
76+ """Returns a string representing the Python Enum class definition."""
77+ lines : list [str ] = []
78+ lines .extend ([f"class { self .name } (StrEnum):" ])
79+
80+ value_lines : list [str ] = []
81+ for v in self .values :
82+ value_lines .extend ([f' { v .name } = "{ v .value } "' ])
83+
84+ lines .extend (value_lines )
85+ return "\n " .join (lines )
86+
87+
3988class ModelField (_Base ):
4089 """Represents a field in a model with validation and computed properties."""
4190
0 commit comments