|
1 | 1 | from enum import Enum |
2 | | -from typing import Any, Self, TypeVar |
| 2 | +from typing import TypeVar |
3 | 3 |
|
4 | 4 | from pydantic import BaseModel, ConfigDict |
5 | | -from pydantic._internal._model_construction import ModelMetaclass |
6 | 5 |
|
7 | 6 | # Generic types |
8 | 7 | T = TypeVar("T", bound=Enum) |
9 | 8 |
|
10 | 9 |
|
11 | | -class FieldStr(str): |
12 | | - """Type-safe field name reference string. |
13 | | -
|
14 | | - Allows referencing Pydantic model field names as class attributes |
15 | | - instead of hardcoded strings, enabling IDE autocompletion and |
16 | | - refactoring support. |
17 | | -
|
18 | | - Examples: |
19 | | - >>> class UserDTO(BaseDTO): |
20 | | - ... name: str |
21 | | - ... email: str |
22 | | - >>> UserDTO.name # returns FieldStr("name") |
23 | | - 'name' |
24 | | - >>> UserDTO.name == "name" |
25 | | - True |
26 | | - """ |
27 | | - |
28 | | - __slots__ = ("name",) |
29 | | - |
30 | | - def __new__(cls, value: str) -> Self: |
31 | | - """Create a new FieldStr instance. |
32 | | -
|
33 | | - Args: |
34 | | - value: The field name string. |
35 | | -
|
36 | | - Returns: |
37 | | - FieldStr: A string subclass carrying the field name. |
38 | | - """ |
39 | | - obj = super().__new__(cls, value) |
40 | | - obj.name = value |
41 | | - return obj |
42 | | - |
43 | | - |
44 | | -class BaseMeta(ModelMetaclass): |
45 | | - """Metaclass that adds FieldStr class attributes for each Pydantic model field. |
46 | | -
|
47 | | - After Pydantic's ModelMetaclass constructs the class and populates |
48 | | - ``model_fields``, this metaclass overwrites the corresponding class |
49 | | - attributes with :class:`FieldStr` instances so that |
50 | | - ``MyDTO.field_name`` returns a type-safe string equal to ``"field_name"``. |
51 | | -
|
52 | | - Instance attribute access is unaffected because Python resolves |
53 | | - instance ``__dict__`` entries before class attributes. |
54 | | - """ |
55 | | - |
56 | | - def __new__(mcs, name: str, bases: tuple[type, ...], namespace: dict[str, Any], **kwargs: Any) -> type: # noqa: ANN401 |
57 | | - """Create a new class with FieldStr attributes for each model field. |
58 | | -
|
59 | | - Args: |
60 | | - name: The class name. |
61 | | - bases: The base classes. |
62 | | - namespace: The class namespace. |
63 | | - **kwargs: Additional keyword arguments forwarded to ModelMetaclass. |
64 | | -
|
65 | | - Returns: |
66 | | - The newly created class with FieldStr attributes. |
67 | | - """ |
68 | | - cls = super().__new__(mcs, name, bases, namespace, **kwargs) |
69 | | - for field_name in cls.model_fields: # ty:ignore[unresolved-attribute] |
70 | | - setattr(cls, field_name, FieldStr(field_name)) |
71 | | - return cls |
72 | | - |
73 | | - |
74 | | -class BaseDTO(BaseModel, metaclass=BaseMeta): |
| 10 | +class BaseDTO(BaseModel): |
75 | 11 | """Base Data Transfer Object class. |
76 | 12 |
|
77 | | - This class extends Pydantic's BaseModel with a custom metaclass that |
78 | | - provides type-safe field name references. After class construction, |
79 | | - each field name is accessible as a :class:`FieldStr` class attribute. |
80 | | -
|
81 | | - Examples: |
82 | | - >>> class ProductDTO(BaseDTO): |
83 | | - ... title: str |
84 | | - ... price: float |
85 | | - >>> ProductDTO.title # FieldStr("title") |
86 | | - 'title' |
87 | | - >>> product = ProductDTO(title="Widget", price=9.99) |
88 | | - >>> product.title # actual value |
89 | | - 'Widget' |
| 13 | + This class extends Pydantic's BaseModel to provide common configuration |
| 14 | + for all DTOs in the application. |
90 | 15 | """ |
91 | 16 |
|
92 | 17 | model_config = ConfigDict( |
|
0 commit comments