How to register users in Django REST framework?

2019-01-09 23:45发布

I'm coding a REST API with Django REST framework. The API will be the backend of a social mobile app. After following the tutorial, I can serialise all my models and I am able to create new resources and update them.

I'm using AuthToken for authentication.

My question is:

Once I have the /users resource, I want the app user to be able to register. So, is it better to have a separate resource like /register or allow anonymous users to POST to /users a new resource?

Also, some guidance about permissions would be great.

8条回答
祖国的老花朵
2楼-- · 2019-01-10 00:19

The simplest solution, working in DRF 3.x:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'username', 'password', 'email', 'first_name', 'last_name')
        write_only_fields = ('password',)
        read_only_fields = ('id',)

    def create(self, validated_data):
        user = User.objects.create(
            username=validated_data['username'],
            email=validated_data['email'],
            first_name=validated_data['first_name'],
            last_name=validated_data['last_name']
        )

        user.set_password(validated_data['password'])
        user.save()

        return user

No need for other changes, just make sure that unauthenticated users have the permission to create a new user object.

write_only_fields will make sure passwords (actually: their hash we store) are not displayed, while the overwritten create method ensures that the password is not stored in clear text, but as a hash.

查看更多
淡お忘
3楼-- · 2019-01-10 00:22

Django REST Framework 3 allow override create method in serializers:

from rest_framework import serializers
from django.contrib.auth import get_user_model # If used custom user model

UserModel = get_user_model()


class UserSerializer(serializers.ModelSerializer):

    password = serializers.CharField(write_only=True)

    def create(self, validated_data):

        user = UserModel.objects.create(
            username=validated_data['username']
        )
        user.set_password(validated_data['password'])
        user.save()

        return user

    class Meta:
        model = UserModel
        # Tuple of serialized model fields (see link [2])
        fields = ( "id", "username", "password", )

Serialized fields for classes inherited from ModelSerializer must be declared patently in Meta for Django Rest Framework v3.5 and newest.

File api.py:

from rest_framework import permissions
from rest_framework.generics import CreateAPIView
from django.contrib.auth import get_user_model # If used custom user model

from .serializers import UserSerializer


class CreateUserView(CreateAPIView):

    model = get_user_model()
    permission_classes = [
        permissions.AllowAny # Or anon users can't register
    ]
    serializer_class = UserSerializer
查看更多
贼婆χ
4楼-- · 2019-01-10 00:23

@cpury above suggested using write_only_fields option. This however did not work for me in DRF 3.3.3

In DRF 3.0 the write_only_fields option on ModelSerializer has been moved to PendingDeprecation and in DRF 3.2 replaced with a more generic extra_kwargs:

extra_kwargs = {'password': {'write_only': True}}

查看更多
The star\"
5楼-- · 2019-01-10 00:24

I went ahead and made my own custom view for handling registration since my serializer doesn't expect to show/retrieve the password. I made the url different from the /users resource.

My url conf:

url(r'^users/register', 'myapp.views.create_auth'),

My view:

@api_view(['POST'])
def create_auth(request):
    serialized = UserSerializer(data=request.DATA)
    if serialized.is_valid():
        User.objects.create_user(
            serialized.init_data['email'],
            serialized.init_data['username'],
            serialized.init_data['password']
        )
        return Response(serialized.data, status=status.HTTP_201_CREATED)
    else:
        return Response(serialized._errors, status=status.HTTP_400_BAD_REQUEST)

I may be wrong, but it doesn't seem like you'll need to limit permissions on this view since you'd want unauthenticated requests ...

查看更多
够拽才男人
6楼-- · 2019-01-10 00:25

A little late to the party, but might help someone who do not want to write more lines of code.

We can user the super method to achieve this.

class UserSerializer(serializers.ModelSerializer):

    password = serializers.CharField(
          write_only=True,
    )

    class Meta:
       model = User
       fields = ('password', 'username', 'first_name', 'last_name',)

    def create(self, validated_data):
        user = super(UserSerializer, self).create(validated_data)
        if 'password' in validated_data:
              user.set_password(validated_data['password'])
              user.save()
        return user
查看更多
forever°为你锁心
7楼-- · 2019-01-10 00:26

I updated Cahlan's answer to support custom user models from Django 1.5 and return the user's ID in the response.

from django.contrib.auth import get_user_model

from rest_framework import status, serializers
from rest_framework.decorators import api_view
from rest_framework.response import Response

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = get_user_model()

@api_view(['POST'])
def register(request):
    VALID_USER_FIELDS = [f.name for f in get_user_model()._meta.fields]
    DEFAULTS = {
        # you can define any defaults that you would like for the user, here
    }
    serialized = UserSerializer(data=request.DATA)
    if serialized.is_valid():
        user_data = {field: data for (field, data) in request.DATA.items() if field in VALID_USER_FIELDS}
        user_data.update(DEFAULTS)
        user = get_user_model().objects.create_user(
            **user_data
        )
        return Response(UserSerializer(instance=user).data, status=status.HTTP_201_CREATED)
    else:
        return Response(serialized._errors, status=status.HTTP_400_BAD_REQUEST)
查看更多
登录 后发表回答