I extended my user class to include other fields (city, country, bio). I would like to pass these down along to customizing my JWT access_token to include these fields as a payload in addition to username.
This is what I have in my models.py (extending my user class):
from django.db import models
from django.contrib.auth.models import User
#extending user model to include
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
city = models.CharField(max_length=50,blank=True)
country = models.CharField(max_length=50, blank=True)
bio = models.CharField(max_length=500, blank=True)
def __str__(self):
return self.user.username
serializers.py:
#changed from serializers.HyperLinked to ModelSerializer
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
#removed url from fields
fields = ['username', 'email', 'password', 'first_name', 'last_name', 'city', 'country', 'bio']
extra_kwargs = {
'password': {'write_only': True},
}
def create(self,validated_data):
user = Profile.objects.create_user(
username=validated_data['username'],
first_name=validated_data['first_name'],
last_name=validated_data['last_name'],
city=validated_data['city'],
country=validated_data['country'],
bio=validated_data['bio'],
email=validated_data['email'])
user.set_password(validated_data['password'])
user.save()
return user
#customizing the payload we get from our access tokens
class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
#classmethod
def get_token(cls, user):
token = super().get_token(user)
token['username'] = user.username
token['first_name'] = user.first_name
token['last_name'] = user.last_name
token['country'] = user.profile.country
token['city'] = user.profile.city
token['bio'] = user.profile.bio
return token
and api.py
#Register API
class RegisterApi(generics.GenericAPIView):
serializer_class = RegisterSerializer
#remove this if it doesn't work
authentication_classes = (TokenAuthentication,)
permission_classes = (AllowAny,)
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
return Response({
"user": UserSerializer(user, context=self.get_serializer_context()).data,
"message": "User Created Successfully. Now perform Login to get your token",
})
When I attempt to login now I get the error message:
AttributeError at /api/token/
'User' object has no attribute 'profile'
Request Method: POST
Request URL: http://127.0.0.1:8000/api/token/
Related
I'm newbie with django. I want to create an login, signup API so I find a solution on Internet. But it's not user my own User model, it use django.contrib.auth.models import AbstractUser. I don't need some field of AbtractUser so I give it =none.
Here is my models code:
from django.db import models
from django.contrib.postgres.fields import ArrayField
from django.contrib.postgres.fields import JSONField
from django.contrib.auth.models import AbstractUser
# Create your models here.
# class User(models.Model):
# name = models.CharField(null=False,blank=False, max_length=512)
# username = models.CharField(null=False,blank=False, max_length=512, unique=True)
# email = models.EmailField(null=False,blank=False, max_length=512, unique=True)
# password = models.CharField(null=False,blank=False, max_length=512)
# status = models.CharField(null=True,blank=True, max_length=512)
# role = models.IntegerField(null=False, blank=False, default=1)
# USERNAME_FIELD = 'username'
# REQUIRED_FIELDS = []
# def __str__(self):
# return self.username
class User(AbstractUser):
last_login = None
is_staff = None
is_superuser = None
first_name = None
last_name = None
name = models.CharField(null=False,blank=False, max_length=512)
username = models.CharField(null=False,blank=False, max_length=512, unique=True)
email = models.EmailField(null=False,blank=False, max_length=512, unique=True)
status = models.CharField(null=True,blank=True, max_length=512)
role = models.IntegerField(null=False, blank=False, default=1)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = []
def __str__(self):
return self.username
class Category(models.Model):
title = models.TextField(null=False, blank=False)
description = models.TextField(null=False, blank=False)
class Question(models.Model):
title = models.TextField(null=False, blank=False)
description = models.TextField(null=False, blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
choices = models.JSONField(null=False, blank=False)
answer = models.TextField(null=False,blank=False)
level = models.IntegerField(null=True, blank=True)
But I can't login to my Django Admin with my Superuser. I create new superuser but I got an error TypeError: User() got an unexpected keyword argument 'is_staff'
My command to create superuser: python manage.py createsuperuser
I don't understand why my model can affect to my app superuser. Anyone can explain for me and give me some solution.
Here is my code in serializer:
from django.contrib.postgres import fields
from rest_framework import serializers
from app.models import User, Question, Category
class RegistrationSerializer(serializers.ModelSerializer):
email = serializers.EmailField(max_length=50, min_length=6)
username = serializers.CharField(max_length=50, min_length=6)
password = serializers.CharField(max_length=150, write_only=True)
class Meta:
model = User
fields = ['id', 'name','email', 'username', 'password', 'role']
def validate(self, args):
email = args.get('email', None)
username = args.get('username', None)
if User.objects.filter(email=email).exists():
raise serializers.ValidationError({'email': ('Email already exists')})
if User.objects.filter(username=username).exists():
raise serializers.ValidationError({'username': ('Username already exists')})
return super().validate(args)
# def create(self, validated_data):
# return User.objects.create_user(**validated_data)
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ['id', 'name', 'username', 'email', 'role']
class CategorySerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Category
fields = ['id', 'title', 'description']
class QuestionSerializer(serializers.ModelSerializer):
# category = CategorySerializer(read_only=False)
class Meta:
model = Question
fields = ['id', 'description', 'category', 'choices', 'answer', 'level']
Here is my code in views:
from django.db.models import query
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework import generics
from rest_framework import viewsets
from rest_framework import serializers
from rest_framework import permissions
import uuid
from app.models import User, Category, Question
from app.serializers import UserSerializer, QuestionSerializer, CategorySerializer, RegistrationSerializer
from django.shortcuts import get_object_or_404
# Create your views here.
class RegistrationAPIView(generics.GenericAPIView):
serializer_class = RegistrationSerializer
permission_classes = (permissions.IsAuthenticated,)
def post(self, request):
serializer = self.get_serializer(data = request.data)
if(serializer.is_valid()):
serializer.save()
return Response({
"RequestId": str(uuid.uuid4()),
"Message": "User created successfully",
"User": serializer.data}, status=status.HTTP_201_CREATED
)
return Response({"Errors": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
class ListUsersAPIView(APIView):
permission_classes = (permissions.IsAuthenticated,)
def get(self, request):
listUser = User.objects.all()
info = UserSerializer(listUser, many = True)
return Response(data = info.data, status = status.HTTP_200_OK)
# def post(self, request, format=None):
# serializer = UserSerializer(data=request.data)
# if serializer.is_valid():
# serializer.save()
# return Response(serializer.data, status=status.HTTP_201_CREATED)
# return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class DetailUsersAPIView(APIView):
permission_classes = (permissions.IsAuthenticated,)
def get(self,request, id):
try:
user = get_object_or_404(User, pk=id)
data = UserSerializer(user).data
return Response(data, status = status.HTTP_200_OK)
except:
return Response("Error",status = status.HTTP_404_NOT_FOUND)
def patch(self, request, id):
user = get_object_or_404(Users, pk=id)
serializer = UserSerializer(user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class CategoryViewSet(viewsets.ModelViewSet):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = CategorySerializer
queryset = Category.objects.all()
class QuestionViewSet(viewsets.ModelViewSet):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = QuestionSerializer
queryset = Question.objects.all()
Thanks very much.
You will need to define your own UserManager [Django-doc] to register the user without making use of is_staff, is_superuser, etc. This manager is used to create user objects. Since you have set fields like is_staff, is_superuser, etc. to None, these are no longer model fields.
Therefore you should implement your own user manager that will no longer try to set these fields in the user model. So this means it looks like:
from django.contrib.auth.models import UserManager
class CustomUserManager(UserManager):
def create_user(self, username, email=None, password=None, **extra_fields):
return self._create_user(username, email, password, **extra_fields)
def create_superuser(self, username, email=None, password=None, **extra_fields):
return self._create_user(username, email, password, **extra_fields)
This UserManager will not add is_staff and/or is_superuser to the fields that are used. You thus assign the CutomUserManager as objects manager to the user model:
class User(AbstractUser):
# ⋮
objects = CustomUserModel()
I was trying to register user in django using User model from 'django.contrib.auth.models', but when encrypting password, it shows 'The crypt module is not supported on Windows'. Also I tried importing from passlib.hash the pbkdf2_sha256 to encrypt the data.
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email')
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'email', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
encryptedpass = pbkdf2_sha256.encrypt(validated_data['password'], rounds=12000, salt_size=32)
user = User.objects.create_user(username=validated_data['username'], email=validated_data['email'], password=encryptedpass)
return user
views.py
class RegisterAPI(generics.GenericAPIView):
serializer_class = RegisterSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
return Response({
"user": UserSerializer(user, context=self.get_serializer_context()).data,
"token": AuthToken.objects.create(user)[1]
})
class LoginAPI(KnoxLoginView):
permission_classes = (permissions.AllowAny,)
def post(self, request, format=None):
encryptedpass = pbkdf2_sha256.encrypt(request.data['password'], rounds=12000, salt_size=32)
mydata = {'username':request.data['username'], 'password':encryptedpass}
serializer = AuthTokenSerializer(data=mydata)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
login(request, user)
return super(LoginAPI, self).post(request, format=None)
# Get User API
class UserAPI(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated,]
serializer_class = UserSerializer
def get_object(self):
return self.request.user
But during authentication in login part, it does not match the credentials, maybe because the function checking for validity uses some other encryption method. How to solve this issue.
After saving the user password field, the app shows an Invalid password format or unknown hashing algorithm.
User is created, but the password field is Invalid password format or unknown hashing algorithm.
The code does not return any error during registration.
I also tried user.set_unusable_password() in serializer but with the same result. Could not figure it out.
Serializers.py
from rest_framework import serializers
from accounts.models import User
class CreateUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'phone' , 'password',)
write_only_fields = ('password',)
def create(self, validated_data):
user = User.objects.create(**validated_data)
user.set_password(validated_data['password'])
user.save()
return user
})
view.py
class Register(APIView):
def post(self, request, *args, **kwargs):
phone = request.data.get('phone', False)
password = request.data.get('password', False)
print(phone)
print(password)
if phone and password:
old = PhoneOTP.objects.filter(phone__iexact=phone)
if old.exists():
old = old.first()
validated = old.validate
if validated:
temp_data = {
'phone': phone,
'password': password
}
serializers = CreateUserSerializer(data=temp_data)
serializers.is_valid(raise_exception=True)
user = serializers.save()
old.delete()
return Response({
'status': True,
'detail': 'Account is created '
})
serializers.py
class UserRegistrationSerializer(serializers.ModelSerializer):
profile = ProfileSerializer(required=False)
class Meta:
model = User
fields = ('phone', 'username', 'password', 'profile')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
profile_data = validated_data.pop('profile')
user = User.objects.create_user(**validated_data)
users = Profile.objects.create(
user=user,
state=profile_data['state'],
city=profile_data['city'],
date_Of_Birth=profile_data['date_Of_Birth'],
address=profile_data['address']
)
users.save()
return users
views.py
class UserRegistrationView(CreateAPIView):
serializer_class = UserRegistrationSerializer
permission_classes = (IsAuthenticated,)
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
response = {
'success' : 'True',
'status code' : status.HTTP_200_OK,
'message': 'User registered successfully',
}
status_code = status.HTTP_200_OK
return Response(response, status=status_code)
Hope The answer is useful.
I am trying to implement multiple user types in DRF and I'm doing that by having a
User Model - which has login related fields and common fields in all the roles and also a choice field denoting type of user.
Customer Model - OneToOneField with user model.
Seller Model - OneToOneField with user model.
I have set up authentication and permissions for User model and now I'm able to log in. I want the logged in user to be able to create his respective profile (based on user_type field of User model).
class User(AbstractBaseUser, PermissionsMixin):
""" A Generic User inside our system. The fields used are common to all users in system. """
....
class Customer(models.Model):
"""A Class to represent a Customer in System """
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
Now I am trying to figure out how to allow a user to create a profile respective to his user_type (customer/seller). and the more confusing part is how do I set the user to current logged in user for my CustomerSerializer or SellerSerializer
This is the permission class I'm trying to use:
class UpdateCustomerProfile(permissions.BasePermission):
"""Allow users to edit their own profile """
def has_object_permission(self, request, view, obj):
"""Check if user is trying to edit their own profile"""
return obj.user.id == request.user.id
and this is the customer serializer:
class CustomerSerializer(serializers.ModelSerializer):
"""A Serizlier class for customer """
class Meta:
model = models.Customer
fields = ('user', 'first_name', 'last_name', 'dob', 'gender')
def create(self, validated_data):
"""Create and return a new customer."""
CustomerViewSet:
class CustomerViewSet(viewsets.ModelViewSet):
"""Handle creating reading and updating Users in system"""
serializer_class = serializers.CustomerSerializer
queryset = models.User.objects.filter( user_type = "CS" )
authentication_classes = (TokenAuthentication,)
permission_classes = (permissions.UpdateCustomerProfile,)
But I get an error
AttributeError at /api/customer-profile/
Got AttributeError when attempting to get a value for field user on serializer CustomerSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the User instance.
Original exception text was: 'User' object has no attribute 'user'.
I'm new to Django so I'm not sure If this is a way to do it or if I'm doing anything wrong. How can I fix this? Any examples projects following similar strategy would also be very helpful.
Since your serializer is for a Customer, your queryset should be for a Customer:
queryset = models.Customer.objects.filter(user=request.user)
for example, if you only want to the Customer profile of the current user.
class CustomerViewSet(viewsets.ModelViewSet):
"""Handle creating reading and updating Users in system"""
serializer_class = serializers.CustomerSerializer
#you are using customer model for serializer but for query set you are using
#User model.
queryset = models.Customer.objects.filter( user__type = "CS" )
authentication_classes = (TokenAuthentication,)
permission_classes = (permissions.UpdateCustomerProfile,)
Hey got my code below to work for serializing registration with multiple user types. I followed this: https://www.freecodecamp.org/news/nested-relationships-in-serializers-for-onetoone-fields-in-django-rest-framework-bdb4720d81e6/
here is my models.py:
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
is_individual = models.BooleanField(default=False)
is_company = models.BooleanField(default=False)
class Company(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
company_name = models.CharField(max_length=100)
email_address = models.EmailField(max_length=254, blank=True, null=True)
class Individual(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
email_address = models.EmailField(max_length=254)
here is my serializers.py:
from rest_framework import serializers
from classroom.models import User, Individual, Company
from django.contrib.auth import authenticate
class IndividualSerializer(serializers.ModelSerializer):
class Meta:
model = Individual
fields = ('user', 'email_address')
class CompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = ('user', 'email_address', 'company_name')
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'password', 'is_individual', 'is_company')
extra_kwargs = {'password': {'write_only': True}}
class IndividualRegisterSerializer(serializers.ModelSerializer):
user = UserSerializer(required=True)
class Meta:
model = Individual
fields = ('user', 'email_address')
extra_kwargs = {'password': {'write_only': True}, 'username': {'write_only': True}}
def create(self, validated_data, *args, **kwargs):
user = User.objects.create_user(validated_data['user']['username'], validated_data['email_address'], validated_data['user']['password'])
individual = Individual.objects.create(user=user, email_address=validated_data.pop('email_address'))
return individual
class CompanyRegisterSerializer(serializers.ModelSerializer):
user = UserSerializer(required=True)
class Meta:
model = Company
fields = ('user', 'company_name', 'email_address')
extra_kwargs = {'password': {'write_only': True}, 'username': {'write_only': True}}
def create(self, validated_data, *args, **kwargs):
user = User.objects.create_user(validated_data['user']['username'], validated_data['email_address'],
validated_data['user']['password'])
company = Company.objects.create(user=user, email_address=validated_data.pop('email_address'), company_name=validated_data.pop('company_name'))
return company
class IndividualLoginSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
def validate(self, data):
individual = authenticate(**data)
if individual and individual.is_active:
return individual
raise serializers.ValidationError("Incorrect Credentials")
class Meta:
fields = ['username','password','is_individual','is_company']
extra_kwargs = {'is_individual': {'required': False},
'is_company': {'required': False}}
and here is my api.py:
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import generics, permissions
from knox.models import AuthToken
from ..serializers import \
UserSerializer, \
IndividualRegisterSerializer, CompanyRegisterSerializer, \
class RegisterIndividualAPI(generics.GenericAPIView):
serializer_class = IndividualRegisterSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
individual = serializer.save()
individual_data = IndividualSerializer(individual, context=self.get_serializer_context()).data
return Response({
"individual": individual_data,
"username": individual.user.username,
"token": AuthToken.objects.create(individual.user)[1]
})
class RegisterCompanyAPI(generics.GenericAPIView):
serializer_class = CompanyRegisterSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
company = serializer.save()
company_data = CompanySerializer(company, context=self.get_serializer_context()).data
return Response({
"company": company_data,
"username": company.user.username,
"token": AuthToken.objects.create(company.user)[1]
})
class LoginCompanyAPI(generics.GenericAPIView):
serializer_class = CompanyLoginSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
company = serializer.validated_data
company_data = CompanySerializer(company, context=self.get_serializer_context()).data
return Response({
"company": company_data,
"username": company.user.username,
"token": AuthToken.objects.create(company.user)[1]
})
And here is my API POST request's body:
{
"user": {
"username":"nind5",
"password": "123456",
"is_individual" : "True",
"is_company" : "False"
},
"email_address":"n#gmail.com"
}
I also used this resource https://www.youtube.com/watch?v=0d7cIfiydAc&t=437s. Knox is explained in this video as well.
Also, use this online tool to view your db (db.sqlite3) tables : https://sqliteonline.com/
Best,
Nick
I am trying to build an app which has user login and signup functionality.I can create login and signup both from django, and DRF but could not hook in oAuth2 with DRF to make it functional.I have no idea where should i use it.Should I generate token on signup or login?How can I make it functional?
Here is my code
serializers.py
class UserSerializer(ModelSerializer):
class Meta:
model = User
class UserCreateSerializer(ModelSerializer):
email = EmailField()
username = CharField()
first_name = CharField(required=False)
last_name = CharField(required=False)
password = CharField()
confirm_password = CharField()
class Meta:
model = User
fields = [
'username',
'email',
'first_name',
'last_name',
'password',
'confirm_password'
]
extra_kwargs = {"password": {"write_only": True}}
def create(self, validated_data):
username = validated_data['username']
first_name = validated_data['first_name']
last_name = validated_data['last_name']
email = validated_data['email']
password = validated_data['password']
confirm_password = validated_data['password']
user_obj = User(
username = username,
first_name = first_name,
last_name = last_name,
email = email
)
user_obj.set_password(password)
user_obj.save()
return validated_data
class UserLoginSerializer(ModelSerializer):
username = CharField()
class Meta:
model = User
fields = [
'username',
# 'email',
'password',
# 'token',
]
extra_kwargs = {"password":
{"write_only": True}
}
def validate(self, data):
return data
views.py
class UserCreateAPI(CreateAPIView):
serializer_class = UserCreateSerializer
queryset = User.objects.all()
permission_classes = [AllowAny]
class UserLoginAPI(APIView):
permission_classes = [AllowAny]
serializer_class = UserLoginSerializer
def post(self, request, *args, **kwargs):
data = request.data
print('data',data)
serializer = UserLoginSerializer(data=data)
if serializer.is_valid(raise_exception=True):
new_data = serializer.data
if new_data:
try:
user = User.objects.get(username=data['username'])
print ('user',user)
except ObjectDoesNotExist:
return HttpResponse("Can't find this user")
login(request, user)
return Response(new_data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
tools.py
def get_token_json(access_token):
return JsonResponse({
'access_token':access_token.token,
'expires_in':datetime.now() + timedelta(days=365),
'token_type':'Bearer',
'refresh_token':access_token.refresh_token.token,
'scope':access_token.scope
})
def get_access_token(user):
application = Application.objects.get(name="Foodie")
try:
old_access_token = AccessToken.objects.get(user=user, application=application)
old_refresh_token = RefreshToken.objects.get(user=user, access_token=old_access_token)
except ObjectDoesNotExist:
return HttpResponse('Have not set any token')
else:
old_access_token.delete()
old_refresh_token.delete()
new_token = generate_token()
refresh_token = generate_token()
access_token=AccessToken.objects.create(user=user, application=app, expires=datetime.now() + timedelta(days=365),token=new_token)
RefreshToken.objects.create(user=user, application=app, token=refresh_token, access_token=access_token)
print('aceess',AccessToken)
return get_token_json(access_token)
How can i bridge the gap between DRF and oAuth2 to make login and user registration functional?
Try using python social auth.
Add social.apps.django_app.default to INSTALLED_APPS
Add social.backends.facebook.FacebookOAuth2 to AUTHENTICATION_BACKENDS
Add url(r'^auth/', include('social.apps.django_app.urls', namespace='social')) to your urls.py
However this will work if you have session authentication in your app. If you want to use token based only, then either add a pipe to create the token and send it or take a look at https://github.com/PhilipGarnero/django-rest-framework-social-oauth2