So i want to make a registration system with django rest framework so i can use it with React. Here is my serializers.py
from rest_framework import serializers
from backApp.models import Client
class RegistrationSerializer(serializers.ModelSerializer):
password2 = serializers.CharField(style={'input_type': 'password'}, write_only=True)
class Meta:
model = Client
fields = ['username', 'email', 'password', 'password2']
extra_kwargs = {
'password': {'write_only': True}
}
def save(self):
client = Client(
email = self.validated_data['email'],
username = self.validated_data['username'],
)
password = self.validated_data['password']
password2 = self.validated_data['password2']
if password != password2:
raise serializers.ValidationError({'password': "password doesn't match"})
client.set_password(password)
client.save()
return client
and here is views.py
from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import api_view, permission_classes
from rest_framework import permissions
from authentication.api.serializers import RegistrationSerializer
#api_view(['POST',])
#permission_classes((permissions.AllowAny,))
def registration_view(request):
if request.method == "POST":
serializer = RegistrationSerializer(data=request.data)
data = {}
if serializer.is_valid():
client = serializer.save()
data['response'] = "successfully registred!"
data['email'] = client.email
data['username'] = client.username
else:
data = serializer.errors
return Response(data)
when i sent a post request with postman i get the following error:
password: field required
password2: field required
here is my input data
enter image description here
So please can you tell me where is the error and how i can fix it ?
Thanks in advance ^^.
You can not pass the password field using url, that I figured from your postman snapshot.
Take a look at this image you will find Body tab where you will be able to send data using postman and use post method. This should work for you.
You are making post request with params not form/json data. you will understand if you look at the url
Related
I have a Django REST App where I want to do the log in. I need the id of the currend logged in user and when I print it, it returns None. This is my code:
serializer.py
class LoginSerializer(serializers.Serializer):
username = serializers.CharField(
label="Username",
write_only=True
)
password = serializers.CharField(
label="Password",
# This will be used when the DRF browsable API is enabled
style={'input_type': 'password'},
trim_whitespace=False,
write_only=True
)
view.py
class LoginAPI(APIView):
def post(self, request):
username = request.data['username']
password = request.data['password']
user = User.objects.filter(username=username).first()
if user is None:
raise AuthenticationFailed('User not found!')
if not user.check_password(password):
raise AuthenticationFailed('Incorrect password!')
payload = {
'id': user.id,
}
token = jwt.encode(payload, 'secret', algorithm='HS256').decode('utf-8')
response = Response()
response.set_cookie(key='token', value=token, httponly=True)
response.data = {
'token': token
}
print(self.request.user.id)
return response
I don't know what I have to change in my log in view in order to print the id, not None.
You didn't login yet in that post function. So self.request.user does not exist.
print(user.id)
I´m trying to get response that include name apart from token.
In the documentation https://www.django-rest-framework.org/api-guide/authentication/ about ObtainAuthToken I see we can overwrite the post fn but I´m not sure how to apply it in my CreateTokenView class.
serializers.py
class AuthTokenSerializer(serializers.Serializer):
"""Serializer for the user authentication object"""
email = serializers.CharField()
password = serializers.CharField(
style={'input_type': 'password'},
trim_whitespace=False
)
def validate(self,attrs):
"""Overwriting the validate() fn"""
email = attrs.get('email')
password = attrs.get('password')
user = authenticate(
request=self.context.get('request'),
username=email,
password=password
)
if not user:
msg = _('Unable to authenticate with provided credentials')
raise serializers.ValidationError(msg, code='authentication')
attrs['user'] = user
return attrs
views.py
from rest_framework.authtoken.views import ObtainAuthToken
class CreateTokenView(ObtainAuthToken):
"""Create a new auth token for user"""
serializer_class = AuthTokenSerializer
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
Add the post fn and customize the kwargs
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
class CreateTokenView(ObtainAuthToken):
"""Create a new auth token for user"""
serializer_class = AuthTokenSerializer
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({
'token': token.key,
'name': user.name
})
I have created a login screen which uses a username and password to login. I have a jwt authentication but I'm getting a bit confused because I have two login urls and I want only one. The jwt url is providing me the token meanwhile the other one that I have created myself I can login but no token is getting generated. This is my code:
serializers.py
class UserLoginSerializer(ModelSerializer):
token = CharField(allow_blank=True, read_only=True)
username = CharField(required=False, allow_blank=True)
class Meta:
model = User
fields = [
'username',
'password',
'token',
]
extra_kwargs = {"password":{"write_only": True}}
def validate(self, data):
user = authenticate(**data)
if user:
if user.is_active:
data['user'] = user
return data
raise exceptions.AuthenticationFailed('Account is not activated')
raise exceptions.AuthenticationFailed('User is not active')
def validate(self, data):
user_obj = None
username = data.get("username", None)
password = data["password"]
if not username:
raise ValidationError("A username is required")
user = User.objects.filter(
Q(username=username)
).distinct()
if user.exists() and user.count() == 1:
user_obj = user.first()
else:
raise ValidationError("This username is not valid")
if user_obj:
if not user_obj.check_password(password):
raise ValidationError("Incorrect credentials, please try again")
data["token"] = "SOME RANDOM TOKEN"
return data
views.py
class UserLoginAPIView(APIView):
permission_classes = [AllowAny]
serializer_class = UserLoginSerializer
def post(self, request, *args, **kwargs):
data = request.data
serializer = UserLoginSerializer(data=data)
if serializer.is_valid(raise_exception=True):
new_data = serializer.data
return Response(new_data, status=HTTP_200_OK)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
You can re-write your login serializer like this:
from rest_framework_jwt.serializers import JSONWebTokenSerializer
class SignInJWTSerializer(JSONWebTokenSerializer):
def validate(self, attrs):
email = attrs.get('email')
password = attrs.get('password')
if email is None or password is None:
message = 'Must include email and password'
raise serializers.ValidationError({'message': message})
...
And in url:
from rest_framework_jwt.views import ObtainJSONWebToken
path('login/', ObtainJSONWebToken.as_view(serializer_class=serializers.SignInJWTSerializer), name='login'),
also remove view class
My goal is to override obtain_jwt_token in order to get more control over the return value of the procedure, and in the doc I found only a bizarre and sketchy info on how to do this:
Note that the default obtain_auth_token view explicitly uses JSON
requests and responses, rather than using default renderer and parser
classes in your settings. If you need a customized version of the
obtain_auth_token view, you can do so by overriding the
ObtainAuthToken view class, and using that in your url conf instead
As for now, my attempt looks like this:
urlpatterns = [
url(r'^api-token-auth/', my_customized_view),
]
class Foo(ObtainAuthToken):
def post(self):
# here goes my customized code
my_customized_view = Foo.as_view()
The odds are that my code looks quite silly, and I am just lost trying to google it. I have little experience in Djagno, so please help me with this !
I have just been going through the same journey for comprehension as I wished to return the user and also allow email or username login. The documentation is not entirely clear, but as described for auth token, you can do the same for JWT.
obtain_auth_token is to ObtainAuthToken, as obtain_jwt_token is to ObtainJSONWebToken.
This is my overwritten login method:
from rest_framework_jwt.settings import api_settings
from rest_framework_jwt.views import ObtainJSONWebToken
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
class LoginView(ObtainJSONWebToken):
def post(self, request, *args, **kwargs):
# by default attempts username / passsword combination
response = super(LoginView, self).post(request, *args, **kwargs)
# token = response.data['token'] # don't use this to prevent errors
# below will return null, but not an error, if not found :)
res = response.data
token = res.get('token')
# token ok, get user
if token:
user = jwt_decode_handler(token) # aleady json - don't serialize
else: # if none, try auth by email
req = request.data # try and find email in request
email = req.get('email')
password = req.get('password')
username = req.get('username')
if email is None or password is None:
return Response({'success': False,
'message': 'Missing or incorrect credentials',
'data': req},
status=status.HTTP_400_BAD_REQUEST)
# email exists in request, try to find user
try:
user = User.objects.get(email=email)
except:
return Response({'success': False,
'message': 'User not found',
'data': req},
status=status.HTTP_404_NOT_FOUND)
if not user.check_password(password):
return Response({'success': False,
'message': 'Incorrect password',
'data': req},
status=status.HTTP_403_FORBIDDEN)
# make token from user found by email
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
user = UserSerializer(user).data
return Response({'success': True,
'message': 'Successfully logged in',
'token': token,
'user': user},
status=status.HTTP_200_OK)
You can change the default to check by email only if you please by customising Django's auth model, but I was happy to have both options.
I started creating an api boilerplate. There is a requirements.txt file and a config.example.py file for anyone who wants to pull it down to view the rest.
https://github.com/garyburgmann/django-api-boilerplate
In views.py, file add the following code and customize as you want.
def jwt_response_payload_handler(token, user=None, request=None):
return {
'token': token,
'user': UserSerializer(user, context={'request': request}).data
}
Default is {'token': token}
Also In your settings.py file add
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER':
'api.user.views.jwt_response_payload_handler',
}
( 'api.user.views.jwt_response_payload_handler',
) is the path to your custom jwt_response_payload_handler
For more help, you can view here
like title,
what's different about list_route and detail_route in django-rest-framework?
if I want to get 1 in url xxx/books/1/,
how can I write url.py and views.py ?
#list_route and #detail_route are extra actions that we can add to a ViewSet. Both provide a custom routing facility in view set. Any methods on the ViewSet decorated with #detail_route or #list_route will also be routed. list_route will give all the related records, whereas detail_route will provide only a particular record. For example, given a method like this on the UserViewSet class:
class UserViewSet(ModelViewSet):
...
#detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])
def set_password(self, request, pk=None):
The following URL pattern would additionally be generated:
URL pattern: ^users/{pk}/set_password/$ Name: 'user-set-password'
For more information on routers you can visit the official Django Rest Rramewrok documentation on routers.
If you want get xxx/books/1/ then your url.py and views.py should look like this.
urls.py:
url(r'^xxx/books/(?P<id>[0-9]+)$', views.myview)
views.py:
#csrf_exempt
def myview(request , id):
**Read this and definitely you will get the difference and how to use it **
If we have a ad hoc method (for e.g. current method which is in the same viewset that we have used for different methods, basically
ad-hoc means 'this'), we can use custom route for this method, we can
define our own url above the method inside the #list_route and
#detail_route decorator
The difference between the #list_route and #detail_route is the #detail_route decorator contains pk in its URL pattern and is intended for methods which require a single instance. The #list_route decorator is intended for methods which operate on a list of objects (list of records)
Get reference through
enter link description here
For example
**It will hit to the same url at the url.py but for #list_raoute we have append /reset-user-password/ which we have mention on #list_route to the url when we call it.(e.g
/// In url.py**
router = routers.DefaultRouter()
router.register(r'register', api_views.UserProfileViewSet, base_name="userprofileviewset")
urlpatterns = [
url(r'^api/v1/', include(router.urls)),
]
**////// In API call or in url call
for create user**
http://127.0.0.1:8000/api/v1/register/
### forget password
http://127.0.0.1:8000/api/v1/resister/reset-user-password/
)
class UserProfileViewSet(viewsets.ViewSet):
"""
IT's use to create new user(auth user). accepted method is post.
end point /register
"""
permission_classes = (AllowAny,)
serializer_class = UserProfileSerializer
"""
It gives the list of all users
"""
def list(self, request):
queryset = UserProfile.objects.all()
serializer = self.serializer_class(queryset, many=True)
return Response(serializer.data)
"""
It creates new user
"""
def create(self, request):
serializer = self.serializer_class(data=request.data)
# check email address is exists or not.
user_type = request.data['user_type']
user_token = register_by_social(request.data['email'], request.data['username'], user_type)
if not user_token or user_token == True:
if not User.objects.filter(Q(email=request.data['email'])
| Q(username=request.data['username'])).exists():
if serializer.is_valid():
userprofile = serializer.save()
return Response({
'status': status.HTTP_201_CREATED,
'message': 'Successfully signup new user.',
'token': userprofile.user.auth_token.key })
return Response({
'status': status.HTTP_400_BAD_REQUEST,
'message': 'Please provided required fields.',
'error' : serializer.errors })
return Response({
'status': status.HTTP_409_CONFLICT,
'message': 'Email address or username is already exists.'})
return Response({
'status': status.HTTP_200_OK,
'message': 'Social user is already registered.',
'token': user_token })
#list_route(permission_classes=[IsAuthenticated], authentication_classes = (BasicAuthentication, TokenAuthentication),
methods=['post'], url_path='reset-user-password')
def reset_user_password(self, request, pk=None):
"""
It resets the user password
"""
reset_password_serializer = UserResetPasswordSerializer(request.user, data=request.data)
if reset_password_serializer.is_valid():
if not request.user.check_password(request.data.get('password')):
return Response({
'status': status.HTTP_400_BAD_REQUEST,
'message': 'Password id wrong, please enter correct password',
})
request.user.set_password(request.data.get('new_password'))
request.user.save()
return Response({
'status': status.HTTP_201_CREATED,
'message': 'Password updated successfully',
})
class PlayListViewSet(viewsets.ViewSet):
permission_classes = (IsAuthenticated,)
serializer_class = PlayListSerializer
serializer_add_playlist_class = LikeContentSerializer
#detail_route(methods=['post'], url_path='add-content-to-playlist')
def add_playlist(self, request, pk=None):
serializer = self.serializer_add_playlist_class(data=request.data)
playlist = PlayList.objects.filter(id=pk)
if serializer.is_valid():
content_type = request.data['content_type']
if content_type =="audio":
content = Song.objects.filter(id=request.data['content'])
playlist[0].songs.add(content[0])
playlist[0].save()
if content_type =="video":
content = Video.objects.filter(id=request.data['content'])
playlist[0].videos.add(content[0])
playlist[0].save()
if content_type =="youtube":
content = YouTubeVideo.objects.filter(id=request.data['content'])
playlist[0].youtbue_videos.add(content[0])
playlist[0].save()
return Response({
'status': status.HTTP_201_CREATED,
'message': 'Successfully playlist updated'})
return Response({
'status': status.HTTP_400_BAD_REQUEST,
'message': 'Data is not valid, try again',
'error' : serializer.errors })
detail_route, is for an instance. What I mean, method generated with detail route, will be appended after an instance method, i.e. a retrieve.
{prefix}/{lookup}/
Check : django drf router doc
If your model is books and 1 is the id:
class ParkingViewSet(viewsets.ModelViewSet):
serializer_class = BookSerializer
def retrieve(self, request, pk=None):
# Here pk will be 1.
queryset = Book.objects.get(pk=pk)
serializer = BookSerializer(queryset)
return Response({'msg':"",'data':serializer.data, 'status':'OK'})
If xxx is your instance, you should user url_path variable to change default url. Something like this:
#detail_route(methods=['get'], url_path='(books/?P<num>\d+)')
Then in the method, you will have num as a parmeter
urls.py, will be generated with default router:
from django.conf.urls import url, include
from recharge_card import views
from rest_framework.routers import DefaultRouter
# Create a router and register our viewsets with it.
router = DefaultRouter()
router.register(r'xxx', views.XxxViewSet, base_name="xxx")
urlpatterns = [
url(r'^api/', include(router.urls)),
]