Skip to content

Commit 14d56cf

Browse files
committed
Fix regex handling and add related tests
1 parent e0ede50 commit 14d56cf

2 files changed

Lines changed: 61 additions & 2 deletions

File tree

src/drf_pydantic/parse.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ def _convert_type( # noqa: PLR0911
358358
if issubclass(type_, pydantic.BaseModel):
359359
return create_serializer_from_model(type_, drf_config=drf_config)(**kwargs)
360360
# Decimal
361-
elif type_ is decimal.Decimal:
361+
if type_ is decimal.Decimal:
362362
_context = decimal.getcontext()
363363
kwargs["max_digits"] = kwargs.get("max_digits", None) or _context.prec
364364
kwargs["decimal_places"] = (
@@ -367,7 +367,11 @@ def _convert_type( # noqa: PLR0911
367367
return serializers.DecimalField(**kwargs)
368368
# Regex
369369
elif field is not None and any(
370-
isinstance(item, pydantic.StringConstraints) and item.pattern is not None
370+
(isinstance(item, pydantic.StringConstraints) and item.pattern is not None)
371+
or (
372+
isinstance(item, PydanticMetadata)
373+
and getattr(item, "pattern", None) is not None
374+
)
371375
for item in field.metadata
372376
):
373377
regex_patterns: List[Union[str, re.Pattern[str]]] = []
@@ -377,6 +381,11 @@ def _convert_type( # noqa: PLR0911
377381
and item.pattern is not None
378382
):
379383
regex_patterns.append(item.pattern)
384+
elif (
385+
isinstance(item, PydanticMetadata)
386+
and getattr(item, "pattern", None) is not None
387+
):
388+
regex_patterns.append(getattr(item, "pattern"))
380389
if len(regex_patterns) > 1:
381390
raise FieldConversionError(
382391
f"Field has multiple regex patterns: {regex_patterns}"

tests/test_fields.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,41 @@ class Person(BaseModel):
105105
)
106106
assert serializer.fields["phone_number"].allow_null is False
107107

108+
def test_regex_from_field(self):
109+
pattern = r"^\+?[0-9]+$"
110+
111+
class Person(BaseModel):
112+
phone_number: typing.Annotated[
113+
str,
114+
pydantic.Field(pattern=pattern),
115+
]
116+
117+
serializer = Person.drf_serializer()
118+
119+
assert isinstance(serializer.fields["phone_number"], serializers.RegexField)
120+
assert serializer.fields["phone_number"].validators[-1].regex == re.compile(
121+
pattern
122+
)
123+
assert serializer.fields["phone_number"].allow_null is False
124+
125+
def test_compiled_regex(self):
126+
pattern = r"^\+?[0-9]+$"
127+
compiled_pattern = re.compile(pattern)
128+
129+
class Person(BaseModel):
130+
phone_number: typing.Annotated[
131+
str,
132+
pydantic.StringConstraints(pattern=compiled_pattern),
133+
]
134+
135+
serializer = Person.drf_serializer()
136+
137+
assert isinstance(serializer.fields["phone_number"], serializers.RegexField)
138+
assert serializer.fields["phone_number"].validators[-1].regex == re.compile(
139+
pattern
140+
)
141+
assert serializer.fields["phone_number"].allow_null is False
142+
108143
def test_optional_regex(self):
109144
pattern = r"^\+?[0-9]+$"
110145

@@ -137,6 +172,21 @@ class Person(BaseModel):
137172
assert "Error when converting model: Person" in str(exc_info.value)
138173
assert "Field has multiple regex patterns" in str(exc_info.value)
139174

175+
def test_multiple_regex_with_field_error(self):
176+
with pytest.raises(ModelConversionError) as exc_info:
177+
178+
class Person(BaseModel):
179+
phone_number: typing.Annotated[
180+
str,
181+
pydantic.StringConstraints(pattern=r"123"),
182+
pydantic.Field(pattern=r"456"),
183+
]
184+
185+
Person.drf_serializer()
186+
187+
assert "Error when converting model: Person" in str(exc_info.value)
188+
assert "Field has multiple regex patterns" in str(exc_info.value)
189+
140190
def test_url(self):
141191
class Person(BaseModel):
142192
website: pydantic.HttpUrl

0 commit comments

Comments
 (0)