Installing

$ pip install django-extensions

Current Database Model Field Extensions

NoControlCharactersValidator

Validates that Control Characters like new lines or tabs are not allowed. Can optionally specify whitelist of control characters to allow.

NoWhitespaceValidator

Validates that leading and trailing whitespace is not allowed.

HexValidator

Validates that the string is a valid hex string. Can optionally also specify length, min_length and max_length parameters.

Example

from django_extensions.validators import HexValidator

class UserKeys(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

    public_key = models.CharField(max_length=64, validators=[HexValidator(length=64)])
    private_key = models.CharField(max_length=128, validators=[HexValidator(length=128)])
# -*- coding: utf-8 -*-
from unittest.mock import patch

from django.core.exceptions import ValidationError
from django.test import TestCase, SimpleTestCase

from django_extensions.validators import NoControlCharactersValidator, NoWhitespaceValidator, HexValidator


class NoControlCharactersValidatorTests(TestCase):
    """Tests for NoControlCharactersValidator."""

    def test_should_raise_default_message_and_code_if_value_contains_new_line(self):
        self.validator = NoControlCharactersValidator()
        value_with_new_line = 'test\nvalue'

        with self.assertRaises(ValidationError) as cm:
            self.validator(value_with_new_line)

        self.assertEqual(cm.exception.message, 'Control Characters like new lines or tabs are not allowed.')
        self.assertEqual(cm.exception.code, 'no_control_characters')
        self.assertDictEqual(cm.exception.params, {'value': value_with_new_line, 'whitelist': None})

    def test_should_raise_custom_message_and_code_if_value_contains_tabs(self):
        self.validator = NoControlCharactersValidator(message='custom message', code='custom code')
        value_with_tabs = 'test\tvalue'

        with self.assertRaises(ValidationError) as cm:
            self.validator(value_with_tabs)

        self.assertEqual(cm.exception.message, 'custom message')
        self.assertEqual(cm.exception.code, 'custom code')
        self.assertDictEqual(cm.exception.params, {'value': value_with_tabs, 'whitelist': None})

    def test_should_not_raise_if_value_contains_characters_which_is_on_whitelist(self):
        self.validator = NoControlCharactersValidator(message='custom message', code='custom code', whitelist=['\n'])
        value_with_new_line = 'test\nvalue'

        result = self.validator(value_with_new_line)

        self.assertIsNone(result)


class NoWhiteSpaceValidatorTests(TestCase):
    """Tests for NoWhitespaceValidator."""

    def test_should_raise_default_message_and_code_if_value_has_leading_whitespace(self):
        self.validator = NoWhitespaceValidator()

        value_with_leading_whitespace = ' test_value'

        with self.assertRaises(ValidationError) as cm:
            self.validator(value_with_leading_whitespace)

        self.assertEqual(cm.exception.message, 'Leading and Trailing whitespaces are not allowed.')
        self.assertEqual(cm.exception.code, 'no_whitespace')
        self.assertDictEqual(cm.exception.params, {'value': value_with_leading_whitespace})

    def test_should_raise_custom_message_and_code_if_value_has_trailing_whitespace(self):
        self.validator = NoWhitespaceValidator(message='custom message', code='custom code')
        value_with_trailing_whitespace = 'test value '

        with self.assertRaises(ValidationError) as cm:
            self.validator(value_with_trailing_whitespace)

        self.assertEqual(cm.exception.message, 'custom message')
        self.assertEqual(cm.exception.code, 'custom code')
        self.assertDictEqual(cm.exception.params, {'value': value_with_trailing_whitespace})

    def test_should_not_raise_if_value_doesnt_have_leading_or_trailing_whitespaces(self):
        self.validator = NoWhitespaceValidator()
        value_without_leading_or_trailing_whitespaces = 'test value'

        result = self.validator(value_without_leading_or_trailing_whitespaces)

        self.assertIsNone(result)


class TestHexValidator(SimpleTestCase):
    def test_custom_message_and_code(self):
        self.validator = HexValidator(message='message', code='code')

        self.assertEqual(self.validator.message, 'message')
        self.assertEqual(self.validator.code, 'code')

    def test_equality_of_objs_with_obj_of_different_type(self):
        self.assertNotEqual(TypeError(), HexValidator())

    def test_equality_of_objs_with_different_code(self):
        self.assertNotEqual(HexValidator(code='1'), HexValidator(code='a'))

    def test_equality_of_objs_with_different_message(self):
        self.assertNotEqual(HexValidator(code='code', message='a'), HexValidator(code='code', message='acb'))

    def test_equality_of_objs_with_same_code_and_message(self):
        self.assertEqual(HexValidator(code='c', message='m'), HexValidator(code='c', message='m'))

    def test_fixed_length(self):
        value = 'abcd'
        self.validator = HexValidator(length=5)

        with self.assertRaises(ValidationError) as err:
            self.validator(value)

        self.assertEqual(str(err.exception), "['Invalid length. Must be 5 characters.']")
        self.assertEqual(err.exception.code, 'hex_only_length')

    def test_min_length(self):
        value = 'a'
        self.validator = HexValidator(min_length=5)

        with self.assertRaises(ValidationError) as err:
            self.validator(value)

        self.assertEqual(str(err.exception), "['Ensure that there are more than 5 characters.']")
        self.assertEqual(err.exception.code, 'hex_only_min_length')

    def test_with_max_length(self):
        value = 'abcd'
        self.validator = HexValidator(max_length=2)

        with self.assertRaises(ValidationError) as err:
            self.validator(value)

        self.assertEqual(str(err.exception), "['Ensure that there are no more than 2 characters.']")
        self.assertEqual(err.exception.code, 'hex_only_max_length')

    def test_invalid_type(self):
        value = 1
        with patch('django_extensions.validators.force_str', return_value=1):
            self.validator = HexValidator()

            with self.assertRaises(ValidationError) as err:
                self.validator(value)

        self.assertEqual(str(err.exception), "['Only a hex string is allowed.']")
        self.assertEqual(err.exception.code, 'hex_only')

    def test_invalid_hex(self):
        value = '1'
        self.validator = HexValidator()

        with self.assertRaises(ValidationError) as err:
            self.validator(value)

        self.assertEqual(str(err.exception), "['Only a hex string is allowed.']")
        self.assertEqual(err.exception.code, 'hex_only')

    def test_valid_hex(self):
        value = 'b901ef'
        self.validator = HexValidator()

        result = self.validator(value)

        self.assertIsNone(result)

keywords: pythondjango