Source code for django_powerbank.db.models.fields

# coding=utf-8
import json
import logging
from enum import IntEnum

import six
from django.conf import settings
from django.core import checks
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.crypto import get_random_string, salted_hmac
from django.utils.safestring import mark_safe
from django.utils.text import slugify
from django.utils.translation import ugettext as __
from django.utils.translation import ugettext_lazy as _
from markdown import markdown

from django_powerbank.core.validators import MsisdnValidator
from django_powerbank.forms import fields


[docs]class PhoneField(models.CharField): default_validators = [MsisdnValidator()]
[docs] def to_python(self, value): if value in self.empty_values: return None return super(PhoneField, self).to_python(value)
[docs] def formfield(self, **kwargs): defaults = { 'form_class': fields.PhoneField, } defaults.update(kwargs) return super(PhoneField, self).formfield(**defaults)
[docs]class SourceFieldMixin(object): def __init__(self, source_field=None, *args, **kwargs): self.source_field = source_field super(SourceFieldMixin, self).__init__(*args, **kwargs)
[docs] def check(self, **kwargs): errors = super(SourceFieldMixin, self).check(**kwargs) errors.extend(self._check_source_property_attribute(**kwargs)) return errors
def _check_source_property_attribute(self, **kwargs): if self.source_field is None: return [ checks.Error( "{} must define a 'source_property' attribute.".format(self.__class__.__name__), hint=None, obj=self, id='misc.E001', ) ] return []
[docs]class SecretField(SourceFieldMixin, models.CharField):
[docs] def pre_save(self, model_instance, add): if getattr(model_instance, self.attname) or not getattr(model_instance, self.source_field): return super(SecretField, self).pre_save(model_instance, add) value = getattr(model_instance, self.source_field) value = salted_hmac(get_random_string(), value).hexdigest()[::2] setattr(model_instance, self.attname, value) return value
[docs]class AutoSlugField(SourceFieldMixin, models.SlugField): def __init__(self, source_field=None, keep_existing=False, source_fallback=False, *args, **kwargs): super(AutoSlugField, self).__init__(source_field, *args, **kwargs) self.source_fallback = source_fallback self.keep_existing = keep_existing
[docs] def pre_save(self, model_instance, add): if getattr(model_instance, self.attname) and self.keep_existing or not self.get_source_value(model_instance): return super(AutoSlugField, self).pre_save(model_instance, add) value = self.get_slug_value(model_instance) if self.source_fallback and (value is None or value.strip() == ''): value = self.get_source_value(model_instance) setattr(model_instance, self.attname, value) return value
[docs] def get_slug_value(self, model_instance): value = self.get_source_value(model_instance) return slugify(value)
[docs] def get_source_value(self, model_instance): return getattr(model_instance, self.source_field)
[docs]class UniqueSlugField(AutoSlugField, models.SlugField): def __init__(self, source_field=None, keep_existing=False, *args, **kwargs): super(UniqueSlugField, self).__init__(source_field, *args, **kwargs) self.keep_existing = keep_existing
[docs] def get_slug_value(self, model_instance): value = super(UniqueSlugField, self).get_slug_value(model_instance) filters = { self.attname + '__gte': value, self.attname + '__lte': value + '-9999' } last_instance = model_instance.__class__.objects.filter(**filters).order_by('-slug').first() if last_instance: if model_instance.pk and last_instance.pk == model_instance.pk: return value no = last_instance.slug.replace(value, "") try: value += "-" + str(abs(int(no)) + 1 if no else 1) except ValueError as ex: logging.warning("Could not increase counter", exc_info=ex) setattr(model_instance, self.attname, value) return value
[docs]class MarkDownField(SourceFieldMixin, models.TextField):
[docs] def pre_save(self, model_instance, add): logging.debug(": %s", (model_instance, self.source_field)) if not getattr(model_instance, self.source_field): return super(MarkDownField, self).pre_save(model_instance, add) value = getattr(model_instance, self.source_field) extensions = settings.MARK_DOWN_FIELD_EXTENSIONS.split(',') value = markdown(value, extensions=extensions) setattr(model_instance, self.attname, value) return value
[docs] def to_python(self, value): value = super(MarkDownField, self).to_python(value) return mark_safe(value)
[docs] def from_db_value(self, value, expression, connection): return self.to_python(value)
[docs] def contribute_to_class(self, cls, name, private_only=False): super(MarkDownField, self).contribute_to_class(cls, name, private_only) if not self.source_field and name.endswith("_html"): self.source_field = name[:-5]
[docs]class JSONField(models.TextField): """Simple JSON field that stores python structures as JSON strings on database. """ def __init__(self, *args, **kwargs): kwargs.setdefault('default', '{}') super(JSONField, self).__init__(*args, **kwargs)
[docs] def from_db_value(self, value, expression, connection, context): return self.to_python(value)
[docs] def to_python(self, value): """ Convert the input JSON value into python structures, raises django.core.exceptions.ValidationError if the data can't be converted. """ if self.blank and not value: return {} value = value or '{}' if isinstance(value, six.binary_type): value = six.text_type(value, 'utf-8') if isinstance(value, six.string_types): try: # with django 1.6 i have '"{}"' as default value here if value[0] == value[-1] == '"': value = value[1:-1] return json.loads(value) except Exception as err: raise ValidationError(str(err)) else: return value
[docs] def validate(self, value, model_instance): """Check value is a valid JSON string, raise ValidationError on error.""" if isinstance(value, six.string_types): super(JSONField, self).validate(value, model_instance) try: json.loads(value) except Exception as err: raise ValidationError(str(err))
[docs] def get_prep_value(self, value): """Convert value to JSON string before save""" try: return json.dumps(value) except Exception as err: raise ValidationError(str(err))
[docs] def value_to_string(self, obj): """Return value from object converted to string properly""" value = getattr(obj, self.attname) return self.get_prep_value(value)
[docs] def value_from_object(self, obj): value = getattr(obj, self.attname) return self.to_python(value)
[docs]class ChoicesIntEnum(IntEnum): """Extends IntEum with django choices generation capability""" @classmethod def choices(cls): return [(item.value, _(ChoicesIntEnum.capitalize(item))) for item in cls] @classmethod def capitalize(cls, item): name = item.name.replace("_", " ") return name[0].capitalize() + name[1:] @classmethod def values(cls): return [item.value for item in cls]
[docs]class BinaryMaskEnum(ChoicesIntEnum): # TODO: validate that values are actually a binary mask @classmethod def get_display(cls, value): return ", ".join((__(ChoicesIntEnum.capitalize(item)) for item in cls if item.value & value))