I have some tests trying to validate my user creation process in an API application.
The issue I am having is that when I try to validate a users password was created as a hashed object the test is failing.
Test:
CREATE_USER_URL = reverse('user:create')
def create_user(**params):
return get_user_model().objects.create_user(**params)
...
def test_create_valid_user_success(self):
"""Test creating user with valid user is successful"""
payload = {
'email': 'test#email.com',
'password': 'testpass',
'name': 'Test Name'
}
res = self.client.post(CREATE_USER_URL, payload)
self.assertEqual(res.status_code, status.HTTP_201_CREATED)
user = get_user_model().objects.get(**res.data)
self.assertTrue(user.check_password(payload['password']))
self.assertNotIn('password', res.data)
Serializer:
from django.contrib.auth import get_user_model
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
"""Serializer for users object"""
class Meta:
model = get_user_model()
fields = ('email', 'password', 'name')
extra_kwargs = {
'password': {
'write_only': True,
'min_length': 8
}
}
def create(self, validated_data):
"""Create a new user with encrypted password and return it"""
return get_user_model().objects.create_user(**validated_data)
Failed Test:
FAIL: test_create_valid_user_success (user.tests.test_user_api.PublicUserApiTests)
Test creating user with valid user is successful
----------------------------------------------------------------------
Traceback (most recent call last):
File "/app/user/tests/test_user_api.py", line 33, in test_create_valid_user_success
self.assertTrue(user.check_password(payload['password']))
AssertionError: False is not true
From what I can tell from documentation etc, I have the correct syntax.
Wjhat is missing/incorrect that I need in order to fix this error?
Turns out the issue was indentation in my UserSerializer class.
See this issue:
Django users being created with cleartext passwords
Related
I'm making a crud in fastapiI have a user model and I created another one called showuser to only show some specific fields in the query, but when I execute the request I get an error.
I just want my request to show the fields I have in showuser.
my schemas
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
# Create a User model
# Create a class for the user
class User(BaseModel):
username: str
password: str
name: str
lastname: str
address: Optional[str] = None
telephone: Optional[int] = None
email: str
creation_user: datetime = datetime.now()
# Create UserId model
# Create a class for the UserId
class UserId(BaseModel):
id: int
# Create a ShowUser model
# Create a class for the ShowUser
class ShowUser(BaseModel):
username: str
name: str
lastname: str
email: str
class Config():
orm_mode = True
and this is the code from user where I implement the api
#router.get('/{user_id}', response_model=ShowUser)
def get_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(models.User).filter(models.User.id == user_id).first()
if not user:
return {"Error": "User not found"}
return {"User": user}
Terminal Message
pydantic.error_wrappers.ValidationError: 4 validation errors for ShowUser
response -> username
field required (type-value_error.missing)
response -> name
field required (type=value_error.missing)
response -> lastname
field required (type=value_error.missing)
response -> email
field required (type=value_error.missing)
I think the return value of your get_user function is the issue. Rather than returning {"User": user}, try returning just the user object as shown below:
#router.get('/{user_id}', response_model=ShowUser)
def get_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(models.User).filter(models.User.id == user_id).first()
if not user:
return {"Error": "User not found"}
return user
EDIT: The same error will occur if the database does not contain a User object matching the value of user_id. Rather than returning {"Error": "User not found"}, the best way to handle this very common scenario is to raise an HTTPException with a 404 status code and error message:
#router.get('/{user_id}', response_model=ShowUser)
def get_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(models.User).filter(models.User.id == user_id).first()
if not user:
raise HTTPException(
status_code=int(HTTPStatus.NOT_FOUND),
detail=f"No user exists with user.id = {user_id}"
)
return user
Imm getting a key error "password" on serializer.py while validating the data.
Imm using Django's built in User model and Token Authentication to register a user.
here's how my serializer.py looks like.
from django.contrib.auth.models import User
from rest_framework import serializers
class RegistrationSerializer(serializers.Serializer):
password2 = serializers.CharField(style={"input_type" : "password"}, write_only=True)
class Meta:
model = User
fields = ["username", "email", "password", "password2"]
extra_kwarg = {
"password": {
"write_only": True,
}
}
def validate(self, data):
if data["password"] != data["password2"]:
return serializers.ValidationError("Your password must be same!")
if data.objects.filter(email=data["email"]).exists():
return serializers.ValidationError("This Email is already registered!")
return data
views.py
from rest_framework.response import Response
from user_app.api.serializer import RegistrationSerializer
from rest_framework.decorators import api_view
#api_view(["POST",])
def register_user(request):
if request.method == "POST":
serializer = RegistrationSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return serializer.error_messages()```
Errors
serializer.py", line 22, in validate
if data["password"] != data["password2"]:
KeyError: 'password'
i am creating api endpoints for user management using Djoser and i want to use a custom model to create user and login i only want to use email.
the user entity given to me does not have a username field
below i will share the various settings i have set up for my apps
#accounts/model.py
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class CustomUser(AbstractUser):
username = None
email = models.EmailField(unique=True)
REQUIRED_FIELDS = ['first_name', 'last_name']
USERNAME_FIELD = 'email'
def __str__(self):
return self.email
My serializer file
#accounts/serializers.py
from djoser.serializers import UserCreateSerializer, UserSerializer
from rest_framework import serializers
from rest_framework.fields import CurrentUserDefault
from .models import CustomUser
class UserCreateSerializer(UserCreateSerializer):
class Meta:
model = CustomUser
fields = ['id', 'email', 'first_name', 'last_name']
#settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
# 'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSIONS_CLASSES': (
'rest_framework.permissions.IsAuthenticated'
)
}
AUTH_USER_MODEL = 'accounts.CustomUser'
DJOSER = {
'LOGIN_FIELD': 'email',
'USER_CREATE_PASSWORD_RETYPE': True,
'SERIALIZERS': {
'user_create': 'accounts.serializers.UserCreateSerializer',
'user': 'accounts.serializers.UserCreateSerializer',
# 'current_user': 'accounts.serializers.CurrentUserSerializer'
}
when i try to register user i get
TypeError at /auth/users/
create_user() missing 1 required positional argument: 'username'
Request Method: POST
Request URL: http://127.0.0.1:8000/auth/users/
Django Version: 3.1
Exception Type: TypeError
Exception Value:
create_user() missing 1 required positional argument: 'username'
Exception Location: /home/femiir/.virtualenvs/codegarage/lib/python3.8/site-packages/djoser/serializers.py, line 73, in perform_create
Python Executable: /home/femiir/.virtualenvs/codegarage/bin/python
Python Version: 3.8.5
please what i my doing wrong ?
You need to have a custom user manager, probably something like this:
from django.contrib.auth.base_user import BaseUserManager
class MyUserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
"""
Creates and saves a User with the given email, first name,
last name and password.
"""
if not email:
raise ValueError("Users must have an email address")
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=None, **extra_fields):
"""
Creates and saves a superuser with the given email, first name,
last name and password.
"""
user = self.create_user(
email,
password=password,
**extra_fields,
)
user.is_admin = True
user.save(using=self._db)
return user
And in your custom user model:
class CustomUser(AbstractBaseUser):
# [...]
objects = MyUserManager()
# [...]
I've taken the code from the django documentation about customizing the User model. They provide an example using the email as the username field (which is what you want).
You may keep the inheritance from AbstractUser but if you do not need most of the things that are in that model, you can also inherit your model from AbstractBaseUser, as in the example.
I've created a Login APIView for my Django app. I'm continually getting the error, AttributeError: 'WSGIRequest' object has no attribute 'session'. I've looked at other posts and people mention reordering the middleware placing sessionMIddleware first which doesn't work. Here is the setup of the API. The error is occurring at login(request, user).
from django.contrib.auth import authenticate, login
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import LoginSerializer
from rest_framework.permissions import AllowAny
class Login(APIView):
permission_classes = (AllowAny, )
authentication_classes = ()
serializer = LoginSerializer
def post(self, request, format=None):
login_form = self.serializer(data=request.data)
data = dict()
if login_form.is_valid():
username = login_form.data['username']
password = login_form.data['password']
user = authenticate(username=username, password=password)
if user is not None:
print(request.data)
login(request, user)
data['data'] = 'OK'
return Response(data=data, status=200)
else:
data['error'] = 'Invalid login information provided'
return Response(data=data, status=401)
else:
data['invalid'] = 'Bad Request, form is invalid'
return Response(data=data, status=400)
Ok, I had my middleware defined as MIDDLEWARE in my settings.py instead of MIDDLEWARE_CLASSES. Pycharm generated it for me like that when I setup the new project. I believe Django 1.10 uses MIDDLEWARE and I'm using 1.9.
Need to preform simple check: If user exist return True, False if not.I am trying to solve thise problem using custom json answers, not sure it is the right way to deal with it. Please help me .
My serializer:
User = get_user_model()
Class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (User.USERNAME_FIELD, )
My views:
#api_view(['GET'])
def UserViewSet(self, request):
if request.method == 'GET':
try:
user = User.objects.get(username=User.USERNAME_FIELD)
if User.DoesNotExist:
json = {}
json['message']= 'False'
return Response(json)
else:
json = {}
json['message']= 'True'
return Response(json)
Instead of using UserViewSet and UserSerializer, you can just use APIView for that.
class UserExistsView(APIView):
def get(self, request, *args, **kwargs):
# use this if username is in url kwargs
username = self.kwargs.get('username')
# use this if username is being sent as a query parameter
username = self.request.query_params.get('username')
try:
user = User.objects.get(username=username) # retrieve the user using username
except User.DoesNotExist:
return Response(data={'message':False}) # return false as user does not exist
else:
return Response(data={'message':True}) # Otherwise, return True