I researched about it in Google, and have tried in lot of ways but still not able to get it right. Here are the requirements:
- A one to one field for extending User model, so this has been achieved and the new model is called Customer.
- Now, with new user 201 response is returned BUT not all the data is serialized, only date_of_birth is coming in json format, I want even the User to be in that json response.
- If I try to add a user with a username which already exists it plays up really bad. I am trying with Try and Except but it really doesnt work. I want a response of 409 conflict to be sent if the username already exists. Here is UserSerializer.py:
-
from django.contrib.auth.models import User
from rest_framework import serializers
class UserSerializer(serializers.HyperlinkedModelSerializer):
new_username = serializers.SerializerMethodField()
class Meta:
model = User
fields = ('url', 'pk', 'username', 'email', 'is_staff', 'new_username')
extra_kwargs = {
'username': {'validators': []},
}
def get_new_username(self, obj):
return obj.username
Here is Customer model:
from django.db import models
from django.contrib.auth.models import User
class Customer(models.Model):
user = models.OneToOneField(User, related_name="customer", on_delete=models.CASCADE)
date_of_birth = models.DateField(max_length=8)
def __unicode__(self):
return u'%s' % self.user.username
Here is CustomerSerializer class:
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model
from rest_framework import serializers, status
from rest_framework.response import Response
from customers.models import Customer
from api.serializers import UserSerializer
class CustomerSerializer(serializers.HyperlinkedModelSerializer):
user = UserSerializer()
class Meta:
model = Customer
fields = ('url', 'date_of_birth', 'user')
def create(self, validated_data):
print "coming inside serializer create"
user_data = validated_data.pop("user")
print user_data
try:
userinstance = User.objects.get_or_create(**user_data)[0]
print "user..."
print userinstance
print validated_data
customer = Customer.objects.create(user=userinstance, **validated_data)
print customer.user
return customer
except Exception as exception:
print exception
# print "customer --> %s " % customer
return customer
def update(self, instance, validated_data):
print "coming inside update"
user_data = validated_data.pop("user")
username = user_data.pop('username')
user = get_user_model().objects.get_or_create(username=username)[0]
user.username = username
user.email = user_data.get('email', user.email)
user.save()
# instance.user = user
instance.date_of_birth = validated_data.get('date_of_birth', instance.date_of_birth)
instance.save()
And here is view set for Customer:
from rest_framework import viewsets
from customers.models import Customer
from customers.serializers import CustomerSerializer
from api.permissions import IsOwnerOrAdmin
from rest_framework import authentication, permissions, status
from rest_framework.response import Response
class CustomerViewSet(viewsets.ModelViewSet):
serializer_class = CustomerSerializer
queryset = Customer.objects.all()
authentication_classes = (authentication.TokenAuthentication,
authentication.SessionAuthentication,
authentication.SessionAuthentication, )
def get_permissions(self):
if self.action == 'list':
self.permission_classes = (permissions.IsAdminUser,)
elif self.action == 'create':
self.permission_classes = (permissions.AllowAny,)
return super(self.__class__, self).get_permissions()
def create(self, request, *args, **kwargs):
print "This is view create -----------------------------"
serializer = self.get_serializer(data=request.data)
# print serializer
if serializer.is_valid(): # It passes because here there are no new objects created yet
print "serializer is valid ......"
# self.pre_save(serializer.object)
# user_data = serializer.validated_data.get("user")
# print user_data
self.object = serializer.create(serializer.validated_data) # It creates the User (triggering the signal) instance and then when saving UserProfile, it give the integrity error
# self.post_save(self.object, created=True)
# headers = self.get_success_headers(serializer.data)
print 'coming here ....1'
print self.object
return Response(serializer.validated_data, status=status.HTTP_201_CREATED)
print 'coming here..'
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
So, basically I would like to create new customers with all data returned as response and status 201 and if username already exists, then 409 or status code which I define and the some data which DRF should not complaint about i.e; right now it says that OrderDict does not contain PK if I modify the serializer.
Thanks
Edit 1
Here is updated serilizer with custom exception:
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model
from django.db import IntegrityError
from rest_framework import serializers, status
from rest_framework.response import Response
from rest_framework.exceptions import APIException
from customers.models import Customer
from api.serializers import UserSerializer
class CustomerSerializer(serializers.HyperlinkedModelSerializer):
user = UserSerializer()
class Meta:
model = Customer
fields = ('url', 'pk', 'date_of_birth', 'user')
def create(self, validated_data):
print "coming inside serializer create"
user_data = validated_data.pop("user")
print user_data
try:
userinstance = User.objects.create_user(**user_data)
print "user..."
print userinstance
print validated_data
customer = Customer.objects.create(user=userinstance, **validated_data)
print customer.user
return customer
# except TypeError as exception:
# print exception
# # print "customer --> %s " % customer
# raise TypeError(exception)
except IntegrityError as exception:
raise Custom409(exception)
def update(self, instance, validated_data):
print "coming inside update"
user_data = validated_data.pop("user")
username = user_data.pop('username')
user = get_user_model().objects.get_or_create(username=username)[0]
user.username = username
user.email = user_data.get('email', user.email)
user.save()
# instance.user = user
instance.date_of_birth = validated_data.get('date_of_birth', instance.date_of_birth)
instance.save()
return instance
class Custom409(APIException):
status_code = status.HTTP_409_CONFLICT
default_detail = "User already there."
But still get :
Traceback (most recent call last):
File "/home/naveen/projects/gratis/customers/tests.py", line 37, in test_if_anyone_could_create_customers
format='json')
File "/home/naveen/.virtualenvs/gratis/local/lib/python2.7/site-packages/rest_framework/test.py", line 299, in post
path, data=data, format=format, content_type=content_type, **extra)
File "/home/naveen/.virtualenvs/gratis/local/lib/python2.7/site-packages/rest_framework/test.py", line 221, in post
return self.generic('POST', path, data, content_type, **extra)
File "/home/naveen/.virtualenvs/gratis/local/lib/python2.7/site-packages/django/test/client.py", line 379, in generic
return self.request(**r)
File "/home/naveen/.virtualenvs/gratis/local/lib/python2.7/site-packages/rest_framework/test.py", line 288, in request
return super(APIClient, self).request(**kwargs)
File "/home/naveen/.virtualenvs/gratis/local/lib/python2.7/site-packages/rest_framework/test.py", line 240, in request
request = super(APIRequestFactory, self).request(**kwargs)
File "/home/naveen/.virtualenvs/gratis/local/lib/python2.7/site-packages/django/test/client.py", line 466, in request
six.reraise(*exc_info)
File "/home/naveen/.virtualenvs/gratis/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 132, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/naveen/.virtualenvs/gratis/local/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
return view_func(*args, **kwargs)
File "/home/naveen/.virtualenvs/gratis/local/lib/python2.7/site-packages/rest_framework/viewsets.py", line 83, in view
return self.dispatch(request, *args, **kwargs)
File "/home/naveen/.virtualenvs/gratis/local/lib/python2.7/site-packages/rest_framework/views.py", line 477, in dispatch
response = self.handle_exception(exc)
File "/home/naveen/.virtualenvs/gratis/local/lib/python2.7/site-packages/rest_framework/views.py", line 437, in handle_exception
self.raise_uncaught_exception(exc)
File "/home/naveen/.virtualenvs/gratis/local/lib/python2.7/site-packages/rest_framework/views.py", line 448, in raise_uncaught_exception
raise exc
IntegrityError: duplicate key value violates unique constraint "auth_user_username_key"
DETAIL: Key (username)=(user2) already exists.
And also the test case is as follows:
def test_if_anyone_could_create_customers(self):
create_user = self.client.post('/api/customers/',
{'user':{'username': 'user2', 'email': '[email protected]'}, 'date_of_birth':"1982-10-20"},
format='json')
print create_user
self.assertEqual(create_user.status_code, 201)
create_user = self.client.post('/api/customers/',
{'user': {'username': 'user2', 'email': '[email protected]'},'date_of_birth': "1982-10-20"},
format='json')
print create_user
# no duplicates
user = User.objects.all()
print user
self.assertEqual(create_user.status_code, 409)