Skip to content

Commit

Permalink
Add country hinting at forms that have country and phone number
Browse files Browse the repository at this point in the history
  • Loading branch information
costas-basdekis committed Oct 23, 2013
1 parent fe06a5e commit ed5834f
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 4 deletions.
4 changes: 3 additions & 1 deletion oscar/apps/address/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from django import forms
from django.db.models import get_model

from oscar.views.generic import PhoneNumberMixin

UserAddress = get_model('address', 'useraddress')


Expand All @@ -18,7 +20,7 @@ def __init__(self, *args, **kwargs):
self.fields[field_name].required = True


class UserAddressForm(AbstractAddressForm):
class UserAddressForm(PhoneNumberMixin, AbstractAddressForm):

class Meta:
model = UserAddress
Expand Down
4 changes: 3 additions & 1 deletion oscar/apps/checkout/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
from oscar.apps.customer.utils import normalise_email
from oscar.core.compat import get_user_model

from oscar.views.generic import PhoneNumberMixin

User = get_user_model()
Country = get_model('address', 'Country')


class ShippingAddressForm(AbstractAddressForm):
class ShippingAddressForm(PhoneNumberMixin, AbstractAddressForm):

def __init__(self, *args, **kwargs):
super(ShippingAddressForm, self).__init__(*args, **kwargs)
Expand Down
4 changes: 3 additions & 1 deletion oscar/apps/dashboard/orders/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from django.utils.translation import ugettext_lazy as _
from oscar.apps.address.forms import AbstractAddressForm

from oscar.views.generic import PhoneNumberMixin

Order = get_model('order', 'Order')
OrderNote = get_model('order', 'OrderNote')
ShippingAddress = get_model('order', 'ShippingAddress')
Expand Down Expand Up @@ -112,7 +114,7 @@ class Meta:
exclude = ('order', 'user', 'note_type')


class ShippingAddressForm(AbstractAddressForm):
class ShippingAddressForm(PhoneNumberMixin, AbstractAddressForm):

class Meta:
model = ShippingAddress
Expand Down
4 changes: 3 additions & 1 deletion oscar/apps/payment/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from oscar.apps.address.forms import AbstractAddressForm
from . import bankcards

from oscar.views.generic import PhoneNumberMixin

Country = get_model('address', 'Country')
BillingAddress = get_model('order', 'BillingAddress')
Bankcard = get_model('payment', 'Bankcard')
Expand Down Expand Up @@ -226,7 +228,7 @@ def bankcard(self):
ccv=self.cleaned_data['ccv'])


class BillingAddressForm(AbstractAddressForm):
class BillingAddressForm(PhoneNumberMixin, AbstractAddressForm):

def __init__(self, *args, **kwargs):
super(BillingAddressForm, self).__init__(*args, **kwargs)
Expand Down
61 changes: 61 additions & 0 deletions oscar/views/generic.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
from django import forms
from django.core import validators
from django.core.exceptions import ValidationError
from django.utils.encoding import smart_str
from django.contrib import messages
from django.http import HttpResponseRedirect
from django.utils.translation import ugettext_lazy as _

import phonenumbers
from oscar.core.phonenumber import PhoneNumber
from oscar.models.fields import PhoneNumberField


class PostActionMixin(object):
"""
Expand Down Expand Up @@ -85,3 +92,57 @@ def get_objects(self, ids):

def get_object_dict(self, ids):
return self.model.objects.in_bulk(ids)


class PhoneNumberMixin(object):
"""
Validation mixin for forms with a phone number, and optionaly a country.
It tries to validate the phone number, and on failure tries to validate it
using a hint (the country provided), and treating it as a local number.
It looks for ``self.country``, or ``self.fields['country'].queryset``
"""

phone_number = forms.CharField(max_length=32, required=False)

def clean_phone_number(self):
number = self.cleaned_data['phone_number']

# empty
if number in validators.EMPTY_VALUES:
return None

# Check for an international phone format
try:
phone_number = PhoneNumber.from_string(number)
except phonenumbers.NumberParseException:
# Try hinting with the shipping country
if hasattr(self.instance, 'country'):
country = self.instance.country
elif hasattr(self.fields.get('country'), 'queryset'):
country = self.fields['country'].queryset[0]
else:
country = None

if country:
country = self.cleaned_data.get('country', country)

region_code = country.iso_3166_1_a2
# The PhoneNumber class does not allow specifying
# the region. So we drop down to the underlying phonenumbers library,
# which luckily allows parsing into a PhoneNumber instance
try:
phone_number = PhoneNumber.from_string(number, region=region_code)
if not phone_number.is_valid():
raise ValidationError(
_(u'This is not a valid local phone format for %s.') % country)
except phonenumbers.NumberParseException:
# Not a valid local or international phone number
raise ValidationError(
_(u'This is not a valid local or international phone format.'))
else:
# There is no shipping country, not a valid international number
raise ValidationError(
_(u'This is not a valid international phone format.'))

return phone_number

0 comments on commit ed5834f

Please sign in to comment.