from typing import Any
from django.db import models
from .utils import StartsWith
from django.core import exceptions, validators
from django.db.models.fields import PositiveIntegerRelDbTypeMixin
from .cryptography import encrypt,decrypt
from ast import literal_eval
import datetime
from django import forms
from django.utils import timezone
from timestring import Date
from decimal import Decimal
from uuid import UUID
from django.utils.translation import gettext_lazy as _
"""
to_python() make validations & checks type of the data
get_db_prep_value() encrypts the data
from_db_value() decrypts the data returned from the db
pre_save() generates date ,datetime,time for the respestive fields
get_db_prep_save() saves the value into db
"""
[docs]class CharField(models.CharField):
[docs] def get_internal_type(self)-> str:
return "TextField"
[docs] def to_python(self, value: Any) -> Any:
return self.clean(super().to_python(value),None)
[docs] def get_prep_value(self, value: Any) -> Any:
return self.to_python(value)
[docs] def from_db_value(self, value:Any, expression:Any, connection:Any)-> Any:
return decrypt(value).decode()
[docs] def get_db_prep_value(self, value, connection, prepared=False):
if not prepared:
value = self.get_prep_value(value)
return encrypt(value)
[docs] def clean(self, value, model_instance):
"""
Convert the value's type and run validation. Validation errors
from to_python() and validate() are propagated. Return the correct
value if no error is raised.
"""
self.validate(value, model_instance)
self.run_validators(value)
return value
[docs]class BooleanField(models.BooleanField):
[docs] def get_internal_type(self)-> str:
return "TextField"
[docs] def to_python(self, value: Any) -> Any:
return self.clean(super().to_python(value),None)
[docs] def get_prep_value(self, value: Any) -> Any:
return self.to_python(value)
[docs] def get_db_prep_value(self, value, connection, prepared=False):
if not prepared:
value = self.get_prep_value(value)
return encrypt(value)
[docs] def from_db_value(self, value:Any, expression:Any, connection:Any)-> Any:
return literal_eval(decrypt(value).decode())
[docs] def clean(self, value, model_instance):
"""
Convert the value's type and run validation. Validation errors
from to_python() and validate() are propagated. Return the correct
value if no error is raised.
"""
self.validate(value, model_instance)
self.run_validators(value)
return value
[docs]class DateField(models.DateField):
[docs] def get_internal_type(self)-> str:
return "TextField"
[docs] def to_python(self, value: Any) -> Any:
return self.clean(super().to_python(value),None)
[docs] def get_prep_value(self, value: Any) -> Any:
return self.to_python(value)
[docs] def get_db_prep_value(self, value, connection, prepared=False):
# Casts dates into the format expected by the backend
if not prepared:
value = self.get_prep_value(value)
return encrypt(connection.ops.adapt_datefield_value(value))
[docs] def from_db_value(self, value:Any, expression:Any, connection:Any)-> Any:
return Date(decrypt(value).decode()).date.date()
[docs] def pre_save(self, model_instance, add):
if self.auto_now or (self.auto_now_add and add):
value = datetime.date.today()
setattr(model_instance, self.attname, value)
return value
else:
return super().pre_save(model_instance, add)
[docs] def clean(self, value, model_instance):
"""
Convert the value's type and run validation. Validation errors
from to_python() and validate() are propagated. Return the correct
value if no error is raised.
"""
self.validate(value, model_instance)
self.run_validators(value)
return value
[docs]class DateTimeField(models.DateTimeField):
[docs] def get_internal_type(self)-> str:
return "TextField"
[docs] def to_python(self, value: Any) -> Any:
return self.clean(super().to_python(value),None)
[docs] def get_prep_value(self, value: Any) -> Any:
return super().get_prep_value(value)
[docs] def get_db_prep_value(self, value, connection, prepared=False):
# Casts dates into the format expected by the backend
if not prepared:
value = self.get_prep_value(value)
return encrypt(connection.ops.adapt_datetimefield_value(value))
[docs] def from_db_value(self, value:Any, expression:Any, connection:Any)-> Any:
return Date(decrypt(value).decode()).date
[docs] def pre_save(self, model_instance, add):
if self.auto_now or (self.auto_now_add and add):
value = timezone.now()
setattr(model_instance, self.attname, value)
return value
else:
return super().pre_save(model_instance, add)
[docs] def clean(self, value, model_instance):
"""
Convert the value's type and run validation. Validation errors
from to_python() and validate() are propagated. Return the correct
value if no error is raised.
"""
self.validate(value, model_instance)
self.run_validators(value)
return value
[docs]class TimeField(models.TimeField):
[docs] def get_internal_type(self)-> str:
return "TextField"
[docs] def to_python(self, value: Any) -> Any:
return self.clean(super().to_python(value),None)
[docs] def get_prep_value(self, value: Any) -> Any:
return self.to_python(value)
[docs] def get_db_prep_value(self, value, connection, prepared=False):
# Casts dates into the format expected by the backend
if not prepared:
value = self.get_prep_value(value)
return encrypt(connection.ops.adapt_timefield_value(value))
[docs] def from_db_value(self, value:Any, expression:Any, connection:Any)-> Any:
return Date(decrypt(value).decode()).date.time()
[docs] def pre_save(self, model_instance, add):
if self.auto_now or (self.auto_now_add and add):
value = datetime.datetime.now().time()
setattr(model_instance, self.attname, value)
return value
else:
return super().pre_save(model_instance, add)
[docs] def clean(self, value, model_instance):
"""
Convert the value's type and run validation. Validation errors
from to_python() and validate() are propagated. Return the correct
value if no error is raised.
"""
self.validate(value, model_instance)
self.run_validators(value)
return value
[docs]class DecimalField(models.DecimalField):
[docs] def get_internal_type(self)-> str:
return "TextField"
[docs] def to_python(self, value: Any) -> Any:
return super().to_python(value)
[docs] def get_prep_value(self, value: Any) -> Any:
return self.to_python(value)
[docs] def get_db_prep_save(self, value, connection,):
return encrypt(connection.ops.adapt_decimalfield_value(self.get_prep_value(value), self.max_digits, self.decimal_places))
[docs] def from_db_value(self, value:Any, expression:Any, connection:Any)-> Any:
return float(decrypt(value).decode())
#return value
[docs] def clean(self, value, model_instance):
"""
Convert the value's type and run validation. Validation errors
from to_python() and validate() are propagated. Return the correct
value if no error is raised.
"""
self.validate(value, model_instance)
self.run_validators(value)
return value
[docs]class EmailField(CharField):
default_validators = [validators.validate_email]
description = _("Email address")
def __init__(self, *args, **kwargs):
# max_length=254 to be compliant with RFCs 3696 and 5321
kwargs.setdefault('max_length', 254)
super().__init__(*args, **kwargs)
[docs]class FloatField(models.FloatField):
[docs] def get_internal_type(self)-> str:
return "TextField"
[docs] def to_python(self, value: Any) -> Any:
return self.clean(super().to_python(value),None)
[docs] def get_prep_value(self, value: Any) -> Any:
return self.to_python(value)
[docs] def from_db_value(self, value:Any, expression:Any, connection:Any)-> Any:
return float(decrypt(value).decode())
[docs] def get_db_prep_value(self, value, connection, prepared=False):
if not prepared:
value = self.get_prep_value(value)
return encrypt(value)
[docs] def clean(self, value, model_instance):
"""
Convert the value's type and run validation. Validation errors
from to_python() and validate() are propagated. Return the correct
value if no error is raised.
"""
self.validate(value, model_instance)
self.run_validators(value)
return value
[docs]class IntegerField(models.IntegerField):
[docs] def get_internal_type(self)-> str:
return "TextField"
[docs] def to_python(self, value: Any) -> Any:
return self.clean(super().to_python(value),None)
[docs] def get_prep_value(self, value: Any) -> Any:
return self.to_python(value)
[docs] def from_db_value(self, value:Any, expression:Any, connection:Any)-> Any:
return int(decrypt(value).decode())
[docs] def get_db_prep_value(self, value, connection, prepared=False):
if not prepared:
value = self.get_prep_value(value)
return encrypt(value)
[docs] def clean(self, value, model_instance):
"""
Convert the value's type and run validation. Validation errors
from to_python() and validate() are propagated. Return the correct
value if no error is raised.
"""
self.validate(value, model_instance)
self.run_validators(value)
return value
[docs]class BigIntegerField(IntegerField):
error_messages = {
'invalid': _('“%(value)s” value must be less than %(max) & greater than %(min).'),
}
description = _("Big (8 byte) integer")
MAX_BIGINT = 9223372036854775807
[docs] def to_python(self, value: Any) -> Any:
value=self.clean(super().to_python(value),None)
if value < (-self.MAX_BIGINT-1) or value > self.MAX_BIGINT:
raise exceptions.ValidationError(self.error_messages['invalid'],code='invalid',
params={'value': value,'max':self.MAX_BIGINT,'min':(-self.MAX_BIGINT-1)})
return value
[docs]class GenericIPAddressField(models.GenericIPAddressField):
[docs] def get_internal_type(self)-> str:
return "TextField"
[docs] def to_python(self, value: Any) -> Any:
return self.clean(super().to_python(value),None)
[docs] def get_prep_value(self, value: Any) -> Any:
return self.to_python(value)
[docs] def get_db_prep_value(self, value, connection, prepared=False):
if not prepared:
value=self.get_prep_value(value)
return encrypt(connection.ops.adapt_ipaddressfield_value(value))
[docs] def from_db_value(self, value:Any, expression:Any, connection:Any)-> Any:
return decrypt(value).decode()
[docs] def clean(self, value, model_instance):
"""
Convert the value's type and run validation. Validation errors
from to_python() and validate() are propagated. Return the correct
value if no error is raised.
"""
self.validate(value, model_instance)
self.run_validators(value)
return value
[docs]class PositiveBigIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField):
description = _('Positive big integer')
error_messages = {
'invalid': _('“%(value)s” value must be less than greater than %(min).'),
}
[docs] def to_python(self, value: Any) -> Any:
value=self.clean(super().to_python(value),None)
if value < 0:
raise exceptions.ValidationError(self.error_messages['invalid'],code='invalid',
params={'value': value,'min':0})
return value
[docs]class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField):
description = _("Positive integer")
error_messages = {
'invalid': _('“%(value)s” value must be less than greater than %(min).'),
}
[docs] def to_python(self, value: Any) -> Any:
value=self.clean(super().to_python(value),None)
if value < 0:
raise exceptions.ValidationError(self.error_messages['invalid'],code='invalid',
params={'value': value,'min':0})
return value
[docs]class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField):
description = _("Positive small integer")
error_messages = {
'invalid': _('“%(value)s” value must be less than greater than %(min).'),
}
[docs] def to_python(self, value: Any) -> Any:
value=self.clean(super().to_python(value),None)
if value < 0:
raise exceptions.ValidationError(self.error_messages['invalid'],code='invalid',
params={'value': value,'min':0})
return value
[docs]class SlugField(CharField):
default_validators = [validators.validate_slug]
description = _("Slug (up to %(max_length)s)")
def __init__(self, *args, max_length=50, db_index=True, allow_unicode=False, **kwargs):
self.allow_unicode = allow_unicode
if self.allow_unicode:
self.default_validators = [validators.validate_unicode_slug]
super().__init__(*args, max_length=max_length, db_index=db_index, **kwargs)
[docs] def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
if kwargs.get("max_length") == 50:
del kwargs['max_length']
if self.db_index is False:
kwargs['db_index'] = False
else:
del kwargs['db_index']
if self.allow_unicode is not False:
kwargs['allow_unicode'] = self.allow_unicode
return name, path, args, kwargs
[docs] def get_internal_type(self):
return "SlugField"
[docs]class SmallIntegerField(IntegerField):
description = _("Small integer")
[docs]class TextField(models.TextField):
[docs] def get_internal_type(self)-> str:
return "TextField"
[docs] def to_python(self, value: Any) -> Any:
return self.clean(super().to_python(value),None)
[docs] def get_prep_value(self, value: Any) -> Any:
return self.to_python(value)
[docs] def from_db_value(self, value:Any, expression:Any, connection:Any)-> Any:
return decrypt(value).decode()
[docs] def get_db_prep_value(self, value, connection, prepared=False):
if not prepared:
value = self.get_prep_value(value)
return encrypt(value)
[docs] def clean(self, value, model_instance):
"""
Convert the value's type and run validation. Validation errors
from to_python() and validate() are propagated. Return the correct
value if no error is raised.
"""
self.validate(value, model_instance)
self.run_validators(value)
return value
[docs]class URLField(CharField):
default_validators = [validators.URLValidator()]
description = _("URL")
def __init__(self, verbose_name=None, name=None, **kwargs):
kwargs.setdefault('max_length', 200)
super().__init__(verbose_name, name, **kwargs)
[docs] def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
if kwargs.get("max_length") == 200:
del kwargs['max_length']
return name, path, args, kwargs
[docs]class BinaryField(models.BinaryField):
[docs] def get_internal_type(self)-> str:
return "TextField"
[docs] def to_python(self, value: Any) -> Any:
value=self.clean(value,None)
if isinstance(value ,str):
value=bytes(value,"UTF-8").hex()
elif isinstance(value,memoryview):
value=value.bytes.hex()
return value
[docs] def get_prep_value(self, value: Any) -> Any:
return self.to_python(value)
[docs] def from_db_value(self, value:Any, expression:Any, connection:Any)-> Any:
return decrypt(value)
[docs] def get_db_prep_value(self, value, connection, prepared=False):
if not prepared:
value = self.get_prep_value(value)
return encrypt(connection.Database.Binary(value))
[docs] def clean(self, value, model_instance):
"""
Convert the value's type and run validation. Validation errors
from to_python() and validate() are propagated. Return the correct
value if no error is raised.
"""
self.validate(value, model_instance)
self.run_validators(value)
return value
[docs]class UUIDField(models.UUIDField):
[docs] def get_internal_type(self)-> str:
return "TextField"
[docs] def to_python(self, value: Any) -> Any:
return self.clean(super().to_python(value),None)
[docs] def get_prep_value(self, value: Any) -> Any:
return self.to_python(value)
[docs] def from_db_value(self, value:Any, expression:Any, connection:Any)-> Any:
#return UUID.hex(decrypt(value).decode())
return UUID(hex=decrypt(value).decode())
[docs] def get_db_prep_value(self, value, connection, prepared=False):
if not prepared:
value = self.get_prep_value(value)
return encrypt(value.hex)
[docs] def clean(self, value, model_instance):
"""
Convert the value's type and run validation. Validation errors
from to_python() and validate() are propagated. Return the correct
value if no error is raised.
"""
self.validate(value, model_instance)
self.run_validators(value)
return value
[docs]class FilePathField(models.FilePathField):
[docs] def get_internal_type(self)-> str:
return "TextField"
[docs] def to_python(self, value: Any) -> Any:
return self.clean(super().to_python(value),None)
[docs] def get_prep_value(self, value: Any) -> Any:
return self.to_python(value)
[docs] def from_db_value(self, value:Any, expression:Any, connection:Any)-> Any:
return decrypt(value).decode()
[docs] def get_db_prep_value(self, value, connection, prepared=False):
if not prepared:
value = self.get_prep_value(value)
return encrypt(value)
CharField.register_lookup(StartsWith)
BooleanField.register_lookup(StartsWith)
DateField.register_lookup(StartsWith)
DateTimeField.register_lookup(StartsWith)
EmailField.register_lookup(StartsWith)
GenericIPAddressField.register_lookup(StartsWith)
SlugField.register_lookup(StartsWith)
TextField.register_lookup(StartsWith)
URLField.register_lookup(StartsWith)
BinaryField.register_lookup(StartsWith)
UUIDField.register_lookup(StartsWith)
FilePathField.register_lookup(StartsWith)
DateField.register_lookup(StartsWith,lookup_name="date")
DateTimeField.register_lookup(StartsWith,lookup_name="date")
TimeField.register_lookup(StartsWith,lookup_name="time")