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