Error TemplateResponseMixin requires either a definition of 'template_name' or an implementation of 'get_template_names()' - django-rest-framework

I am using Django Rest Auth with Django All Auth.
When I hit an endpoint /rest-auth/password/reset/ (POST method with an email address) I receive an email includes a URL to reset a password.
And then I cleck the URL, I have an error like below:
ImproperlyConfigured at /password-reset/confirm/MzY/5l0-c1042120be1e07140e64/
TemplateResponseMixin requires either a definition of 'template_name' or an implementation of 'get_template_names()'
In general, after clicking that URL the screen should transit to a browser of /rest-auth/password/reset/confirm/.
But in my case, it doesn't work like that...
How can I solve this error?
Here are the codes:
project/urls.py
from django.contrib import admin
from django.urls import path, include, re_path
from django.views.generic import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('allauth.urls')),
path('rest-auth/', include('rest_auth.urls')),
path('rest-auth/registration/', include('rest_auth.registration.urls')),
path('account-confirm-email/(?P<key>[-:\w]+)/$', TemplateView.as_view(),
name="account_confirm_email"),
path('account-confirm-email/', include('allauth.urls')),
re_path(
r'^password-reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
TemplateView.as_view(),
name='password_reset_confirm'),
]
serializers.py
from rest_auth.serializers import PasswordResetSerializer
class CustomPasswordResetSerializer(PasswordResetSerializer):
def get_email_options(self):
print("check override")
return {
'domain_override': 'localhost:8000',
'email_template_name': 'account/email/password_reset_key_message.txt',
}
password_reset_key_message.txt
A password reset has been requested.
Click the link below to continue.
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
settings.py
REST_AUTH_SERIALIZERS = {
'PASSWORD_RESET_SERIALIZER': 'path.to.serializers.CustomPasswordResetSerializer',
}
python: 3.7.5
Django:2.2.2
django-allauth:0.41.0
django-rest-auth:0.9.5
djangorestframework:3.12.1

I have no idea about those additional library. You can make your own custom password_reset serializers and views like below, make sure you provide JWT token based authentication while you reset the password from user.
serializers.py:
class ResetPasswordSerializer(Serializer):
new_password = serializers.CharField(error_messages={'required':' new_password key is required', 'blank':'new password is required'})
confirm_password = serializers.CharField(error_messages={'required':' confirm_password key is required', 'blank':'confirm password is required'})
views.py:
class ForgotPasswordResetAPIView(APIView):
permission_classes = (IsAuthenticated,)
authentication_classes=[JSONWebTokenAuthentication,]
def post(self,request,*args,**kwargs):
data = request.data
user = request.user
print(user)
serializer = ResetPasswordSerializer(data=data)
if serializer.is_valid():
new_password = serializer.data.get("new_password")
confirm_password = serializer.data.get("confirm_password")
if new_password == confirm_password:
print('ok')
user.set_password(new_password)
user.save()
return Response({'success':"true",'message':'Your password have been reset successfully'},status=200)
return Response({'success':"False",'message':'Both password fields must be same'},status=400)
return Response(serializer.errors , status=400)

Related

How can i use Simple JWT for custom User Model

I am managing my User Model (for customers), but i don't know how can i use simple-jwt for my Customer Model with it's custom Login View.
models.py
from django.db import models
class Customer(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField(max_length=100)
password = models.CharField(max_length=100)
and also, i want to manage my customer session by saving the Refresh Token,
can anyone please tell me how can i achieve that.
After some research, I came to the conclusion that Simple JWT doesn't have any special support for user models that are not derived from AbstactUserModel.
It means if you are managing the Customer model, either you need to extend it from AbstactUserModel or you should use jwt instead of Simple jwt.
First install simple jwt with the command
pip install djangorestframework-simplejwt
You must then configure your django project to use the libary with following option in settings.py
REST_FRAMEWORK = {
...
'DEFAULT_AUTHENTICATION_CLASSES': (
...
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
...
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
'UPDATE_LAST_LOGIN': False,
'ALGORITHM': 'HS256',
'SIGNING_KEY': settings.SECRET_KEY,
'VERIFYING_KEY': None,
'AUDIENCE': None,
'ISSUER': None,
'AUTH_HEADER_TYPES': ('Bearer',),
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'JTI_CLAIM': 'jti',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
In your root urls.py add the following
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
urlpatterns = [
...
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
...
]
Simple JWT by default only sends user id in the token. You can customize it further to send more information in your serializers.py by doing the following
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
"""Customizes JWT default Serializer to add more information about user"""
#classmethod
def get_token(cls, user):
token = super().get_token(user)
token['name'] = user.name
token['email'] = user.email
token['is_superuser'] = user.is_superuser
token['is_staff'] = user.is_staff
return token
Then in your views.py add the following
from users import serializers
class CustomTokenObtainPairView(TokenObtainPairView):
# Replace the serializer with your custom
serializer_class = serializers.CustomTokenObtainPairSerializer
You can read more here
I could fix this problem adding 'rest_framework_simplejwt' to INSTALLED_APPS after the app that has the custom user model.
in your views.py file, import this
from rest_framework_simplejwt.tokens import RefreshToken
next, create a refresh token in def post - the one you use for logging in
def post(self, request):
...
refresh = RefreshToken.for_user(user)
...
return the generated refresh token and access token in the response body
return Response(
{
'refresh': str(refresh),
'access': str(refresh.access_token),
},
status = status.HTTP_200_OK
)
What I have suggested is a simple and super basic implementation of what I believe you are looking for. I got it from here. But if you want a better and more customized, neater implementation, refer this article.

reverse() in a test only returns relative URL in a test for django-rest-framework and that causes a 404

I am trying to test the endpoints for my API by using this guide. Specifically, this block is supposed to test the get request:
class GetAllPuppiesTest(TestCase):
""" Test module for GET all puppies API """
def setUp(self):
Puppy.objects.create(
name='Casper', age=3, breed='Bull Dog', color='Black')
Puppy.objects.create(
name='Muffin', age=1, breed='Gradane', color='Brown')
Puppy.objects.create(
name='Rambo', age=2, breed='Labrador', color='Black')
Puppy.objects.create(
name='Ricky', age=6, breed='Labrador', color='Brown')
def test_get_all_puppies(self):
# get API response
response = client.get(reverse('get_post_puppies'))
# get data from db
puppies = Puppy.objects.all()
serializer = PuppySerializer(puppies, many=True)
self.assertEqual(response.data, serializer.data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
When I try to adapt this to my own test, it looks like this:
from ..models import DemanderFeature, DemanderFeatureCollection
from rest_framework import status
from django.test import TestCase, Client
from django.urls import reverse
from ..serializers import DemanderFeatureCollectionSerializer
class GetAllDemanderFeatureCollections(TestCase):
def setUp(self):
DemanderFeatureCollection.objects.create(name='testdemanderfeaturecollection0')
DemanderFeatureCollection.objects.create(name='testdemanderfeaturecollection1')
def test_get_all_demandercollections(self):
# get API response
response = client.get(reverse('demandercollections-list'))
# get data from db
demanderfeaturecollections = DemanderFeatureCollection.objects.all()
serializer = DemanderFeatureCollectionSerializer(demanderfeaturecollections, many=True)
self.assertEqual(response.data, serializer.data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
The problem is however the reverse() method is only returning the relative URL, (/demandercollections/) and then client.get(reverse(...)) returns a 404. I don't understand how I can force it to use the actual explicit URL during testing.
I'm using Django 3.
My main urls.py looks like this:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include("app.urls")),
path('api-auth/', include('rest_framework.urls')),
]
And my module urls.py looks like this:
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from app import views
# Create a router and register our viewsets with it.
router = DefaultRouter()
router.register(r'demanders', views.DemanderFeatureViewSet)
router.register(r'demandercollections', views.DemanderFeatureCollectionViewSet, basename="demandercollections")
router.register(r'producers', views.ProducerFeatureViewSet)
router.register(r'producercollections', views.ProducerFeatureCollectionViewSet)
router.register(r'pathfinderrunconfigurations', views.PathfinderRunConfigurationViewSet)
router.register(r'users', views.UserViewSet)
# The API URLs are now determined automatically by the router.
urlpatterns = [
path('', include(router.urls)),
]
The DemanderCollectionViewSet in views.py looks like this:
class DemanderFeatureCollectionViewSet(
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet
):
queryset = DemanderFeatureCollection.objects.all()
serializer_class = DemanderFeatureCollectionSerializer
lookup_field = 'name'
#action(detail=True, methods=["get"])
def geojson(self, request, *args, **kwargs):
demanders = DemanderFeature.objects.filter(demandercollection=self.get_object())
return Response(serialize('geojson', demanders, geometry_field='geom', fields=('name',)))
#action(detail=True, methods=["patch"])
def commit(self, request, *args, **kwargs):
demandercollection = self.get_object()
if not request.data["committed"]:
# User is trying to "uncommit", do not allow this
return Response("You may not un-commit a DemanderCollection. You must copy it and make modifications on the copy.", status=status.HTTP_400_BAD_REQUEST)
demandercollection.committed = True
demandercollection.save()
return Response(status=status.HTTP_204_NO_CONTENT)
def get_queryset(self):
user = get_object_or_404(User, username=self.request.user)
return DemanderFeatureCollection.objects.filter(deleted=False).filter(owner=user).order_by("name")
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
def destroy(self, request, *args, **kwargs):
demandercollection = self.get_object()
demandercollection.deleted = True
demandercollection.save()
return Response(f"Successfully deleted DemanderCollection.")
POST ANSWER EDIT
not only was the accepted answer indeed the culprit, but it also revealed that the DemanderFeatureCollection objects being created must also be created with an owner attribute, and the client object must call its login() method to a valid user credential pair.
The test class therefore had to be updated to look like this:
class GetAllDemanderFeatureCollections(TestCase):
""" Test module for GET all puppies API """
def setUp(self):
self.test_user = User.objects.create_user('test_user', 'a#b.com', 'test_user')
self.other_user = User.objects.create_user('other_user', 'a#b.com', 'other_user')
client.login(username="test_user", password="test_user")
DemanderFeatureCollection.objects.create(name='testdemanderfeaturecollection0', owner=self.test_user)
DemanderFeatureCollection.objects.create(name='testdemanderfeaturecollection1', owner=self.test_user)
DemanderFeatureCollection.objects.create(name='otherdemanderfeaturecollection0', owner=self.other_user)
def test_get_all_demandercollections_for_user(self):
# get API response
response = client.get(reverse('demandercollections-list'))
# get data from db
demanderfeaturecollections = DemanderFeatureCollection.objects.filter(owner=self.test_user).all()
serializer = DemanderFeatureCollectionSerializer(demanderfeaturecollections, many=True)
self.assertEqual(response.data, serializer.data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
In get_queryset() method of DemanderFeatureCollectionViewSet class you are filtering the model instances with owner field against the logged-in user.
In your test-cases, you are creating the DemanderFeatureCollection instances without linking the user and hence DRF raising an HTTP 404 error. So, attaching the user to the instance and making the request with the same user will give you a proper response from the API.

Can we Use Django GraphQL JWT For authentification and also Django rest framework for other apis together?

I feel very difficult to cover DRF_jwt/DRF_oauth2 but Django GraphQL
JWT seems easy....
Can i use both of them together for my ease
I am new in Rest Framework
You can create a custom authentication backend for DRF that extends rest_framework.authentication.BaseAuthentication and uses graphql_jwt.utils to authenticate the user in DRF the exact same way django-graphql-jwt does it.
This is what I have:
from graphql_jwt.exceptions import JSONWebTokenError
from graphql_jwt.utils import get_payload, get_user_by_payload
from rest_framework.authentication import BaseAuthentication, get_authorization_header
from rest_framework import exceptions
class TokenAuthentication(BaseAuthentication):
keyword = 'JWT'
def authenticate(self, request):
auth = get_authorization_header(request).split()
if not auth or auth[0].lower() != self.keyword.lower().encode():
return None
if len(auth) == 1:
msg = 'Invalid token header. No credentials provided.'
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = 'Invalid token header. Token string should not contain spaces.'
raise exceptions.AuthenticationFailed(msg)
try:
token = auth[1].decode()
except UnicodeError:
msg = 'Invalid token header. Token string should not contain invalid characters.'
raise exceptions.AuthenticationFailed(msg)
try:
payload = get_payload(token)
user = get_user_by_payload(payload)
except JSONWebTokenError as e:
raise exceptions.AuthenticationFailed(str(e))
if user is None or not user.is_active:
raise exceptions.AuthenticationFailed('User inactive or deleted.')
return (user, None)
def authenticate_header(self, request):
return self.keyword
If you mean using django-graphql-jwt to authentication and using DRF for other user account related actions, like updating, retrieving, deleting, reset password...
You can use django-graphql-auth.
It extends django-graphql-jwt, and provide a full api to handle user account.

How to check if the current user is logged in django rest framework? how to notify other django app that the current user is logged?

I use ListCreateAPIView for POST and GET requests. I want to check if current user is logged in GET request.
How to get current user (if he logged) in GET methods ?
To make it work, i have to send with token , t's not what I want because if user is logout, user can not access listView.
I thought django signals,or to rewrite authorization.
I thought django signals, or to rewrite Permissions or Authorization.
class PropertyList(generics.ListCreateAPIView):
"""To create a property"""
permission_classes = [permissions.IsAuthenticatedOrReadOnly, ]
queryset = Property.objects.filter(published=True)
serializer_class = PropertySerializer
filterset_class = PropertyFilter
pagination_class = LimitOffsetPagination
#
# def perform_create(self, serializer):
# serializer.save(created_by=self.request.user)
# for _ in range(100):
# logger.info("Your log message is here")
def get_serializer_context(self):
context = super().get_serializer_context()
context['is_create'] = True
print(self.request.user)
if self.request.user.is_authenticated:
print(self.request.user)
current_user = self.request.user
context['user_favs'] = (Bookmark.objects.filter(
bookUser = current_user
).values(
))
else:
context['user_favs'] = False
return context
In get_serializer_context(self) , i want to get current user because i return properties that user has bookmarked.
I need to add token in my Get request to have current user but that's mean , we have to login to see properties , it's not what I want
settings
REST_FRAMEWORK = {
"DATE_INPUT_FORMATS": ["%d-%m-%Y"],
# 'DATETIME_FORMAT': "%d-%m-%Y %H:%M:%S",
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
# 'rest_framework_simplejwt.authentication.JWTAuthentication',
),
'DEFAULT_FILTER_BACKENDS': (
'django_filters.rest_framework.DjangoFilterBackend',
),
'EXCEPTION_HANDLER': 'dooba.utils.custom_exception_handler',
'TEST_REQUEST_DEFAULT_FORMAT': 'json',
# 'DEFAULT_PARSER_CLASSES': (
# 'rest_framework.parsers.JSONParser',
# 'rest_framework.parsers.FormParser',
# 'rest_framework.parsers.MultiPartParser',
# )
}
As you can see
You can access user object in your APIView methods by self.request.user if there is no logged-in user, it should be AnonymousUser or else you should get the logged in user.
EDIT: Further research revealed that when you use JSONWebTokenAuthentication with IsAuthenticatedOrReadOnly returns 401 with expired tokens even if you make GET request. I recommend you to never put Authenticate header when making a GET request to your PropertyList view, that will solve your problem.

Post from Angular 5 front-end to Django Rest Framework back-end

I am trying to create a post request from Angular 5 to DRF. The field is a form that a user submits. I created the serializer and i can post something from DRF interface
models.py
class UserForm(models.Model):
id_user_form = models.AutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, db_column='idUser', unique=False)
name = models.CharField(max_length=50)
type = models.CharField(max_length=25)
location = models.CharField(max_length=200)
serilizers.py
`class UserFormSerializer(serializers.ModelSerializer):
class Meta:
model = UserForm
fields = ('user', 'name', 'type', 'location')
def create(self, validated_data):
user_data = validated_data.pop('user')
user_form = UserForm.objects.create(user_id=user_data, **validated_data)
return user_form
views.py
class FormSubmit(generics.ListCreateAPIView):
queryset = UserForm.objects.all()
serializer_class = UserFormSerializer
When i try to post it via Angular I get this error:
Forbidden (CSRF token missing or incorrect.): /api/form/
Am I doing something wrong?
Had the exact same problem when I wanted to upload a profile picture to my Django REST backend.
You have 2 options, basically.
The first one is disabling the CSRF checks, which are by default enforced by DRF. Have a read here. You may do this for testing purposes.
The second option would be to pass the CSRF Token inside your request header. To do that, have a look at the $cookies API.
With that, you can get the token out of the cookie and paste it into your header like so: 'X-CSRFToken': your-csrf-token-here.
You can verify this by opening your dev tools inside the browser and navigating to your site's cookies. You should see one called csrftoken there.
I hope this sent you on the right track.

Resources