Skip to content

Commit 68668b1

Browse files
authored
update: Make it easier to support new types (#26)
1 parent 1436b0d commit 68668b1

9 files changed

Lines changed: 191 additions & 240 deletions

File tree

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
from uuid import uuid4
2+
3+
from pydantic import BaseModel
4+
5+
from fastapi_forge.enums import FieldDataType
6+
7+
8+
class DataTypeInfo(BaseModel):
9+
sqlalchemy_type: str
10+
sqlalchemy_prefix: bool
11+
python_type: str
12+
faker_field_value: str
13+
value: str
14+
test_value: str
15+
test_func: str = ""
16+
17+
18+
class DataTypeInfoRegistry:
19+
def __init__(self):
20+
self._registry: dict[FieldDataType, DataTypeInfo] = {}
21+
22+
def register(self, field_data_type: FieldDataType, data_type: DataTypeInfo):
23+
if field_data_type in self._registry:
24+
raise ValueError(f"Data type '{field_data_type}' is already registered.")
25+
self._registry[field_data_type] = data_type
26+
27+
def get(self, field_data_type: FieldDataType) -> DataTypeInfo:
28+
if field_data_type not in self._registry:
29+
raise ValueError(f"Data type '{field_data_type}' not found.")
30+
return self._registry[field_data_type]
31+
32+
def all(self) -> list[DataTypeInfo]:
33+
return list(self._registry.values())
34+
35+
36+
registry = DataTypeInfoRegistry()
37+
faker_placeholder = "factory.Faker({placeholder})"
38+
39+
40+
registry.register(
41+
FieldDataType.STRING,
42+
DataTypeInfo(
43+
sqlalchemy_type="String",
44+
sqlalchemy_prefix=True,
45+
python_type="str",
46+
faker_field_value=faker_placeholder.format(placeholder='"text"'),
47+
value="hello",
48+
test_value="'world'",
49+
),
50+
)
51+
52+
registry.register(
53+
FieldDataType.INTEGER,
54+
DataTypeInfo(
55+
sqlalchemy_type="Integer",
56+
sqlalchemy_prefix=True,
57+
python_type="int",
58+
faker_field_value=faker_placeholder.format(placeholder='"random_int"'),
59+
value="1",
60+
test_value="2",
61+
),
62+
)
63+
64+
registry.register(
65+
FieldDataType.FLOAT,
66+
DataTypeInfo(
67+
sqlalchemy_type="Float",
68+
sqlalchemy_prefix=True,
69+
python_type="float",
70+
faker_field_value=faker_placeholder.format(
71+
placeholder='"pyfloat", positive=True, min_value=0.1, max_value=100'
72+
),
73+
value="1.0",
74+
test_value="2.0",
75+
),
76+
)
77+
78+
registry.register(
79+
FieldDataType.BOOLEAN,
80+
DataTypeInfo(
81+
sqlalchemy_type="Boolean",
82+
sqlalchemy_prefix=True,
83+
python_type="bool",
84+
faker_field_value=faker_placeholder.format(placeholder='"boolean"'),
85+
value="True",
86+
test_value="False",
87+
),
88+
)
89+
90+
registry.register(
91+
FieldDataType.DATETIME,
92+
DataTypeInfo(
93+
sqlalchemy_type="DateTime(timezone=True)",
94+
sqlalchemy_prefix=True,
95+
python_type="datetime",
96+
faker_field_value=faker_placeholder.format(placeholder='"date_time"'),
97+
value="datetime.now(timezone.utc)",
98+
test_value="datetime.now(timezone.utc)",
99+
test_func=".isoformat()",
100+
),
101+
)
102+
103+
registry.register(
104+
FieldDataType.UUID,
105+
DataTypeInfo(
106+
sqlalchemy_type="UUID(as_uuid=True)",
107+
sqlalchemy_prefix=True,
108+
python_type="UUID",
109+
faker_field_value=str(uuid4()),
110+
value=str(uuid4()),
111+
test_value=str(uuid4()),
112+
),
113+
)
114+
115+
registry.register(
116+
FieldDataType.JSONB,
117+
DataTypeInfo(
118+
sqlalchemy_type="JSONB",
119+
sqlalchemy_prefix=False,
120+
python_type="dict[str, Any]",
121+
faker_field_value="{}",
122+
value="{}",
123+
test_value='{"another_key": 123}',
124+
),
125+
)

fastapi_forge/dtos.py

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
model_validator,
1010
)
1111

12+
from fastapi_forge.data_type_registry import DataTypeInfo, registry
1213
from fastapi_forge.enums import FieldDataType
1314
from fastapi_forge.string_utils import camel_to_snake_hyphen, snake_to_camel
1415

@@ -44,10 +45,8 @@ class ModelField(_Base):
4445
nullable: bool = False
4546
unique: bool = False
4647
index: bool = False
47-
4848
default_value: str | None = None
4949
extra_kwargs: dict[str, Any] | None = None
50-
5150
metadata: ModelFieldMetadata = ModelFieldMetadata()
5251

5352
@computed_field
@@ -56,6 +55,11 @@ def name_cc(self) -> str:
5655
"""Convert field name to camelCase."""
5756
return snake_to_camel(self.name)
5857

58+
@computed_field
59+
@property
60+
def type_info(self) -> DataTypeInfo:
61+
return registry.get(self.type)
62+
5963
@model_validator(mode="after")
6064
def _validate(self) -> Self:
6165
"""Validate field constraints."""
@@ -88,32 +92,6 @@ def _validate(self) -> Self:
8892
)
8993
return self
9094

91-
@computed_field
92-
@property
93-
def factory_field_value(self) -> str | dict | None:
94-
"""Return the appropriate factory default for the model field."""
95-
faker_placeholder = "factory.Faker({placeholder})"
96-
97-
if "email" in self.name and self.type == FieldDataType.STRING:
98-
return faker_placeholder.format(placeholder='"email"')
99-
100-
type_to_faker = {
101-
FieldDataType.STRING: '"text"',
102-
FieldDataType.INTEGER: '"random_int"',
103-
FieldDataType.FLOAT: '"pyfloat", positive=True, min_value=0.1, max_value=100',
104-
FieldDataType.BOOLEAN: '"boolean"',
105-
FieldDataType.DATETIME: '"date_time"',
106-
FieldDataType.JSONB: {},
107-
}
108-
109-
if self.type not in type_to_faker:
110-
return None
111-
112-
if self.type == FieldDataType.JSONB:
113-
return type_to_faker[FieldDataType.JSONB]
114-
115-
return faker_placeholder.format(placeholder=type_to_faker[self.type])
116-
11795

11896
class ModelRelationship(_Base):
11997
"""Represents a relationship between models."""

fastapi_forge/enums.py

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
from enum import StrEnum
22

33

4+
class HTTPMethod(StrEnum):
5+
GET = "get"
6+
GET_ID = "get_id"
7+
POST = "post"
8+
PATCH = "patch"
9+
DELETE = "delete"
10+
11+
412
class FieldDataType(StrEnum):
513
STRING = "String"
614
INTEGER = "Integer"
@@ -9,22 +17,3 @@ class FieldDataType(StrEnum):
917
DATETIME = "DateTime"
1018
UUID = "UUID"
1119
JSONB = "JSONB"
12-
13-
def as_python_type(self) -> str:
14-
return {
15-
FieldDataType.STRING: "str",
16-
FieldDataType.INTEGER: "int",
17-
FieldDataType.FLOAT: "float",
18-
FieldDataType.BOOLEAN: "bool",
19-
FieldDataType.DATETIME: "datetime",
20-
FieldDataType.UUID: "UUID",
21-
FieldDataType.JSONB: "dict[str, Any]",
22-
}[self]
23-
24-
25-
class HTTPMethod(StrEnum):
26-
GET = "get"
27-
GET_ID = "get_id"
28-
POST = "post"
29-
PATCH = "patch"
30-
DELETE = "delete"

0 commit comments

Comments
 (0)