drf-spectacular
drf-spectacular copied to clipboard
many serializer field regex validator, but show single pattern
When I have used multiple RegexValidator, I only see a single pattern.
I am using google translator because my english is not good. Sorry if you are uncomfortable with reading.
This is a problem that occurs in AutoSchema's _map_field_validators method, and the pattern is overwritten in schema['pattern'] when validator of the same type is used.
If you don't use multiple RegexValidators and just use one RegexValidator with complex patterns, you can solve the problem. However, I prefer to use multiple RegexValidators in smaller units rather than using RegexValidators with fat patterns.
I am defining CustomAutoSchema to solve the problem in the following way.
# schema['pattern'] = pattern
if "pattern" not in schema:
schema["pattern"] = pattern
else:
if isinstance(schema["pattern"], str):
schema["pattern"] = [schema["pattern"], pattern]
elif isinstance(schema["pattern"], list):
schema["pattern"].append(pattern)
Is the process of handling Schema of RegexValidator intended?
I always use drf_spectacular with gratitude.
Hi @by-Exist,
interesting case! OpenAPI requires the pattern
to be a string, so a list of patterns is invalid strictly speaking.
If the validators were joined with an OR this would be a solution: pattern: (pattern1|pattern2|pattern3)
.
However, it looks like multiple validators are connected with an AND (DRF processing logic). This is a lot harder to do with an regex as this involves lookahead among other things. i'm not sure if there is even a ECMA-compliant regex (OpenAPI requirement) that can model this properly
I designed the following code by searching.
class CustomAutoSchema(AutoSchema):
def _get_converted_regex_pattern(
self, pattern: str, regex_validator: validators.RegexValidator
):
format_string = "(?={})" if not regex_validator.inverse_match else "(?!{})"
return format_string.format(pattern)
def _combine_regex_patterns(self, patterns):
combine_pattern_format_string = "^{}.*$"
return combine_pattern_format_string.format("".join(patterns))
def _map_field_validators(self, field, schema):
converted_regex_patterns = []
for v in field.validators:
# ...
elif isinstance(v, validators.RegexValidator):
pattern = v.regex.pattern.encode("ascii", "backslashreplace").decode()
pattern = pattern.replace(r"\x", r"\u00")
pattern = pattern.replace(r"\Z", "$").replace(r"\A", "^")
# schema['pattern'] = pattern
if len(converted_regex_patterns) == 0:
schema["pattern"] = pattern
converted_pattern = self._get_converted_regex_pattern(pattern, v)
converted_regex_patterns.append(converted_pattern)
else:
converted_pattern = self._get_converted_regex_pattern(pattern, v)
converted_regex_patterns.append(converted_pattern)
combine_pattern = self._combine_regex_patterns(
converted_regex_patterns
)
schema["pattern"] = combine_pattern
However, I am new to regular expressions so I can't check if the code is correct.