You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

__init__.py 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. from django.core.exceptions import ImproperlyConfigured
  2. from django.db.models.fields import CharField, DecimalField, Field
  3. from django.db.models import SubfieldBase
  4. from django.utils import six
  5. from django.utils.translation import ugettext_lazy as _
  6. from django.core.validators import MaxLengthValidator
  7. from oscar.core import validators
  8. from oscar.forms import fields
  9. import oscar.core.phonenumber as phonenumber
  10. # allow importing as oscar.models.fields.AutoSlugField
  11. from .autoslugfield import AutoSlugField
  12. AutoSlugField = AutoSlugField
  13. try:
  14. from south.modelsinspector import add_introspection_rules
  15. except ImportError:
  16. pass
  17. else:
  18. add_introspection_rules([], ["^oscar\.models\.fields\.ExtendedURLField$"])
  19. add_introspection_rules([], [
  20. "^oscar\.models\.fields\.PositiveDecimalField$"])
  21. add_introspection_rules([], [
  22. "^oscar\.models\.fields\.UppercaseCharField$"])
  23. add_introspection_rules([], [
  24. "^oscar\.models\.fields\.NullCharField$"])
  25. add_introspection_rules([], [
  26. "^oscar\.models\.fields\.PhoneNumberField$"])
  27. add_introspection_rules([], [
  28. "^oscar\.models\.fields\.AutoSlugField$"])
  29. class ExtendedURLField(CharField):
  30. description = _("URL")
  31. def __init__(self, verbose_name=None, name=None,
  32. verify_exists=None, **kwargs):
  33. kwargs['max_length'] = kwargs.get('max_length', 200)
  34. CharField.__init__(self, verbose_name, name, **kwargs)
  35. # 'verify_exists' was deprecated in Django 1.4. To ensure backwards
  36. # compatibility, it is still accepted here, but only passed
  37. # on to the parent class if it was specified.
  38. self.verify_exists = verify_exists
  39. if verify_exists is not None:
  40. validator = validators.ExtendedURLValidator(
  41. verify_exists=verify_exists)
  42. else:
  43. validator = validators.ExtendedURLValidator()
  44. self.validators.append(validator)
  45. def formfield(self, **kwargs):
  46. # As with CharField, this will cause URL validation to be performed
  47. # twice.
  48. defaults = {
  49. 'form_class': fields.ExtendedURLField,
  50. 'verify_exists': self.verify_exists
  51. }
  52. defaults.update(kwargs)
  53. return super(ExtendedURLField, self).formfield(**defaults)
  54. def deconstruct(self):
  55. """
  56. deconstruct() is needed by Django's migration framework
  57. """
  58. name, path, args, kwargs = super(ExtendedURLField, self).deconstruct()
  59. # Add verify_exists to kwargs if it's not the default value.
  60. if self.verify_exists is not None:
  61. kwargs['verify_exists'] = self.verify_exists
  62. # We have a default value for max_length; remove it in that case
  63. if self.max_length == 200:
  64. del kwargs['max_length']
  65. return name, path, args, kwargs
  66. class PositiveDecimalField(DecimalField):
  67. """
  68. A simple subclass of ``django.db.models.fields.DecimalField`` that
  69. restricts values to be non-negative.
  70. """
  71. def formfield(self, **kwargs):
  72. return super(PositiveDecimalField, self).formfield(min_value=0)
  73. class UppercaseCharField(six.with_metaclass(SubfieldBase, CharField)):
  74. """
  75. A simple subclass of ``django.db.models.fields.CharField`` that
  76. restricts all text to be uppercase.
  77. Defined with the with_metaclass helper so that to_python is called
  78. https://docs.djangoproject.com/en/1.6/howto/custom-model-fields/#the-subfieldbase-metaclass # NOQA
  79. """
  80. def to_python(self, value):
  81. val = super(UppercaseCharField, self).to_python(value)
  82. if isinstance(val, six.string_types):
  83. return val.upper()
  84. else:
  85. return val
  86. class NullCharField(six.with_metaclass(SubfieldBase, CharField)):
  87. """
  88. CharField that stores '' as None and returns None as ''
  89. Useful when using unique=True and forms. Implies null==blank==True.
  90. When a ModelForm with a CharField with null=True gets saved, the field will
  91. be set to '': https://code.djangoproject.com/ticket/9590
  92. This breaks usage with unique=True, as '' is considered equal to another
  93. field set to ''.
  94. """
  95. description = "CharField that stores '' as None and returns None as ''"
  96. def __init__(self, *args, **kwargs):
  97. if not kwargs.get('null', True) or not kwargs.get('blank', True):
  98. raise ImproperlyConfigured(
  99. "NullCharField implies null==blank==True")
  100. kwargs['null'] = kwargs['blank'] = True
  101. super(NullCharField, self).__init__(*args, **kwargs)
  102. def to_python(self, value):
  103. val = super(NullCharField, self).to_python(value)
  104. return val if val is not None else u''
  105. def get_prep_value(self, value):
  106. prepped = super(NullCharField, self).get_prep_value(value)
  107. return prepped if prepped != u"" else None
  108. def deconstruct(self):
  109. """
  110. deconstruct() is needed by Django's migration framework
  111. """
  112. name, path, args, kwargs = super(NullCharField, self).deconstruct()
  113. del kwargs['null']
  114. del kwargs['blank']
  115. return name, path, args, kwargs
  116. class PhoneNumberField(Field):
  117. """
  118. An international phone number.
  119. * Validates a wide range of phone number formats
  120. * Displays it nicely formatted
  121. * Can be given a hint for the country, so that it can accept local numbers,
  122. that are not in an international format
  123. Notes
  124. -----
  125. This field is based on work in django-phonenumber-field
  126. https://github.com/maikhoepfel/django-phonenumber-field/
  127. See ``oscar/core/phonenumber.py`` for the relevant copyright and
  128. permission notice.
  129. """
  130. attr_class = phonenumber.PhoneNumber
  131. descriptor_class = phonenumber.PhoneNumberDescriptor
  132. default_validators = [phonenumber.validate_international_phonenumber]
  133. description = _("Phone number")
  134. def __init__(self, *args, **kwargs):
  135. if kwargs.get('null', False):
  136. raise ImproperlyConfigured(
  137. "null=True is not supported on PhoneNumberField")
  138. kwargs['max_length'] = kwargs.get('max_length', 128)
  139. super(PhoneNumberField, self).__init__(*args, **kwargs)
  140. self.validators.append(MaxLengthValidator(self.max_length))
  141. def get_internal_type(self):
  142. return "CharField"
  143. def get_prep_value(self, value):
  144. """
  145. Returns field's value prepared for saving into a database.
  146. """
  147. value = phonenumber.to_python(value)
  148. if value is None:
  149. return u''
  150. return value.as_e164 if value.is_valid() else value.raw_input
  151. def contribute_to_class(self, cls, name):
  152. super(PhoneNumberField, self).contribute_to_class(cls, name)
  153. setattr(cls, self.name, self.descriptor_class(self))
  154. def deconstruct(self):
  155. """
  156. deconstruct() is needed by Django's migration framework
  157. """
  158. name, path, args, kwargs = super(PhoneNumberField, self).deconstruct()
  159. if self.max_length == 128:
  160. del kwargs['max_length']
  161. return name, path, args, kwargs