瀏覽代碼

Check for existing email when updating profile

A user in Oscar is identified by email address instead of the
username. This is, however, not set as a ``unique`` constraint
in the user model in ``django.contrib.auth.models.User``. Checks
if an email already exists are carried out when a user registers
but are ignored when a registered user changes their
profile. This can lead to multiple users having the same email
address which should not happen.

I provide a failing test with a mixin that can be used in both
the ``UserForm`` and ``UserAndProfileForm`` to clean the email
field when validating the form. A ``ValidationError`` is raised
when a user with this email address already exists and is not
the currently edited instance (makes sure that profile updates
with unchanged email work still).

Fixes #324
master
Sebastian Vetter 13 年之前
父節點
當前提交
91cd80d6bc
共有 2 個檔案被更改,包括 54 行新增5 行删除
  1. 29
    2
      oscar/apps/customer/forms.py
  2. 25
    3
      tests/functional/customer/profile_tests.py

+ 29
- 2
oscar/apps/customer/forms.py 查看文件

@@ -10,6 +10,7 @@ from django.contrib.auth.models import User
10 10
 from django.contrib.auth import forms as auth_forms
11 11
 from django.conf import settings
12 12
 from django.core import validators
13
+from django.core.exceptions import ValidationError
13 14
 from django.utils.http import int_to_base36
14 15
 from django.contrib.sites.models import get_current_site
15 16
 from django.contrib.auth.tokens import default_token_generator
@@ -211,7 +212,33 @@ class SearchByDateRangeForm(forms.Form):
211 212
         return {}
212 213
 
213 214
 
214
-class UserForm(forms.ModelForm):
215
+class CleanEmailMixin(object):
216
+
217
+    def clean_email(self):
218
+        """
219
+        Make sure that the email address is aways unique as it is
220
+        used instead of the username. This is necessary because the
221
+        unique-ness of email addresses is *not* enforced on the model
222
+        level in ``django.contrib.auth.models.User``.
223
+        """
224
+        email = self.cleaned_data['email']
225
+
226
+        try:
227
+            user = User.objects.get(email=email)
228
+        except User.DoesNotExist:
229
+            # this email address is unique so we don't have to worry
230
+            # about it
231
+            return email
232
+
233
+        if self.instance and self.instance.id != user.id:
234
+            raise ValidationError(
235
+                _("A user with this email address already exists")
236
+            )
237
+
238
+        return email
239
+
240
+
241
+class UserForm(forms.ModelForm, CleanEmailMixin):
215 242
 
216 243
     def __init__(self, user, *args, **kwargs):
217 244
         self.user = user
@@ -229,7 +256,7 @@ if hasattr(settings, 'AUTH_PROFILE_MODULE'):
229 256
 
230 257
     Profile = get_profile_class()
231 258
 
232
-    class UserAndProfileForm(forms.ModelForm):
259
+    class UserAndProfileForm(forms.ModelForm, CleanEmailMixin):
233 260
 
234 261
         first_name = forms.CharField(label=_('First name'), max_length=128)
235 262
         last_name = forms.CharField(label=_('Last name'), max_length=128)

+ 25
- 3
tests/functional/customer/profile_tests.py 查看文件

@@ -1,11 +1,8 @@
1
-import httplib
2 1
 from mock import patch
3 2
 from decimal import Decimal as D
4 3
 
5 4
 from django.contrib.auth.models import User
6 5
 from django.core.urlresolvers import reverse
7
-from django.test import TestCase
8
-from django.test.client import Client
9 6
 
10 7
 from oscar.test.helpers import create_product, create_order
11 8
 from oscar.test import ClientTestCase, WebTestCase
@@ -45,6 +42,31 @@ class TestASignedInUser(WebTestCase):
45 42
         self.assertEqual('Barry', user.first_name)
46 43
         self.assertEqual('Chuckle', user.last_name)
47 44
 
45
+    def test_cant_update_their_email_address_if_it_already_exists(self):
46
+        User.objects.create_user(username='testuser', email='new@example.com',
47
+                                 password="somerandompassword")
48
+        self.assertEquals(User.objects.count(), 2)
49
+
50
+        profile_form_page = self.app.get(reverse('customer:profile-update'),
51
+                                user=self.user)
52
+        self.assertEqual(200, profile_form_page.status_code)
53
+        form = profile_form_page.forms['profile_form']
54
+        form['email'] = 'new@example.com'
55
+        form['first_name'] = 'Barry'
56
+        form['last_name'] = 'Chuckle'
57
+        response = form.submit()
58
+
59
+        user = User.objects.get(id=self.user.id)
60
+        self.assertEqual(self.email, user.email)
61
+
62
+        try:
63
+            User.objects.get(email='new@example.com')
64
+        except User.MultipleObjectsReturned:
65
+            self.fail("email for user changed to existing one")
66
+
67
+        self.assertContains(response,
68
+                            'A user with this email address already exists')
69
+
48 70
     def test_can_change_their_password(self):
49 71
         password_form_page = self.app.get(reverse('customer:change-password'),
50 72
                                           user=self.user)

Loading…
取消
儲存