0

I'm trying to validate an expected IP value in my Django model as per the below:

dmvpn_dsl = models.GenericIPAddressField(protocol='IPv4', verbose_name="DMVPN DSL IP", \
            validators=[validate_dmvpn_ip('','172.16.100.')], blank=True, null=True)

and in my functions i have

def validate_dmvpn_ip(value,subnet):
    from django.core.exceptions import ValidationError
    if subnet not in value or value != '0.0.0.0':
        raise ValidationError(u'Invalid IP address, please check corrent octects have been assigned')

i just need to pass the current value to the function to check it, but am not sure how to set this?

Thanks

AlexW
  • 2,843
  • 12
  • 74
  • 156
  • Why not to move subnet into validation function if it's hardcoded anyway? Then you can have just validators=[validate_dmvpn_ip] – zelenyjan Apr 18 '17 at 12:40

1 Answers1

1

When you're doing validators=[validate_dmvpn_ip('','172.16.100.')], you end up with the return value of calling the validation function as the validator (which is validators=[None] because you're not returning anything). It looks like you're trying to create a customizable validation function. For that you'll need to write a function that returns a another:

def validate_dmvpn_ip(subnet):
    from django.core.exceptions import ValidationError

    def validator(value):
        if subnet not in value or value != '0.0.0.0':
            raise ValidationError('Invalid IP address, please check corrent octects have been assigned')

    return validator

And you use it like so:

# outside model:
main_validator = validate_dmvpn_ip('172.16.100.')
main_validator.__name__ = 'main_validator'
main_validator.__module__ = 'path.to.this.module'

# inside model
dmvpn_dsl = models.GenericIPAddressField(protocol='IPv4', verbose_name="DMVPN DSL IP", \
    validators=[main_validator], blank=True, null=True)

The outer function returns another function that takes a value to validate, but still has access to the subnet to validate against.

Notice how I'm signing the validator to another variable and assigning the __name__ and __module__ attributes. While this is normally not needed, django needs to be able to refer to the validator directly for migration purposes, and a returned function is not assigned anywhere. Of course if you only have one such validator, you can hardcode the subnet and avoid this mess:

def validate_dmvpn_ip(value):
    subnet = '172.16.100.'
    from django.core.exceptions import ValidationError

    if subnet not in value or value != '0.0.0.0':
        raise ValidationError('Invalid IP address, please check corrent octects have been assigned')

and

dmvpn_dsl = models.GenericIPAddressField(protocol='IPv4', verbose_name="DMVPN DSL IP", \
    validators=[validate_dmvpn_ip], blank=True, null=True)

To learn more about nested functions (closures), take a look at decorators in python which are basically this + special @ syntax.

Community
  • 1
  • 1
Anonymous
  • 11,740
  • 3
  • 40
  • 50
  • Thanks!, the path.to.this.module value, I'm not sure what i need to set this to? i will use this validator for multiple subnet validations, which is why i couldn't hard code it, but thank you for the explanations of it all, it helps! – AlexW Apr 19 '17 at 06:28
  • It's just the path to the current module. In fact, I think you can just set it to `__name__` which is the magic variable containing the value you want. – Anonymous Apr 19 '17 at 08:41
  • dmvpn_dsl = validate_dmvpn_ip('172.16.100.') dmvpn_dsl.__name__ = 'dmvpn_dsl' dmvpn_dsl.__module__ = dmvpn_dsl.__name__ gives, __name__ must be set to a string – AlexW Apr 19 '17 at 08:54
  • I meant `new_validator.__module__ = __name__`. – Anonymous Apr 19 '17 at 10:35
  • its working, but i think my logic statement is wrong, if subnet not in value or value != '0.0.0.0': is returning false if the subnet is 172.16.100. or 0.0.0.0 I'm trying to allow either of those values – AlexW Apr 19 '17 at 12:28