且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

在Django URLField中允许空方案的最干净方法

更新时间:2023-11-25 12:36:16

您可以在调用super()之前将URLValidator子类化,并在http://之前添加无方案值的前缀.这样可以避免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)

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?

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)