如何在Django REST框架中注册用户?

时间:2013-05-31 12:36:28

标签: django python-2.7 django-models django-rest-framework

我使用Django REST framework编写REST API代码。 API将成为社交移动应用的后端。在学完本教程之后,我可以序列化所有模型,并且能够创建新资源并更新它们。

我正在使用AuthToken进行身份验证。

我的问题是:

我拥有/users资源后,我希望应用用户能够注册。那么,拥有像/register这样的单独资源或允许匿名用户向/users发布新资源更好吗?

此外,关于权限的一些指导会很棒。

10 个答案:

答案 0 :(得分:62)

Django REST Framework 3 allow覆盖序列化程序中的create方法:

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", )

继承自ModelSerializer的类的序列化字段必须在Meta中为Django Rest Framework v3.5和最新版本声明。

档案 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

答案 1 :(得分:38)

由于我的序列化程序不希望显示/检索密码,因此我继续制作自己的自定义视图以处理注册。我使网址与/ users资源不同。

我的网址:

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

我的观点:

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

我可能错了,但您似乎不需要限制此视图的权限,因为您需要未经身份验证的请求...

答案 2 :(得分:33)

最简单的解决方案,在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

无需进行其他更改,只需确保未经身份验证的用户有权创建新的用户对象。

write_only_fields将确保不显示密码(实际上:我们存储的哈希值),而覆盖的create方法确保密码不以明文形式存储,而是以哈希形式存储。 / p>

答案 3 :(得分:26)

我更新了Cahlan的答案,以支持Django 1.5中的自定义用户模型,并在响应中返回用户ID。

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)

答案 4 :(得分:25)

我通常将用户视图视为需要授权的任何其他API端点,除了我只是用我自己的POST(aka create)覆盖视图类的权限集。我通常使用这种模式:

from django.contrib.auth import get_user_model
from rest_framework import viewsets
from rest_framework.permissions import AllowAny


class UserViewSet(viewsets.ModelViewSet):
    queryset = get_user_model().objects
    serializer_class = UserSerializer

    def get_permissions(self):
        if self.request.method == 'POST':
            self.permission_classes = (AllowAny,)

        return super(UserViewSet, self).get_permissions()

为了更好的衡量,这里是我通常使用的序列化器:

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = get_user_model()
        fields = (
            'id',
            'username',
            'password',
            'email',
            ...,
        )
        extra_kwargs = {
            'password': {'write_only': True},
        }

    def create(self, validated_data):
        user = get_user_model().objects.create_user(**validated_data)
        return user

    def update(self, instance, validated_data):
        if 'password' in validated_data:
            password = validated_data.pop('password')
            instance.set_password(password)
        return super(UserSerializer, self).update(instance, validated_data)

djangorestframework 3.3.x / Django 1.8.x

答案 5 :(得分:7)

以上@cpury建议使用write_only_fields选项。然而,这在DRF 3.3.3中对我不起作用

DRF 3.0中,ModelSerializer上的write_only_fields选项已移至PendingDeprecation,DRF 3.2替换为更通用的extra_kwargs:

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

答案 6 :(得分:4)

到目前为止,所有答案都会创建用户,然后更新用户的密码。这导致两个DB写入。为避免额外不必要的DB写入,请在保存之前设置用户密码:

console.log('H' < 'e', 'H'.charCodeAt(0), 'e'.charCodeAt(0));

答案 7 :(得分:2)

派对有点晚了,但可能会帮助那些不想写更多代码的人。

我们可以使用super方法来实现这一目标。

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

答案 8 :(得分:1)

虽然这个问题有很多答案,但没有一个答案(在我撰写本文时)解决关键的安全问题,即 settings.AUTH_PASSWORD_VALIDATORS 中定义的密码验证。因此,可以创建像 '1' 这样的密码,这一定是不可接受的。所以我已经解决了这个主要的安全问题。这是我的解决方案:

在 serializers.py 中:

from django.contrib.auth import get_user_model
from django.contrib.auth.password_validation import validate_password
from rest_framework import serializers


class SignupSerializer(serializers.ModelSerializer):
    class Meta:
        model = get_user_model()
        fields = ['username', 'first_name', 'last_name', 'email', 'password', ]
        extra_kwargs = {
            'password': {'write_only': True}
        }

    def validate_password(self, value):
        validate_password(value)
        return value

    def create(self, validated_data):
        user = get_user_model()(**validated_data)

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

        return user

在 views.py 中:

from rest_framework import mixins, viewsets
from rest_framework.permissions import AllowAny, IsAuthenticated

from . import forms, serializers


class SignupViewSet(mixins.CreateModelMixin,
                    viewsets.GenericViewSet):
    permission_classes = [AllowAny]
    serializer_class = serializers.SignupSerializer

API 响应:

现在,如果您尝试使用像 '1' 这样的简单密码,此响应将自动返回:

{
    "password": [
        "This password is too short. It must contain at least 8 characters.",
        "This password is too common.",
        "This password is entirely numeric."
    ]
}

对于像 '12345678' 这样的密码,响应是:

{
    "password": [
        "This password is too common.",
        "This password is entirely numeric."
    ]
}

通过这种方式,最终客户端将确切知道密码有效还需要什么。

答案 9 :(得分:0)

基于Python 3,Django 2和Django REST Framework视图集的实现:

文件: serializers.py

from rest_framework.serializers import ModelSerializers
from django.contrib.auth import get_user_model

UserModel = get_user_model()

class UserSerializer(ModelSerializer):
    password = serializers.CharField(write_only=True)

    def create(self, validated_data):
        user = UserModel.objects.create_user(
            username=validated_data['username'],
            password=validated_data['password'],
            first_name=validated_data['first_name'],
            last_name=validated_data['last_name'],
        )
        return user

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

文件 views.py

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import CreateModelMixin
from django.contrib.auth import get_user_model
from .serializers import UserSerializer

class CreateUserView(CreateModelMixin, GenericViewSet):
    queryset = get_user_model().objects.all()
    serializer_class = UserSerializer

文件 urls.py

from rest_framework.routers import DefaultRouter
from .views import CreateUserView

router = DefaultRouter()
router.register(r'createuser', CreateUserView)

urlpatterns = router.urls