Source code for xmm.models.fields.dynamic

from functools import lru_cache

from flask_babel import lazy_gettext as _
from mongoengine import fields
from werkzeug.utils import import_string

from .base import AbstractBaseField
from .string import StringField


class DynamicField(AbstractBaseField, fields.DynamicField):
    """A dynamic field."""

    field_name = _('Dynamisch')
    _allow_multilingual = True
    _allow_multivalue = True


class FieldClassField(StringField):
    """A field that references another field class."""

    field_name = _('Feld Typ')

    FIELDS_MODULE = 'xmm.models.fields'

    @classmethod
    @lru_cache()
    def get_field_class(cls, import_path):
        """
        Get the Field class for a given import path.

        :param str import_path:
        """
        if isinstance(import_path, type) and issubclass(import_path, fields.BaseField):
            return import_path
        if import_path.islower():
            # old style datatype references
            import_path = '{}.{}Field'.format(cls.FIELDS_MODULE, import_path.capitalize())
        if '.' not in import_path:
            import_path = '{}.{}'.format(cls.FIELDS_MODULE, import_path)

        try:
            return import_string(import_path)
        except ImportError:
            raise

    def __set__(self, instance, value):
        if isinstance(value, str):
            value = FieldClassField.get_field_class(value)
        return super().__set__(instance, value)

    def __get__(self, instance, owner):
        value = super().__get__(instance, owner)
        if isinstance(value, str):
            return FieldClassField.get_field_class(value)
        return value

    def to_mongo(self, value):
        return value.get_name()

    def to_python(self, value):
        return FieldClassField.get_field_class(value)

    def to_str(self, value, format_spec=None):
        value = self.to_python(value)
        return value.type_name

    def validate(self, value):
        if not issubclass(value, AbstractBaseField):
            self.error('Only Fields may be used.')
        return super().validate(self.to_mongo(value))