Skip to content

Commit 334a29c

Browse files
committed
Merge pull request #151 from Tivix/pr/130
Refactored registration logic
2 parents d632322 + 99c4dc9 commit 334a29c

File tree

7 files changed

+117
-73
lines changed

7 files changed

+117
-73
lines changed

docs/api_endpoints.rst

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,6 @@ Registration
5656
- password2
5757
- email
5858

59-
.. note:: This endpoint is based on ``allauth.account.views.SignupView`` and uses the same form as in this view. To override fields you have to create custom Signup Form and define it in django settings:
60-
61-
.. code-block:: python
62-
63-
ACCOUNT_FORMS = {
64-
'signup': 'path.to.custom.SignupForm'
65-
}
66-
67-
See allauth documentation for more details.
68-
6959
- /rest-auth/registration/verify-email/ (POST)
7060

7161
- key

docs/configuration.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ Configuration
2929
...
3030
}
3131
32+
- **REST_AUTH_REGISTRATION_SERIALIZERS**
33+
34+
You can define your custom serializers for registration endpoint.
35+
Possible key values:
36+
37+
- REGISTER_SERIALIZER - serializer class in ``rest_auth.register.views.RegisterView``, default value ``rest_auth.register.serializers.RegisterSerializer``
38+
39+
3240

3341
- **REST_SESSION_LOGIN** - Enable session login in Login API view (default: True)
3442

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from django.conf import settings
2+
3+
from rest_auth.registration.serializers import (
4+
RegisterSerializer as DefaultRegisterSerializer)
5+
from ..utils import import_callable
6+
7+
8+
serializers = getattr(settings, 'REST_AUTH_REGISTER_SERIALIZERS', {})
9+
10+
RegisterSerializer = import_callable(
11+
serializers.get('REGISTER_SERIALIZER', DefaultRegisterSerializer))

rest_auth/registration/serializers.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
from django.http import HttpRequest
22
from django.conf import settings
33

4+
try:
5+
from allauth.account import app_settings as allauth_settings
6+
from allauth.utils import (email_address_exists,
7+
get_username_max_length)
8+
from allauth.account.adapter import get_adapter
9+
from allauth.account.utils import setup_user_email
10+
except ImportError:
11+
raise ImportError('allauth needs to be added to INSTALLED_APPS.')
12+
413
from rest_framework import serializers
514
from requests.exceptions import HTTPError
615
# Import is needed only if we are using social login, in which
@@ -109,3 +118,57 @@ def validate(self, attrs):
109118
attrs['user'] = login.account.user
110119

111120
return attrs
121+
122+
123+
class RegisterSerializer(serializers.Serializer):
124+
username = serializers.CharField(
125+
max_length=get_username_max_length(),
126+
min_length=allauth_settings.USERNAME_MIN_LENGTH,
127+
required=allauth_settings.USERNAME_REQUIRED
128+
)
129+
email = serializers.EmailField(required=allauth_settings.EMAIL_REQUIRED)
130+
password1 = serializers.CharField(required=True, write_only=True)
131+
password2 = serializers.CharField(required=True, write_only=True)
132+
133+
def validate_username(self, username):
134+
username = get_adapter().clean_username(username)
135+
return username
136+
137+
def validate_email(self, email):
138+
email = get_adapter().clean_email(email)
139+
if allauth_settings.UNIQUE_EMAIL:
140+
if email and email_address_exists(email):
141+
raise serializers.ValidationError(
142+
"A user is already registered with this e-mail address.")
143+
return email
144+
145+
def validate_password1(self, password):
146+
return get_adapter().clean_password(password)
147+
148+
def validate(self, data):
149+
if data['password1'] != data['password2']:
150+
raise serializers.ValidationError("The two password fields didn't match.")
151+
return data
152+
153+
def custom_signup(self, request, user):
154+
pass
155+
156+
def get_cleaned_data(self):
157+
return {
158+
'username': self.validated_data.get('username', ''),
159+
'password1': self.validated_data.get('password1', ''),
160+
'email': self.validated_data.get('email', '')
161+
}
162+
163+
def save(self, request):
164+
adapter = get_adapter()
165+
user = adapter.new_user(request)
166+
self.cleaned_data = self.get_cleaned_data()
167+
adapter.save_user(request, user, self)
168+
self.custom_signup(request, user)
169+
setup_user_email(request, user, [])
170+
return user
171+
172+
173+
class VerifyEmailSerializer(serializers.Serializer):
174+
key = serializers.CharField()

rest_auth/registration/views.py

Lines changed: 29 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,42 @@
1-
from django.http import HttpRequest
21
from rest_framework.views import APIView
32
from rest_framework.response import Response
43
from rest_framework.permissions import AllowAny
4+
from rest_framework.generics import CreateAPIView
55
from rest_framework import status
66
from rest_framework.authtoken.models import Token
7+
from rest_framework.exceptions import MethodNotAllowed
78

8-
from allauth.account.views import SignupView, ConfirmEmailView
9+
from allauth.account.views import ConfirmEmailView
910
from allauth.account.utils import complete_signup
10-
from allauth.account import app_settings
11+
from allauth.account import app_settings as allauth_settings
1112

1213
from rest_auth.app_settings import TokenSerializer
13-
from rest_auth.registration.serializers import SocialLoginSerializer
14+
from rest_auth.registration.serializers import (SocialLoginSerializer,
15+
VerifyEmailSerializer)
16+
from .app_settings import RegisterSerializer
1417
from rest_auth.views import LoginView
1518

1619

17-
class RegisterView(APIView, SignupView):
18-
"""
19-
Accepts the credentials and creates a new user
20-
if user does not exist already
21-
Return the REST Token if the credentials are valid and authenticated.
22-
Calls allauth complete_signup method
23-
24-
Accept the following POST parameters: username, email, password
25-
Return the REST Framework Token Object's key.
26-
"""
27-
28-
permission_classes = (AllowAny,)
29-
allowed_methods = ('POST', 'OPTIONS', 'HEAD')
30-
token_model = Token
31-
serializer_class = TokenSerializer
32-
33-
def get(self, *args, **kwargs):
34-
return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
35-
36-
def put(self, *args, **kwargs):
37-
return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
38-
39-
def form_valid(self, form):
40-
self.user = form.save(self.request)
41-
self.token, created = self.token_model.objects.get_or_create(
42-
user=self.user
43-
)
44-
if isinstance(self.request, HttpRequest):
45-
request = self.request
46-
else:
47-
request = self.request._request
48-
return complete_signup(request, self.user,
49-
app_settings.EMAIL_VERIFICATION,
50-
self.get_success_url())
51-
52-
def get_form_kwargs(self, *args, **kwargs):
53-
kwargs = super(RegisterView, self).get_form_kwargs(*args, **kwargs)
54-
kwargs['data'] = self.request.data
55-
return kwargs
56-
57-
def post(self, request, *args, **kwargs):
58-
self.initial = {}
59-
form_class = self.get_form_class()
60-
self.form = self.get_form(form_class)
61-
if self.form.is_valid():
62-
self.form_valid(self.form)
63-
return self.get_response()
64-
else:
65-
return self.get_response_with_errors()
20+
class RegisterView(CreateAPIView):
21+
serializer_class = RegisterSerializer
22+
permission_classes = (AllowAny, )
6623

67-
def get_response(self):
68-
# serializer = self.user_serializer_class(instance=self.user)
69-
serializer = self.serializer_class(instance=self.token,
70-
context={'request': self.request})
71-
return Response(serializer.data, status=status.HTTP_201_CREATED)
24+
def create(self, request, *args, **kwargs):
25+
serializer = self.get_serializer(data=request.data)
26+
serializer.is_valid(raise_exception=True)
27+
user = self.perform_create(serializer)
28+
headers = self.get_success_headers(serializer.data)
29+
return Response(TokenSerializer(user.auth_token).data,
30+
status=status.HTTP_201_CREATED,
31+
headers=headers)
7232

73-
def get_response_with_errors(self):
74-
return Response(self.form.errors, status=status.HTTP_400_BAD_REQUEST)
33+
def perform_create(self, serializer):
34+
user = serializer.save(self.request)
35+
Token.objects.get_or_create(user=user)
36+
complete_signup(self.request._request, user,
37+
allauth_settings.EMAIL_VERIFICATION,
38+
None)
39+
return user
7540

7641

7742
class VerifyEmailView(APIView, ConfirmEmailView):
@@ -80,10 +45,12 @@ class VerifyEmailView(APIView, ConfirmEmailView):
8045
allowed_methods = ('POST', 'OPTIONS', 'HEAD')
8146

8247
def get(self, *args, **kwargs):
83-
return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
48+
raise MethodNotAllowed('GET')
8449

8550
def post(self, request, *args, **kwargs):
86-
self.kwargs['key'] = self.request.data.get('key', '')
51+
serializer = VerifyEmailSerializer(data=request.data)
52+
serializer.is_valid(raise_exception=True)
53+
self.kwargs['key'] = serializer.validated_data['key']
8754
confirmation = self.get_object()
8855
confirmation.confirm(self.request)
8956
return Response({'message': 'ok'}, status=status.HTTP_200_OK)

rest_auth/tests/test_api.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,12 @@ def test_registration(self):
318318
self._login()
319319
self._logout()
320320

321+
def test_registration_with_invalid_password(self):
322+
data = self.REGISTRATION_DATA.copy()
323+
data['password2'] = 'foobar'
324+
325+
self.post(self.register_url, data=data, status_code=400)
326+
321327
@override_settings(
322328
ACCOUNT_EMAIL_VERIFICATION='mandatory',
323329
ACCOUNT_EMAIL_REQUIRED=True

rest_auth/tests/test_social.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ def test_edge_case(self):
9999

100100
# test empty payload
101101
self.post(self.register_url, data={}, status_code=400)
102-
103102
self.post(
104103
self.register_url,
105104
data=self.REGISTRATION_DATA,

0 commit comments

Comments
 (0)