web-dev-qa-db-fra.com

Comment enregistrer des utilisateurs dans Django REST cadre?

Je code une API REST avec le framework Django REST . L'API sera le backend d'une application mobile sociale. Après avoir suivi le didacticiel, je peux sérialiser tous mes modèles et créer de nouvelles ressources et les mettre à jour.

J'utilise AuthToken pour l'authentification.

Ma question est:

Une fois que j'ai la ressource /users, je veux que l'utilisateur de l'application puisse s'enregistrer. Alors, est-il préférable d'avoir une ressource distincte comme /register ou d'autoriser les utilisateurs anonymes à POST à /users une nouvelle ressource?

En outre, des conseils sur les autorisations seraient très utiles.

54
chaim

Je suis allé de l'avant et j'ai créé mon propre affichage personnalisé pour gérer l'enregistrement, car mon sérialiseur ne s'attend pas à afficher/récupérer le mot de passe. J'ai rendu l'URL différente de la ressource/users.

Mon url conf:

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

Mon avis:

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

Je me trompe peut-être, mais il ne semble pas que vous deviez limiter les autorisations sur cette vue, car vous voudriez des requêtes non authentifiées ...

37
Cahlan Sharp

Django REST Framework 3 allow override create dans les sérialiseurs:

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

Les champs sérialisés pour les classes héritées de ModelSerializer doivent être déclarés clairement dans Meta pour Django Rest Framework v3.5 et plus récent.

Fichier 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

La solution la plus simple, fonctionnant dans 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

Pas besoin d'autres modifications, assurez-vous simplement que les utilisateurs non authentifiés ont l'autorisation de créer un nouvel objet utilisateur.

write_only_fields s'assurera que les mots de passe (en réalité: leur hachage que nous stockons) ne sont pas affichés, tandis que la méthode create écrasée garantit que le mot de passe n'est pas stocké en texte clair mais sous forme de hachage.

33
cpury

J'ai mis à jour la réponse de Cahlan afin de prendre en charge les modèles utilisateur personnalisés à partir de Django 1.5 et de renvoyer l'ID utilisateur dans la réponse.

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

En général, je traite la vue Utilisateur comme tout autre point de terminaison API nécessitant une autorisation, sauf que je remplace simplement le jeu d'autorisations de la classe de vue par le mien pour POST (aka créer). J'utilise généralement ce modèle:

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

Pour faire bonne mesure, voici le sérialiseur que j'utilise généralement avec ce dernier:

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 ci-dessus a suggéré d'utiliser l'option write_only_fields. Cela n'a cependant pas fonctionné pour moi dans DRF 3.3.3

Dans DRF 3.0 l'option write_only_fields de ModelSerializer a été déplacée vers PendingDeprecation et dans DRF 3.2 par un extra_kwargs plus générique:

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

7
Tadej Krevh

Jusqu'à présent, toutes les réponses créent l'utilisateur, puis mettent à jour le mot de passe de l'utilisateur. Cela se traduit par deux écritures dans la base de données. Pour éviter une écriture supplémentaire inutile dans la base de données, définissez le mot de passe de l'utilisateur avant de l'enregistrer:

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

Un peu tard pour la fête, mais pourrait aider quelqu'un qui ne veut pas écrire plus de lignes de code.

Nous pouvons utiliser la méthode super pour y parvenir. 

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

Une implémentation basée sur les vues Framework de Python 3, Django 2 et Django REST

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

Fichier 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

Fichier urls.py

from rest_framework.routers import DefaultRouter
from .views import CreateUserView

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

urlpatterns = router.urls
0
Nick Van Berckelaer