webentwicklung-frage-antwort-db.com.de

So registrieren Sie Benutzer in Django REST Rahmen?

Ich programmiere eine REST - API mit Django REST - Framework . Die API wird das Backend einer Social Mobile App sein. Nach dem Tutorial kann ich alle meine Modelle serialisieren und neue Ressourcen erstellen und aktualisieren.

Ich verwende AuthToken zur Authentifizierung.

Meine Frage ist:

Sobald ich die Ressource /users habe, möchte ich, dass sich der App-Benutzer registrieren kann. Ist es also besser, eine separate Ressource wie /register zu haben oder anonyme Benutzer POST die neue Ressource /users zu erlauben?

Einige Hinweise zu Berechtigungen wären ebenfalls großartig.

54
chaim

Ich habe meine eigene benutzerdefinierte Ansicht für die Bearbeitung der Registrierung erstellt, da mein Serializer nicht erwartet, dass das Passwort angezeigt/abgerufen wird. Ich habe die URL von der/users-Ressource unterschieden.

Mein url conf:

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

Meine Sicht:

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

Ich bin möglicherweise falsch, aber es scheint nicht so, als müssten Sie die Berechtigungen für diese Ansicht einschränken, da Sie nicht authentifizierte Anfragen möchten.

37
Cahlan Sharp

Django REST Framework 3 allow Überschreiben der create-Methode in Serialisierern:

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

Serialisierte Felder für Klassen, die von ModelSerializer geerbt wurden, müssen in Meta für Django Rest Framework v3.5 und newest offen deklariert werden.

Datei 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
59
Dunaevsky Maxim

Die einfachste Lösung, die in DRF 3.x arbeitet:

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

Sie brauchen keine weiteren Änderungen, sondern stellen sicher, dass nicht authentifizierte Benutzer die Berechtigung haben, ein neues Benutzerobjekt zu erstellen.

write_only_fields stellt sicher, dass Passwörter (eigentlich: ihr Hash, den wir speichern) nicht angezeigt werden, während die überschriebene create-Methode dafür sorgt, dass das Passwort nicht im Klartext, sondern als Hash gespeichert wird.

33
cpury

Ich habe die Antwort von Cahlan aktualisiert, um benutzerdefinierte Benutzermodelle aus Django 1.5 zu unterstützen und die Benutzer-ID in der Antwort zurückzugeben.

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)
26
Chris

Normalerweise behandele ich die Benutzeransicht genauso wie alle anderen API-Endpunkte, für die eine Autorisierung erforderlich war, außer, ich überschreibe einfach die Berechtigungssätze der Ansichtsklasse mit meinen eigenen für POST (aka create). Ich verwende normalerweise dieses Muster:

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

Hier ist der Serializer, den ich normalerweise damit verwende:

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

25
digitalfoo

@cpury oben vorgeschlagen, die write_only_fields-Option zu verwenden. Dies funktionierte bei DRF 3.3.3 jedoch nicht

In DRF 3.0 wurde die Option write_only_fields in ModelSerializer nach PendingDeprecation verschoben und in DRF 3.2 durch ein generisches extra_kwargs ersetzt:

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

7
Tadej Krevh

Alle bisherigen Antworten erstellen den Benutzer und aktualisieren das Kennwort des Benutzers. Dies führt zu zwei DB-Schreibvorgängen. Legen Sie das Kennwort des Benutzers fest, bevor Sie es speichern:

from rest_framework.serializers import ModelSerializer

class UserSerializer(ModelSerializer):

    class Meta:
        model = User

    def create(self, validated_data):
        user = User(**validated_data)
        # Hash the user's password.
        user.set_password(validated_data['password'])
        user.save()
        return user
4
yndolok

Etwas spät zur Party, kann aber jemandem helfen, der nicht mehr Codezeilen schreiben möchte.

Wir können die super-Methode verwenden, um dies zu erreichen. 

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
2
Karan Kumar

Eine auf Python 3, Django 2 und Django REST basierende Viewset-basierte Implementierung:

Datei: 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',)

Datei 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

Datei urls.py

from rest_framework.routers import DefaultRouter
from .views import CreateUserView

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

urlpatterns = router.urls
0