From 10fd0c0f2ead618dfebb753ffd48e4a1dcac88b4 Mon Sep 17 00:00:00 2001 From: sabuhish Date: Mon, 11 May 2020 21:53:16 +0400 Subject: [PATCH 01/13] simple RestAPI with JWT && TODO application --- .../RestDjango/django_restframework/.env | 2 +- .../api/migrations/0003_todo.py | 27 ++++ .../api/migrations/0004_auto_20200511_1749.py | 21 +++ .../django_restframework/api/models.py | 12 +- .../django_restframework/api/serializer.py | 11 +- .../django_restframework/api/urls.py | 9 +- .../django_restframework/api/utils.py | 16 ++ .../django_restframework/api/views.py | 148 ++++++++++++++++-- .../django_restframework/requirements.txt | 3 + .../django_restframework/restApi/settings.py | 52 ++++-- 10 files changed, 274 insertions(+), 27 deletions(-) create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/migrations/0003_todo.py create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/migrations/0004_auto_20200511_1749.py diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/.env b/Project1_Simple_REST_API/RestDjango/django_restframework/.env index fcb1e31..cd4bb2a 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/.env +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/.env @@ -1,2 +1,2 @@ -KEY='$2t!11nw4*dvhcuhc3b=)6-@-m$r$^9r9s9^_arq(m3-p4^c_=' +SECRET_KEY='$2t!11nw4*dvhcuhc3b=)6-@-m$r$^9r9s9^_arq(m3-p4^c_=' diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/migrations/0003_todo.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/migrations/0003_todo.py new file mode 100644 index 0000000..a825003 --- /dev/null +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/migrations/0003_todo.py @@ -0,0 +1,27 @@ +# Generated by Django 3.0.5 on 2020-05-10 10:26 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('api', '0002_delete_user'), + ] + + operations = [ + migrations.CreateModel( + name='Todo', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=50)), + ('description', models.CharField(max_length=250)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/migrations/0004_auto_20200511_1749.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/migrations/0004_auto_20200511_1749.py new file mode 100644 index 0000000..b326d99 --- /dev/null +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/migrations/0004_auto_20200511_1749.py @@ -0,0 +1,21 @@ +# Generated by Django 3.0.5 on 2020-05-11 17:49 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('api', '0003_todo'), + ] + + operations = [ + migrations.AlterField( + model_name='todo', + name='user', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/models.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/models.py index 508029f..27021d4 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/models.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/models.py @@ -1,5 +1,15 @@ from django.db import models +from django.contrib.auth import get_user_model -# Create your models here. +User = get_user_model() +class Todo(models.Model): + user = models.ForeignKey(User, models.SET_NULL,blank=True,null=True) + title = models.CharField(max_length=50,null=False,blank=False) + description = models.CharField(max_length=250,null=False,blank=False) + + def __str__(self): + return f'{self.title}' + + diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/serializer.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/serializer.py index 07ed726..6e291f7 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/serializer.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/serializer.py @@ -2,7 +2,7 @@ from django.contrib.auth import get_user_model import django.contrib.auth.password_validation as validators from django.contrib.auth import password_validation - +from .models import Todo User = get_user_model() @@ -21,6 +21,7 @@ class Meta: "password": {'required': True}} #validate user password with deault django password validator + #You can specify custom field-level validation by adding .validate_ methods to your Serializer subclass. These are similar to the .clean_ methods on Django forms.These are similar to the .clean_ methods on Django forms. def validate_password(self, value): password_validation.validate_password(value, self.instance) return value @@ -47,4 +48,12 @@ class Meta: model = User fields =("first_name", "last_name", "email","username","id") + + +class TodoSerializer(serializers.ModelSerializer): + class Meta: + model = Todo + + fields = ("id","user","title","description",) + \ No newline at end of file diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/urls.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/urls.py index b63c895..f3bdbce 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/urls.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/urls.py @@ -1,5 +1,6 @@ from django.urls import path -from .views import * +from .views import UserDetail,UserList,CreateUser, Todos, TodoOperations,Login +from rest_framework_simplejwt.views import token_refresh urlpatterns = [ @@ -8,4 +9,10 @@ path('users/',UserList.as_view()), path('user/',CreateUser.as_view()), + path("user/todo/",Todos.as_view()),#POST ,#GET all todos + path("user/todo//",TodoOperations.as_view()), #PUT,GET,DELETE specifc + + path("login/", Login.as_view()), + path("token-refresh/", token_refresh) + ] diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/utils.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/utils.py index 374f0aa..f9be4c0 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/utils.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/utils.py @@ -1,5 +1,7 @@ import logging, logging.config import sys +import jwt +from django.conf import settings LOGGING = { 'version': 1, @@ -18,3 +20,17 @@ logging.config.dictConfig(LOGGING) + + +def jwt_decode_handler(token): + + secret_key = settings.SECRET_KEY + + return jwt.decode( + token, + secret_key, + audience=settings.SIMPLE_JWT.get("AUDIENCE"), + issuer=settings.SIMPLE_JWT.get("ISSUER"), + algorithms=[settings.SIMPLE_JWT.get("ALGORITHM")] + ) + diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py index e130343..6086cad 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py @@ -1,26 +1,21 @@ from django.shortcuts import render from django.http import HttpResponse -# Create your views here. -from rest_framework.response import Response from rest_framework.views import APIView from rest_framework import status -from rest_framework.authtoken.models import Token -from rest_framework.authtoken.views import ObtainAuthToken from rest_framework.response import Response -# from .models import User from django.shortcuts import get_object_or_404 import logging from .utils import LOGGING -from rest_framework.permissions import AllowAny +from rest_framework.permissions import AllowAny,IsAuthenticated from rest_framework.decorators import api_view -from .serializer import UserSerializer,UserSerializerDetails +from .serializer import UserSerializer,UserSerializerDetails, TodoSerializer from django.shortcuts import get_object_or_404 - -logging.config.dictConfig(LOGGING) - +from .models import Todo from django.contrib.auth import get_user_model +from .utils import jwt_decode_handler +from rest_framework_simplejwt.views import TokenObtainPairView - +logging.config.dictConfig(LOGGING) User = get_user_model() @@ -88,7 +83,7 @@ def put(self, request, pk): data = dict(serializer.data) data.pop("password") - return Response(data, status=status.HTTP_201_CREATED) + return Response(data) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @@ -103,4 +98,131 @@ def delete(self, request, pk): return Response({"message": f"User with username {user.username} has been deleted"},status=status.HTTP_200_OK) - \ No newline at end of file + + +class Todos(APIView): + permission_classes = [IsAuthenticated] + + + def post(self, request): + + token = request.headers.get("Authorization").split(" ")[1] + + details = jwt_decode_handler(token) + user_id = details.get("user_id") + + if not User.objects.filter(id=user_id).last(): return Response({"message": "No such a user found"},status=status.HTTP_404_NOT_FOUND) + + request.data["user"] = user_id + serializer = TodoSerializer(data=request.data) + + + + if serializer.is_valid(): + + serializer.save() + + return Response(serializer.data, status=status.HTTP_201_CREATED) + + else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + + def get(self,request): + token = request.headers.get("Authorization").split(" ")[1] + + details = jwt_decode_handler(token) + + + user_id = details.get("user_id") + + if not User.objects.filter(id=user_id).last(): return Response({"error": True,"message": "No such a user found"},status=status.HTTP_404_NOT_FOUND) + + + todos = Todo.objects.filter(user=user_id).all() + if not todos: return Response({"message":"No todos found for this user"}) + data = TodoSerializer(todos, many=True).data + + return Response(data) + + +class TodoOperations(APIView): + permission_classes = [IsAuthenticated] + + def put(self,request,pk): + + token = request.headers.get("Authorization").split(" ")[1] + details = jwt_decode_handler(token) + + if not (User.objects.filter(id=details.get("user_id")).last() and Todo.objects.filter(id=pk).last()): return Response({"message": "No such a user or todo"},status=status.HTTP_404_NOT_FOUND) + + todo = Todo.objects.filter(id=pk).first() + + + + serializer = TodoSerializer(instance=todo, data=request.data, partial=True) + + if serializer.is_valid(): + + serializer.save() + + return Response(serializer.data) + + else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + + + def delete(self,request,pk): + + token = request.headers.get("Authorization").split(" ")[1] + + details = jwt_decode_handler(token) + + if not (User.objects.filter(id=details.get("user_id")).last() and Todo.objects.filter(id=pk).last()): return Response({"message": "No such a user or todo"},status=status.HTTP_404_NOT_FOUND) + + todo = Todo.objects.filter(id=pk).last() + + todo.delete() + + + return Response({"status": "OK", "message": "Todo has been deleted"}) + + + def get(self,request,pk): + + token = request.headers.get("Authorization").split(" ")[1] + + details = jwt_decode_handler(token) + + + if not (User.objects.filter(id=details.get("user_id")).last() and Todo.objects.filter(id=pk).last()): return Response({"message": "No such a user or todo"},status=status.HTTP_404_NOT_FOUND) + + todos = Todo.objects.filter(id=pk,user=details.get("user_id")).last() + + if not todos: return Response({"message":"no todos found"}, status=status.HTTP_404_NOT_FOUND) + + data = TodoSerializer(todos).data + + return Response(data) + + + + +class Login(TokenObtainPairView): + + def post(self, request, *args, **kwargs): + data = super().post(request, *args, **kwargs) + + data = data.data + + acces_token = jwt_decode_handler(data.get("access")) + + if not User.objects.filter(id=acces_token.get("user_id")).last(): return Response({"error": True,"message": "No such a user"},status=status.HTTP_404_NOT_FOUND) + + todos = Todo.objects.filter(user=acces_token.get("user_id")).all() + + todos = TodoSerializer(todos, many=True).data + + data["todos"] = todos + + + return Response(data) \ No newline at end of file diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/requirements.txt b/Project1_Simple_REST_API/RestDjango/django_restframework/requirements.txt index b2a783c..823eece 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/requirements.txt +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/requirements.txt @@ -1,6 +1,9 @@ asgiref==3.2.7 Django==3.0.5 djangorestframework==3.11.0 +djangorestframework-jwt==1.11.0 +djangorestframework-simplejwt==4.4.0 #pkg-resources==0.0.0 +PyJWT==1.7.1 pytz==2019.3 sqlparse==0.3.1 diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py b/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py index 1391e17..b16fb64 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py @@ -11,6 +11,8 @@ """ import os +from django.conf import settings +from datetime import timedelta # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -23,10 +25,15 @@ ''' Securty key designed for PasswordResetView tokens, usage of cryptographic signing, unless a different key is provided. There are many things in a Django app which require a cryptographic signature, and the ‘SECRET_KEY’ setting is the key used for those. ''' -# SECURITY WARNING: keep the secret key used in production secret! +# Django automatically hides settings if they contain any of the following words: +# API +# TOKEN +# KEY +# SECRET +# PASS +# SIGNATURE -SECRET_KEY = os.getenv("KEY") -print(SECRET_KEY) +SECRET_KEY = os.getenv("SECRET_KEY") # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -54,18 +61,43 @@ 'django.contrib.staticfiles', "api.apps.MyAppConfig", "rest_framework", + ] +# rest_framework_simplejwt.token_blacklist +# for more about black list: https://django-rest-framework-simplejwt.readthedocs.io/en/latest/blacklist_app.html + +#JWT stand for JSON Web Token and it is an authentication strategy used by client/server applications where the client is a Web application using JavaScript and some frontend framework like Angular, React or VueJS.The JWT is acquired by exchanging an username + password for an access token and an refresh token. +SIMPLE_JWT = { + 'ROTATE_REFRESH_TOKENS': True, #When set to True, if a refresh token is submitted to the TokenRefreshView, a new refresh token will be returned along with the new access token. + 'BLACKLIST_AFTER_ROTATION': True, #refresh tokens submitted to the TokenRefreshView to be added to the blacklist + + 'ALGORITHM': 'HS256', #TWO types either HMAC or RSA for HMAC 'HS256', 'HS384', 'HS512: SIGNING_KEY setting will be used as both the signing key and the verifying key. asymmetric RSA RS256', 'RS384', 'RS512' SIGNING_KEY setting must be set to a string that contains an RSA private key. Likewise, the VERIFYING_KEY + 'SIGNING_KEY': settings.SECRET_KEY, #content of generated tokens. + 'VERIFYING_KEY': None, #The verifying key which is used to verify the content of generated tokens + 'AUDIENCE': None, #The audience claim to be included in generated tokens and/or validated in decoded tokens + 'ISSUER': None, #ssuer claim to be included in generated tokens + + 'AUTH_HEADER_TYPES': ('Bearer',), #Authorization: Bearer ('Bearer', 'JWT') + 'USER_ID_FIELD': 'id', #The database field from the user model that will be included in generated tokens to identify users. + 'USER_ID_CLAIM': 'user_id', #value of 'user_id' would mean generated tokens include a “user_id” claim that contains the user’s identifier. + + 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), + 'TOKEN_TYPE_CLAIM': 'token_type', #The claim name that is used to store a token’s type + + 'JTI_CLAIM': 'jti',#The claim name that is used to store a token’s unique identifier. + 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), #which specifies how long access tokens are valid + 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), # how long refresh tokens are valid. +} + #rest framework config REST_FRAMEWORK = { - # Use Django's standard `django.contrib.auth` permissions, - # or allow read-only access for unauthenticated users. - 'DEFAULT_PERMISSION_CLASSES': [ - 'rest_framework.permissions.IsAuthenticated' - ], + 'DEFAULT_PERMISSION_CLASSES': ( + 'rest_framework.permissions.IsAuthenticated', + ), 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'rest_framework.authentication.TokenAuthentication', + 'rest_framework_simplejwt.authentication.JWTAuthentication', ), } @@ -128,7 +160,6 @@ # https://docs.djangoproject.com/en/2.2/ref/settings/#databases ''' Django officially supports the following databases: - PostgreSQL MariaDB MySQL @@ -158,6 +189,7 @@ } + # Password validation # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators ''' From e90c9fd7fdc5aee575e4ab84e9441db47223b8d0 Mon Sep 17 00:00:00 2001 From: sabuhish Date: Tue, 12 May 2020 22:33:41 +0400 Subject: [PATCH 02/13] login has been updated --- .../RestDjango/django_restframework/api/views.py | 5 +++++ .../RestDjango/django_restframework/restApi/settings.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py index 6086cad..7874bfc 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py @@ -221,8 +221,13 @@ def post(self, request, *args, **kwargs): todos = Todo.objects.filter(user=acces_token.get("user_id")).all() todos = TodoSerializer(todos, many=True).data + user = User.objects.filter(id=acces_token.get("user_id")).last() + + user_details = UserSerializerDetails(user) + data["todos"] = todos + data["user_details"] = user_details.data return Response(data) \ No newline at end of file diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py b/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py index b16fb64..1190ca9 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py @@ -85,7 +85,7 @@ 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), 'TOKEN_TYPE_CLAIM': 'token_type', #The claim name that is used to store a token’s type - 'JTI_CLAIM': 'jti',#The claim name that is used to store a token’s unique identifier. + 'JTI_CLAIM': 'jti', #The claim name that is used to store a token’s unique identifier. 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), #which specifies how long access tokens are valid 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), # how long refresh tokens are valid. } From e8a207188bc4099cd0c78bef3cc981d1bc07d2f7 Mon Sep 17 00:00:00 2001 From: sabuhish Date: Thu, 14 May 2020 17:18:22 +0400 Subject: [PATCH 03/13] fix settings --- .../RestDjango/django_restframework/.env | 2 +- .../django_restframework/api/urls.py | 4 ++-- .../django_restframework/api/views.py | 24 +++++++++++++++---- .../django_restframework/restApi/settings.py | 2 +- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/.env b/Project1_Simple_REST_API/RestDjango/django_restframework/.env index cd4bb2a..5922f43 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/.env +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/.env @@ -1,2 +1,2 @@ -SECRET_KEY='$2t!11nw4*dvhcuhc3b=)6-@-m$r$^9r9s9^_arq(m3-p4^c_=' +export SECRET_KEY='$2t!11nw4*dvhcuhc3b=)6-@-m$r$^9r9s9^_arq(m3-p4^c_=' diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/urls.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/urls.py index f3bdbce..bfac783 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/urls.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/urls.py @@ -1,5 +1,5 @@ from django.urls import path -from .views import UserDetail,UserList,CreateUser, Todos, TodoOperations,Login +from .views import UserDetail,UserList,CreateUser, Todos, TodoOperations,Login,RefreshToken from rest_framework_simplejwt.views import token_refresh @@ -13,6 +13,6 @@ path("user/todo//",TodoOperations.as_view()), #PUT,GET,DELETE specifc path("login/", Login.as_view()), - path("token-refresh/", token_refresh) + path("token-refresh/", RefreshToken.as_view()) ] diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py index 7874bfc..5909ef6 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py @@ -13,7 +13,7 @@ from .models import Todo from django.contrib.auth import get_user_model from .utils import jwt_decode_handler -from rest_framework_simplejwt.views import TokenObtainPairView +from rest_framework_simplejwt.views import TokenObtainPairView,TokenRefreshView logging.config.dictConfig(LOGGING) User = get_user_model() @@ -192,7 +192,7 @@ def get(self,request,pk): token = request.headers.get("Authorization").split(" ")[1] details = jwt_decode_handler(token) - + print(details) if not (User.objects.filter(id=details.get("user_id")).last() and Todo.objects.filter(id=pk).last()): return Response({"message": "No such a user or todo"},status=status.HTTP_404_NOT_FOUND) @@ -214,8 +214,11 @@ def post(self, request, *args, **kwargs): data = data.data + print(data) acces_token = jwt_decode_handler(data.get("access")) - + ref = jwt_decode_handler(data.get("refresh")) + print(ref) + print(acces_token) if not User.objects.filter(id=acces_token.get("user_id")).last(): return Response({"error": True,"message": "No such a user"},status=status.HTTP_404_NOT_FOUND) todos = Todo.objects.filter(user=acces_token.get("user_id")).all() @@ -230,4 +233,17 @@ def post(self, request, *args, **kwargs): data["user_details"] = user_details.data - return Response(data) \ No newline at end of file + return Response(data) + +class RefreshToken(TokenRefreshView): + def post(self, request, *args, **kwargs): + data = super().post(request, *args, **kwargs) + + new_token = data.data + print(new_token) + acces_token = jwt_decode_handler(new_token.get("access")) + ref = jwt_decode_handler(new_token.get("refresh")) + print(ref) + print(acces_token) + + return data \ No newline at end of file diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py b/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py index 1190ca9..8aaf39d 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py @@ -82,7 +82,7 @@ 'USER_ID_FIELD': 'id', #The database field from the user model that will be included in generated tokens to identify users. 'USER_ID_CLAIM': 'user_id', #value of 'user_id' would mean generated tokens include a “user_id” claim that contains the user’s identifier. - 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), + 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), #token_type 'TOKEN_TYPE_CLAIM': 'token_type', #The claim name that is used to store a token’s type 'JTI_CLAIM': 'jti', #The claim name that is used to store a token’s unique identifier. From 82a61b2a77d0d1adce68536e25327eb1785c0f26 Mon Sep 17 00:00:00 2001 From: sabuhish Date: Wed, 27 May 2020 21:59:28 +0400 Subject: [PATCH 04/13] Simple rest api Django video 3 Unit Test has been added --- .../django_restframework/api/tests.py | 3 - .../api/tests/__init__.py | 0 .../api/tests/test_api_views.py | 298 ++++++++++++++++++ .../api/tests/test_function.py | 22 ++ .../api/tests/test_models.py | 59 ++++ .../api/tests/test_paths.py | 109 +++++++ .../api/tests/test_serializers.py | 144 +++++++++ .../django_restframework/api/urls.py | 15 +- .../django_restframework/api/views.py | 18 +- .../django_restframework/restApi/settings.py | 6 + 10 files changed, 656 insertions(+), 18 deletions(-) delete mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/tests.py create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/__init__.py create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_api_views.py create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_function.py create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_models.py create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_paths.py create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_serializers.py diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/__init__.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_api_views.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_api_views.py new file mode 100644 index 0000000..3b01738 --- /dev/null +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_api_views.py @@ -0,0 +1,298 @@ +from api.views import CreateUser,UserList,UserDetail, Todos,TodoOperations,Login,RefreshToken +from django.contrib.auth import get_user_model +from api.serializer import UserSerializer +from django.test import TestCase, Client +from rest_framework.test import APIRequestFactory +from rest_framework.test import APIClient +from api.models import Todo +from rest_framework.test import APITestCase +from django.urls import reverse + +User = get_user_model() + + + + +class TestUserViews(APITestCase): + + def setUp(self): + self.client = APIClient() + + self.user = { + "first_name": "test_user", + "username":"testname", + "password":"test12345", + "email":"test@gmail.com", + "last_name":"testlastname" + } + self.user_2 = { + "first": "test_user", + "username":"testname", + "password":"test12345", + "email":"test@gmailcom", + "last_name":"testlastname" + } + + + + def test_create_user(self): + path = reverse("user_create") + + response = self.client.post(path,self.user,format='json') + + self.assertEqual(response.status_code, 201) + + response_2 = self.client.post(path,self.user,format='json') + + self.assertEqual(response_2.status_code, 400) + + user = User.objects.filter(id=1).first() + + self.assertEqual(user.first_name, "test_user") + self.assertEqual(user.username, "testname") + self.assertEqual(user.email, "test@gmail.com") + + + # print(response_2.json()) + + def test_user_list(self): + + path = reverse("user_list") + + response = self.client.get(path) + + self.assertEqual(response.status_code, 200) + + + + def test_user_detail_get(self): + user = User.objects.create( + first_name="test_user", + username="testname", + password="test12345", + email="test@gmail.com", + last_name="testlastname" + ) + + path = reverse("user_detail",args="1") + + + response = self.client.get(path,format='json') + + self.assertEqual(response.status_code, 200) + + self.assertEqual(user.first_name, response.json().get("first_name")) + self.assertEqual(user.username, response.json().get("username")) + self.assertEqual(user.email, response.json().get("email")) + self.assertEqual(user.last_name, response.json().get("last_name")) + + + + def test_user_detail_put(self): + path = reverse("user_detail",args="1") + + new_user = User.objects.create( + first_name="test_user", + username="testname", + password="test12345", + email="test@gmail.com", + last_name="testlastname" + ) + data = {"first_name": "TuralYek"} + + + response = self.client.put(path,data,format='json') + + + edited_user = User.objects.filter(id=1).first() + + self.assertEqual(response.status_code, 200) + + self.assertEqual(edited_user.first_name, response.json().get("first_name")) + + + def test_user_detail_delete(self): + + new_user = User.objects.create( + first_name="test_user", + username="testname", + password="test12345", + email="test@gmail.com", + last_name="testlastname" + ) + + path = reverse("user_detail",args="1") + + response = self.client.delete(path,format='json') + self.assertEqual(response.status_code, 200) + + + self.assertIsNone(User.objects.filter(id=1).first()) + + + + def test_login(self): + self.user = User.objects.create_user( + username="test_user", + password="test_password", + ) + + path = reverse("login") + + response = self.client.post(path,{"username":"test_user","password":"test_password"},format='json') + + + self.assertEqual(response.status_code, 200) + self.assertIn('access', response.data) + self.assertIn('refresh', response.data) + + def test_missingfields(self): + + path = reverse("login") + + response = self.client.post(path,format='json') + + self.assertEqual(response.status_code, 400) + + + + def test_token_refresh(self): + self.user = User.objects.create_user( + username="test_user", + password="test_password") + + path_login = reverse("login") + path_token_refresh = reverse("token_refresh") + + response = self.client.post(path_login,{"username":"test_user","password":"test_password"},format='json') + + response_login =response.json() + + response_refresh = self.client.post(path_token_refresh,{"refresh":response_login.get("refresh")},format='json') + + print("json",response_refresh.json()) + + self.assertEqual(response.status_code, 200) + + self.assertEqual(response_refresh.status_code, 200) + + + +class TestTodoViews(APITestCase): + + + + def setUp(self): + self.client = APIClient() + + self.user = User.objects.create_user( + username="test_user", + password="test_password", + ) + path_login = reverse("login") + + self.response = self.client.post(path_login,{"username":"test_user","password":"test_password"},format='json') + + self.response_login =self.response.json() + + def test_post_todo(self): + path = reverse("user_todo") + + data = { + "title": "Test Title", + "description": "Test description" + } + + self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {self.response_login.get('access')}") + + res = self.client.post(path,data,format='json') + + + self.assertEqual(res.status_code, 201) + + self.assertEqual(res.json().get("title"), data["title"]) + self.assertEqual(res.json().get("description"), data["description"]) + + + + + def test_get_all_todos(self): + path = reverse("user_todo") + + self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {self.response_login.get('access')}") + + res = self.client.get(path,format='json') + self.assertEqual(res.status_code, 200) + + + + def test_todo_operation_get(self): + + path = reverse("user_todo_operation",args="1") + + user_todo = reverse("user_todo") + + data = { + "title": "Test Title", + "description": "Test description" + } + + self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {self.response_login.get('access')}") + + res = self.client.post(user_todo,data,format='json') + + + self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {self.response_login.get('access')}") + + res = self.client.get(path,format='json') + + self.assertEqual(res.status_code, 200) + self.assertEqual(res.json().get("id"), 1) + + + + def test_todo_operation_put(self): + + path = reverse("user_todo_operation",args="1") + + user_todo = reverse("user_todo") + + data_1 = { + "title": "Test Title", + "description": "Test description" + } + + data_2 = { + "title": "Test Title2", + "description": "Test description2" + } + + self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {self.response_login.get('access')}") + + res = self.client.post(user_todo,data_1,format='json') + + + self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {self.response_login.get('access')}") + + res = self.client.get(path,format='json') + self.assertEqual(res.json().get("title"), data_1["title"]) + + res_2 = self.client.put(path,data_2,format='json') + + + self.assertEqual(res_2.json().get("title"), data_2["title"]) + + self.assertEqual(res.status_code, 200) + + + def test_todo_operation_delete(self): + + path = reverse("user_todo_operation",args="1") + + self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {self.response_login.get('access')}") + + res = self.client.delete(path,format='json') + + self.assertEqual(res.status_code, 404) + + diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_function.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_function.py new file mode 100644 index 0000000..51425e5 --- /dev/null +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_function.py @@ -0,0 +1,22 @@ +from django.test import SimpleTestCase +from api.utils import jwt_decode_handler +from django.conf import settings +from jwt.exceptions import ExpiredSignatureError + + + +class TestFunc(SimpleTestCase): + + + def test_jwt(self): + + token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTkwNTczMzIzLCJqdGkiOiI2YjVkZDBhOGU0ZDE0YzMzODQwM2ZiZWFiZjE3ZmE0MSIsInVzZXJfaWQiOjEwfQ.WZFnpB805o045MJnzqhsMit6p30mu-2TVgA6oitYWI0" + result = {'token_type': 'access', 'exp': 1590573323, 'jti': '6b5dd0a8e4d14c338403fbeabf17fa41', 'user_id': 10} + + with self.assertRaises(ExpiredSignatureError) as expired: + + + self.assertEqual(jwt_decode_handler(token),result) + self.assertEqual(ExpiredSignatureError, type(expired.exception)) + + \ No newline at end of file diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_models.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_models.py new file mode 100644 index 0000000..9033f76 --- /dev/null +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_models.py @@ -0,0 +1,59 @@ + +# What is test actually ? +# Test is piece of code that makes sure that code you wrote works perfectly under speicif conditions + +# main reason companies requires test: more quality meaning application without bugs, make sure peiace code working correctly + + +#There types of Tests exists: + +# 1) Unit Test +# 2) Integration Test +# 3) Functional Test + +# 1) Testing one piece independently of another code, we need to assert function reterun value + +# 2) Tetsing multiple part of your code that integrated with each other + +# 3) Functional test is actullay testing applications from end user point view, basicly it is done by selinum,what user will type or fill gthe form + +from django.test import TestCase +from django.contrib.auth import get_user_model +from api.models import Todo +from django.db.utils import IntegrityError + +User = get_user_model() + +class TestModels(TestCase): + + def setUp(self): + + self.user = User.objects.create(username="testuser", email="test@gmail.com") + + + def test_user_model(self): + + self.assertEqual(self.user.username, "testuser") + self.assertEqual(self.user.email,"test@gmail.com") + + + def test_todo_model(self): + self.todo = Todo.objects.create( + user=self.user, + title="Test title", + description="Test description" + ) + + + self.assertEqual(self.todo.user.username, "testuser") + self.assertEqual(self.todo.title,"Test title") + self.assertEqual(self.todo.description,"Test description") + + + def test_new_insert(self): + + with self.assertRaises(Exception) as raised: # top level exception as we want to figure out its exact type + new_user = User.objects.create(username="testuser", email="test@gmail.com") + self.assertEqual(IntegrityError, type(raised.exception)) + + diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_paths.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_paths.py new file mode 100644 index 0000000..1803fcc --- /dev/null +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_paths.py @@ -0,0 +1,109 @@ +from django.test import SimpleTestCase,TestCase +from django.urls import reverse, resolve,include,path +from api.views import UserDetail,UserList,CreateUser,Todos,TodoOperations,Login,RefreshToken +from rest_framework.test import APITestCase, URLPatternsTestCase,APIRequestFactory +from rest_framework import status + + + + +#SimpleTestCase is just basic testing class that in case if you dont need to interact with DB + +# APITestCase, +# URLPatternsTestCase +# TestCase + +#NOTE: be carefull django testcase works it looks basily what folder and files start with test work then class and functions. So in every class or function we should begin with Test keyword. + +#AssertionError: Database queries to 'default' are not allowed in SimpleTestCase subclasses. Either subclass TestCase or TransactionTestCase to ensure proper test isolation or add 'default' to api.tests.test_paths.TestApiEndPoints.databases to silence this failure. + +class TestApiEndPoints(TestCase): + + + def test_api_users_path(self): + + path = reverse("user_detail",args="1") + + # print("the acutal path =>", path) + + self.assertEquals(resolve(path).func.view_class, UserDetail) + response = self.client.get(reverse("user_detail",args="1"),format='json') + + # print(response.data) + # print(response.status_code) + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) #checking if it is 404 + + + def test_api_user_list_path(self): + path = reverse("user_list") + + # print("the acutal path =>", path) + + self.assertEquals(resolve(path).func.view_class, UserList) + + response = self.client.get(reverse("user_detail",args="1"),format='json') + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_api_user_create_path(self): + path = reverse("user_create") + + # print("the acutal path =>", path) + + self.assertEquals(resolve(path).func.view_class, CreateUser) + + response = self.client.post(reverse("user_create"),format='json') + # print(response.data) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_api_user_todo_path(self): + path = reverse("user_todo") + + # print("the acutal path =>", path) + + self.assertEquals(resolve(path).func.view_class, Todos) + + response = self.client.post(reverse("user_todo"),format='json') + # print(response.data) + + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_api_user_todo_operation_path(self): + path = reverse("user_todo_operation",args="1") + + # print("the acutal path =>", path) + + self.assertEquals(resolve(path).func.view_class, TodoOperations) + + response = self.client.post(path,format='json') + # print(response.data) + + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_api_login_path(self): + path = reverse("login") + + # print("the acutal path =>", path) + + self.assertEquals(resolve(path).func.view_class, Login) + + response = self.client.post(reverse("login"),format='json') + # print(response.data) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_api_toke_refresh_path(self): + path = reverse("token_refresh") + + # print("the acutal path =>", path) + + self.assertEquals(resolve(path).func.view_class, RefreshToken) + + response = self.client.post(path,format='json') + # print(response.data) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_serializers.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_serializers.py new file mode 100644 index 0000000..17472fc --- /dev/null +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_serializers.py @@ -0,0 +1,144 @@ +from django.test import TestCase +from api.serializer import UserSerializer,UserSerializerDetails,TodoSerializer +from api.models import Todo +from django.contrib.auth import get_user_model + +User = get_user_model() + +class TestUserSerializers(TestCase): + + + def setUp(self): + + self.user_data = { + "first_name":"TestName", + "last_name":"TestSurname", + "username":"testuser", + "email":"test@gmail.com", + "password":"test12345" + } + + + self.user_serializer = UserSerializer(instance=self.user_data) + + def test_user_serializer_fields(self): + data = self.user_serializer.data + + + self.assertEqual(data['first_name'], self.user_data["first_name"]) + self.assertEqual(data['last_name'], self.user_data["last_name"]) + self.assertEqual(data['username'], self.user_data["username"]) + self.assertEqual(data['password'], self.user_data["password"]) + self.assertEqual(data['first_name'], self.user_data["first_name"]) + + + + def test_validate_password(self): + + + data = self.user_serializer.data + + self.assertTrue(self.user_serializer.validate_password(data["password"]),self.user_data["password"]) + + def test_is_valid(self): + + user_serializer = UserSerializer(data=self.user_data) + + self.assertTrue(user_serializer.is_valid()) + + self.assertTrue(self.user_serializer.create(user_serializer.validated_data)) + + + with self.assertRaises(Exception): + self.assertTrue(user_serializer.save()) + + + + def test_update(self): + initial_data = { + 'first_name': "initial_test", + "last_name": "intial_surname", + "email": "intialtest@gmail.com", + "username": "intialtestuser", + "password": "intial12345" + } + + new_data = { + 'first_name': "new_test", + "last_name": "test_surname", + "email": "test@gmail.com", + "username": "newtestuser", + } + user_ = User.objects.create(**initial_data) + + update_serializer = UserSerializer(instance=user_, data=new_data, partial=True) + + self.assertTrue(update_serializer.is_valid()) + + self.assertTrue(update_serializer.save()) + new_user_db = User.objects.filter(id=1).first() + + + self.assertEqual(new_user_db.username, "newtestuser") + self.assertEqual(new_user_db.last_name,"test_surname") + self.assertEqual(new_user_db.first_name,"new_test") + self.assertEqual(new_user_db.email,"test@gmail.com") + + + + +class TestTodoSerializers(TestCase): + + + def setUp(self): + + self.user_data = { + "first_name":"TestName", + "last_name":"TestSurname", + "username":"testuser", + "email":"test@gmail.com", + "password":"test12345" + } + + self.todo_data = { + "title": "Test Title", + "description": "Test description" + } + + self.user = User.objects.create(**self.user_data) + + + self.todo_data["user"] = self.user + + self.todo_seriralizer = TodoSerializer(instance=self.todo_data) + + def test_todo_serializer_fields(self): + + data = self.todo_seriralizer.data + + self.assertEqual(data['title'],self.todo_data["title"]) + + self.assertEqual(data['description'],self.todo_data["description"]) + + self.assertEqual(data['user'], self.user.id) + + + def test_is_valid(self): + + + self.todo_data["user"] = self.user.id + + todo_serializer = TodoSerializer(data=self.todo_data) + + self.assertTrue(todo_serializer.is_valid()) + + self.assertTrue(todo_serializer.save()) + + + + + + + + + \ No newline at end of file diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/urls.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/urls.py index bfac783..50c8895 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/urls.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/urls.py @@ -5,14 +5,15 @@ urlpatterns = [ - path('users//',UserDetail.as_view()), - path('users/',UserList.as_view()), - path('user/',CreateUser.as_view()), + path('users//',UserDetail.as_view(),name="user_detail"), + path('users/',UserList.as_view(),name="user_list"), + path('user/',CreateUser.as_view(),name="user_create"), + - path("user/todo/",Todos.as_view()),#POST ,#GET all todos - path("user/todo//",TodoOperations.as_view()), #PUT,GET,DELETE specifc + path("user/todos/",Todos.as_view(),name="user_todo"),#POST ,#GET all todos + path("user/todos//",TodoOperations.as_view(),name="user_todo_operation"), #PUT,GET,DELETE specifc - path("login/", Login.as_view()), - path("token-refresh/", RefreshToken.as_view()) + path("login/", Login.as_view(),name="login"), + path("token-refresh/", RefreshToken.as_view(),name="token_refresh") ] diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py index 5909ef6..d79d97c 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py @@ -63,7 +63,7 @@ def get(self, request, pk): user_data = get_object_or_404(User, pk=pk) - print(user_data) + # print(user_data) data = UserSerializerDetails(user_data).data return Response(data) @@ -192,7 +192,7 @@ def get(self,request,pk): token = request.headers.get("Authorization").split(" ")[1] details = jwt_decode_handler(token) - print(details) + # print(details) if not (User.objects.filter(id=details.get("user_id")).last() and Todo.objects.filter(id=pk).last()): return Response({"message": "No such a user or todo"},status=status.HTTP_404_NOT_FOUND) @@ -210,15 +210,17 @@ def get(self,request,pk): class Login(TokenObtainPairView): def post(self, request, *args, **kwargs): + + data = super().post(request, *args, **kwargs) data = data.data - print(data) + # print(data) acces_token = jwt_decode_handler(data.get("access")) ref = jwt_decode_handler(data.get("refresh")) - print(ref) - print(acces_token) + # print(ref) + # print(acces_token) if not User.objects.filter(id=acces_token.get("user_id")).last(): return Response({"error": True,"message": "No such a user"},status=status.HTTP_404_NOT_FOUND) todos = Todo.objects.filter(user=acces_token.get("user_id")).all() @@ -240,10 +242,10 @@ def post(self, request, *args, **kwargs): data = super().post(request, *args, **kwargs) new_token = data.data - print(new_token) + # print(new_token) acces_token = jwt_decode_handler(new_token.get("access")) ref = jwt_decode_handler(new_token.get("refresh")) - print(ref) - print(acces_token) + # print(ref) + # print(acces_token) return data \ No newline at end of file diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py b/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py index 8aaf39d..3134b34 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py @@ -99,6 +99,12 @@ 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_simplejwt.authentication.JWTAuthentication', ), + 'TEST_REQUEST_DEFAULT_FORMAT': 'json', + 'TEST_REQUEST_RENDERER_CLASSES': [ + 'rest_framework.renderers.MultiPartRenderer', + 'rest_framework.renderers.JSONRenderer', + 'rest_framework.renderers.TemplateHTMLRenderer' + ] } ''' From b6a68886ccd5a1e6e62fbc8bff96475bd0819158 Mon Sep 17 00:00:00 2001 From: sabuhish Date: Mon, 1 Jun 2020 22:42:03 +0400 Subject: [PATCH 05/13] pytest has been added to test case --- .../django_restframework/api/tests/test_api_views.py | 2 -- .../RestDjango/django_restframework/api/tests/test_models.py | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_api_views.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_api_views.py index 3b01738..346b601 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_api_views.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_api_views.py @@ -7,7 +7,6 @@ from api.models import Todo from rest_framework.test import APITestCase from django.urls import reverse - User = get_user_model() @@ -170,7 +169,6 @@ def test_token_refresh(self): response_refresh = self.client.post(path_token_refresh,{"refresh":response_login.get("refresh")},format='json') - print("json",response_refresh.json()) self.assertEqual(response.status_code, 200) diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_models.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_models.py index 9033f76..b7a3475 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_models.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_models.py @@ -17,6 +17,8 @@ # 3) Functional test is actullay testing applications from end user point view, basicly it is done by selinum,what user will type or fill gthe form +# + from django.test import TestCase from django.contrib.auth import get_user_model from api.models import Todo @@ -32,7 +34,7 @@ def setUp(self): def test_user_model(self): - + self.assertEqual(self.user.username, "testuser") self.assertEqual(self.user.email,"test@gmail.com") From 574d4724e9bc724014983b4fadf98ba9cf71232b Mon Sep 17 00:00:00 2001 From: sabuhish Date: Mon, 1 Jun 2020 22:42:21 +0400 Subject: [PATCH 06/13] pytest has been added to test case --- .../api/pytest/__init__.py | 0 .../api/pytest/api_views_pytest.py | 320 ++++++++++++++++++ .../api/pytest/function_pytest.py | 19 ++ .../api/pytest/model_pytest.py | 63 ++++ .../django_restframework/api/pytest/pytest.py | 37 ++ .../api/pytest/serializers_pytest.py | 175 ++++++++++ .../api/pytest/urls_pytest.py | 71 ++++ .../django_restframework/pytest.ini | 5 + 8 files changed, 690 insertions(+) create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/__init__.py create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/function_pytest.py create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/model_pytest.py create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/pytest.py create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/serializers_pytest.py create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/urls_pytest.py create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/pytest.ini diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/__init__.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py new file mode 100644 index 0000000..9571ca1 --- /dev/null +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py @@ -0,0 +1,320 @@ +from api.views import CreateUser,UserList,UserDetail, Todos,TodoOperations,Login,RefreshToken +from django.contrib.auth import get_user_model +from api.serializer import UserSerializer +from django.test import TestCase, Client +from rest_framework.test import APIRequestFactory +from rest_framework.test import APIClient +from api.models import Todo +from rest_framework.test import APITestCase +from django.urls import reverse +import pytest +User = get_user_model() + + + +@pytest.fixture +def setup(): + data = { + "user":{ + "first_name": "test_user", + "username":"testname", + "password":"test12345", + "email":"test@gmail.com", + "last_name":"testlastname" + }, + "user_2":{ + "first": "test_user", + "username":"testname", + "password":"test12345", + "email":"test@gmailcom", + "last_name":"testlastname"} + } + return data + + + + +@pytest.mark.django_db +def create_user_test(client,setup): + + path = reverse("user_create") + + response = client.post(path,setup.get("user"),format='json') + + assert response.status_code == 201 + + response_2 = client.post(path, setup.get("user"), format='json') + + assert response_2.status_code == 400 + + user = User.objects.filter(id=1).first() + + assert user.first_name == "test_user" + assert user.username == "testname" + assert user.email == "test@gmail.com" + +@pytest.mark.django_db +def user_list_test(client): + + path = reverse("user_list") + + response = client.get(path) + + assert response.status_code == 200 + + + +def user_detail_test(client): + user = User.objects.create( + first_name="test_user", + username="testname", + password="test12345", + email="test@gmail.com", + last_name="testlastname" + ) + + path = reverse("user_detail",args="1") + + + response = client.get(path,format='json') + + assert response.status_code == 200 + + assert user.first_name == response.json().get("first_name") + assert user.username == response.json().get("username") + assert user.email == response.json().get("email") + assert user.last_name == response.json().get("last_name") + + + +def user_detail_test(client): + path = reverse("user_detail",args="1") + + new_user = User.objects.create( + first_name="test_user", + username="testname", + password="test12345", + email="test@gmail.com", + last_name="testlastname" + ) + data = {"first_name": "TuralYek"} + + + response = client.put(path,data,format='json') + + + edited_user = User.objects.filter(id=1).first() + + assert response.status_code == 200 + + assert edited_user.first_name == response.json().get("first_name") + + +@pytest.mark.django_db +def user_detail_test(client): + + new_user = User.objects.create( + first_name="test_user", + username="testname", + password="test12345", + email="test@gmail.com", + last_name="testlastname" + ) + + path = reverse("user_detail",args="1") + + response = client.delete(path,format='json') + assert response.status_code == 200 + + + assert User.objects.filter(id=1).first() == None + + +@pytest.mark.django_db +def login_test(client): + user = User.objects.create_user( + username="test_user", + password="test_password", + ) + + path = reverse("login") + + response = client.post(path,{"username":"test_user","password":"test_password"},format='json') + + + assert response.status_code == 200 + assert ['access'] == [k for k,v in response.data.items() if k =="access" ] + + + assert ['refresh'] == [k for k,v in response.data.items() if k =="refresh" ] + +@pytest.mark.django_db +def missingfields_test(client): + + path = reverse("login") + + response = client.post(path,format='json') + + assert response.status_code == 400 + +@pytest.mark.django_db +def token_refresh_test(client): + user = User.objects.create_user( + username="test_user", + password="test_password") + + path_login = reverse("login") + path_token_refresh = reverse("token_refresh") + + response = client.post(path_login,{"username":"test_user","password":"test_password"},format='json') + + response_login = response.json() + + response_refresh = client.post(path_token_refresh,{"refresh":response_login.get("refresh")},format='json') + + + assert response.status_code == 200 + + assert response_refresh.status_code == 200 + + + +@pytest.fixture +def user_setUp(client): + + user = User.objects.create_user( + username="test_user", + password="test_password", + ) + path_login = reverse("login") + + response = client.post(path_login,{"username":"test_user","password":"test_password"},format='json') + + return response.json() + +@pytest.mark.django_db +def post_todo_test(user_setUp): + + client = APIClient() + + path = reverse("user_todo") + + data = { + "title": "Test Title", + "description": "Test description" + } + + client.credentials(HTTP_AUTHORIZATION=f"Bearer {user_setUp.get('access')}") + + res = client.post(path,data,format='json') + + + assert res.status_code == 201 + + assert res.json().get("title") == data["title"] + assert res.json().get("description") == data["description"] + + + +@pytest.mark.django_db +def get_all_todos_test(user_setUp): + client = APIClient() + + path = reverse("user_todo") + + client.credentials(HTTP_AUTHORIZATION=f"Bearer {user_setUp.get('access')}") + + res = client.get(path,format='json') + + assert res.status_code == 200 + + +@pytest.mark.django_db +def todo_operation_test(user_setUp): + client = APIClient() + + path = reverse("user_todo_operation",args="1") + + user_todo = reverse("user_todo") + + data = { + "title": "Test Title", + "description": "Test description" + } + + client.credentials(HTTP_AUTHORIZATION=f"Bearer {user_setUp.get('access')}") + + res = client.post(user_todo,data,format='json') + + + client.credentials(HTTP_AUTHORIZATION=f"Bearer {user_setUp.get('access')}") + + res = client.get(path,format='json') + + assert res.status_code == 200 + assert res.json().get("id") == 1 + + +@pytest.mark.django_db +def test_todo_operation_test(user_setUp): + client = APIClient() + + path = reverse("user_todo_operation",args="1") + + user_todo = reverse("user_todo") + + data_1 = { + "title": "Test Title", + "description": "Test description" + } + + data_2 = { + "title": "Test Title2", + "description": "Test description2" + } + + client.credentials(HTTP_AUTHORIZATION=f"Bearer {user_setUp.get('access')}") + + res = client.post(user_todo,data_1,format='json') + + + client.credentials(HTTP_AUTHORIZATION=f"Bearer {user_setUp.get('access')}") + + res = client.get(path,format='json') + assert res.json().get("title") == data_1["title"] + + res_2 = client.put(path,data_2,format='json') + + + assert res_2.json().get("title") == data_2["title"] + + assert res.status_code == 200 + +@pytest.mark.django_db +def todo_operation_test(user_setUp): + client = APIClient() + + path = reverse("user_todo_operation",args="1") + + client.credentials(HTTP_AUTHORIZATION=f"Bearer {user_setUp.get('access')}") + + res = client.delete(path,format='json') + + assert res.status_code == 404 + + + + + + + + + + + + + + + + + diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/function_pytest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/function_pytest.py new file mode 100644 index 0000000..89bfc1d --- /dev/null +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/function_pytest.py @@ -0,0 +1,19 @@ +import pytest +from api.utils import jwt_decode_handler +from django.conf import settings +from jwt.exceptions import ExpiredSignatureError + + + +def jwt_test(): + + token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTkwNTczMzIzLCJqdGkiOiI2YjVkZDBhOGU0ZDE0YzMzODQwM2ZiZWFiZjE3ZmE0MSIsInVzZXJfaWQiOjEwfQ.WZFnpB805o045MJnzqhsMit6p30mu-2TVgA6oitYWI0" + result = {'token_type': 'access', 'exp': 1590573323, 'jti': '6b5dd0a8e4d14c338403fbeabf17fa41', 'user_id': 10} + + with pytest.raises(ExpiredSignatureError) as expired: + + + assert jwt_decode_handler(token)== result + + assert expired.type == ExpiredSignatureError + \ No newline at end of file diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/model_pytest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/model_pytest.py new file mode 100644 index 0000000..e89d7f1 --- /dev/null +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/model_pytest.py @@ -0,0 +1,63 @@ +import pytest +from django.db import models +from django.contrib.auth import get_user_model +from api.models import Todo +from django.db.utils import IntegrityError + +User = get_user_model() + +''' + +You can use pytest marks to tell pytest-django your test needs database access: +import pytest + +It is also possible to mark all tests in a class or module at once. This demonstrates all the ways of marking, even +though they overlap. Just one of these marks would have been sufficient. See t +''' + +@pytest.fixture +def created_user(): + + user = User.objects.create(username="testuser", email="test@gmail.com") + return user + + +@pytest.fixture +@pytest.mark.django_db +def create_todo(created_user): + todo = Todo.objects.create( + user=created_user, + title="Test title", + description="Test description" + ) + return todo + + +@pytest.mark.django_db +def my_user_test(created_user): + + assert created_user.username == "testuser" + + assert created_user.email == "test@gmail.com" + + + +@pytest.mark.django_db +def todo_model_test(create_todo): + + assert create_todo.user.username == "testuser" + assert create_todo.title == "Test title" + assert create_todo.description == "Test description" + + + + + +@pytest.mark.django_db +def new_test(created_user): + with pytest.raises(IntegrityError) as error: + user = User.objects.create(username="testuser", email="test@gmail.com") + + assert user.username == "testuser" + + assert error.type == IntegrityError \ No newline at end of file diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/pytest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/pytest.py new file mode 100644 index 0000000..9df55ab --- /dev/null +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/pytest.py @@ -0,0 +1,37 @@ +import pytest + + +# You can test your Django application without using a Library but pytest offers some features that are not present in Django’s standard test mechanism: : + +# Detailed info on failing assert statements (no need to remember self.assert* names); +# Auto-discovery of test modules and functions; +# Modular fixtures for managing small or parametrized long-lived test resources; +# Can run unit test (including trial) and nose test suites out of the box; +#to run test with multiple markeers use dec + +#wherner we need to run some code before test we use pytets fixture + +# def func(x): +# return x + 5 + + +# def test_method(): +# assert func(3) ==8 + + +# pytest.fxiture replament deye bilerik setupdaki eslinde + + +@pytest.fixture +def numbers(): + a = 10 + b = 15 + c = 10 + return [a,b,c] + +#so below two methods is going to be used for oyfuxture + +# @pytest.mark.skip #this will skip the test +def method_test(numbers): + x = 13 + assert numbers[1] == 15 \ No newline at end of file diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/serializers_pytest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/serializers_pytest.py new file mode 100644 index 0000000..e5d07fc --- /dev/null +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/serializers_pytest.py @@ -0,0 +1,175 @@ +import pytest +from api.serializer import UserSerializer,UserSerializerDetails,TodoSerializer +from api.models import Todo +from django.contrib.auth import get_user_model +from django.db.utils import IntegrityError + +User = get_user_model() + + + + + + +@pytest.fixture +def user_serlize(): + user_data = { + "first_name":"TestName", + "last_name":"TestSurname", + "username":"testuser", + "email":"test@gmail.com", + "password":"test12345" + } + + + user_serializer = UserSerializer(instance=user_data) + return user_serializer + +@pytest.fixture +def data(): + user_data = { + "first_name":"TestName", + "last_name":"TestSurname", + "username":"testuser", + "email":"test@gmail.com", + "password":"test12345" + } + return user_data + + + +def check_test(user_serlize): + + + assert user_serlize.data['first_name'] == "TestName" + assert user_serlize.data['last_name'] == "TestSurname" + assert user_serlize.data['username'] == "testuser" + assert user_serlize.data['email'] == "test@gmail.com" + assert user_serlize.data['password'] == "test12345" + + + +def validate_password_test(user_serlize): + + + assert user_serlize.validate_password(user_serlize.data['password']) == user_serlize.data['password'] + +@pytest.mark.django_db +def is_valid_test(data): + + + user_serializer = UserSerializer(data=data) + + assert user_serializer.is_valid() == True + + assert user_serializer.create(user_serializer.validated_data) ==User.objects.filter(id=1).first() + + with pytest.raises(Exception) as error: + + user_serializer.save() + + assert user.username == "testuser" + + assert error.type == IntegrityError + + +@pytest.fixture +def update_func(): + temp = [{ + 'first_name': "initial_test", + "last_name": "intial_surname", + "email": "intialtest@gmail.com", + "username": "intialtestuser", + "password": "intial12345" + }, + { + 'first_name': "new_test", + "last_name": "test_surname", + "email": "test@gmail.com", + "username": "newtestuser", + }] + + + return temp + + +@pytest.mark.django_db +def update_test(update_func): + + user_ = User.objects.create(**update_func[0]) + + update_serializer = UserSerializer(instance=user_, data=update_func[1], partial=True) + + assert update_serializer.is_valid() == True + + assert update_serializer.save() == User.objects.filter(id=1).first() + new_user_db = User.objects.filter(id=1).first() + + + assert new_user_db.username == "newtestuser" + assert new_user_db.last_name == "test_surname" + assert new_user_db.first_name =="new_test" + assert new_user_db.email == "test@gmail.com" + + + + + + +@pytest.fixture +def saved_user(): + user_data = { + "first_name":"TestName", + "last_name":"TestSurname", + "username":"testuser", + "email":"test@gmail.com", + "password":"test12345" + } + + + + user = User.objects.create(**user_data) + + return user + + + +@pytest.fixture +@pytest.mark.django_db +def todo_fucntion(saved_user): + todo_data = { + "title": "Test Title", + "description": "Test description" + } + + todo_data["user"] = saved_user + + todo = todo_seriralizer = TodoSerializer(instance=todo_data) + + return todo + + +@pytest.mark.django_db +def todo_serializer_fields_test(todo_fucntion): + + + assert todo_fucntion.data['title'] == "Test Title" + + assert todo_fucntion.data['description'] == "Test description" + + assert todo_fucntion.data['user'] == User.objects.get(id=1).id + + +@pytest.mark.django_db +def is_valid_test(todo_fucntion): + + todo_serializer = TodoSerializer(data={"title":"Test Title", "description": "Test description" }) + + assert todo_serializer.is_valid() == True + + assert todo_serializer.save() == Todo.objects.get(id=1) + + + + + diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/urls_pytest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/urls_pytest.py new file mode 100644 index 0000000..d69c552 --- /dev/null +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/urls_pytest.py @@ -0,0 +1,71 @@ + +import pytest +from django.urls import reverse, resolve + + + +''' +client fixture is included with django, client instance passed in whenever you add this as argument to your test funtions. BTW you can create your own fixture as well below an example: + +@pytest.fixture +def fixture_test(): + return "Hello World!" + +So the fucntion above, will allow us fixture_test add as an argument to any test function in our testsute So you can put that globally to acces accros project. + +fixetures are powerfull +''' + + +@pytest.mark.django_db +def api_users_test(client): + + path = reverse("user_detail",args="1") + response = client.get(path) + assert response.status_code == 404 + + assert resolve(path).func.view_class, UserDetail + + + +@pytest.mark.django_db +def test_api_user_list_test(client): + path = reverse("user_create") + + response = client.post(path) + + assert resolve(path).func.view_class, CreateUser + + assert response.status_code == 400 + +# @pytest.mark.django_db +def api_user_todo_operation_test(client): + path = reverse("user_todo_operation",args="1") + + response = client.post(path) + + assert response.status_code == 401 + assert resolve(path).func.view_class, TodoOperations + + +# @pytest.mark.django_db +def api_login_path_test(client): + path = reverse("login") + + response = client.post(path) + + assert response.status_code == 400 + assert resolve(path).func.view_class, Login + + print("the acutal path =>", path) + + +# @pytest.mark.django_db +def api_toke_refresh_path_test(client): + path = reverse("token_refresh") + + response = client.post(path) + + assert resolve(path).func.view_class, RefreshToken + + assert response.status_code == 400 \ No newline at end of file diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/pytest.ini b/Project1_Simple_REST_API/RestDjango/django_restframework/pytest.ini new file mode 100644 index 0000000..cc12879 --- /dev/null +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +DJANGO_SETTINGS_MODULE = restApi.settings +python_files = *pytest.py +python_classes = *_test +python_functions = *_test \ No newline at end of file From 78c51dc214cc336827a8d60ab17557ac8d64789c Mon Sep 17 00:00:00 2001 From: sabuhish Date: Mon, 1 Jun 2020 23:11:25 +0400 Subject: [PATCH 07/13] commit has been fixed --- .../RestDjango/django_restframework/.gitignore | 2 +- .../api/pytest/api_views_pytest.py | 2 +- .../RestDjango/django_restframework/api/urls.py | 12 ++---------- .../RestDjango/django_restframework/api/views.py | 10 +++++----- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/.gitignore b/Project1_Simple_REST_API/RestDjango/django_restframework/.gitignore index c7a0414..5cd64c2 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/.gitignore +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/.gitignore @@ -107,7 +107,7 @@ celerybeat.pid # SageMath parsed files *.sage.py - +../.vscode/ # Environments .venv env/ diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py index 9571ca1..937a6b8 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py @@ -23,7 +23,7 @@ def setup(): "last_name":"testlastname" }, "user_2":{ - "first": "test_user", + "first": "test_user", "username":"testname", "password":"test12345", "email":"test@gmailcom", diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/urls.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/urls.py index 01d5d0d..8c3ffaa 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/urls.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/urls.py @@ -1,8 +1,6 @@ from django.urls import path from .views import UserDetail,UserList,CreateUser, Todos, TodoOperations,Login,RefreshToken - -from .views import UserDetail,UserList,CreateUser, Todos, TodoOperations,Login - +from rest_framework_simplejwt.views import token_refresh urlpatterns = [ @@ -18,10 +16,4 @@ path("login/", Login.as_view(),name="login"), path("token-refresh/", RefreshToken.as_view(),name="token_refresh") - path("user/todo/",Todos.as_view()),#POST ,#GET all todos - path("user/todo//",TodoOperations.as_view()), #PUT,GET,DELETE specifc - - path("login/", Login.as_view()), - path("token-refresh/", token_refresh) - -] +] \ No newline at end of file diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py index 97c46eb..d79d97c 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/views.py @@ -192,7 +192,7 @@ def get(self,request,pk): token = request.headers.get("Authorization").split(" ")[1] details = jwt_decode_handler(token) - + # print(details) if not (User.objects.filter(id=details.get("user_id")).last() and Todo.objects.filter(id=pk).last()): return Response({"message": "No such a user or todo"},status=status.HTTP_404_NOT_FOUND) @@ -212,15 +212,15 @@ class Login(TokenObtainPairView): def post(self, request, *args, **kwargs): ->>>>>> master data = super().post(request, *args, **kwargs) data = data.data + # print(data) acces_token = jwt_decode_handler(data.get("access")) ref = jwt_decode_handler(data.get("refresh")) - acces_token = jwt_decode_handler(data.get("access")) - + # print(ref) + # print(acces_token) if not User.objects.filter(id=acces_token.get("user_id")).last(): return Response({"error": True,"message": "No such a user"},status=status.HTTP_404_NOT_FOUND) todos = Todo.objects.filter(user=acces_token.get("user_id")).all() @@ -248,4 +248,4 @@ def post(self, request, *args, **kwargs): # print(ref) # print(acces_token) - return Response(data) + return data \ No newline at end of file From 60b95be24390a09d1643487b36b379f872e1595b Mon Sep 17 00:00:00 2001 From: sabuhish Date: Tue, 2 Jun 2020 11:12:05 +0400 Subject: [PATCH 08/13] conftest has been added --- .../api/pytest/api_views_pytest.py | 34 +---------------- .../api/pytest/model_pytest.py | 15 +------- .../django_restframework/api/pytest/pytest.py | 37 ------------------- .../api/pytest/serializers_pytest.py | 25 ------------- .../api/pytest/urls_pytest.py | 33 +++++++++-------- .../api/tests/test_api_views.py | 3 +- .../api/tests/test_models.py | 1 - 7 files changed, 21 insertions(+), 127 deletions(-) delete mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/pytest.py diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py index 937a6b8..7f393a4 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py @@ -12,27 +12,6 @@ -@pytest.fixture -def setup(): - data = { - "user":{ - "first_name": "test_user", - "username":"testname", - "password":"test12345", - "email":"test@gmail.com", - "last_name":"testlastname" - }, - "user_2":{ - "first": "test_user", - "username":"testname", - "password":"test12345", - "email":"test@gmailcom", - "last_name":"testlastname"} - } - return data - - - @pytest.mark.django_db def create_user_test(client,setup): @@ -143,6 +122,7 @@ def login_test(client): assert response.status_code == 200 + assert ['access'] == [k for k,v in response.data.items() if k =="access" ] @@ -179,18 +159,6 @@ def token_refresh_test(client): -@pytest.fixture -def user_setUp(client): - - user = User.objects.create_user( - username="test_user", - password="test_password", - ) - path_login = reverse("login") - - response = client.post(path_login,{"username":"test_user","password":"test_password"},format='json') - - return response.json() @pytest.mark.django_db def post_todo_test(user_setUp): diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/model_pytest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/model_pytest.py index e89d7f1..5e5d521 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/model_pytest.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/model_pytest.py @@ -1,5 +1,6 @@ import pytest from django.db import models +from django.db.utils import IntegrityError from django.contrib.auth import get_user_model from api.models import Todo from django.db.utils import IntegrityError @@ -15,22 +16,8 @@ though they overlap. Just one of these marks would have been sufficient. See t ''' -@pytest.fixture -def created_user(): - - user = User.objects.create(username="testuser", email="test@gmail.com") - return user -@pytest.fixture -@pytest.mark.django_db -def create_todo(created_user): - todo = Todo.objects.create( - user=created_user, - title="Test title", - description="Test description" - ) - return todo @pytest.mark.django_db diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/pytest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/pytest.py deleted file mode 100644 index 9df55ab..0000000 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/pytest.py +++ /dev/null @@ -1,37 +0,0 @@ -import pytest - - -# You can test your Django application without using a Library but pytest offers some features that are not present in Django’s standard test mechanism: : - -# Detailed info on failing assert statements (no need to remember self.assert* names); -# Auto-discovery of test modules and functions; -# Modular fixtures for managing small or parametrized long-lived test resources; -# Can run unit test (including trial) and nose test suites out of the box; -#to run test with multiple markeers use dec - -#wherner we need to run some code before test we use pytets fixture - -# def func(x): -# return x + 5 - - -# def test_method(): -# assert func(3) ==8 - - -# pytest.fxiture replament deye bilerik setupdaki eslinde - - -@pytest.fixture -def numbers(): - a = 10 - b = 15 - c = 10 - return [a,b,c] - -#so below two methods is going to be used for oyfuxture - -# @pytest.mark.skip #this will skip the test -def method_test(numbers): - x = 13 - assert numbers[1] == 15 \ No newline at end of file diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/serializers_pytest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/serializers_pytest.py index e5d07fc..aacfbd1 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/serializers_pytest.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/serializers_pytest.py @@ -11,31 +11,6 @@ -@pytest.fixture -def user_serlize(): - user_data = { - "first_name":"TestName", - "last_name":"TestSurname", - "username":"testuser", - "email":"test@gmail.com", - "password":"test12345" - } - - - user_serializer = UserSerializer(instance=user_data) - return user_serializer - -@pytest.fixture -def data(): - user_data = { - "first_name":"TestName", - "last_name":"TestSurname", - "username":"testuser", - "email":"test@gmail.com", - "password":"test12345" - } - return user_data - def check_test(user_serlize): diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/urls_pytest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/urls_pytest.py index d69c552..e893bc9 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/urls_pytest.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/urls_pytest.py @@ -2,7 +2,10 @@ import pytest from django.urls import reverse, resolve +from django.contrib.auth import get_user_model +from api.models import Todo +User = get_user_model() ''' client fixture is included with django, client instance passed in whenever you add this as argument to your test funtions. BTW you can create your own fixture as well below an example: @@ -40,32 +43,32 @@ def test_api_user_list_test(client): # @pytest.mark.django_db def api_user_todo_operation_test(client): - path = reverse("user_todo_operation",args="1") + path = reverse("user_todo_operation",args="1") - response = client.post(path) + response = client.post(path) - assert response.status_code == 401 - assert resolve(path).func.view_class, TodoOperations + assert response.status_code == 401 + assert resolve(path).func.view_class, TodoOperations # @pytest.mark.django_db def api_login_path_test(client): - path = reverse("login") - - response = client.post(path) + path = reverse("login") + + response = client.post(path) - assert response.status_code == 400 - assert resolve(path).func.view_class, Login + assert response.status_code == 400 + assert resolve(path).func.view_class, Login - print("the acutal path =>", path) + print("the acutal path =>", path) # @pytest.mark.django_db def api_toke_refresh_path_test(client): - path = reverse("token_refresh") - - response = client.post(path) + path = reverse("token_refresh") + + response = client.post(path) - assert resolve(path).func.view_class, RefreshToken + assert resolve(path).func.view_class, RefreshToken - assert response.status_code == 400 \ No newline at end of file + assert response.status_code == 400 \ No newline at end of file diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_api_views.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_api_views.py index 346b601..9153138 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_api_views.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_api_views.py @@ -11,7 +11,6 @@ - class TestUserViews(APITestCase): def setUp(self): @@ -40,7 +39,7 @@ def test_create_user(self): response = self.client.post(path,self.user,format='json') self.assertEqual(response.status_code, 201) - + response_2 = self.client.post(path,self.user,format='json') self.assertEqual(response_2.status_code, 400) diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_models.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_models.py index b7a3475..e032274 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_models.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/tests/test_models.py @@ -17,7 +17,6 @@ # 3) Functional test is actullay testing applications from end user point view, basicly it is done by selinum,what user will type or fill gthe form -# from django.test import TestCase from django.contrib.auth import get_user_model From 0b27530d7409070ac35facc8b66d213ea85676d5 Mon Sep 17 00:00:00 2001 From: sabuhish Date: Tue, 2 Jun 2020 11:12:32 +0400 Subject: [PATCH 09/13] file conftest has been added --- .../api/pytest/conftest.py | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/conftest.py diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/conftest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/conftest.py new file mode 100644 index 0000000..55eb478 --- /dev/null +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/conftest.py @@ -0,0 +1,98 @@ +import pytest +from api.serializer import UserSerializer +from django.contrib.auth import get_user_model +from api.models import Todo +from django.db.utils import IntegrityError +from django.urls import reverse, resolve + +User = get_user_model() +# You can test your Django application without using a Library but pytest offers some features that are not present in Django’s standard test mechanism: : + +# Detailed info on failing assert statements (no need to remember self.assert* names); +# Auto-discovery of test modules and functions; +# Modular fixtures for managing small or parametrized long-lived test resources; +# Can run unit test (including trial) and nose test suites out of the box; +#to run test with multiple markeers use dec + +#wherner we need to run some code before test we use pytets fixture + +@pytest.fixture +def created_user(): + + user = User.objects.create(username="testuser", email="test@gmail.com") + return user + + + +@pytest.fixture +@pytest.mark.django_db +def create_todo(created_user): + todo = Todo.objects.create( + user=created_user, + title="Test title", + description="Test description" + ) + return todo + + + +@pytest.fixture +def user_serlize(): + user_data = { + "first_name":"TestName", + "last_name":"TestSurname", + "username":"testuser", + "email":"test@gmail.com", + "password":"test12345" + } + + + user_serializer = UserSerializer(instance=user_data) + return user_serializer + +@pytest.fixture +def data(): + user_data = { + "first_name":"TestName", + "last_name":"TestSurname", + "username":"testuser", + "email":"test@gmail.com", + "password":"test12345" + } + return user_data + + +@pytest.fixture +def user_setUp(client): + + user = User.objects.create_user( + username="test_user", + password="test_password", + ) + path_login = reverse("login") + + response = client.post(path_login,{"username":"test_user","password":"test_password"},format='json') + + return response.json() + + + +@pytest.fixture +def setup(): + data = { + "user":{ + "first_name": "test_user", + "username":"testname", + "password":"test12345", + "email":"test@gmail.com", + "last_name":"testlastname" + }, + "user_2":{ + "first": "test_user", + "username":"testname", + "password":"test12345", + "email":"test@gmailcom", + "last_name":"testlastname"} + } + return data + From 72bc2498ba145bd220d7a255d4f86d9bd68db99e Mon Sep 17 00:00:00 2001 From: sabuhish Date: Tue, 2 Jun 2020 11:27:14 +0400 Subject: [PATCH 10/13] warnings has been removed --- .../RestDjango/django_restframework/api/pytest/conftest.py | 3 ++- .../RestDjango/django_restframework/pytest.ini | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/conftest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/conftest.py index 55eb478..f81d46d 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/conftest.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/conftest.py @@ -6,8 +6,9 @@ from django.urls import reverse, resolve User = get_user_model() -# You can test your Django application without using a Library but pytest offers some features that are not present in Django’s standard test mechanism: : +#wherner we need to run some code before test we use pytets fixture +# You can test your Django application without using a Library but pytest offers some features that are not present in Django’s standard test mechanism: : # Detailed info on failing assert statements (no need to remember self.assert* names); # Auto-discovery of test modules and functions; # Modular fixtures for managing small or parametrized long-lived test resources; diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/pytest.ini b/Project1_Simple_REST_API/RestDjango/django_restframework/pytest.ini index cc12879..4eb4ce4 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/pytest.ini +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/pytest.ini @@ -2,4 +2,5 @@ DJANGO_SETTINGS_MODULE = restApi.settings python_files = *pytest.py python_classes = *_test -python_functions = *_test \ No newline at end of file +python_functions = *_test +addopts = -p no:warnings From bc0da6f146761d8e4805ecb98e3f7fcb5c1e980a Mon Sep 17 00:00:00 2001 From: sabuhish Date: Thu, 4 Jun 2020 12:49:33 +0400 Subject: [PATCH 11/13] token has been added for chekc --- .../api/pytest/api_views_pytest.py | 56 ++++++++++++++++++- .../api/pytest/conftest.py | 2 + .../django_restframework/restApi/settings.py | 2 +- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py index 7f393a4..f91b492 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py @@ -8,6 +8,7 @@ from rest_framework.test import APITestCase from django.urls import reverse import pytest +import time User = get_user_model() @@ -110,7 +111,9 @@ def user_detail_test(client): @pytest.mark.django_db -def login_test(client): +def login_test(): + client = APIClient() + user = User.objects.create_user( username="test_user", password="test_password", @@ -128,6 +131,57 @@ def login_test(client): assert ['refresh'] == [k for k,v in response.data.items() if k =="refresh" ] + time.sleep(5) + + todo_path = reverse("user_todo") + + data = { + "title": "Test Title", + "description": "Test description" + } + + client.credentials(HTTP_AUTHORIZATION=f"Bearer {response.get('access')}") + + res = client.post(todo_path,data,format='json') + + + assert res.status_code == 401 + + assert res.json().get("detail") == "Given token not valid for any token type" + assert res.json().get("messages") == [{'message': 'Token is invalid or expired', 'token_class': 'AccessToken', 'token_type': 'access'}] + + refresh_path = reverse("token_refresh") + + + response_refresh = client.post(refresh_path,{"refresh":response.json().get("refresh")},format='json') + + assert response_refresh.status_code == 200 + + + data = { + "title": "Test Title", + "description": "Test description" + } + + client.credentials(HTTP_AUTHORIZATION=f"Bearer {response_refresh.json().get('access')}") + + response_after_Refresh = client.post(todo_path,data,format='json') + + assert response_after_Refresh.status_code == 201 + + client.credentials(HTTP_AUTHORIZATION=f"Bearer {response_refresh.json().get('refresh')}") + + another_Request = client.post(todo_path,data,format='json') + + assert another_Request.status_code == 401 + + assert another_Request.json().get("detail") == "Given token not valid for any token type" + assert another_Request.json().get("code") == "token_not_valid" + assert another_Request.json().get("messages") == [{'token_class': 'AccessToken', 'token_type': 'access', 'message': 'Token has wrong type'}] + + + + @pytest.mark.django_db def missingfields_test(client): diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/conftest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/conftest.py index f81d46d..55de53a 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/conftest.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/conftest.py @@ -17,6 +17,8 @@ #wherner we need to run some code before test we use pytets fixture +# A number of third-party testing frameworks attempt to address some of the issues with unittest, and pytest has proven to be one of the most popular. pytest is a feature-rich, plugin-based ecosystem for testing your Python code. + @pytest.fixture def created_user(): diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py b/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py index 94c8456..8fd2377 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/restApi/settings.py @@ -87,7 +87,7 @@ 'TOKEN_TYPE_CLAIM': 'token_type', #The claim name that is used to store a token’s type 'JTI_CLAIM': 'jti', #The claim name that is used to store a token’s unique identifier. - 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), #which specifies how long access tokens are valid + 'ACCESS_TOKEN_LIFETIME': timedelta(seconds=3), #which specifies how long access tokens are valid 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), # how long refresh tokens are valid. } From b00e7418e3392ec8f9453affa625363757b99499 Mon Sep 17 00:00:00 2001 From: sabuhish Date: Thu, 4 Jun 2020 20:03:42 +0400 Subject: [PATCH 12/13] test --- .../django_restframework/api/pytest/api_views_pytest.py | 1 + .../RestDjango/django_restframework/pytest.ini | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py index f91b492..ca1286f 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/api/pytest/api_views_pytest.py @@ -32,6 +32,7 @@ def create_user_test(client,setup): assert user.first_name == "test_user" assert user.username == "testname" assert user.email == "test@gmail.com" + @pytest.mark.django_db def user_list_test(client): diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/pytest.ini b/Project1_Simple_REST_API/RestDjango/django_restframework/pytest.ini index 4eb4ce4..de2f4a2 100644 --- a/Project1_Simple_REST_API/RestDjango/django_restframework/pytest.ini +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/pytest.ini @@ -1,5 +1,5 @@ [pytest] -DJANGO_SETTINGS_MODULE = restApi.settings +DJANGO_SETTINGS_MODULE = testApi.settings python_files = *pytest.py python_classes = *_test python_functions = *_test From 02af89081d59e6023adc719e1931bfcbbe6001ef Mon Sep 17 00:00:00 2001 From: sabuhish Date: Thu, 4 Jun 2020 20:04:19 +0400 Subject: [PATCH 13/13] test --- .../django_restframework/testApi/__init__.py | 0 .../django_restframework/testApi/settings.py | 90 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/testApi/__init__.py create mode 100644 Project1_Simple_REST_API/RestDjango/django_restframework/testApi/settings.py diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/testApi/__init__.py b/Project1_Simple_REST_API/RestDjango/django_restframework/testApi/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Project1_Simple_REST_API/RestDjango/django_restframework/testApi/settings.py b/Project1_Simple_REST_API/RestDjango/django_restframework/testApi/settings.py new file mode 100644 index 0000000..3428592 --- /dev/null +++ b/Project1_Simple_REST_API/RestDjango/django_restframework/testApi/settings.py @@ -0,0 +1,90 @@ +import os +import os +from django.conf import settings +from datetime import timedelta + +SECRET_KEY = os.getenv("SECRET_KEY") + + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": "mem_db" + } +} + +SIMPLE_JWT = { + 'ROTATE_REFRESH_TOKENS': True, + 'BLACKLIST_AFTER_ROTATION': True, + + 'ALGORITHM': 'HS256', + 'SIGNING_KEY': settings.SECRET_KEY, + 'VERIFYING_KEY': None, + 'AUDIENCE': None, + 'ISSUER': None, + + 'AUTH_HEADER_TYPES': ('Bearer',), + 'USER_ID_FIELD': 'id', + 'USER_ID_CLAIM': 'user_id', + + 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), + 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), + 'TOKEN_TYPE_CLAIM': 'token_type', + + 'JTI_CLAIM': 'jti', + 'ACCESS_TOKEN_LIFETIME': timedelta(seconds=3), + 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), +} + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + "api.apps.MyAppConfig", + "rest_framework", + + +] + + +#rest framework config +REST_FRAMEWORK = { + 'DEFAULT_PERMISSION_CLASSES': ( + 'rest_framework.permissions.IsAuthenticated', + ), + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework_simplejwt.authentication.JWTAuthentication', + ), + 'TEST_REQUEST_DEFAULT_FORMAT': 'json', + 'TEST_REQUEST_RENDERER_CLASSES': [ + 'rest_framework.renderers.MultiPartRenderer', + 'rest_framework.renderers.JSONRenderer', + 'rest_framework.renderers.TemplateHTMLRenderer' + ] +} + + +ROOT_URLCONF = 'restApi.urls' + + + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +