Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cleanest Way to Allow Empty Scheme in Django URLField

I'm using Django 2.0 and trying to allow users to input a link to their website in a django ModelForm. I would like for my URLField to validate urls even when they have no scheme prefix.

I have read this solution: django urlfield http prefix. I'm wondering if there is a way to achieve this without writing my own custom regex in a RegexValidator. It doesn't seem like a very DRY solution to repeat all of the regex logic that is built into the URLValidator.

I want to do something like the below:

website = models.CharField(max_length=400, blank=True, null=True, validators=[URLValidator(schemes=['', 'http', 'https', 'ftp', 'ftps'])])

This obviously doesn't work because of the way the URLValidator's call method validates against the given schemes.

    def __call__(self, value):
        # Check first if the scheme is valid
        scheme = value.split('://')[0].lower()
        if scheme not in self.schemes:
            raise ValidationError(self.message, code=self.code)

Adding an empty string to the schemes list doesn't work because splitting the url in this way returns the entire URL (as a user typically does not include :// if that user is not including a scheme prefix).

Now, I could subclass URLField and overwrite the entire call method in order to change the way that it handles schemes, but the call method includes all of the regex so this seems to be no more DRY than simply copying the URLField regex into my own RegexValidator.

Is there a cleaner or more "Djangonic" way to achieve this?

like image 304
cmanbst Avatar asked Apr 23 '18 14:04

cmanbst


1 Answers

You could subclass the URLValidator and prefix schemeless values with http:// before calling super(). This avoids any duplication of code from URLValidator.

from django.core.validators import URLValidator

class OptionalSchemeURLValidator(URLValidator):
    def __call__(self, value):
        if '://' not in value:
            # Validate as if it were http://
            value = 'http://' + value
        super(OptionalSchemeURLValidator, self).__call__(value)
like image 133
Alasdair Avatar answered Oct 13 '22 19:10

Alasdair