Here is my code
from rest_framework.decorators import api_view
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response
from rest_framework import viewsets, status
from .models import Actor
from .serializers import ActorSerializer
# Create your views here.
class ActorView(viewsets.ModelViewSet):
model = Actor
queryset = model.objects.all()
permission_classes = (AllowAny,)
serializer_class = ActorSerializer
http_method_names = ['get', 'post', 'put', 'delete']
def list(self, request):
serializer = ActorSerializer(self.queryset, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def create(self, request):
serializer = ActorSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
print(serializer.validated_data['name'])
return Response("OK", status=status.HTTP_200_OK)
else:
return Response("nie ok")
Pylance thinks that this line:
print(serializer.validated_data['name'])
is incorrect, but it is correct. Code works fine and it doesn't displaying any errors while server is running and request is sent. But it is underlined in red, so something must be wrong. How can i get rid of those "errors"?
"__getitem__" method not defined on type "empty"
Object of type "None" is not subscriptable
I have alredy tried uninstalling Pylance and changing its' version but all for nothing.
It allow me to pass in json format data to post for creating record after i make def create so that json format like [{data:data,data:data}] can post in. How should i do so that i can also make put request with multiple object in one request or using post method to update?
Below is views.py.
from django.shortcuts import render
from .models import ListForm
# Create your views here.
from rest_framework import viewsets
from .serializers import ListFormSerializer
from rest_framework import filters
import django_filters.rest_framework
from rest_framework.response import Response
from rest_framework import status, viewsets
class ListFormViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = ListForm.objects.all().order_by('group')
serializer_class = ListFormSerializer
filter_backends = (django_filters.rest_framework.DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter,)
filterset_fields = ['group','key_description']
search_fields = ['group']
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data, many=isinstance(request.data,list))
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
# return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
Maybe you need something like this:
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.decorators import action
import json
from .models import Post
from .serializers import PostSerializer
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.none()
serializer_class = serializers.PostSerializer
#action(detail=False, methods=['post'])
def update_this(self, request):
try:
data = json.loads(request.data)
except:
try:
data = dict(request.data)
except:
data = request.data
# You can do everything you want here with data
return Response({'code': 0, 'desc': 'OK', 'more_data': {}})
Can I use Serializer just for validation purpose?
for example if I had CommentSerializer as
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
Is it as as per motive of DRF to use this serializer just for validation of request or some data without refering to any Django models? For example:
from rest_framework.views import APIView
from rest_framework.response import Response
class MyAPIView(APIView):
def post(self, request, format=None):
serializer = CommentSerializer(data=dict(request.data))
if serializer.is_valid():
validated_data = serializer.validated_data
# do something with validated_data
# return some data as Response()
or am I fighting against the framework guidance?
I am new to Django and am confused on handling post requests to my server. I want to accept post requests in this json format: {“username”:”john#doe.com”,”password”:”pass#word1”} and to reply with these json formated responses:
{“status”:”success”,”message”:”user_authed”} if the user and password combination is found
{“status”:”failure”,”message”:”user_not_found”} if the user and password combination is not found
This is my views.py code
from django.shortcuts import render
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from . models import users
from . serializers import userSerializer
class userList(APIView):
def get(self, request):
user = users.objects.all()
serializer = userSerializer(user, many=True)
return Response(serializer.data)
def post(self, request):
serializer = userSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I want to know how I can format the post function to accept requests in the format above and how to customize the reply as authenticated or not. Any help would be appreciated with this
What you are trying to accomplish is called authentication.
Please read https://www.django-rest-framework.org/api-guide/authentication/ in advance.
Then edit your question according to what you learn.
Hope to hear from you soon!
#serializers.py
from rest_framework import serializers
UserRequestSerializer(serializers.Serializer)
username = CharField(required=True, max_length=128)
password = CharField(required=True, max_length=128)
#views.py
from .serializers import *
UserCheckView(APIView):
def post(request, *args, **kwargs):
serializer = UserRequestSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
try:
user = User.objects.get(username=serializer.validated_data['username'])
if user.check_password(serializer.validated_data['password']:
return Response(data={“status”:”success”,”message”:”user_authed”},status=status.HTTP_200_OK)
return Response(data={“status”:”failure”,”message”:”password_did_not_match”}, status=status.HTTP_400_BAD_REQUEST)
except User.DoesNotExist:
return Response(data={“status”:”failure”,”message”:”user_not_found”}, status=status.HTTP_404_NOT_FOUND)
I'm working in a project which relies in a Django User model and TokenAuthentication under DjangoRestFramework
I was requested to get last login datetime for each user and I've realized that this field is not getting updated when I call the authentication REST endpoint.
Is this a known fact? Am I missing something I must do in order to get that field updated each time the token authentication is called?
Thanks
Well, at the end I inherited from the REST Framework TokenAuthentication, pointing to it in the urls file
url(r'^api-token-auth/', back_views.TokenAuthenticationView.as_view()),
and the View handles the request and manually calls the update_last_login like this:
from django.contrib.auth.models import update_last_login
class TokenAuthenticationView(ObtainAuthToken):
"""Implementation of ObtainAuthToken with last_login update"""
def post(self, request):
result = super(TokenAuthenticationView, self).post(request)
try:
request_user, data = requests.get_parameters(request)
user = requests.get_user_by_username(data['username'])
update_last_login(None, user)
except Exception as exc:
return None
return result
#F.D.F. answer is great. Here is another way of doing it.
We send user_logged_in signals that will call update_last_login:
user_logged_in.send(sender=user.__class__, request=request, user=user)
Here is a working view (based on a custom User model that uses email as USERNAME_FIELD) :
from rest_framework import parsers, renderers
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from rest_framework.views import APIView
from django.contrib.auth.signals import user_logged_in
from emailauth.serializers import AuthTokenSerializer, UserSerializer
class ObtainAuthToken(APIView):
throttle_classes = ()
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
serializer_class = AuthTokenSerializer
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
user_logged_in.send(sender=user.__class__, request=request, user=user)
return Response({'token': token.key, 'user': UserSerializer(user).data})
obtain_auth_token = ObtainAuthToken.as_view()
You can find the full source code here : Api View with last_login updated
Hope this helps.
A cleaner way to do it:
from django.contrib.auth.models import update_last_login
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import ObtainAuthToken
class LoginToken(ObtainAuthToken):
def post(self, request, *args, **kwargs):
result = super().post(request, *args, **kwargs)
token = Token.objects.get(key=result.data['token'])
update_last_login(None, token.user)
return result
And then setup urls.py:
url(r'^api-token-auth/', views.LoginToken.as_view()),
My answer for Django==2.0.5, django-rest-framework-social-oauth2==1.1.0
from django.contrib.auth import user_logged_in
from oauth2_provider.models import AccessToken
from rest_framework import status
from rest_framework_social_oauth2.views import TokenView
class MyTokenView(TokenView):
def post(self, request, *args, **kwargs):
response = super().post(request, *args, **kwargs)
if response.status_code == status.HTTP_200_OK:
token = AccessToken.objects.get(token=response.data['access_token'])
user = token.user
user_logged_in.send(sender=type(user), request=request, user=user)
return response
urls.py:
from django.urls import path
urlpatterns = [
path('token', MyTokenView.as_view(), name='token'),
]
Here is my solution using a ViewSet:
views.py:
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.serializers import AuthTokenSerializer
from rest_framework import viewsets
from django.contrib.auth.models import update_last_login
class LoginViewSet(viewsets.ViewSet):
"""Checks email and password and returns an auth token."""
serializer_class = AuthTokenSerializer
def create(self, request):
"""Use the ObtainAuthToken APIView to validate and create a token."""
##update last_login
try:
user = models.User.objects.get(email = request.data['username'])
update_last_login(None, user)
except:
pass
return ObtainAuthToken().post(request)
Now just add this viewset to the urls.py:
router.register('login', views.LoginViewSet, base_name="login")
This is the newest code for django 3.0.8.
:)
thx F.D.F.!
from django.contrib.auth.models import update_last_login
from rest_framework.authtoken.views import ObtainAuthToken
from django.contrib.auth import get_user_model
class TokenAuthenticationView(ObtainAuthToken):
"""Implementation of ObtainAuthToken with last_login update"""
def post(self, request):
result = super(TokenAuthenticationView, self).post(request)
currentUserModel = get_user_model()
try:
user = currentUserModel.objects.get(username=request.data['username'])
update_last_login(None, user)
except Exception as exc:
return None
return result
impl via signals
from django.dispatch import receiver
from django.db.models.signals import post_save
from django.contrib.auth import user_logged_in
from oauth2_provider.models import AccessToken
#receiver(post_save, sender=AccessToken)
def post_save_access_token(instance, created, raw, **kwargs):
if not created or raw:
return
user_logged_in.send(sender=instance.user.__class__, user=instance.user)
for anyone using knox to authenticate the user then need to edit api.py file and import user_logged_in like below
from django.contrib.auth.signals import user_logged_in
after that in LoginAPI class in the same api.py file add the below line after _, token = AuthToken.objects.create(user) like below
user_logged_in.send(sender=user.__class__, request=request, user=user)
If you're using Simple JWT for Django, you can do it pretty much easily by following this link. You just have to add
SIMPLE_JWT = {
...,
'UPDATE_LAST_LOGIN': True,
...,
}
Sometimes you don't want exactly the login time, since front-end stores the token and use it without logging in again.
You can save the current time in every request that user is authenticated and authorized by subclassing APIView from rest_framework
from django.contrib.auth.models import update_last_login
from rest_framework.views import APIView
class CustomAPIView(APIView):
def check_permissions(self, request):
super().check_permissions(request)
# here user is authorized (otherwise an exception would have been raised)
update_last_login(None, request.user)
class my_endpoint(CustomAPIView):
permission_classes = [IsAuthenticated]
def get(self, request, company_id=None):
...