在Django Rest框架中使用唯一约束创建用户

时间:2016-11-13 15:41:55

标签: django python-2.7 django-rest-framework django-users django-1.8

我在Google上研究过这个问题,并尝试了很多方法,但仍然无法做到正确。以下是要求:

  1. 用于扩展用户模型的一对一字段,因此已实现,新模型称为客户。
  2. 现在,新用户201返回了响应,但并非所有数据都被序列化,只有date_of_birth以json格式出现,我甚至希望用户进入json响应。
  3. 如果我尝试添加一个已经存在的用户名的用户,那么它的播放效果非常糟糕。我正在尝试使用Try和Except但它确实不起作用。如果用户名已存在,我希望发送409冲突的响应。 这是UserSerializer.py:
  4. -

    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
    

    以下是客户模型:

    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
    

    这是CustomerSerializer类:

    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()
    

    以下是客户的观点设置:

    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)
    

    所以,基本上我想创建新客户,其中所有数据都作为响应和状态201返回,如果用户名已经存在,那么我定义的409或状态代码以及DRF不应该抱怨的一些数据,即;现在它说如果修改序列化程序,OrderDict不包含PK。

    由于

    修改1

    这是更新的serilizer,带有自定义异常:

    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."
    

    但仍然得到:

        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.
    

    此外,测试案例如下:

    def test_if_anyone_could_create_customers(self):
            create_user = self.client.post('/api/customers/',
                                           {'user':{'username': 'user2', 'email': 'user2@gmail.com'}, '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': 'user2@gmail.com'},'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)
    

3 个答案:

答案 0 :(得分:2)

这是因为您在create方法中过于广泛地捕获异常。事实上,你永远不应该这样做(无论你是否使用DRF)

    except Exception as exception:
        print exception
        # print "customer --> %s " % customer
        return customer

您应该只捕获需要捕获的特定异常。而你根本不应该回归客户。在这种情况下,对ModelSerializer的检查告诉我们,你真正应该捕获的唯一一个是TypeError。即使是再次为调用者提出处理程序。

    except TypeError as exception:
        print exception
        # print "customer --> %s " % customer
        raise TypeError(exception)

现在你应该得到你想要的东西,但根据你的评论,你不是那么试着提出自定义错误。

来自rest_framework.exceptions的

导入APIException 来自django.utils.encoding import force_text

class Custom409(APIException):
    status_code = status.HTTP_409_CONFLICT
    default_detail = 'A conflict occurred'

然后

    except IntergrityError as ext:
        print exception
        # print "customer --> %s " % customer
        raise Custom409(ext)

答案 1 :(得分:1)

您的代码正在提升IntegrityErorr,因为您在创建个人资料时违反了唯一约束。

创建用户个人资料/客户实例时,您应该使用get_or_create。这样的事情应该有效。

# CustomerSerializer
def create(self, validated_data):
    """...snip..."""
    try:
        userinstance = User.objects.get_or_create(**user_data)[0]
        customer = Customer.objects.get_or_create(
            user=userinstance, defaults=validated_data)[0]
        return customer
    except Exception as exception:
        # custom validationerror
        raise ConflictError({"user": "User already exists"})

答案 2 :(得分:0)

也许您应该尝试直接在视图中捕捉它:

views.py

library(dplyr)
library(tidyr)
d %>% mutate(name=strsplit(name,split="[|]")) %>% 
      group_by(id) %>% 
      unnest() %>% 
      mutate(score=strsplit(score,split="[|]")) %>% 
      unnest()
##Source: local data frame [15 x 3]
##Groups: id [4]
##
##      id  name score
##   <dbl> <chr> <chr>
##1     22     c     e
##2     22     e     e
##3    565     m     k
##4    565     m     e
##5    565     q     k
##6    565     q     e
##7    893     w     e
##8    893     w     k
##9    893     w     e
##10   415     w     e
##11   415     w     o
##12   415     s     e
##13   415     s     o
##14   415     e     e
##15   415     e     o

删除您在序列化程序中创建的逻辑。 DRF异常意味着在视图而非序列化器中使用。