make UserProfile fully optional; version 0.2.3

This commit is contained in:
Mateusz Sikora 2014-07-08 11:36:59 +02:00
parent 0a1d239374
commit 445c211ebe
5 changed files with 231 additions and 125 deletions

View File

@ -26,8 +26,6 @@ class UserProfile(models.Model):
class Meta: class Meta:
app_label = 'rest_auth' app_label = 'rest_auth'
settings.REST_PROFILE_MODULE = UserProfile
""" """
overwrite register to avoid sending email overwrite register to avoid sending email
@ -46,15 +44,13 @@ class RegistrationView(BaseRegistrationView):
request=request) request=request)
# create user profile # create user profile
user_profile_model = _resolve_model( profile_model_path = getattr(settings, 'REST_PROFILE_MODULE', None)
getattr(settings, 'REST_PROFILE_MODULE', None)) if profile_model_path:
user_profile_model = _resolve_model(profile_model_path)
user_profile_model.objects.create(user=new_user) user_profile_model.objects.create(user=new_user)
return new_user return new_user
settings.REST_REGISTRATION_BACKEND = 'rest_auth.runtests.RegistrationView'
def runtests(): def runtests():
TestRunner = get_runner(settings) TestRunner = get_runner(settings)

View File

@ -5,7 +5,8 @@ from rest_framework import serializers
from rest_framework.serializers import _resolve_model from rest_framework.serializers import _resolve_model
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
profile_model_path = getattr(settings, 'REST_PROFILE_MODULE', None)
profile_model_path = lambda: getattr(settings, 'REST_PROFILE_MODULE', None)
class LoginSerializer(serializers.Serializer): class LoginSerializer(serializers.Serializer):
username = serializers.CharField(max_length=30) username = serializers.CharField(max_length=30)
@ -74,7 +75,8 @@ class UserUpdateSerializer(DynamicFieldsModelSerializer):
if profile_model_path: def get_user_registration_profile_serializer():
if profile_model_path():
class UserRegistrationProfileSerializer(serializers.ModelSerializer): class UserRegistrationProfileSerializer(serializers.ModelSerializer):
""" """
@ -82,10 +84,17 @@ if profile_model_path:
""" """
class Meta: class Meta:
model = _resolve_model(profile_model_path) model = _resolve_model(profile_model_path())
fields = filter(lambda x: x != 'id' and x != 'user', fields = filter(lambda x: x != 'id' and x != 'user',
map(lambda x: x.name, model._meta.fields)) map(lambda x: x.name, model._meta.fields))
else:
class UserRegistrationProfileSerializer(serializers.Serializer):
pass
return UserRegistrationProfileSerializer
def get_user_profile_serializer():
if profile_model_path():
class UserProfileSerializer(serializers.ModelSerializer): class UserProfileSerializer(serializers.ModelSerializer):
""" """
@ -96,8 +105,18 @@ if profile_model_path:
class Meta: class Meta:
# http://stackoverflow.com/questions/4881607/django-get-model-from-string # http://stackoverflow.com/questions/4881607/django-get-model-from-string
model = _resolve_model(profile_model_path) model = _resolve_model(profile_model_path())
def __init__(self, *args, **kwargs):
super(UserProfileSerializer, self).__init__(*args, **kwargs)
else:
class UserProfileSerializer(serializers.Serializer):
pass
return UserProfileSerializer
def get_user_profile_update_serializer():
if profile_model_path():
class UserProfileUpdateSerializer(serializers.ModelSerializer): class UserProfileUpdateSerializer(serializers.ModelSerializer):
""" """
@ -108,15 +127,11 @@ if profile_model_path:
class Meta: class Meta:
# http://stackoverflow.com/questions/4881607/django-get-model-from-string # http://stackoverflow.com/questions/4881607/django-get-model-from-string
model = _resolve_model(profile_model_path) model = _resolve_model(profile_model_path())
else: else:
class UserRegistrationProfileSerializer(serializers.Serializer):
pass
class UserProfileSerializer(serializers.Serializer):
pass
class UserProfileUpdateSerializer(serializers.Serializer): class UserProfileUpdateSerializer(serializers.Serializer):
pass pass
return UserProfileUpdateSerializer
class SetPasswordSerializer(serializers.Serializer): class SetPasswordSerializer(serializers.Serializer):

View File

@ -112,19 +112,40 @@ class BaseAPITestCase(object):
# T E S T H E R E # T E S T H E R E
# ----------------------- # -----------------------
user_profile_model = _resolve_model(
getattr(settings, 'REST_PROFILE_MODULE', None))
class LoginAPITestCase(TestCase, BaseAPITestCase):
class APITestCase1(TestCase, BaseAPITestCase):
""" """
just run: python manage.py test rest_auth Case #1:
- user profile: defined
- custom registration: backend defined
""" """
USERNAME = 'person' USERNAME = 'person'
PASS = 'person' PASS = 'person'
EMAIL = "person1@world.com" EMAIL = "person1@world.com"
NEW_PASS = 'new-test-pass' NEW_PASS = 'new-test-pass'
PROFILE_MODEL = 'rest_auth.UserProfile'
REGISTRATION_VIEW = 'rest_auth.runtests.RegistrationView'
# data without user profile
BASIC_REGISTRATION_DATA = {
"username": USERNAME,
"password": PASS,
"email": EMAIL
}
# data with user profile
REGISTRATION_DATA = BASIC_REGISTRATION_DATA.copy()
REGISTRATION_DATA['newsletter_subscribe'] = False
BASIC_USER_DATA = {
'first_name': "John",
'last_name': 'Smith',
'email': EMAIL
}
USER_DATA = BASIC_USER_DATA.copy()
USER_DATA['newsletter_subscribe'] = True
def setUp(self): def setUp(self):
self.init() self.init()
@ -132,6 +153,17 @@ class LoginAPITestCase(TestCase, BaseAPITestCase):
self.password_change_url = reverse('rest_password_change') self.password_change_url = reverse('rest_password_change')
self.register_url = reverse('rest_register') self.register_url = reverse('rest_register')
self.password_reset_url = reverse('rest_password_reset') self.password_reset_url = reverse('rest_password_reset')
self.user_url = reverse('rest_user_details')
setattr(settings, 'REST_PROFILE_MODULE', self.PROFILE_MODEL)
self.user_profile_model = None
if self.PROFILE_MODEL:
self.user_profile_model = _resolve_model(self.PROFILE_MODEL)
if self.REGISTRATION_VIEW:
setattr(settings, 'REST_REGISTRATION_BACKEND', self.REGISTRATION_VIEW)
elif hasattr(settings, 'REST_REGISTRATION_BACKEND'):
delattr(settings, 'REST_REGISTRATION_BACKEND')
def test_login(self): def test_login(self):
payload = { payload = {
@ -202,59 +234,60 @@ class LoginAPITestCase(TestCase, BaseAPITestCase):
# send empty payload # send empty payload
self.post(self.password_change_url, data={}, status_code=400) self.post(self.password_change_url, data={}, status_code=400)
def test_registration_user_with_profile(self): def test_registration(self):
payload = { user_count = User.objects.all().count()
"username": self.USERNAME,
"password": self.PASS,
"email": "person@world.com",
"newsletter_subscribe": "false"
}
# test empty payload # test empty payload
self.post(self.register_url, data={}, status_code=400) self.post(self.register_url, data={}, status_code=400)
self.post(self.register_url, data=payload, status_code=201) self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201)
self.assertEqual(User.objects.all().count(), user_count+1)
new_user = get_user_model().objects.latest('id')
if self.REGISTRATION_VIEW:
activation_key = RegistrationProfile.objects.latest('id').activation_key activation_key = RegistrationProfile.objects.latest('id').activation_key
verify_url = reverse('verify_email', verify_url = reverse('verify_email',
kwargs={'activation_key': activation_key}) kwargs={'activation_key': activation_key})
# new user at this point shouldn't be active # new user at this point shouldn't be active
new_user = get_user_model().objects.latest('id')
self.assertEqual(new_user.is_active, False) self.assertEqual(new_user.is_active, False)
# let's active new user and check is_active flag # let's active new user and check is_active flag
self.get(verify_url) self.get(verify_url)
new_user = get_user_model().objects.latest('id') new_user = get_user_model().objects.latest('id')
self.assertEqual(new_user.is_active, True) self.assertEqual(new_user.is_active, True)
user_profile = user_profile_model.objects.get(user=new_user) if self.user_profile_model:
user_profile = self.user_profile_model.objects.get(user=new_user)
self.assertIsNotNone(user_profile) self.assertIsNotNone(user_profile)
else:
self.assertEqual(new_user.is_active, True)
def test_registration_user_without_profile(self): def test_registration_without_profile_data(self):
user_count = User.objects.all().count()
payload = { self.post(self.register_url, data=self.BASIC_REGISTRATION_DATA,
"username": self.USERNAME, status_code=201)
"password": self.PASS, self.assertEqual(User.objects.all().count(), user_count+1)
"email": self.EMAIL, new_user = get_user_model().objects.latest('id')
}
self.post(self.register_url, data=payload, status_code=201)
if self.REGISTRATION_VIEW:
activation_key = RegistrationProfile.objects.latest('id').activation_key activation_key = RegistrationProfile.objects.latest('id').activation_key
verify_url = reverse('verify_email', verify_url = reverse('verify_email',
kwargs={'activation_key': activation_key}) kwargs={'activation_key': activation_key})
# new user at this point shouldn't be active # new user at this point shouldn't be active
new_user = get_user_model().objects.latest('id')
self.assertEqual(new_user.is_active, False) self.assertEqual(new_user.is_active, False)
# let's active new user and check is_active flag # let's active new user and check is_active flag
self.get(verify_url) self.get(verify_url)
new_user = get_user_model().objects.latest('id') new_user = get_user_model().objects.latest('id')
self.assertEqual(new_user.is_active, True) self.assertEqual(new_user.is_active, True)
if self.user_profile_model:
user_profile = user_profile_model.objects.get(user=new_user) user_profile = self.user_profile_model.objects.get(user=new_user)
self.assertIsNotNone(user_profile) self.assertIsNotNone(user_profile)
else:
self.assertEqual(new_user.is_active, True)
def test_password_reset(self): def test_password_reset(self):
user = User.objects.create_user(self.USERNAME, self.EMAIL, self.PASS) user = User.objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
@ -280,6 +313,33 @@ class LoginAPITestCase(TestCase, BaseAPITestCase):
} }
self.post(self.login_url, data=payload, status_code=200) self.post(self.login_url, data=payload, status_code=200)
def test_user_details(self):
user = User.objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
if self.user_profile_model:
self.user_profile_model.objects.create(user=user)
payload = {
"username": self.USERNAME,
"password": self.PASS
}
self.post(self.login_url, data=payload, status_code=200)
self.token = self.response.json['key']
self.get(self.user_url, status_code=200)
self.post(self.user_url, data=self.BASIC_USER_DATA, status_code=200)
user = User.objects.get(pk=user.pk)
if self.user_profile_model:
self.post(self.user_url, data=self.USER_DATA, status_code=200)
user = User.objects.get(pk=user.pk)
self.assertEqual(user.first_name, self.response.json['user']['first_name'])
self.assertEqual(user.last_name, self.response.json['user']['last_name'])
self.assertEqual(user.email, self.response.json['user']['email'])
self.assertIn('newsletter_subscribe', self.response.json)
else:
self.assertEqual(user.first_name, self.response.json['first_name'])
self.assertEqual(user.last_name, self.response.json['last_name'])
self.assertEqual(user.email, self.response.json['email'])
def generate_uid_and_token(self, user): def generate_uid_and_token(self, user):
result = {} result = {}
@ -295,3 +355,30 @@ class LoginAPITestCase(TestCase, BaseAPITestCase):
result['token'] = default_token_generator.make_token(user) result['token'] = default_token_generator.make_token(user)
return result return result
class APITestCase2(APITestCase1):
"""
Case #2:
- user profile: not defined
- custom registration backend: not defined
"""
PROFILE_MODEL = None
class APITestCase3(APITestCase1):
"""
Case #3:
- user profile: defined
- custom registration backend: not defined
"""
REGISTRATION_VIEW = None
class APITestCase4(APITestCase1):
"""
Case #4:
- user profile: not defined
- custom registration backend: not defined
"""
PROFILE_MODEL = None
REGISTRATION_VIEW = None

View File

@ -23,31 +23,31 @@ from registration.models import RegistrationProfile
from registration import signals from registration import signals
from registration.views import ActivationView from registration.views import ActivationView
from .utils import construct_modules_and_import from rest_auth.utils import construct_modules_and_import
from .models import * from rest_auth.models import *
from .serializers import TokenSerializer, UserDetailsSerializer, \ from rest_auth.serializers import (TokenSerializer, UserDetailsSerializer,
LoginSerializer, UserRegistrationSerializer, \ LoginSerializer, UserRegistrationSerializer,
UserRegistrationProfileSerializer, \ SetPasswordSerializer, PasswordResetSerializer, UserUpdateSerializer,
UserProfileUpdateSerializer, SetPasswordSerializer, \ get_user_registration_profile_serializer, get_user_profile_serializer,
PasswordResetSerializer get_user_profile_update_serializer)
def get_user_profile_model():
# Get the UserProfile model from the setting value # Get the UserProfile model from the setting value
user_profile_path = getattr(settings, 'REST_PROFILE_MODULE', None) user_profile_path = getattr(settings, 'REST_PROFILE_MODULE', None)
user_profile_model = None
if user_profile_path: if user_profile_path:
user_profile_model = _resolve_model(user_profile_path) setattr(settings, 'AUTH_PROFILE_MODULE', user_profile_path)
return _resolve_model(user_profile_path)
def get_registration_backend():
# Get the REST Registration Backend for django-registration # Get the REST Registration Backend for django-registration
registration_backend = getattr(settings, 'REST_REGISTRATION_BACKEND', registration_backend = getattr(settings, 'REST_REGISTRATION_BACKEND',
'registration.backends.default.views.RegistrationView') 'registration.backends.simple.views.RegistrationView')
if not registration_backend:
raise Exception('Please configure a registration backend')
# Get the REST REGISTRATION BACKEND class from the setting value via above # Get the REST REGISTRATION BACKEND class from the setting value via above
# method # method
RESTRegistrationView = construct_modules_and_import(registration_backend) return construct_modules_and_import(registration_backend)
class LoggedInRESTAPIView(APIView): class LoggedInRESTAPIView(APIView):
@ -139,13 +139,15 @@ class Register(LoggedOutRESTAPIView, GenericAPIView):
""" """
serializer_class = UserRegistrationSerializer serializer_class = UserRegistrationSerializer
profile_serializer_class = UserRegistrationProfileSerializer
def get_profile_serializer_class(self):
return get_user_registration_profile_serializer()
def post(self, request): def post(self, request):
# Create serializers with request.DATA # Create serializers with request.DATA
serializer = self.serializer_class(data=request.DATA) serializer = self.serializer_class(data=request.DATA)
profile_serializer = self.profile_serializer_class( profile_serializer_class = self.get_profile_serializer_class()
data=request.DATA) profile_serializer = profile_serializer_class(data=request.DATA)
if serializer.is_valid() and profile_serializer.is_valid(): if serializer.is_valid() and profile_serializer.is_valid():
# Change the password key to password1 so that RESTRegistrationView # Change the password key to password1 so that RESTRegistrationView
@ -158,6 +160,7 @@ class Register(LoggedOutRESTAPIView, GenericAPIView):
data = serializer.data.copy() data = serializer.data.copy()
data.update(profile_serializer.data) data.update(profile_serializer.data)
RESTRegistrationView = get_registration_backend()
RESTRegistrationView().register(request, **data) RESTRegistrationView().register(request, **data)
# Return the User object with Created HTTP status # Return the User object with Created HTTP status
@ -182,28 +185,33 @@ class UserDetails(LoggedInRESTAPIView, GenericAPIView):
Optional: email, first_name, last_name and UserProfile fields Optional: email, first_name, last_name and UserProfile fields
Returns the updated UserProfile and/or User object. Returns the updated UserProfile and/or User object.
""" """
def get_profile_serializer_class(self):
return get_user_profile_serializer()
serializer_class = UserProfileUpdateSerializer def get_profile_update_serializer_class(self):
return get_user_profile_update_serializer()
def get(self, request): def get(self, request):
# Create serializers with request.user and profile # Create serializers with request.user and profile
user_details = UserDetailsSerializer(request.user) user_profile_model = get_user_profile_model()
try: if user_profile_model:
serializer = self.serializer_class(request.user.get_profile()) profile_serializer_class = self.get_profile_serializer_class()
profile_data = serializer.data serializer = profile_serializer_class(request.user.get_profile())
except ObjectDoesNotExist: else:
serializer = UserDetailsSerializer(request.user)
profile_data = {}
# Send the Return the User and its profile model with OK HTTP status # Send the Return the User and its profile model with OK HTTP status
return Response({ return Response(serializer.data, status=status.HTTP_200_OK)
'user': user_details.data,
'profile': profile_data},
status=status.HTTP_200_OK)
def post(self, request): def post(self, request):
# Get the User object updater via this Serializer # Get the User object updater via this Serializer
serializer = self.serializer_class( user_profile_model = get_user_profile_model()
request.user.get_profile(), data=request.DATA, partial=True) if user_profile_model:
profile_serializer_class = self.get_profile_update_serializer_class()
serializer = profile_serializer_class(request.user.get_profile(),
data=request.DATA, partial=True)
else:
serializer = UserUpdateSerializer(request.user, data=request.DATA,
partial=True)
if serializer.is_valid(): if serializer.is_valid():
# Save UserProfileUpdateSerializer # Save UserProfileUpdateSerializer

View File

@ -18,7 +18,7 @@ f.close()
setup( setup(
name='django-rest-auth', name='django-rest-auth',
version='0.2.2', version='0.2.3',
author='Sumit Chachra', author='Sumit Chachra',
author_email='chachra@tivix.com', author_email='chachra@tivix.com',
url='http://github.com/Tivix/django-rest-auth', url='http://github.com/Tivix/django-rest-auth',