Django's django.db.models.URLField
uses a django.core.validators.URLValidator
:
class URLField(CharField):
default_validators = [validators.URLValidator()]
Since it does not specify the schemes to accept, URLValidator defaults to this set:
schemes = ['http', 'https', 'ftp', 'ftps']
I want my URLField to accept ssh://
URLs, so I tried this:
class SSHURLField(models.URLField):
'''URL field that accepts URLs that start with ssh:// only.'''
default_validators = [URLValidator(schemes=['ssh'])]
However when I try to save a new object with a valid ssh://
URL, I get rejected.
This also happens if I skip inheriting from URLField and inherit from CharField directly: (Edit: Actually this does work after I recreated my database. I'm not sure why the former doesn't work.)
class SSHURLField(models.CharField):
'''URL field that accepts URLs that start with ssh:// only.'''
default_validators = [URLValidator(schemes=['ssh'])]
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 64
super(SSHURLField, self).__init__(*args, **kwargs)
When I use the URLValidator directly in a test, it works:
def test_url(url):
try:
URLValidator(schemes=['ssh'])(url)
return True
except:
return False
>>> test_url('ssh://example.com/')
True
>>> test_url('http://example.com/')
False
As @IainDillingham mentioned in the comments, this is a bug in Django: overriding the default_validator of a subclassed ModelField does not necessarily override the default_validator of the FormField to which that base class is associated.
For your example, django.db.models.URLField, we can see its associated form field[0] is django.forms.fields.URLField[1]. So the workaround here is to also override def formfield(...)
for your customized SSHURLField
, to reference a custom django.forms.fields.URLField subclass with the same validator, like so:
from django.core import validators
from django.db import models
from django.forms.fields import URLField as FormURLField
class SSHURLFormField(FormURLField):
default_validators = [validators.URLValidator(schemes=['ssh'])]
class SSHURLField(models.URLField):
'''URL field that accepts URLs that start with ssh:// only.'''
default_validators = [validators.URLValidator(schemes=['ssh'])]
def formfield(self, **kwargs):
return super(SSHURLField, self).formfield(**{
'form_class': SSHURLFormField,
})
[0] https://github.com/django/django/blob/e17088a108e604cad23b000a83189fdd02a8a2f9/django/db/models/fields/init.py#L2275,L2293
[1] https://github.com/django/django/blob/e17088a108e604cad23b000a83189fdd02a8a2f9/django/forms/fields.py#L650
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With