import enumfields
from django import forms
from django.apps import apps
from django.core.exceptions import ObjectDoesNotExist
from django.core.validators import validate_email
from django.utils.text import camel_case_to_spaces
from django.utils.translation import gettext_lazy as _
[docs]
class MultiEmailField(forms.Field):
"""
From https://docs.djangoproject.com/en/1.11/ref/forms/validation/#form-field-default-cleaning
"""
[docs]
def validate(self, value):
"""Check if value consists only of valid emails."""
# Use the parent's handling of required fields, etc.
super().validate(value)
if value:
for email in value.split(","):
if email:
validate_email(email)
[docs]
class Type:
name = None
identifier = None
[docs]
def get_field(self, **kwargs):
"""
Get a Django form field for this type.
The kwargs are passed directly to the field
constructor.
:param kwargs: Kwargs for field constructor.
:type kwargs: dict
:return: Form field.
:rtype: django.forms.Field
"""
return forms.CharField(**kwargs)
[docs]
def unserialize(self, value):
return self.get_field().to_python(value)
[docs]
def validate(self, value):
return self.get_field().validate(value)
[docs]
def is_coercible_from(self, other_type):
return self.identifier == other_type.identifier
class _String(Type):
pass
class _Number(Type):
pass
[docs]
class Boolean(Type):
name = _("Boolean")
identifier = "boolean"
[docs]
def get_field(self, **kwargs):
return forms.BooleanField(**kwargs)
[docs]
class Integer(_Number):
name = _("Integer Number")
identifier = "integer"
[docs]
def get_field(self, **kwargs):
return forms.IntegerField(**kwargs)
[docs]
class Decimal(_Number):
name = _("Decimal Number")
identifier = "decimal"
[docs]
def get_field(self, **kwargs):
return forms.DecimalField(**kwargs)
[docs]
class Text(_String):
name = _("Text")
identifier = "text"
[docs]
def is_coercible_from(self, other_type):
# All variables can be used as raw text
return True
[docs]
class Language(_String):
name = _("Language")
identifier = "language"
[docs]
class Email(_String):
name = _("Email Address")
identifier = "email"
[docs]
def get_field(self, **kwargs):
return MultiEmailField(**kwargs)
[docs]
class URL(_String):
name = _("URL Address")
identifier = "url"
[docs]
def get_field(self, **kwargs):
return forms.URLField(**kwargs)
[docs]
class Phone(_String):
name = _("Phone Number")
identifier = "phone"
[docs]
class Model(Type):
model_label = None
identifier = "model"
@property
def name(self):
return self.get_model()._meta.verbose_name
[docs]
def __init__(self, model_label):
"""
:param model_label: Model label in Django `app.Model` format (e.g. `shuup.Order`).
:type model_label: str
"""
self.model_label = model_label
[docs]
def unserialize(self, value):
if isinstance(value, self.get_model()):
return value
try:
return self.get_model().objects.get(pk=value)
except ObjectDoesNotExist:
return None
[docs]
def is_coercible_from(self, other_type):
return isinstance(other_type, Model) and self.get_model() == other_type.get_model()
[docs]
def get_model(self):
"""
:rtype: django.db.models.Model
"""
return apps.get_model(self.model_label)
[docs]
def get_field(self, **kwargs):
kwargs.setdefault("queryset", self.get_model().objects.all())
return forms.ModelChoiceField(**kwargs)
[docs]
class Enum(Type):
enum_class = None
identifier = "enum"
@property
def name(self):
if self.enum_class:
return camel_case_to_spaces(self.enum_class.__class__.__name__)
return "<Invalid Enum>"
[docs]
def __init__(self, enum_class):
self.enum_class = enum_class
assert issubclass(enum_class, enumfields.Enum), f"{enum_class!r} is not an enum"
[docs]
def unserialize(self, value):
if isinstance(value, self.enum_class):
return value
try:
return self.enum_class(value)
except ValueError:
try:
return self.enum_class(int(value))
except ValueError:
pass
return None
[docs]
def get_field(self, **kwargs):
return enumfields.EnumField(self.enum_class).formfield(**kwargs)