Why is Django authentication is now working for AbstractUser - python

So I have these Django models in the users app, and I have added AUTH_USER_MODEL = 'users.User' to settings.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
login_count = models.PositiveIntegerField(default=0)
class Supplier(User):
company_name= models.CharField(max_length=30)
company_domain=models.CharField(max_length=30)
class Meta:
verbose_name = 'supplier'
verbose_name_plural = 'suppliers'
class Worker(User):
ACCOUNT_TYPE = (
('1', 'Admin'),
('2', 'Regular'),
)
is_hub_manager = models.BooleanField(default=False)
account_type = models.CharField(max_length=1, choices=ACCOUNT_TYPE)
class Meta:
verbose_name = 'worker'
verbose_name_plural = 'workers'
I also created an authenetication endpoint using Django rest framework. Surprisingly when I authenticate the admin everything works well. When I create a supplier and try to authenticate them. They always return invalid credentials. Here are my API views
from rest_framework.generics import GenericAPIView
from .serializers import UserSerializer
from rest_framework.response import Response
from rest_framework import status
from django.conf import settings
from django.contrib import auth
import jwt
class LoginView(GenericAPIView):
serializer_class = UserSerializer
def post(self, request):
data = request.data
username = data.get('username', '')
password = data.get('password', '')
user = auth.authenticate(username=username, password=password)
if user:
auth_token = jwt.encode({'username': user.username}, settings.JWT_SECRET_KEY)
serializer = UserSerializer(user)
data = {'user': serializer.data, 'token': auth_token}
return Response(data, status=status.HTTP_200_OK)
# SEND RES
return Response({'detail': 'Invalid credentials'}, status=status.HTTP_401_UNAUTHORIZED)
What could I be doing wrong?

The first thing, you should check AUTH_USER_MODEL keyword in setting file.
E.g. AUTH_USER_MODEL = '<module>. User'.
Regarding your case, the root cause is not yet custom your authenticate() method in your backend. You defined Supplier and Worker which are inherited from the User class.
Also, you can refer to this link for detail.

Related

In the following code I write the Login API but when I print the user it's give me None result

In the following code I created a Login API but when I hit the request in Postman it's always give me error response. How to rectify the problem?
This is my views.py file
from django.shortcuts import render
from rest_framework.permissions import AllowAny
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import UserSerializer, RegisterSerializer, UserLoginSerializer
from django.contrib.auth.models import User
from rest_framework import generics, permissions
from rest_framework import status
from django.contrib.auth import authenticate,login
from django.contrib import auth
# from knox.models import AuthToken
# Create your views here.
from django.views.decorators.csrf import csrf_exempt
from rest_framework.decorators import api_view
#Login Credentials
"//Create a Login API in Django?"
class UserLoginView(APIView):
permission_classes = (AllowAny,)
serializer_class = UserLoginSerializer
def get(self,request,format=None):
serializer=UserLoginSerializer(data=request.data)
# print(serializer.is_valid(raise_exception=True));
if serializer.is_valid(raise_exception=True):
email = serializer.data.get('email')
password = serializer.data.get('password')
print(email)
print(password)
user=authenticate(email=email,password=password)
print(user)
if user is not None:
login(request, user)
return Response({'msg':'Login Success'},status=status.HTTP_200_OK)
else:
return Response({'msg':{'Email or Password Does not match'}},status=status.HTTP_404_NOT_FOUND)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
#This is my serializer.py file.
class UserLoginSerializer(serializers.ModelSerializer):
email=serializers.EmailField(max_length=200)
class Meta:
model = User
fields= ['email','password']
When I hit the request then its always give me error
Django default User model takes the (username & password) field for Authentication.
in your code, you tried to authenticate with (email & password) so it gives None on every login request.
try for Authentication (username & password) instead of (email & password) I mean change like this...
user=authenticate(username=username,password=password)
Instead of...
user=authenticate(email=email,password=password)
If you still want to Authenticate with email then you need to Customize the Default User model
for Customizing Django User Model refer to the link here
User Model Cutomizaion code is here...
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from .managers import CustomUserManager
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
def __str__(self):
return self.email
In the above code this line USERNAME_FIELD = 'email' give permission to authenticate with Email field Insted of Username field

The django rest framework is not able to recognize the User defined as foreign key

I'm trying to save a user profile information in a django model. The model is defined as follows
class UserProfile(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
reg_date = models.DateTimeField(auto_now_add=True, blank=True)
name = models.CharField(max_length=30, blank=True)
family = models.CharField(max_length=30, blank=True)
phoneNumber = models.CharField(max_length=20, blank=True)
def __str__(self) :
return self.name + self.family
The serializer for the model is defined as:
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model= UserProfile
fields = ['user', 'reg_date', 'name', 'family', 'phoneNumber']
and the view is as follows:
class UserProfileView(viewsets.ViewSet):
def create(self, request):
UserProfile.objects.create(user = request.user)
I'm sending a post request using axios as follows:
const a = await ProfileAPI.post('',
{
headers: {
'Authorization': mytoken
}
})
in which mytoken is a logged in user token provided by dj-rest-auth API. Although the token is OK and the UserProfileView is executed by the request, I got the following error by the django rest:
ValueError: Cannot assign "<django.contrib.auth.models.AnonymousUser object at 0x000001F069C59190>": "UserProfile.user" must be a "User" instance.
Am I missed something? Is there any problem with my request or view?
Please help me to fix this problem
How authentication is determined
The authentication schemes are always defined as a list of classes. REST framework will attempt to authenticate with each class in the list, and will set request.user and request.auth using the return value of the first class that successfully authenticates.
If no class authenticates, request.user will be set to an instance of django.contrib.auth.models.AnonymousUser, and request.auth will be set to None.
The value of request.user and request.auth for unauthenticated requests can be modified using the UNAUTHENTICATED_USER and UNAUTHENTICATED_TOKEN settings.
Settings
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
]
}
Views
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
class ExampleView(APIView):
authentication_classes = [SessionAuthentication, BasicAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request, format=None):
content = {
'user': str(request.user), # `django.contrib.auth.User` instance.
'auth': str(request.auth), # None
}
return Response(content)
Add in settings.py
AUTH_USER_MODEL = '(app name).UserProfile'

The User ForiegnKey is now showing up as a field in django rest framework viewset

I am building a backend for a web app using django rest framework. I have a profile model that has a user forieingkey referencing a django user. Everything is loading correctly except for one issue which is that the User field is not showing up in the django rest framework backend urls so that I can assign a user to the profile object i want to create... does anyone know why this is happening...
models.py:
class Profile(models.Model):
user = models.ForeignKey(
User, on_delete=models.CASCADE
)
synapse = models.CharField(max_length=25, null=True)
bio = models.TextField(null=True)
profile_pic = models.ImageField(
upload_to='./profile_pics/',
max_length=150
)
facebook = models.URLField(max_length=150)
twitter = models.URLField(max_length=150)
updated = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.user.username + ' profile'
viewset:
from users.models import Profile
from users.api.serializers.ProfileSerializer import ProfileSerializer
from rest_framework import viewsets
class ProfileViewSet(viewsets.ModelViewSet):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
lookup_field = 'user__username'
url:
from users.api.views.ProfileView import ProfileViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'', ProfileViewSet, base_name='profile')
urlpatterns = router.urls
serializer:
from rest_framework import serializers
from users.models import Profile
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = (
'id',
'user',
'synapse',
'bio',
'profile_pic',
'facebook',
'twitter'
)
depth=2
That happens when you set a depth bigger that 0, foreign key fields become not editable (if you send a POST with that field containing some value, DRF viewset would ignore it, and if the field is required, it will raise an exception).
One solution for that is to override to_representation() method of the serializer and set the depth and restore it to zero:
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = (
'id',
'user',
'synapse',
'bio',
'profile_pic',
'facebook',
'twitter'
)
def to_representation(self, instance):
self.Meta.depth = 2
representation = super().to_representation(instance)
self.Meta.depth = 0
return representation

Creating a user profile to user auth django

I'm trying to create a profile for my user using django rest auth model, but when I'm sending the data to the user's creation, the user's image is not being filled, that is, it's getting null, I tried to send the node as much as image, as much as profile.image, but without success below my code follows:
models\profile.py
from django.conf import settings
from django.db import models
class Profile(models.Model):
image = models.ImageField(blank=True)
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return self.user.email
serializers\user.py
from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from django.contrib.auth.models import User
from src.v1.user.models.profile import Profile
from .profile import ProfileSerializer
class UserSerializer(serializers.ModelSerializer):
profile = serializers.SerializerMethodField()
email = serializers.EmailField(
required=True,
validators=[UniqueValidator(queryset=User.objects.all())]
)
username = serializers.CharField(
max_length=32,
validators=[UniqueValidator(queryset=User.objects.all())]
)
password = serializers.CharField(min_length=6, write_only=True)
#staticmethod
def get_profile(user):
"""
Get or create profile
"""
profile, created = Profile.objects.get_or_create(user=user)
return ProfileSerializer(profile, read_only=True).data
def create(self, validated_data):
user = User(email=validated_data['email'],
username=validated_data['username'])
user.set_password(validated_data['password'])
user.save()
return user
class Meta:
model = User
fields = ('id', 'username', 'email', 'password', 'profile')
serializers\profile.py
from rest_framework import serializers
from src.v1.user.models.profile import Profile
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = '__all__'
class ProfileSerializerUpdate(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('image',)
views\user.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from src.v1.user.serializers.user import UserSerializer
from rest_framework.authtoken.models import Token
class UserView(APIView):
"""
Creates the user.
"""
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
if user:
token = Token.objects.create(user=user)
json = serializer.data
json['token'] = token.key
return Response(json, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
SerializerMethodField is read only by design. So it is not just the image, nothing is getting posted on the profile.
Also, creating the profile if it doesn't exist during a GET request is a bad design because generally, GET requests should be safe and not alter the state of the application.
You should instead, create the profile during user creation. Since there is just one field to be posted in the profile, it may be simpler and more effective to use a flat representation for the post data.
This is what you should do.
serializers/user.py
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer(read_only=True)
image = serializers.ImageField(write_only=True)
email = serializers.EmailField(
required=True,
validators=[UniqueValidator(queryset=User.objects.all())]
)
username = serializers.CharField(
max_length=32,
validators=[UniqueValidator(queryset=User.objects.all())]
)
password = serializers.CharField(min_length=6, write_only=True)
def create(self, validated_data):
user = User(email=validated_data['email'], username=validated_data['username'])
user.set_password(validated_data['password'])
user.save()
Profile.objects.create(user=user, image=validated_data['image')
return user
Now you can post your image field with the rest of the user fields

Serialize the creation of user and profile django rest framework

I'm trying to create an user and his profile through DRF, but I don't find the correct way to do it.
Here's my models.py
from __future__ import unicode_literals
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
from django.db import models
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, blank=True, null=True)
language = models.CharField(max_length=4, default='es')
def __unicode__(self):
return "%s - %s" % (self.user, self.language)
my serializers.py
from rest_framework import serializers
from models import Profile
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model
class ProfileCreateSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username')
class Meta:
model = Profile
fields = [
'username',
'language',
]
def create (self, validated_data):
user = get_user_model().objects.create(username=validated_data['username'])
user.set_password(User.objects.make_random_password())
user.save()
profile = Profile.objects.create(user = user)
return profile
my views.py
from rest_framework.response import Response
from rest_framework.generics import CreateAPIView
from serializers import ProfileCreateSerializer
from models import Profile
class ProfileCreateAPIView(CreateAPIView):
model = Profile
serializer_class = ProfileCreateSerializer
My urls.py
from django.conf.urls import patterns, include, url
import views
from django.contrib import admin
urlpatterns = [
url(r'^', views.ProfileCreateAPIView.as_view(), name='crear perfil'),
]
if I try to create it shows me this error:
Cannot assign "{u'username': u'test'}": "Profile.user" must be a "User" instance.
if i create an user and his profile via admin panel doesn't show me any error.
my admin.py:
from django.contrib import admin
from models import Profile
from django.contrib.auth.admin import UserAdmin
class ProfileInline(admin.StackedInline):
model = Profile
can_delete = False
verbose_name_plural = 'Perfil'
fk_name = 'user'
class UserAdmin(UserAdmin):
inlines = (ProfileInline,)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
I'm using django 1.9.1 and django rest framework 3.3.2
Why do you need UserSerializer at first place? Change your ProfileCreateSerializer to following. It should work
class ProfileCreateSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username')
class Meta:
model = Profile
fields = [
'username',
'language',
]
def create (self, validated_data):
user = get_user_model().objects.create(username=validated_data['username'])
user.set_password(User.objects.make_random_password())
user.save()
profile = Profile.objects.create(user = user)
return profile
Since the create method is override at your serializer, you need to send the data as a format the serializer process. So, you need to override the POST request at the views and create data as follows :
from rest_framework import status
from rest_framework.response import Response
class ProfileCreateAPIView(CreateAPIView):
model = Profile
serializer_class = ProfileCreateSerializer
def post(self, request, *args, **kwargs):
data = {
'username': request.data.get('username', None),
'language': request.data.get('language', None),
}
serializer = ProfileCreateSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(
serializer.data, status=status.HTTP_201_CREATED)
else:
return Response( serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Categories