Pytest login view with knox authentication - django-rest-framework

I am using knox token in authentication so Here's my Signup and Signin view, How to test the signing view with APIRequestFactory
class SignUpAPI(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()
token = AuthToken.objects.create(user)
return Response({"user": UserSerializer(user, context=self.get_serializer_context()).data})
class SignInAPI(generics.GenericAPIView):
serializer_class = LoginSerializer
def post(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data
return Response(
{
"user": UserSerializer(user, context=self.get_serializer_context()).data,
"token": AuthToken.objects.create(user)[1],
}
)
the endpoints
path("auth/register", SignUpAPI.as_view(), name="RegisterView"),
path("auth/login", SignInAPI.as_view(), name="LoginView"),```

Assuming you work with DRF, you could try something like this:
from rest_framework.test import APIClient
from knox.serializers import User
class SignInAPITests(TestCase):
def test_sign_in(self):
self.user = User.objects.create_user(
username='test_user',
password='12345')
self.client = APIClient()
response = self.client.post(auth_url,
data=dict(username="test_user",
password="12345"))
access_token = res.data["token"]
self.assertIsNotNone(access_token)

Related

ObtainAuthToken post function customization. How to get token and name in the response?

I´m trying to get response that include name apart from token.
In the documentation https://www.django-rest-framework.org/api-guide/authentication/ about ObtainAuthToken I see we can overwrite the post fn but I´m not sure how to apply it in my CreateTokenView class.
serializers.py
class AuthTokenSerializer(serializers.Serializer):
"""Serializer for the user authentication object"""
email = serializers.CharField()
password = serializers.CharField(
style={'input_type': 'password'},
trim_whitespace=False
)
def validate(self,attrs):
"""Overwriting the validate() fn"""
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 credentials')
raise serializers.ValidationError(msg, code='authentication')
attrs['user'] = user
return attrs
views.py
from rest_framework.authtoken.views import ObtainAuthToken
class CreateTokenView(ObtainAuthToken):
"""Create a new auth token for user"""
serializer_class = AuthTokenSerializer
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
Add the post fn and customize the kwargs
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
class CreateTokenView(ObtainAuthToken):
"""Create a new auth token for user"""
serializer_class = AuthTokenSerializer
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
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,
'name': user.name
})

How to add the serializer sample response with custom response parameter in drfyasg swagger docs?

ViewSet
class LoginAPI(generics.GenericAPIView):
serializer_class = LoginSerializer
def post(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data
# Generate user token
_, token = AuthToken.objects.create(user)
return Response({
"user": UserSerializer(user, context=self.get_serializer_context()).data,
"token": token
})
As in the above viewset i've added custom field token added as a custom field manually. But it's not showing in swagger docs.
What I've tried
from rest_framework import status
from drf_yasg import openapi
from authprofile.serializers import UserSerializer
login_response_schema = {
status.HTTP_201_CREATED: openapi.Response(
description="User Created",
examples= {
"application/json":{
"user":UserSerializer,
"token": "<Token>",
}
}
)
}
class LoginAPI(generics.GenericAPIView):
serializer_class = LoginSerializer
#swagger_auto_schema(operation_description="Login with email and password", \
responses=login_response_schema)
def post(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data
# Generate user token
_, token = AuthToken.objects.create(user)
return Response({
"user": UserSerializer(user, context=self.get_serializer_context()).data,
"token": token
})
Doing so gives me error saying:
TypeError: Object of type 'SerializerMetaclass' is not JSON serializable
Code in serializers.py file
class LoginSerializer(serializers.Serializer):
email = serializers.EmailField(required=True)
password = serializers.CharField(write_only=True, required=True)
def validate(self, attrs):
# Get required info for validation
email = attrs['email']
password = attrs['password']
"""
Check that the email is available in the User table
"""
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
raise serializers.ValidationError({"email": 'Details do not match an active account'})
if not user.check_password(password):
raise serializers.ValidationError({"password": 'Your password is incorrect'})
return attrs
The below will be used to format the response sample shown in swagger
class LoginResponseSerializer200(serializers.Serializer):
user = UserSerializer()
tokens = JWTTokenResponseSerializer()
Code in views.py file
class LoginAPI(generics.GenericAPIView):
serializer_class = LoginSerializer
#swagger_auto_schema(
request_body=serializers.LoginSerializer,
responses={
200: LoginResponseSerializer200
}
)
def post(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data
# Generate user token
_, token = AuthToken.objects.create(user)
return Response({
"user": UserSerializer(user, context=self.get_serializer_context()).data,
"token": token
})

I am trying implement authentication using the jwt token using djangorest framework but am getting assertion error

AssertionError: 'LoginView' should either include a serializer_class attribute, or override the get_serializer_class() method
Here is the code
class LoginView(GenericAPIView):
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)
return Response({'detail': 'Invalid credential'}, status=status.HTTP_401_UNAUTHORIZED)

JWT Token authentication - generate token

I have created a login screen which uses a username and password to login. I have a jwt authentication but I'm getting a bit confused because I have two login urls and I want only one. The jwt url is providing me the token meanwhile the other one that I have created myself I can login but no token is getting generated. This is my code:
serializers.py
class UserLoginSerializer(ModelSerializer):
token = CharField(allow_blank=True, read_only=True)
username = CharField(required=False, allow_blank=True)
class Meta:
model = User
fields = [
'username',
'password',
'token',
]
extra_kwargs = {"password":{"write_only": True}}
def validate(self, data):
user = authenticate(**data)
if user:
if user.is_active:
data['user'] = user
return data
raise exceptions.AuthenticationFailed('Account is not activated')
raise exceptions.AuthenticationFailed('User is not active')
def validate(self, data):
user_obj = None
username = data.get("username", None)
password = data["password"]
if not username:
raise ValidationError("A username is required")
user = User.objects.filter(
Q(username=username)
).distinct()
if user.exists() and user.count() == 1:
user_obj = user.first()
else:
raise ValidationError("This username is not valid")
if user_obj:
if not user_obj.check_password(password):
raise ValidationError("Incorrect credentials, please try again")
data["token"] = "SOME RANDOM TOKEN"
return data
views.py
class UserLoginAPIView(APIView):
permission_classes = [AllowAny]
serializer_class = UserLoginSerializer
def post(self, request, *args, **kwargs):
data = request.data
serializer = UserLoginSerializer(data=data)
if serializer.is_valid(raise_exception=True):
new_data = serializer.data
return Response(new_data, status=HTTP_200_OK)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
You can re-write your login serializer like this:
from rest_framework_jwt.serializers import JSONWebTokenSerializer
class SignInJWTSerializer(JSONWebTokenSerializer):
def validate(self, attrs):
email = attrs.get('email')
password = attrs.get('password')
if email is None or password is None:
message = 'Must include email and password'
raise serializers.ValidationError({'message': message})
...
And in url:
from rest_framework_jwt.views import ObtainJSONWebToken
path('login/', ObtainJSONWebToken.as_view(serializer_class=serializers.SignInJWTSerializer), name='login'),
also remove view class

is it possible make two GET endpoints in djangorestframework via APIVIEW

Me need make two GET endpoint in django rest framework in one classes
this is endpoints will work with another system via api
i tried
class MyApiView(APIView):
permission_classes = [AllowAny]
def get(self, request):
return Response({'test':'test'})
#list_route(methods=['GET'], )
def two_endpoint(self, request):
return Response({'test': 'test'})
but this not work
thanks in advance
try to use ModelViewset class,
views.py
from rest_framework import viewsets
class MyApiView(viewsets.ModelViewSet):
permission_classes = (AllowAny,)
serializer_class = MySerializer
queryset = Sample.objects.all()
def function_name_1(self, request):
# do stuff
return Response(data="return your data")
def function_name_2(self, request):
# do stuff
return Response(data="return your data")
def function_name_3(self, request, pk):
# do stuff
return Response(data="return your data")
urls.py
from rest_framework.routers import DefaultRouter
from django.conf.urls import url
router = DefaultRouter()
router.register(r'myendpoint', MyApiView)
urlpatterns = [
url(r'^myendpoint/end_point_1/$', MyApiView.as_view({'get': 'function_name_1'}), name='function_name_1'),
url(r'^myendpoint/end_point_2/$', MyApiView.as_view({'post': 'function_name_2'}), name='function_name_2'),
url(r'^myendpoint/end_point_3/$', MyApiView.as_view({'put': 'function_name_3'}), name='function_name_3'),
]+router.urls
UPDATE-1
Change your views.py to this,
from rest_framework import viewsets
class MyApiView(viewsets.ViewSet):
permission_classes = (AllowAny,)
def function_name_1(self, request):
# do stuff
return Response(data="return your data")
def function_name_2(self, request):
# do stuff
return Response(data="return your data")
def function_name_3(self, request):
# do stuff
return Response(data="return your data")

Resources