I am trying to build a system where users are able to define and test their own regex patterns. To do this, I have the following setup:
import re
class ExtendedRegexValidator(models.Model):
pattern = models.TextField(
_('pattern'),
help_text=_('Required. Must be a valid regular expression pattern.')
)
def save(self, *args, **kwargs):
try:
re.compile(self.pattern)
except Exception as e:
# handle exception
super(ExtendedRegexValidator, self).save(*args, **kwargs)
Before saving, I try to compile a regex pattern using the value of the pattern
field of the model, which is a TextField
. Is this actually necessary? Is there a more ideal way to do this? This kinda feels hacky.
Is this actually necessary?
Yes, validation is necessary, as there will be valid strings that are not valid regular expressions. See Python's docs on re.error
:
Exception raised when a string passed to one of the functions here is not a valid regular expression (for example, it might contain unmatched parentheses) or when some other error occurs during compilation or matching.
Others have suggested doing this validation during form submission instead, but for the sake of data integrity, I think you were right to do it at the model layer. In your handling of re.error
, you can raise a ValidationError
that can be caught at the form submission layer.
Is there a more ideal way to do this? This kinda feels hacky.
Your validation code is in line with Python's philosophy of EAFP:
Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many
try
andexcept
statements. The technique contrasts with the LBYL style common to many other languages such as C.
I also don't see any built-in way to validate a string as a regex pattern without trying to use or compile it. However, I would suggest creating a custom model field for regex patterns so you can encapsulate this validation and potentially reuse the functionality in other models.