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