How to access session variable from response object in django test? - django

How can i access session variable using response object in django test.
def test(self):
postdata={'user':'staff'}
self.client.login(username='vishnu',password='vishnu#12345')
session=self.client.session
session['user']='manager'
session.save()
response=self.client.post(reverse('userhome'),postdata)
self.assertEqual(session['user'],'staff')
This test fails when run. In the above test, after calling post request the session variable 'user' will change to staff. How do i check the change? session['user'] gives same result after request. However in the view function request.session['user'] gives exact result.
How can i access session data from response?

Checking the final session state in self.client.session is working for me. self.client and response.client are the same object, so you could use response.client.session if that better signals intent:
def test(self):
postdata={'user':'staff'}
self.client.login(username='vishnu',password='vishnu#12345')
session=self.client.session
session['user']='manager'
session.save()
response=self.client.post(reverse('userhome'),postdata)
self.assertIn('user', self.client.session)
self.assertEqual(self.client.session['user'], 'staff')
self.assertIn('user', response.client.session)
self.assertEqual(response.client.session['user'], 'staff')
The documentation would seem to suggest that this wouldn't work: "To modify the session and then save it, it must be stored in a variable first (because a new SessionStore is created every time this property is accessed)." But I guess that's only true of setting the session before sending a request? Not sure, but it could use some clarification.

Related

<HttpResponseNotFound status_code=404, "text/html"> in django-test

I'm new to unit testing and I've been trying to test a GET method of the card game that I've built.
My TestCase is the following:
def test_rooms(self):
c = APIClient()
room_id = PokerRoom.objects.get(name='planning').id
request = c.get('http://127.0.0.1:8000/room/{}'.format(room_id))
print(request)
The id is a UUID that's why I'm using the room_id method.
My url:
path('room/<int:pk>', room),
where room is a #api_view(['GET']) method and pk is the id of the room. But when I try to test it, an error occurs:
<HttpResponseNotFound status_code=404, "text/html">
Checked if the room exists in the test database and it exists, now I don't know what is happening. Can someone help me?
Can you add more details to your code above, such as how the test suite has been created, how is the data set up, etc? One problem I am noticing straight away is how the request is being made. Why are you using a complete URL? If you are using the Django/DRF test API client, you should use the view path URL instead of the complete URL.
Reference: https://docs.djangoproject.com/en/4.0/topics/testing/tools/#overview-and-a-quick-example

django-channels: differentiate between different `AnonymousUser`s

Unfortunately I'm using django-channels channels 1.1.8, as I missed all the
updates to channels 2.0. Upgrading now is unrealistic as we've just
launched and this will take some time to figure out correctly.
Here's my problem:
I'm using the *message.user.id *to differentiate between authenticated
users that I need to send messages to. However, there are cases where I'll
need to send messages to un-authenticated users as well - and that message
depends on an external API call. I have done this in ws_connect():
#channel_session_user_from_http
def ws_connect(message):
# create group for user
if str(message.user) == "AnonymousUser":
user_group = "AnonymousUser" + str(uuid.uuid4())
else:
user_group = str(message.user.id)
print(f"user group is {user_group}")
Group(user_group).add(message.reply_channel)
Group(user_group).send({"accept": True})
message.channel_session['get_user'] = user_group
This is only the first part of the issue, basically I'm appending a random
string to each AnonymousUser instance. But I can't find a way to access
this string from the request object in a view, in order to determine who
I am sending the message to.
Is this even achievable? Right now I'm not able to access anything set in
the ws_connect in my view.
EDIT: Following kagronick's advice, I tried this:
#channel_session_user_from_http
def ws_connect(message):
# create group for user
if str(message.user) == "AnonymousUser":
user_group = "AnonymousUser" + str(uuid.uuid4())
else:
user_group = str(message.user.id)
Group(user_group).add(message.reply_channel)
Group(user_group).send({"accept": True})
message.channel_session['get_user'] = user_group
message.http_session['get_user'] = user_group
print(message.http_session['get_user'])
message.http_session.save()
However, http_session is None when user is AnonymousUser. Other decorators didn't help.
Yes you can save to the session and access it in the view. But you need to use the http_session and not the channel session. Use the #http_session decorator or #channel_and_http_session. You may need to call message.http_session.save() (I don't remember, I'm on Channels 2 now.). But after that you will be able to see the user's group in the view.
Also, using a group for this is kind of overkill. If the group will only ever have 1 user, put the reply_channel in the session and do something like Channel(request.session['reply_channel']).send() in the view. That way it doesn't need to look up the one user that is in the group and can send directly to the user.
If this solves your problem please mark it as accepted.
EDIT: unfortunately this only works locally but not in production. when AnonymousUser, message.http_sesssion doesn't exist.
user kagronick got me on the right track, where he pointed that message has an http_session attribute. However, it seems http_session is always None in ws_connect when user is AnonymousUser, which defeats our purpose.
I've solved it by checking if the user is Anonymous in the view, and if he is, which means he doesn't have a session (or at least channels can't see it), initialize one, and assign the key get_user the value "AnonymousUser" + str(uuid.uuid4()) (this way previously done in the consumer).
After I did this, every time ws_connect is called message will have an http_session attribute: Either the user ID when one is logged in, or AnonymousUser-uuid.uuid4().

How to mock get_current_user in tornado for unittest?

I have a tornado web application. Many pages require a valid cookie to access. It makes it impossible to test those handlers.
I want to use the mock library to mock the tornado.web.RequestHandler.get_current_user method. But I just cannot get right.
This is how I am doing right now:
class MyUT(tornado.testing.AsyncHTTPTestCase):
def get_app(self):
settings = {
"template_path": '../../../templates',
"cookie_secret": 'secret',
"login_url": '/admin/login',
"debug": True
}
return Application([
(r'/admin/create/super', handlers.CreateSuperUserHandler)
], **settings)
def testGet(self):
with mock.patch.object(handlers.CreateSuperUserHandler, 'get_current_user') as m:
m.return_value = {}
response = self.fetch('/admin/create/super')
print(response.body)
self.assertGreater(response.body.index('create'), 0)
If I execute this code, I got a 404 error. Because I did not define the login hanlder. But I am expecting the mocked get_current_user method return a user object, so do not go to the login page while testing.
One strange thing I found is, if I add m.assert_any_call() in the with block, there's no assertion error.
The #authenticated decorator redirects to the login page if get_current_user returns a falsy value. Try returning a non-empty dict.
You also use handlers.CreateSuperUserHandler in one place and handlers.AdminArea.CreateSuperUserHandler in another; are those the same class object?

How to check user is authenticated properly in Django and Backbone.js

I want to build single page application using Backbone.js and Django.
For checking user is authenticated or not,
I wrote a method get_identity method in django side.
If request.user.is_authenticated is true it returns request.user.id otherwise it returns Http404
In backbone side, I defined a User model and periodically make ajax call to get_identity.
I think it is the most straightforward way to check user is authenticated or not.
For learning single page application, I want to do this operation more sensible and efficient than this way if it is possible.
So what is your advice about this? When I search Django+Backbone.js + User Authentication, I couldn't find any satisfactory result and I really wonder how people do this simple operation.
Any help or idea will be appreciated.
(By the way I tried to read cookie periodically but HttpOnly True flagged cookies are not reacheable in client side.)
Django views.py
def get_identity(request):
if not request.user.is_authenticated():
raise Http404
return HttpResponse(json.dumps({'identity':request.user.id}), mimetype="application/json")
Backbone.js side.
updateUser:function(){
var $self=this;
$.ajaxSetup({async:false});
$.get(
'/get_identity',
function(response){
// update model...
$self.user.id =response.identity;
//check user every five minutes...
$self.user.fetch({success: function() {
$self.user.set('is_authenticated',true);
setTimeout($self.updateUser, 1000*60*1);
}
},this);
}).fail(function(){
//clear model
$self.user.clear().set($self.user.defaults);
setTimeout($self.updateUser, 1000*60*1);
});
$.ajaxSetup({async:true});
}
I had var is_authenticated = {{request.user.is_authenticated}}; in my base.html
and used the global variable to check.
I'm in pursuit of better solution (because this breaks when you start caching).
But you might find it useful.

Set a "global pre-request variable" in Django in Middleware

I'm trying to combine Google App Engine with RPX Now user authentication and a per-user limited access pattern.
The per user access-limiting pattern relies upon GAE's global User.get_current_user(), like-so:
from google.appengine.api import users
class CurrentUserProperty(db.UserProperty):
def checkCurrentUser(self, value):
if value != users.get_current_user():
raise db.BadValueError(
'Property %s must be the current user' % self.name)
return value
def __init__(self, verbose_name=None, name=None):
super(CurrentUserProperty, self).__init__(
verbose_name, name, required=True,
validator=self.checkCurrentUser)
However, with GAE Utilities' sessions storing RPX's user identifier, there is no global User.
The most obvious solution to me is to create some global User variable (somehow localized to the current request/thread) in the middleware. However, I wouldn't do this unless I was confident there were no race conditions.
Is there any way to get the identity of the current user (as stored in the request session variable) to the CurrentUserProperty when CurrentUserProperty is constructed?
Thank you for reading.
EDIT:
Reading GAE's google/appengine/tools/dev_appserver.py:578 which does a:
579 env['USER_ID'] = user_id
and google/appengine/api/users.py:92ff which reads:
92 if _user_id is None and 'USER_ID' in os.environ:
93 _user_id = os.environ['USER_ID']
seems to suggest that you can safely set the environment in a single Google App Engine request (but I may be wrong!).
Presumably, then, I can set an environment variable in middleware. It smells of trouble, so I'd like to know if anyone else has (similarly) tackled this.
App Engine instances (and indeed, CGI in general) are guaranteed to be single-threaded, so setting an environment variable per request is indeed safe - in fact, all the information about the current request is passed in through the current environment! Just make sure that you _un_set the environment variable if the user is not logged in, so you can't have an unauthenticated request get the authentication of the previous request to hit the same instance.