I have a Django Rest API that sets the cookie in a response object. When I hit my browsable API, the browser stores the cookie under Application>Cookies. When I try to reproduce the same result using an AJAX call, the cookie isn't stored automatically; although I do get a response object in AJAX call.
My code is as follows
views.py
class UserLogin(generics.GenericAPIView):
serializer_class = serializers.UserLoginSerializer
permission_classes = (
permissions.AllowAny,
)
def finalize_response(self, request, *args, **kwargs):
"""
Set Authorization in cookie.
"""
response_obj = super(UserLogin, self).finalize_response(
request, *args, **kwargs)
if request.POST and response_obj.status_code == 200:
response_obj['Authorization'] = 'Token '\
+ response_obj.data['auth_token']
print 'COOKIE NOT SET'
response_obj.set_cookie(
'Authorization', response_obj['Authorization'])
print 'COOKIE SET'
return response_obj
def post(self, request):
"""
If serializer is valid.
- call action.
"""
serializer = self.get_serializer(
data=request.data)
if serializer.is_valid():
user = serializer.validated_data.get('user')
token, boolean = Token.objects.get_or_create(user=user)
if not boolean:
token.created = datetime.datetime.now()
token.save()
# user.login_attempts = 0
user.save()
data = serializers.TokenSerializer(token).data
return response.Response(
data=data,
status=status.HTTP_200_OK,)
return response.Response(
data=serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
serializers.py
class UserLoginSerializer(serializers.Serializer):
def __init__(self, *args, **kwargs):
super(UserLoginSerializer, self).__init__(*args, **kwargs)
self.user = None
self.fields[User.USERNAME_FIELD] = serializers.CharField()
password = serializers.CharField(
style={'input_type': 'password'})
def validate(self, data):
username = data.get(User.USERNAME_FIELD).lower()
password = data.get('password')
try:
user = User.objects.get(username=username)
except:
raise serializers.ValidationError(
messages.INVALID_CREDENTIALS_ERROR)
data['user'] = user
user_service = UserService()
is_valid = user_service.verify_account(user, password)
if not is_valid:
raise serializers.ValidationError(
messages.INACTIVE_ACCOUNT_ERROR)
return data
class Meta:
fields = (User.USERNAME_FIELD, 'password')
ajax call
$(document).ready(function() {
$('form').submit(function(event) {
var formData = {
'username' : $('input[name=username]').val(),
'password' : $('input[name=password]').val(),
};
// process the form
$.ajax({
type : 'POST',
url : 'http://13.232.122.165/users/login/',
data : formData,
dataType : 'json',
encode : true
})
// using the done promise callback
.done(function(data) {
// log data to the console so we can see
console.log(data);
location.href = "localhost:5000/profile"
// here we will handle errors and validation messages
});
// stop the form from submitting the normal way and refreshing the page
event.preventDefault();
});
});
The cookie is being attached but you can't see it from developer tools unless you're on the request url domain.
Try logging in using ajax and hitting the request url (any end point) on browser, you'll see the cookie.
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)
AssertionError: 'LoginView' should either include a serializer_class attribute, or override the get_serializer_class() method
Here is the code
class LoginView(GenericAPIView):
def post(self, request):
data = request.data
username = data.get('username', '')
password = data.get('password', '')
user = auth.authenticate(username=username, password=password)
if user:
auth_token = jwt.encode({'username': user.username}, settings.JWT_SECRET_KEY)
serializer = UserSerializer(user)
data = {'user': serializer.data, 'token': auth_token}
return Response(data, status=status.HTTP_200_OK)
return Response({'detail': 'Invalid credential'}, status=status.HTTP_401_UNAUTHORIZED)
I have the following 2 views and on a specific param I need to send a PATCH request to another view on receiving certain params. However i get the following error, how to rectify this?
Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` to be returned from the view,
but received a `<class 'requests.models.Response'>`
The view are as follows:
class Emp_Status_Trans_ListView(APIView):
permission_classes = [DjangoCustomModelPermissions]
queryset = Emp_Status_Trans.objects.none()
def get(self, request, format=None):
emp_mast_id=request.query_params.get('employee',None)
linked_model_data = [("hr","Emp_Status_Mast","Emp_Status_Mast_Serializer",("emp_status_mast_id","emp_status_short"))]
final_resp = {}
db_data = Emp_Status_Trans.objects.all().prefetch_related("emp_mast","emp_status_mast")
if emp_mast_id:
db_data = db_data.filter(emp_mast=emp_mast_id)
serializer = Emp_Status_Trans_Serializer(db_data, many=True)
final_resp['emp_status_trans'] = serializer.data
get_linked_data(final_resp, linked_model_data)
return Response(final_resp)
def post(self, request, format=None):
patch_emp_mast=request.query_params.get('patch_emp_mast',None)
serializer = Emp_Status_Trans_Serializer(data=request.data)
if serializer.is_valid():
serializer.save()
if patch_emp_mast:
return self.patch_emp_mast(serializer.data,request.META.get("HTTP_HOST"),request.META.get("HTTP_AUTHORIZATION"))
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def patch_emp_mast(self,data,domain,access_token):
url = "http://"+domain+"/hr/emp_mast/"+str(data['emp_mast']['id'])+"/"
headers = {'Content-Type': 'application/json', 'Authorization':access_token}
data = {
'emp_status_mast': data['emp_status_mast'],
}
return requests.patch(url,headers=headers, data=json.dumps(data))
patch_emp_mast is called when query param is received however it fails with the error mentioned earlier. How to rectify this?
you can rewrite your code like ;
def patch_emp_mast(self,data,domain,access_token):
url = "http://"+domain+"/hr/emp_mast/"+str(data['emp_mast']['id'])+"/"
headers = {'Content-Type': 'application/json', 'Authorization':access_token}
data = {
'emp_status_mast': data['emp_status_mast'],
}
try:
response= requests.patch(url,headers=headers, data=json.dumps(data))
return Response("status":True,"response":response.json())
expect:
return Response("status":False,"response":{})
Best regards
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
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)),
]