how to add more attributes to what an api returns - django

I am trying to write an API using django rest framework in which, you give a username and a password and in return you get an AuthToken or in other words you login. now I want this API to also return some fields like the email of the user along with the AuthToken. so if the authentication was successful, the get an authToken and the user's email. Can anyone help me on how I could be able to do this by adding or changing a bit of my code?
These are my models:
class UserManager(BaseUserManager):
def createUser(self, email, password=None, **extra_fields):
if not email:
raise ValueError('Email Not Found!!!')
user = self.model(email=self.normalize_email(email), **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def createSuperUser(self, email, password):
user = self.createUser(email, password)
user.isAdmin = True
user.isSuperUser = True
user.save(using=self._db)
return user
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=100, unique=True, validators=[RegexValidator(regex="^(?=[a-z0-9._]{5,20}$)(?!.*[_.]{2})[^_.].*[^_.]$")])
email= models.EmailField(max_length=100, unique=True, validators=[EmailValidator()])
name = models.CharField(max_length=100)
isSuspended = models.BooleanField(default=False)
isAdmin = models.BooleanField(default=False)
emailActivation = models.BooleanField(default=False)
balance = models.IntegerField(default=0)
objects = UserManager()
USERNAME_FIELD = 'username'
These are my serializers:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ('username','email', 'password', 'name')
extra_kwargs = {'password': {'write_only': True, 'min_length': 8}}
def create(self, validated_data):
return get_user_model().objects.createUser(**validated_data)
def update(self, instance, validated_data):
password = validated_data.pop('password', None)
user = super().update(instance, validated_data)
if password:
user.set_password(password)
user.save()
return user
class AuthTokenSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField(trim_whitespace=False)
def validate(self, attrs):
username = attrs.get('username')
password = attrs.get('password')
user = authenticate(
request=self.context.get('request'),
username= username,
password= password
)
if not user:
msg = 'Authentication Failed.'
raise serializers.ValidationError(msg, code='authentication')
attrs['user'] = user
return attrs
And finally, these are my views:
class CreateUserView(generics.CreateAPIView):
serializer_class = UserSerializer
class CreateTokenView(ObtainAuthToken):
serializer_class = AuthTokenSerializer
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
class ManageUserView(generics.RetrieveAPIView):
serializer_class = UserSerializer
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
def get_object(self):
return self.request.user

create a new serializer inside serializer.py
from rest_framework.authtoken.models import Token as DefaultTokenModel
class TokenSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = DefaultTokenModel
fields = ('key', 'user',)
add this function in views.py
def get_token_response(user):
serializer_class = TokenSerializer
token, _ = DefaultTokenModel.objects.get_or_create(user=user)
serializer = serializer_class(instance=token)
return Response(serializer.data, status=status.HTTP_200_OK)
now override post method of CreateTokenView
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
return get_token_response(user)

For what i understand you just want to return the toekn and the email of the user right? I used this class based view to login users using token authentication.
from rest_framework.authtoken.views import ObtainAuthToken
class UserLoginView(ObtainAuthToken):
def post(self, request, **kwargs):
serializer = self.serializer_class(data=request.data,
context={
'request':request
})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response(
{
'token':token.key,
'email':user.email,
}
)

Related

How to use captcha for user login in django rest framework?

I want to pass the captcha when the user wants to enter his account
If he does not enter the captcha correctly, display the appropriate message
Captcha must be a number and 4 digits
views.py
class LoginApiView(generics.GenericAPIView):
"""login user"""
serializer_class = LoginSerializer
renderer_classes = [CustomizeJSONRenderer]
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
return Response(serializer.data, status=status.HTTP_200_OK)
serializers.py
class LoginSerializer(serializers.ModelSerializer):
username = serializers.CharField(max_length=255)
password = serializers.CharField(max_length=68, min_length=8, write_only=True)
email = serializers.EmailField(max_length=255, min_length=3, read_only=True)
tokens = serializers.CharField(max_length=255, read_only=True)
class Meta:
model=User
fields = ['username', 'password', 'email', 'tokens']
def validate(self, attrs):
username = attrs.get('username', '')
password = attrs['password']
user = authenticate(username=username, password=password)
if not user:
raise exceptions.AuthenticationFailed('Invalid credentials, try again')
if not user.is_active:
raise exceptions.AuthenticationFailed('Account disabled, contact admin')
return {
'username':user.username,
'email': user.email,
'tokens':user.tokens
}

Unable to login because Django saving passwords in plain text but

class UserManager(BaseUserManager):
def create_user(self, username, email, password=None):
if username is None:
raise TypeError('User should have a username')
if email is None:
raise TypeError('User should have an email')
user = self.model(
username=username,
email=self.normalize_email(email)
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, username, email, password=None):
if password is None:
raise TypeError('Password should not be none')
user = self.create_user(username, email, password)
user.is_superuser = True
user.is_staff = True
user.save()
return user
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(
max_length = 255,
unique = True,
db_index = True
)
email = models.EmailField(
max_length = 255,
unique = True,
db_index = True
)
is_verified = models.BooleanField(default = False)
is_staff = models.BooleanField(default = True)
is_active = models.BooleanField(default = True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = UserManager()
def __str__(self):
return self.email
def tokens(self):
refresh = RefreshToken.for_user(self)
return {
'refresh': str(refresh),
'access': str(refresh.access_token)
}
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(
max_length = 255,
min_length = 6,
write_only = True
)
class Meta:
model = User
fields = ['email', 'username', 'password']
extra_kwargs = {'password': {'write_only': True, 'min_length': 5}}
def validate(self, attrs):
email = attrs.get('email', '')
username = attrs.get('username', '')
if not username.isalnum():
raise serializers.ValidationError(
"Username should contain only alphanumeric characters"
)
return attrs
def create(self, validated_data):
return User.objects.create_user(**validated_data)
class RegisterView(generics.GenericAPIView):
permission_classes = (AllowAny,)
serializer_class = RegisterSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
user_data = serializer.data
return Response(
serializer.data,
status=status.HTTP_201_CREATED
)
Currently I'm using django 3.1.3 and djangorestframework 3.12.2. While I'm able to login with superuser and getting tokens properly but couldn't login with staff users. From Django admin I have seen only superuser has hashed password and all other users have plain text password.
You do not set the password with user.password, that is just a text field on a model and works like any other. You must either:
preferred: Call user.set_password(value)
backup: Calculate the password manually using make_password before saving
You already have a create method so we'll work with that first.
from django.contrib.auth.hashers import make_password
def create(self, validated_data):
pwd = validated_data.pop("password")
user = User.objects.create(**attrs)
user.set_password(pwd)
user.save(updated_fields=["password"])
return user
# or you could replace it in validated_data
def create(self, validated_data):
validated_data["password"] = make_password(validated_data["password"])
return User.objects.create(**attrs)
You have a validation routine, but it is global. If you make it specific to the password field then you can calculate it there and leave create alone. People don't usually do that, because it is standard to have a "confirm password" field as well.
def validate_password(self, value):
# ...
return make_password(value)
Now if you want to add a confirm_password field then you are going to need to a validate(self, attrs) anyway to compare the two fields, so I would recommend against this last method.

django.db.utils.IntegrityError: UNIQUE constraint failed: authentication_user.email

I am trying create user through an API, But i am struck on above error.
Below are the code of the User and its manager. Here, I am creating custom user model.
class UserManager(BaseUserManager):
def create_user(self,username,email, password=None):
if username is None:
raise TypeError('Users should have a Username')
if email is None:
raise TypeError('Users should have a Email')
user = self.model(username=username,email=self.normalize_email)
user.set_password(password)
user.save()
return user
def create_superuser(self,username,email, password=None):
if password is None:
raise TypeError('Password should not be none')
user = self.create_user(username, email, password)
user.save()
return user
class User(AbstractBaseUser,PermissionsMixin):
username = models.CharField(max_length=255, unique=True, db_index=True)
email = models.EmailField(max_length=255,unique=True,db_index=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects= UserManager()
def __str__(self):
return self.email
Below is serializers.py file.
class RegisterSerializers(serializers.ModelSerializer):
password = serializers.CharField(max_length=68, min_length=6, write_only=True)
class Meta:
model = User
fields = ['email','username','password']
def validate(self, attrs):
email = attrs.get('email','')
username = attrs.get('username','')
if not username.isalnum():
raise serializers.ValidationError('The username should only contain alphanumeric character')
return attrs
def create(self, validated_data):
return User.objects.create_user(**validated_data)
Here is POST request in views.py
class RegisterView(generics.GenericAPIView):
serializer_class = RegisterSerializers
def post(self, request):
user = request.data
serializer = self.serializer_class(data=user)
serializer.is_valid(raise_exception=True)
serializer.save()
user_data = serializer.data
return Response(user_data,status=status.HTTP_201_CREATED)
I am new to drf. Kindly help me out, thanks.

Object of type data is not JSON serializable error in Django

I have a registration page that allows a user to sign up. After doing so, I want to call an API and then, save the data to my model (not saving it to a form though). I tried doing this:
models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE, primary_key=True, related_name = 'profile')
address = models.TextField()
birthday = models.DateField()
def __str__(self):
return str(self.user)
views.py:
def signup(request):
if request.method == 'POST':
user_form = UserForm(request.POST)
register_form = RegisterForm(request.POST)
if user_form.is_valid() and register_form.is_valid():
username = user_form.cleaned_data.get('username'),
first_name = user_form.cleaned_data.get('first_name'),
last_name=user_form.cleaned_data.get('last_name'),
email=user_form.cleaned_data.get('email'),
password=user_form.cleaned_data.get('password2'),
birthday = register_form.cleaned_data.get('dob'),
address=register_form.cleaned_data.get('address'),
payload = {'username': username,'first_name': first_name,'last_name': last_name,'email':email,'password':password,'register' : {'birthday': birthday,'address': address}}
response = requests.post('http://127.0.0.1:8000/my_api/',json=payload)
return redirect("home") #re-direct if login is successful
else:
user_form = UserForm()
register_form = RegisterForm()
return render(request, 'users/register.html', {'user_form': user_form, 'register_form': register_form})
class RegisterAPI(APIView):
permission_classes = [AllowAny]
def post(self, request, format=None):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
content = {'status': 'You are registered'}
return Response(content, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py:
from users.models import Profile
from django.contrib.auth.models import User
class ProfileSerializer(serializers.ModelSerializer):
birthday = serializers.DateField(format="%Y-%m-%d")
class Meta:
model = Profile
fields = ('birthday','address')
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ('username','first_name','last_name','email', 'password', 'profile')
def create(self, request, validated_data, *args, **kwargs):
register_data = validated_data.pop('profile')
password = validated_data.pop('password', None)
user = User.objects.create(**validated_data)
if password is not None:
user.set_password(password)
user.save()
Profile.objects.create(user = user, **register_data)
return validated_data
However, I am getting this error:
Object of type data is not JSON serializable error in Django
It seems that it's got to do with the birthday. On my template, a user can display the date of birth as 'YYYY-MM-DD'. How can I fix this error?
The create method in your UserSerializer should return a User instance instead of validated_data.
def create(self, request, validated_data, *args, **kwargs):
register_data = validated_data.pop('profile')
password = validated_data.pop('password', None)
user = User.objects.create(**validated_data)
if password is not None:
user.set_password(password)
user.save()
Profile.objects.create(user = user, **register_data)
return user

How to return user details in response after login in django rest_framework

Hi I am beginner in Django Here I want user email and name in response after user login using api. Thanks in advance for your help.
models.py
I want user email and user name in response only getting auth token in response
class UserManager(BaseUserManager):
def create_user(self, email, password, **extra_fields):
if not email:
raise ValueError('Users must have an email address')
if not password:
raise ValueError('Users must have a password')
user = self.model(email=self.normalize_email(email), **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password):
user = self.create_user(email,password)
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
serializers.py
I want user email and user name in response only getting auth token in response
class UserSerializer(serializers.ModelSerializer):
email = serializers.EmailField(
required=True,
validators=[UniqueValidator(queryset=User.objects.all())]
)
class Meta:
model = get_user_model()
fields = ('email', 'password', 'name')
extra_kwargs = {'password': {'write_only': True, 'min_length': 6}}
def create(self, validated_data):
user = User.objects.create_user('name',validated_data['email'],
validated_data['password'],)
return user
class AuthTokenSerializer(serializers.Serializer):
email = serializers.CharField()
password = serializers.CharField(
style = {'input_type':'password'},
trim_whitespace = False
)
def validate(self, attrs):
email = attrs.get('email')
password = attrs.get('password')
user = authenticate(
request = self.context.get('request'),
username = email,
password = password
)
if not user:
msg = _('Unable to authenticate with provided crenditial')
raise serializers.ValidationError(msg, code = 'authorization')
attrs['user'] = user
return attrs
urls.py
I want user email and user name in response only getting auth token in response
path('api/login/',views.CreateTokenView.as_view(),name='token'),
View file for creating view and I dont now how to return user from Auth token. I want user email and user name in response only getting auth token in response
views.py
class UserCreate(APIView):
"""
Creates the user.
"""
def post(self, request, format='json'):
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)
class CreateTokenView(ObtainAuthToken):
serializer_class = AuthTokenSerializer
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
def post(self, request, *args, **kwargs):
response = super(CreateTokenView, self).post(request, *args, **kwargs)
token = Token.objects.get(key=response.data['token'])
return Response({'token': token.key})
I guess what you can do is maybe once you recieve a valid token for the user, you could query for the user using the user's email from the request data.
def post(self, request, *args, **kwargs):
response = super(CreateTokenView, self).post(request, *args, **kwargs)
token = Token.objects.get(key=response.data['token'])
user_email = request.data.get('email', None)
user = User.objects.get(email=user_email)
return Response({'name': user.name, 'email': user.email, 'token': token.key})
Hope this helps!
I know this is an old post but I ended up searching for this.
For the new comers:
There's an example on Django REST Framework, available here
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
class CustomAuthToken(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({
'token': token.key,
'user_id': user.pk,
'email': user.email
})