Reputation: 1
I'm new to backend and started learning djangorestframework. I followed a few tutorials and a question came to my mind. In the example below I use token authentication. With Swagger UI, I can authenticate without any problems and perform crud operations on todo items.
I thought about what it would be like if I started building up the frontend of this project and created a login form that would authenticate the user when he entered user crendentials.
I tried a few times but I couldn't do it. With Swagger UI, I send a request to the api/user/token endpoint and I can authenticate with the token I receive. But I cannot achieve this with the login form I created.
app/user/views.py:
"""
Views for the user API.
"""
from rest_framework import generics, authentication, permissions
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.settings import api_settings
from user.serializers import (
UserSerializer,
AuthTokenSerializer,
)
class CreateUserView(generics.CreateAPIView):
"""Create a new user in the system."""
serializer_class = UserSerializer
class CreateTokenView(ObtainAuthToken):
"""Create a new auth token for user."""
serializer_class = AuthTokenSerializer
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
class ManageUserView(generics.RetrieveUpdateAPIView):
"""Manage the authenticated user."""
serializer_class = UserSerializer
authentication_classes = [authentication.TokenAuthentication]
permission_classes = [permissions.IsAuthenticated]
def get_object(self):
"""Retrieve and return the authenticated user."""
return self.request.user
app/user/urls.py:
"""
URL mappings for the user API.
"""
from django.urls import path
from user import views
app_name = 'user'
urlpatterns = [
path('create/', views.CreateUserView.as_view(), name='create'), # create user
path('token/', views.CreateTokenView.as_view(), name='token'),
path('me/', views.ManageUserView.as_view(), name='me'), # get user
]
app/user/serializers.py:
"""
Serializers for the user API View.
"""
from django.contrib.auth import (
get_user_model,
authenticate,
)
from django.utils.translation import gettext as _
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
"""Serializer for the user object."""
class Meta:
model = get_user_model()
fields = ['email', 'password', 'name']
extra_kwargs = {'password': {'write_only': True, 'min_length': 5}}
def create(self, validated_data):
"""Create and return a user with encrypted password."""
return get_user_model().objects.create_user(**validated_data)
def update(self, instance, validated_data):
"""Update and return user."""
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):
"""Serializer for the user auth token."""
email = serializers.EmailField()
password = serializers.CharField(
style={'input_type': 'password'},
trim_whitespace=False,
)
def validate(self, attrs):
"""Validate and authenticate the user."""
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='authorization')
attrs['user'] = user
return attrs
app/todo/views.py:
"""
Views for the todo APIs
"""
from rest_framework import viewsets
from rest_framework.authentication import TokenAuthentication # noqa
from rest_framework.permissions import IsAuthenticated # noqa
from django.http.response import HttpResponse
from core.models import Todo
from todo import serializers
from rest_framework.response import Response
from rest_framework.renderers import JSONRenderer, TemplateHTMLRenderer
class TodoViewSet(viewsets.ModelViewSet):
"""View for manage todo APIs."""
serializer_class = serializers.TodoDetailSerializer
queryset = Todo.objects.all()
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
renderer_classes = [TemplateHTMLRenderer, JSONRenderer]
template_name = 'partials/todo.html'
def get_queryset(self):
"""Retrieve todos for authenticated user."""
return self.queryset.filter(user=self.request.user).order_by('created_at')
def get_serializer_class(self):
"""Return the serializer class for request."""
if self.action == 'list':
return serializers.TodoSerializer
return self.serializer_class
def create(self, request, *args, **kwargs):
todo = self.get_serializer(data=request.data)
todo_count = self.get_queryset().count()
todo.is_valid(raise_exception=True)
self.perform_create(todo)
if request.accepted_renderer.format == 'html':
return Response({"todo": todo.data, "todo_count": todo_count})
return Response(todo.data)
def perform_create(self, serializer):
"""Create a new todo."""
serializer.save(user=self.request.user)
def partial_update(self, request, *args, **kwargs):
instance = self.get_object()
instance.status = not instance.status
instance.save()
todo = self.get_serializer(instance)
if request.accepted_renderer.format == 'html':
return Response({"todo": todo.data})
return Response(todo.data)
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return HttpResponse()
def perform_destroy(self, instance):
instance.delete()
Simple login form:
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<form id="loginForm" hx-post="/api/login/">
{% csrf_token %}
<!-- Form fields for email and password -->
<label for="email">Email:</label><br>
<input type="email" id="email" name="email"><br>
<label for="password">Password:</label><br>
<input type="password" id="password" name="password"><br><br>
<!-- Submit button -->
<button type="submit">Login</button>
</form>
<script>
</script>
</body>
</html>
With this form, I want to obtain a token by sending a request with user credentials to the api/user/token endpoint and authenticate the user with this token.
I've been looking for the answer for a few days, but I couldn't find it. I apologize if the question has already been asked.
I defined a view to the api/user/token path and rendered it in the view. But I got a "method not allowed" error.
I tried to fetch the token with a js script and sent a request by adding the token to the header, but this did not yield any results either.
Upvotes: 0
Views: 52