Index: core/validators.py =================================================================== --- core/validators.py (revision 17904) +++ core/validators.py (working copy) @@ -33,6 +33,37 @@ if not self.regex.search(smart_unicode(value)): raise ValidationError(self.message, code=self.code) +class DomainNameValidator(RegexValidator): + # from URLValidator + there can be most 127 labels (at most 255 total chars) + regex = re.compile( + r'^(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.){0,126}(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?))$', + re.IGNORECASE + ) + message = 'Enter a valid plain or IDNA domain name value' + + def __init__(self, *args, **kwargs): + if 'accept_idna' in kwargs: + self.accept_idna = kwargs['accept_idna'] + else: + self.accept_idna = True + super(DomainNameValidator, self).__init__(*args, **kwargs) + + def __call__(self, value): + # validate + try: + super(DomainNameValidator, self).__call__(value) + except ValidationError as e: + # maybe this is a unicode-encoded IDNA string? + if not self.accept_idna: raise + if not value: raise + # convert it unicode -> ascii + try: + asciival = smart_unicode(value).encode('idna') + except UnicodeError: + raise e # raise the original ASCII error + # validate the ascii encoding of it + super(DomainNameValidator, self).__call__(asciival) + class URLValidator(RegexValidator): regex = re.compile( r'^(?:http|ftp)s?://' # http:// or https://