This is my test:
class PageTests(APITestCase):
def setUp(self):
Location.objects.create(locationName = 'Location of Mine', LocationCode = 'LOM')
User.objects.create(username='b', password='b', email='b#hotmail.com')
def test_create_page(self):
"""
Ensure only authenticated users can create a new page object.
"""
url = reverse('page-list')
# See if unauthenticated unadmin users can create a page (they shouldn't.)
data = {'location': 1, 'pageName': 'Test Page 1', 'pageDescription': 'This is the first test page', 'pageCode': 'TP1'}
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
# See if authenticated users can create a page (they should).
print(User.objects.get().username)
self.client.login(username='b', password='b')
response = self.client.post(url, data, format='json')
print(response.data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
This is my views.py / viewset:
class IsAuthenticated(permissions.BasePermission):
def has_permission(self, request, view):
print('here!!!!!!!!!!!!')
print(request.user)
return request.user.is_authenticated()
class pageViewSet(viewsets.ModelViewSet):
queryset = Page.objects.all()
serializer_class = PageSerializer
permission_classes = (IsAuthenticated,)
The problem is, even after I log the user in by doing self.client.login(username='b', password='b') it still raises a 403 error when posting. This is what gets printed:
here!!!!!!!!!!!!
AnonymousUser
b
here!!!!!!!!!!!!
AnonymousUser
{'detail': 'Authentication credentials were not provided.'}
As you can see, Django does see the user object (because it prints 'b') but the user does not get signed in for some reason and is still an AnonymousUser. Now, when I change my setup to this:
def setUp(self)
url = reverse('user-list')
# Create the user using the API.
data = {'username': 'b', 'password': 'b', 'email': 'a#hotmail.com', 'location': '1'}
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
and then log the user in, it work perfectly fine and the test doesn't raise any errors. Any idea why it raises errors when creating the user using User.objects.create()?
I've used similar code before in other unittest classes (creating the user using the ORM and then signing him in) and it works. I'm not sure why it's not working here.
Edit: Also, if I create the user and make him a super user and log him in, like so:
User.objects.create_superuser(username='a', password='a', email='a#hotmail.com')
it works as well.
Found the answer. I had to create the user by doing this:
User.objects.create_user()
and not this:
User.objects.create()
Related
In my frontend i'm logging into another app's api in the browser, I'm then redirected back to my app, that hits a View in my backend which gets a code from the other app's api, sends code back in a post request then receives an access token and stores it in a model associated with the current user.
My problem is that after the user gives permission to other app in the browser it redirects back to my backend view without the users token in the header so if i have permissions_classes set it wont allow user to access that view... but if i take the permissions_classes off, the view won't know who the current user is.
View #1 that prepares the other app's API url:
class getAPIAuthURL(APIView):
authentication_class = [authentication.TokenAuthentication]
permission_class = [permissions.IsAuthenticated]
def get(self, request):
scopes = 'scopes'
url = Request('GET', 'https://accounts.api.com/authorize',
params={
'scope': scopes,
'response_type': 'code',
'redirect_uri': REDIRECT_URL,
'client_id': CLIENT_ID
}
).prepare().url
return Response(url, status=status.HTTP_200_OK)
View #2 that gets data and stores it in model (this is the REDIRECT_URL from previous view):
class APICallback(APIView):
authentication_class = [authentication.TokenAuthentication]
permission_class = [permissions.IsAuthenticated]
def api_callback(request, format=None):
code = request.GET.get('code')
if not code:
return Response({'Error': 'Code not found in request'}, status=status.HTTP_400_BAD_REQUEST)
response = post('https://accounts.api.com/api/token', data={
'code': code,
}).json()
print(response)
user = request.user
access_token = response.get('access_token')
token = APITokenModel(user=user, access_token=access_token)
token.save()
return redirect('frontend')
I have other Views that make requests and it has been able to get the token to know who the user is, but when this View is called I get a 401 Unauthorized error.
How do I let Django know the token I'm receiving from the other app's api belongs to the current user?
also... when I take off permissions and authentication class from the View it returns the user as Anonymous User
First, what authentication class are you using? You should know that your TokenAuthentication class uses the Authorization header in your request to authenticate you. If that's not been passed then you should fix that.
It would be worth knowing that you don't send auth tokens as GET and should not be sent as those. Unless of course you want to write an Authentication class of your own.
EDIT
In lieu of our discuss in the comments, try this redirect...
# import the class
from django.http import HttpResponseRedirect
# now redirect
return HttpResponseRedirect(redirect_to="url", headers=dict)
So I am using Django's test framework, and in this case im testing update_password_view that I ve created on top of the built-in PasswordChangeForm.
Could someone please help me with the error from below?
After I run tests I get the following error:
AssertionError: [] is not true : Response didn't redirect as expected: Response code was 200(expected 200)
Here is the code:
#views.py
class UpdatePassword(PasswordChangeView):
form_class = PasswordChangeForm
success_url = reverse_lazy('posts:home')
template_name = 'accounts/password.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# get the number of unseen messages
context['inbox_count'] = Message.objects.filter(
~Q(sender=self.request.user), Q(seen=False),
(Q(chat__user1=self.request.user) |\
Q(chat__user2=self.request.user))).count()
return context
#tests.py
def test_update_password_view(self):
credentials = {
'old_password': '123secret',
'password1': '321secret',
'password2': '321secret',
}
response = self.client.post('http://127.0.0.1:8000/users/change-password/',
credentials, follow=True)
self.assertRedirects(response, '/posts/', status_code=200,
target_status_code=200)
status_code inside assertRedirects has to be redirect status code, which should be 3XX. In your occasion it has to be 302. See more at docs.
For the proper behaviour you should replace this:
self.assertRedirects(response, '/posts/', status_code=200, target_status_code=200)
With this:
self.assertRedirects(response, '/posts/', status_code=302, target_status_code=200)
By the way, it's similar to this:
self.assertRedirects(response, '/posts/')
This is not tested, but perhaps the issue is that it is not redirecting because the user is not created, or logged in with the old password. Try creating a user, logging them in, then running the test.
#tests.py
def test_update_password_view(self):
credentials = {
'old_password': '123secret',
'password1': '321secret',
'password2': '321secret',
}
# Create and login the user
self.client.user = User.objects.create(username="testuser", password="123secret")
c = Client()
logged_in = c.login(username='testuser', password='123secret')
response = self.client.post('http://127.0.0.1:8000/users/change-password/',
credentials, follow=True)
self.assertRedirects(response, '/posts/', status_code=302, target_status_code=200)
Ok, Here's the problem: I have created a POST API in Django REST framework & I want to add logged-in user to request.data when making POST request & It works perfectly fine when I create post calling this API from Postman, but when I visit the post create API endpoint from browser it throws AssertionError: '''When a serializer is passed a data keyword argument you must call .is_valid() before attempting to access the serialized .data representation.
You should either call .is_valid() first, or access .initial_data'''.
Here's the snippet of my get_serializer method which i use to override & add user to request.data:
class PostCreateView(CreateAPIView):
serializer_class = PostSerializer
def get_serializer(self, *args, **kwargs):
serializer_class = self.get_serializer_class()
kwargs["context"] = self.get_serializer_context()
request_data = self.request.data.copy()
request_data["user"] = self.request.user.id
kwargs["data"] = request_data
return serializer_class(*args, **kwargs)
def post(self,request):
serializer = self.get_serializer()
serializer.is_valid(raise_exception=True)
serializer.save()
status_code = status.HTTP_201_CREATED
response = {
'success' : 'True',
'status code' : status_code,
'message': 'Post created successfully',
'post_detail': serializer.data,
}
return Response(response, status=status_code)
Update: I have changed my code in post method to pass user initially in a Post instance & then pass it in PostSerializer as: serializer = self.serializer_class(post, data=request.data) & it works on both Postman as well as browser's DRF request. But I'm curious to know why my above code with get_serializer method gave me AssertionError from browser's DRF request? but worked fine when making a POST request from Postman.
I am testing a view and my test looks like:
def test_profile(self, user_id):
user = User.objects.create_user(username="myusername", password="password", email="abc#testmail.com")
self.client.user = user
print(user.id)
request = self.client.get("/account/profile/{}/".format(user_id), follow=True)
self.assertEqual(request.status_code, 200)
Here my profile view has a login_required decorator. How can I set user to request.user?
I was trying to do the same myself but found out that Django Test Client does not set the user in the request and it is not possible to set request.user while using Client any other way. I used RequestFactory to that.
def setUp(self):
self.request_factory = RequestFactory()
self.user = User.objects.create_user(
username='javed', email='javed#javed.com', password='my_secret')
def test_my_test_method(self):
payload = {
'question_title_name': 'my first question title',
'question_name': 'my first question',
'question_tag_name': 'first, question'
}
request = self.request_factory.post(reverse('home'), payload)
request.user = self.user
response = home_page(request)
More about request factory here
Try this:
from django.test import TestCase, Client
from django.contrib.auth.models import User
class YourTestCase(TestCase):
def test_profile(self, user_id):
user = User.objects.create(username='testuser')
user.set_password('12345')
user.save()
client = Client()
client.login(username='testuser', password='12345')
response = client.get("/account/profile/{}/".format(user.id), follow=True)
self.assertEqual(response.status_code, 200)
Here, I first create the user and set the login credentials for the user. Then I create a client and login with that user. So in your views.py, when you do request.user, you will get this user.
If you use django.test you can do something like that:
self.client.force_login(user)
This works:
self.client.force_authenticate(user=user)
If you have a response, you can access response.context['user'].
If you need a response object, just call any view that will create a context, e.g. response = self.client.get('/').
I tried to use Django AllAuth to make a registration user by Facebook.
I have to make a REST API so I would to use Django REST Framework.
I found this simple tutorial to make the first user registration but probably there are some difference from current implementation:
I tried this code:
def post(self, request):
data = JSONParser().parse(request)
access_token = data.get('access_token', '')
try:
app = SocialApp.objects.get(provider="facebook")
token = SocialToken(app=app, token=access_token)
# check token against facebook
login = fb_complete_login(app, token)
login.token = token
login.state = SocialLogin.state_from_request(request)
# add or update the user into users table
ret = complete_social_login(request, login)
# if we get here we've succeeded
return Response(status=200, data={
'success': True,
'username': request.user.username,
'user_id': request.user.pk,
})
except:
traceback.print_exc()
return Response(status=401, data={
'success': False,
'reason': "Bad Access Token",
})
but now I see that fb_complete_login take 3 parameters: request, app and token.
So, I tried to put also the request like this function parameter but some lines later I have an error on login = fb_complete_login(app, token).
TypeError: add_message() argument must be an HttpRequest object, not
'Request'.
Any suggestions are welcome!
Solution given here: http://tech.agilitynerd.com/django-rest-registration-with-django-rest-auth.html
To disable messaging just for allauth, override the adapter (for instance in main.adapters):
from allauth.account.adapter import DefaultAccountAdapter
class MessageFreeAdapter(DefaultAccountAdapter):
def add_message(self, request, level, message_template,
message_context=None, extra_tags=''):
pass
then add this in settings.py:
ACCOUNT_ADAPTER = 'main.adapters.MessageFreeAdapter'