I am trying to check and display user online status with django and django rest framework.
I have tried this article: Django - detect if user is online / offline
But did not work on APIview only works on views with render and Httpresponse.
Updated
Here is the code for the middleware class
import datetime
from django.core.cache import cache
from django.conf import settings
from django.utils.deprecation import MiddlewareMixin
class ActiveUserMiddleware(MiddlewareMixin):
def process_request(self, request):
current_user = request.user
if request.user.is_authenticated:
now = datetime.datetime.now()
cache.set('seen_%s' % (current_user.username), now, settings.USER_LASTSEEN_TIMEOUT)
The APIView
class ProfilePersonal(APIView):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = UserProfileSerializer
def get(self):
try:
queryset = Account.objects.get()
except Account.DoesNotExist:
return Response(data={'non_field_errors': 'Request could not be validated.'},
status=status.HTTP_400_BAD_REQUEST)
serializer_data = self.serializer_class(queryset)
return Response(
data=serializer_data,
status=status.HTTP_200_OK
)
Related
While I am calling Django url, I get an error:
AssertionError: .accepted_renderer not set on Response.
My code is:
from rest_framework.response import Response
from rest_framework.decorators import api_view, renderer_classes
from rest_framework.renderers import JSONRenderer, TemplateHTMLRenderer
from myapp.models import employees
from .serializers import EmployeeSerializer
#api_view(('GET',))
#renderer_classes((TemplateHTMLRenderer, JSONRenderer))
def getData(request):
employees = {'name':'Bill', 'location':'Kolkata' }
return Response(employees)
def getEmployees(request):
employee_list = employees.objects.all()
serializer = EmployeeSerializer(employee_list, many = True)
return Response(serializer.data)
It should not be any line spaces between #api_view decorators and the views getData and getEmployee should have its individual decorators too
#api_view(('GET',))
#renderer_classes((TemplateHTMLRenderer, JSONRenderer))
def getData(request):
employees = {'name':'Bill', 'location':'Kolkata' }
return Response(employees)
#api_view(('GET',))
#renderer_classes((TemplateHTMLRenderer, JSONRenderer))
def getEmployees(request):
employee_list = employees.objects.all()
serializer = EmployeeSerializer(employee_list, many = True)
return Response(serializer.data)
Trying to write test for Django login. I want to request to login, collect token and access different view functions. How do I write a test in python for this.
It depends on your authentication system, if you use Django default auth, you can just use its helper method login(**credentials) or force_login https://docs.djangoproject.com/en/4.1/topics/testing/tools/#django.test.Client.login
If you use Django Rest Framework with additional 3rd auth system you can use its testing class
from rest_framework.test import APITestCase
class TestMyProtectedView(APITestCase):
def setUp(self) -> None:
self.client.login(user, password)
def test_view(self):
# now you can access your view with logged-in user in setUp
response = self.client.get('/api/views/')
Just like described in Django and DRF documentations. It would be something like:
tests.py
from django.test import TestCase
from django.urls import reverse
from django.contrib.auth import get_user_model
from rest_framework.authtoken.models import Token
from rest_framework.test import APIClient
from rest_framework import status
# Create your tests here.
class UserLoginTestCase(TestCase):
def setUp(self):
self.client = APIClient()
self.admin_user = get_user_model().objects.create_superuser(
email='admin#example.com',
password='testpass123',
)
def test_user_create_or_collect_token(self):
"""User check token created if in post save signal format"""
token = Token.objects.get(user__email=self.admin_user.email)
self.assertTrue(token.key)
def test_user_authenticated(self):
"""Check if user is authenticated"""
token = Token.objects.get(user__email=self.admin_user.email)
self.client.credentials(HTTP_AUTHORIZATION='Token ' + token.key)
r = self.client.get(reverse('user:me'))
self.assertEqual(r.status_code, status.HTTP_200_OK)
def test_user_login(self):
"""test user login"""
url = reverse('user:login')
data = {
'username': 'admin#example.com',
'password': 'testpass123'
}
r = self.client.post(url, data, format='json')
self.assertEqual(r.status_code, status.HTTP_200_OK)
self.assertTrue(r.data['token'])
urls.py
from .api.views import CustomAuthToken, UserAuthenticated
from django.urls import path
app_name = 'user'
urlpatterns = [
path('login/', CustomAuthToken.as_view(), name='login'),
path('me/', UserAuthenticated.as_view(), name='me'),
]
views.py
from rest_framework.views import APIView
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from rest_framework import authentication, permissions
from core.models import User
class CustomAuthToken(ObtainAuthToken):
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,
'user_name': user.name,
'email': user.email
})
class UserAuthenticated(APIView):
queryset = User.objects.all()
authentication_classes = [authentication.TokenAuthentication]
permission_classes = [permissions.IsAdminUser]
def get(self, request):
""" Check if user is authenticated by checking token against db """
is_authenticated = True
return Response({'isAuthenticated': is_authenticated})
Usually, tests are written for both positive and negative outcomes.
Note that in this case in particular I have a custom user model, and also extended the built-in ObtainAuthToken view.
#serializers.py
from rest_framework import serializers
class CommunitysStaffSerializer(serializers.ModelSerializer):
role = serializers.CharField(max_length=100,default='management')
community = serializers.PrimaryKeyRelatedField(read_only=True)
user = serializers.PrimaryKeyRelatedField(read_only=True)
#views.py
from rest_framework.response import Response
from admindashboard.serializers import CommunitysStaffSerializer
#api_view(['POST'])
def Communitystaff(request,pk=None):
if request.method == 'POST':
serializer = CommunitysStaffSerializer(data = request.data)
if serializer.is_valid():
Staff = CommunityStaff.objects.get(id = request.data['id'])
Staff.community=serializer.validated_data['community']
Staff.role=serializer.validated_data['role']
Staff.save()
return Response({'detail': 'entry successfully'})
else:
return Response({'error': 'unable to entry staff'}, status=status.HTTP_400_BAD_REQUEST)
else:
return Response (serializer.data)
#error
File "C:\Users\Pinakee.Somyadarshee\Desktop\mykommunity\admindashboard\urls.py", line 2, in
from admindashboard.views import Login_post,City_post,City_list,City_update,City_remove,Community_update,Community_list,Community_post,Community_detail,Admindash_list,Admindash_update,Admindash_remove,Admindash_post,Admindash_detail,Communitystaff
File "C:\Users\Pinakee.Somyadarshee\Desktop\mykommunity\admindashboard\views.py", line 11, in
from admindashboard.serializers import CommunitysStaffSerializer
ImportError: cannot import name 'CommunitysStaffSerializer' from 'admindashboard.serializers' (C:\Users\Pinakee.Somyadarshee\Desktop\mykommunity\admindashboard\serializers.py)
If serializers.py and views.py are in the same folder, use:
from .serializers import CommunitysStaffSerializer
Anyway, if you are using serializer.ModelSerailizer, you should define the model or, otherwise, you could use other serializer:
https://www.django-rest-framework.org/api-guide/serializers/#modelserializer
I am writing a test for a DetailView that queries get_object() by accessing a value set in a Middleware. This is for a Companies application and Company Model. Each user is in a Company.
To access the company throughout the project, I set the current user's Company.uuid on the request via a custom middleware.
Middleware
from django.utils.deprecation import MiddlewareMixin
class DynamicCompanyUUIDMiddleware(MiddlewareMixin):
""" Adds the current organization's UUID from the current user."""
def process_request(self, request):
try:
company_uuid = request.user.company_uuid
except:
company_uuid = None
request.company_uuid = company_uuid
That is used in the CompanyDetailView's get_object() method via a Mixin that I use for the other Company Views.
Mixin
class CompanyMixin(LoginRequiredMixin, SetHeadlineMixin):
model = Company
def get_object(self):
return get_object_or_404(
self.model,
uuid=self.request.user.company_uuid)
Test
The test that I'm trying to write is:
from django.test import RequestFactory
from django.urls import reverse, resolve
from test_plus.test import TestCase
from ..models import Company
from ..views import CompanyDetailView
class BaseCompanyTestCase(TestCase):
def setUp(self):
self.user = self.make_user()
self.object = Company.objects.create(owner=self.user, name="testcompany")
self.user.company_uuid = self.object.uuid
self.factory = RequestFactory()
class TestCompanyDetailView(BaseCompanyTestCase):
def setUp(self):
super(TestCompanyDetailView, self).setUp()
self.client.login(username="testuser", password="password")
self.view = CompanyDetailView()
self.view.object = self.object
request = self.factory.get(reverse('companies:detail'))
request.user = self.user
request.company_uuid = self.user.company_uuid
response = CompanyDetailView.as_view()(request)
self.assertEqual(response.status_code, 200)
def test_get_headline(self):
self.assertEqual(
self.view.get_headline(),
'%s Members' % self.object.name
Result
This results in a 404 with the testuser's company not being found.
Walking through it:
I create the user
Create the company for this new testuser
Set the user.company_uuid
This should allow the mixin to access the company_uuid
Therefore return the user's company in the request
However I'm not returning the company as the 404 shows.
Question
Where am I going wrong on this? Thanks in advance for your help.
Answer
I was mixing Django's Client & RequestFactory. I have corrected the code above which is correct.
I was mixing Django's Client & RequestFactory. After stepping away, I figured it out below -
from django.test import RequestFactory
from django.urls import reverse, resolve
from test_plus.test import TestCase
from ..models import Company
from ..views import CompanyDetailView
class BaseCompanyTestCase(TestCase):
def setUp(self):
self.user = self.make_user()
self.object = Company.objects.create(owner=self.user, name="testcompany")
self.user.company_uuid = self.object.uuid
self.factory = RequestFactory()
class TestCompanyDetailView(BaseCompanyTestCase):
def setUp(self):
super(TestCompanyDetailView, self).setUp()
self.client.login(username="testuser", password="password")
self.view = CompanyDetailView()
self.view.object = self.object
request = self.factory.get(reverse('companies:detail'))
request.user = self.user
request.company_uuid = self.user.company_uuid
response = CompanyDetailView.as_view()(request)
self.assertEqual(response.status_code, 200)
def test_get_headline(self):
self.assertEqual(
self.view.get_headline(),
'%s Members' % self.object.name
When a user inputs a url that is wrong, my Django app returns an HTML error. How can I get DRF to return a json formatted error?
Currently my urls is
from django.conf.urls import url
from snippets import views
urlpatterns = [
url(r'^snippets/$', views.snippet_list),
url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail),
]
but if a user goes to 127.0.0.1:8000/snip They get the html formatted error rather than a json formatted error.
Simply way to do it, you can use raise Http404, here is your views.py
from django.http import Http404
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from yourapp.models import Snippet
from yourapp.serializer import SnippetSerializer
class SnippetDetailView(APIView):
def get_object(self, pk):
try:
return Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet)
return Response(serializer.data, status=status.HTTP_200_OK)
You also can handle it with Response(status=status.HTTP_404_NOT_FOUND), this answer is how to do with it: https://stackoverflow.com/a/24420524/6396981
But previously, inside your serializer.py
from rest_framework import serializers
from yourapp.models import Snippet
class SnippetSerializer(serializers.ModelSerializer):
user = serializers.CharField(
source='user.pk',
read_only=True
)
photo = serializers.ImageField(
max_length=None,
use_url=True
)
....
class Meta:
model = Snippet
fields = ('user', 'title', 'photo', 'description')
def create(self, validated_data):
return Snippet.objects.create(**validated_data)
To test it, an example using curl command;
$ curl -X GET http://localhost:8000/snippets/<pk>/
# example;
$ curl -X GET http://localhost:8000/snippets/99999/
Hope it can help..
Update
If you want to handle for all error 404 urls with DRF, DRF also provide about it with APIException, this answer may help you; https://stackoverflow.com/a/30628065/6396981
I'll give an example how do with it;
1. views.py
from rest_framework.exceptions import NotFound
def error404(request):
raise NotFound(detail="Error 404, page not found", code=404)
2. urls.py
from django.conf.urls import (
handler400, handler403, handler404, handler500)
from yourapp.views import error404
handler404 = error404
Makesure your DEBUG = False
from rest_framework import status
from rest_framework.response import Response
# return 404 status code
return Response({'status': 'details'}, status=status.HTTP_404_NOT_FOUND)
An easier way is to use get_object_or_404 method in django:
as described in this link:
get_object_or_404(klass, *args, kwargs)
- Calls get() on a given model manager, but it raises Http404 instead of the model’s DoesNotExist exception.
- klass: A Model class, a Manager, or a QuerySet instance from which to get the object.
As an example, pay attention to
obj = get_object_or_404(Snippet, pk=pk)
return obj
in the following code:
from django.shortcuts import get_object_or_404
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
class SnippetDetail(APIView):
"""
Retrieve, update or delete a snippet instance.
"""
def get_object(self, pk):
obj = get_object_or_404(Snippet, pk=pk)
return obj
def get(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet)
return Response(serializer.data)
...
Or simply, you can use the same structure of DRF, without losing I18N and keep the same DRF error message:
from rest_framework import viewsets, status, exceptions
from rest_framework.decorators import action
from rest_framework.response import Response
try:
codename = get_or_bad_request(self.request.query_params, 'myparam')
return Response(self.get_serializer(MyModel.objects.get(myparam=codename), many=False).data)
except MyModel.DoesNotExist as ex:
exc = exceptions.NotFound()
data = {'detail': exc.detail}
return Response(data, exc.status_code)