view.py
class UserViewSet(viewsets.ModelViewSet):
""" API endpoint that allows users to be viewed or edited. """
serializer_class = UserSerializer
queryset = UserRegister.objects.all()
authentication_classes = (TokenAuthentication,)
permission_classes = (permissions.UpdateRegister,)
This is the view.py file. Here i have added authentication and permission to my view set.
permission.py
class UpdateRegister(permissions.BasePermission):
""" Allow user to edit their own profile. """
def has_object_permissions(self, request, view, obj):
""" Check user is trying to edit their own profile. """
if request.method in permissions.SAFE_METHODS:
return True
return obj.id == request.user.id
serializer.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = UserRegister
fields = ('id', 'name', 'email', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
""" Create and return a new user"""
user = UserRegister(
email=validated_data['email'],
name=validated_data['name'],
)
user.set_password(validated_data['password'])
user.save()
return user
Error shown in network tab
So guy i am being bother by this error. Hope you guys will address my mistake and help me to solve the error.
Currently any password that I PUT or PATCH do not get encrypted.
I am using a ModelSerializer.
class UserSerializer (serializers.ModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'pk')
With a ModelViewSet
class UserViewSet (ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
Why are my passwords saved as plain text? And how do I fix this?
Should I overwrite the update () in serialiser or update () in ViewSet? Where's the problem?
You should overwrite create and update methods in your serializers to make encryption possible:
from django.contrib.auth.hashers import make_password
class UserSerializer(serializers.ModelSerializer):
def create(self, validated_data):
user = User.objects.create_user(
password=make_password(
validated_data['user'].pop('password')
),
**validated_data.pop('user')
)
def update(self, instance, validated_data):
if 'user' in validated_data:
instance.user.password = make_password(
validated_data.get('user').get('password', instance.user.password)
)
instance.user.save()
class Meta:
model = User
fields = (
'url', 'username', 'email', 'pk'
)
I have a custom user model and I am using django-rest-framework to create API
models.py:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(
unique=True,
max_length=254,
)
first_name = models.CharField(max_length=15)
last_name = models.CharField(max_length=15)
mobile = models.IntegerField(unique=True)
date_joined = models.DateTimeField(default=timezone.now)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
serializers.py:
class UserSerializer(serializers.ModelSerializer):
password1 = serializers.CharField(write_only=True)
password2 = serializers.CharField(write_only=True)
class Meta:
model = User
fields = ('first_name', 'last_name', 'email', 'mobile', 'password1', 'password2')
views.py:
#api_view(['POST'])
#permission_classes((AllowAny,))
def create_user(request):
serialized = UserSerializer(data=request.data)
if serialized.is_valid():
User.objects.create_user(
serialized.save()
)
return Response(serialized.data, status=status.HTTP_201_CREATED)
else:
return Response(serialized._errors, status=status.HTTP_400_BAD_REQUEST)
However, when I try to create a new user I am getting this error:
Got a TypeError when calling User.objects.create(). This may be because you have a writable field on the serializer class that is not a valid argument to User.objects.create(). You may need to make the field read-only, or override the UserSerializer.create() method to handle this correctly.
This maybe because there's no password1 or password2 fields in the User model. But so, how can I create an API to create a new user using django-rest-framework?
I think one password field is enough. If you want to check the user's twice password input is same, do it in the front-end. You can override a create method from serializer like following.
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
class Meta:
model = User
fields = ('first_name', 'last_name', 'email', 'mobile', 'password')
def create(self, validated_data):
user = super(UserSerializer, self).create(validated_data)
user.set_password(validated_data['password'])
user.save()
return user
views.py
from rest_framework import generics
from rest_framework.permissions import AllowAny
from .models import User
from .serializers import UserSerializer
class UserCreateAPIView(generics.CreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (AllowAny,)
thee are my serializers for different users
class PlayerSerializer(serializers.ModelSerializer):
class Meta:
model = player
fields = ('contact_name', 'contact_email', 'address',)
class ManagerSerializer(serializers.ModelSerializer):
class Meta:
model = Manager
fields = ('contact_name', 'contact_email', 'address',
'city', 'country',)
class CoachSerializer(serializers.ModelSerializer):
class Meta:
model = Shipper
fields = (' 'contact_name', 'contact_email', 'address', 'city', 'about', 'country',)
these are my user Serializers with relation with each user type
class playerUserSerialzers(serializers.ModelSerializer):
player =PlayerSerializer()
class Meta:
model = MyUser
fields = ('email', 'id', 'password', 'player',)
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
player_data = validated_data.pop('player')
user = MyUser(
email=validated_data['email'],
)
user.set_password(validated_data['password'])
user = MyUser.objects.create(**validated_data)
Player.objects.create(user=user, **player_data)
user.save()
return user
class managerUserSerialzers(serializers.ModelSerializer):
manager =ManagerSerializer()
class Meta:
model = MyUser
fields = ('email', 'id', 'password', 'manager',)
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
manager_data = validated_data.pop('manager')
user = MyUser(
email=validated_data['email'],
)
user.set_password(validated_data['password'])
user = MyUser.objects.create(**validated_data)
Manager.objects.create(user=user, **manager_data)
user.save()
return user
class managerUserSerialzers(serializers.ModelSerializer):
coach =CoachSerializer()
class Meta:
model = MyUser
fields = ('email', 'id', 'password', 'coach',)
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
coach_data = validated_data.pop('coach')
user = MyUser(
email=validated_data['email'],
)
user.set_password(validated_data['password'])
user = MyUser.objects.create(**validated_data)
Coach.objects.create(user=user, **coach_data)
user.save()
return user
this doesn't work but i would like to use different serializers based on the front end request
class MyUserViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
mixins.UpdateModelMixin,
mixins.ListModelMixin,
GenericViewSet):
queryset = MyUser.objects.all()
pagination_class = LimitOffsetPagination
permission_classes = (AllowAny,)
how do i switch bases on serializer class here
def get_serializer_class(self,):
if self.request.query_params.get("player", None):
return MyBrokerUserSerialzers
From the documentation you can specify the serializer_class property telling the right serializer you want.
Then, as you are inheriting many mixing classes you can overwrite their methods and get the information from your frontend. For each kind of request method you will have to get the frontend information in a different way.
class MyUserViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
mixins.UpdateModelMixin,
mixins.ListModelMixin,
GenericViewSet):
queryset = MyUser.objects.all()
pagination_class = LimitOffsetPagination
permission_classes = (AllowAny,)
def define_serializer_class(self, serializer_class_name):
serializer_class = serializer_class_name
def get(self, request, *args, **kwargs):
#here in GET METHOD you wil user the RetrieveModelMixin
#So get the kind of serializer from you get params in request.GET["param name"]
#and set the right one with a 'if' condition and calling
# self.define_serializer_class(<the name of the right serializer class>)
return self.retrieve(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
#here in POST METHOD you wil user the CreateModelMixin
#So get the kind of serializer from you post playload in request.POST["param name"]
#and set the right one with a 'if' condition and calling
# self.define_serializer_class(<the name of the right serializer class>)
return self.update(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
#Do the same here get the info from the frontend
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
#Do the same here get the info from the frontend
return self.destroy(request, *args, **kwargs)
I'm sorry I cannot tell the right code to do what you want, but
the comments in the code explain step-by-step a way to solve your problem. I never have done it myself because I don't like to use mixings (they make up everying), but it will work for you. Just find the right way to get the information from the frontend for each kind of HTTP method that the mixins use and set the serializer_class as I said above.
I hope it helps you.
In my opinion this is unclear what you are trying to achieve and why are you doing things this way.
Let me put it in the perspective, if you have 3 seaparate objects, create 3 separate backends for them and you do not need anything else.
class UserMixin(object):
def create(self, validated_data):
user = MyUser(email=validated_data.pop('email'))
user.set_password(validated_data.pop('password')
user.save() # now you can use user in relations
validated_data['user'] = user
super(UserMixin, self).create(**validated_data)
return user
class PlayerSerializer(serializers.ModelSerializer, UserMixin):
class Meta:
model = Player
fields = ('id', 'email', 'password', 'contact_name', 'contact_email',
'address')
read_only_fields = ('id',)
extra_kwargs = {'password': {'write_only': True}}
class ManagerSerializer(serializers.ModelSerializer, UserMixin):
class Meta:
model = Manager
fields = ('id', 'email', 'password', 'contact_name', 'contact_email',
'address', 'city', 'country')
read_only_fields = ('id',)
extra_kwargs = {'password': {'write_only': True}}
class CoachSerializer(serializers.ModelSerializer, UserMixin):
class Meta:
model = Shipper
fields = ('id', 'email', 'password', 'contact_name', 'contact_email',
'address', 'city', 'about', 'country',)
read_only_fields = ('id',)
extra_kwargs = {'password': {'write_only': True}}
# now 3 viewsets
class PlayerViewset(viewsets.ModelViewSet):
model = Player
queryset = Player.objects.all()
serializer_class = PlayerSerializer
class ManagerViewset(viewsets.ModelViewSet):
model = Manager
queryset = Manager.objects.all()
serializer_class = ManagerSerializer
class CoachViewset(viewsets.ModelViewSet):
model = Shipper
queryset = Shipper.objects.all()
serializer_class = CoachSerializer
# register api ends
api_router.register('player', PlayerViewset)
api_router.register('manager', ManagerViewset)
api_router.register('coach', CoachViewset)
simple, efective and not much work...
p.s. class names starts with capital letter - please read pep-8
I'm trying to add a custom create method, and am using the Django REST FRAMEWORK. Once a user is created, I want to create an auth token and then return a JSON response with this user AND an auth token.
UPDATE: I was able to update the below to create the user, but now I am getting Cannot assign "<User: User object>": "Token.user" must be a "User" instance What am i doing wrong?
How can I modify the below so when POST to users/, I create a user, create an auth token, and return both?
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email', 's3_link', 'phone_confirmed', 'agreed_to_tos', 'phone_number', 'facebook_id', 'stripe_id', 'phone_verification_code')
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
##ISSUE WITH CODE STARTS HERE
user = serializer.save()
token = Token.objects.create(user=user)
You could use a custom Response to add the token to the user data:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email', 's3_link', 'phone_confirmed', 'agreed_to_tos', 'phone_number', 'facebook_id', 'stripe_id', 'phone_verification_code')
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
# Customized rest_framework.mixins.CreateModelMixin.create():
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
# Your code
user = serializer.save()
token = Token.objects.create(user=user)
# Create custom response
data = serializer.data
# You may need to serialize your token:
# token = token.your_token_string_field
data.update({'token': token})
headers = self.get_success_headers(serializer.data)
return Response(data, status=status.HTTP_201_CREATED, headers=headers)
If you want to create a user, I would recommend following this format:
serializers.py:
class AccountCreateSerializer(serializers.ModelSerializer):
class Meta:
model = User
# You will need to check accuracy of fields, but this is for demo purposes
fields = ['username', 'email', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User(
username=validated_data['username'],
email=validated_data['email']
)
user.set_password(validated_data['password'])
user.save()
return user
views.py:
from rest_framework import generics
from serializers.py import AccountCreateSerializer
class AccountCreateAPIView(generics.CreateAPIView):
serializer_class = AccountCreateSerializer