Why is django.test.client.Client not keeping me logged in - python

I'm using django.test.client.Client to test whether some text shows up when a user is logged in. However, I the Client object doesn't seem to be keeping me logged in.
This test passes if done manually with Firefox but not when done with the Client object.
class Test(TestCase):
def test_view(self):
user.set_password(password)
user.save()
client = self.client
# I thought a more manual way would work, but no luck
# client.post('/login', {'username':user.username, 'password':password})
login_successful = client.login(username=user.username, password=password)
# this assert passes
self.assertTrue(login_successful)
response = client.get("/path", follow=True)
#whether follow=True or not doesn't seem to work
self.assertContains(response, "needle" )
When I print response it returns the login form that is hidden by:
{% if not request.user.is_authenticated %}
... form ...
{% endif %}
This is confirmed when I run ipython manage.py shell.
The problem seems to be that the Client object is not keeping the session authenticated.

Just happened to me when retesting an app that has been working and forgotten for some months.
The solution (apart from updating to Django 1.2) is Patch #11821. In short, Python 2.6.5 has some bugfix in the Cookie module, triggering an edge case bug in the test client.

FWIW, an update to Django 1.2 (I was running 1.1.1 before) fixed it. I have no idea what was broken there, considering when I last ran that test suite about 2 weeks ago, it worked great.

I use RequestContext to get the logged in user into the template context.
from django.shortcuts import render_to_response
from django.contrib.auth.decorators import login_required
from django.template import RequestContext
#login_required
def index(request):
return render_to_response('page.html',
{},
context_instance=RequestContext(request))
and in the template
{% if user.is_authenticated %} ... {{ user.username }} .. {% endif %}
This works as expected (I don't get to this page without logging in, and when I get there, the username is present in response.content) when driven through the test client.

Related

Error using django-user-sessions to close a session in Firefox from Chrome

I have a Django's middleware controlling when a user try logging in and exists another user logged with the same. My code checks if exist one more user logged and shows a template asking if the user want logout or finish the others sessions. If the user choses finish the other session, he is redirected to system. The request.path is useful to accept the requests only login and session/delete views.
def process_request(self, request):
try:
object_list = request.user.session_set.filter(expire_date__gt=timezone.now())
except:
object_list = None
if object_list and len(object_list) > 1:
if request.path != '/users/session/delete' and request.path != '/login/':
return logout(request)
return None
The error happens when from Chrome I try finish one session running on Firefox. It redirects to logout instead logging in the system. When I test the feature in localhost, it performs normally. Looking for the error I found that the 'referer' in request header is different when I use localhost and web from Chrome.
What can I do to fix this?

python requests 403 error [duplicate]

I have some problem for a while now, I'm experiencing CSRF Cookie not set. Please look at the code below:
views.py:
def deposit(request, account_num):
if request.method == 'POST':
account = get_object_or_404(account_info, acct_number=account_num)
form_ = AccountForm(request.POST or None, instance=account)
form = BalanceForm(request.POST)
info = str(account_info.objects.filter(acct_number=account_num))
inf = info.split()
if form.is_valid():
# cd=form.cleaned_data
now = datetime.datetime.now()
cmodel = form.save()
cmodel.acct_number = account_num
# RepresentsInt(cmodel.acct_number)
cmodel.bal_change = "%0.2f" % float(cmodel.bal_change)
cmodel.total_balance = "%0.2f" % (float(inf[1]) + float(cmodel.bal_change))
account.balance = "%0.2f" % float(cmodel.total_balance)
cmodel.total_balance = "%0.2f" % float(cmodel.total_balance)
# cmodel.bal_change=cmodel.bal_change
cmodel.issued = now.strftime("%m/%d/%y %I:%M:%S %p")
account.recent_change = cmodel.issued
cmodel.save()
account.save()
return HttpResponseRedirect("/history/" + account_num + "/")
else:
return render_to_response('history.html',
{'account_form': form},
context_instance=RequestContext(request))
Template file:
<form action="/deposit/{{ account_num }}/" method="post">
<table>
<tr>
{{ account_form.bal_change }}
<input type="submit" value="Deposit"/>
</tr>
{% csrf_token %}
</table>
</form>
I'n stuck, I already cleared the cookie, used other browser but still csrf cookie not set.
This can also occur if CSRF_COOKIE_SECURE = True is set and you are accessing the site non-securely or if CSRF_COOKIE_HTTPONLY = True is set as stated here and here
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def your_view(request):
if request.method == "POST":
# do something
return HttpResponse("Your response")
If you're using the HTML5 Fetch API to make POST requests as a logged in user and getting Forbidden (CSRF cookie not set.), it could be because by default fetch does not include session cookies, resulting in Django thinking you're a different user than the one who loaded the page.
You can include the session token by passing the option credentials: 'include' to fetch:
var csrftoken = getCookie('csrftoken');
var headers = new Headers();
headers.append('X-CSRFToken', csrftoken);
fetch('/api/upload', {
method: 'POST',
body: payload,
headers: headers,
credentials: 'include'
})
From This
You can solve it by adding the ensure_csrf_cookie decorator to your view
from django.views.decorators.csrf import ensure_csrf_cookie
#ensure_csrf_cookie
def yourView(request):
#...
if this method doesn't work. you will try to comment csrf in middleware. and test again.
If you're using DRF, check if your urlpatterns are correct, maybe you forgot .as_view():
So that how mine code looked like:
urlpatterns += path('resource', ResourceView)
And that's how it should like:
urlpatterns += path('resource', ResourceView.as_view())
I came across a similar situation while working with DRF, the solution was appending .as_view() method to the view in urls.py.
try to check if your have installed in the settings.py
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',)
In the template the data are formatted with the csrf_token:
<form>{% csrf_token %}
</form>
This problem arose again recently due to a bug in Python itself.
http://bugs.python.org/issue22931
https://code.djangoproject.com/ticket/24280
Among the versions affected were 2.7.8 and 2.7.9.
The cookie was not read correctly if one of the values contained a [ character.
Updating Python (2.7.10) fixes the problem.
This also occurs when you don't set the form action.
For me, it was showing this error when the code was:
<form class="navbar-form form-inline my-2 my-lg-0" role="search" method="post">
When I corrected my code into this:
<form class="navbar-form form-inline my-2 my-lg-0" action="{% url 'someurl' %}" role="search" method="post">
my error disappeared.
In my case, the problem was that the path to the static files in nginx was incorrectly specified.
sudo tail -F /var/log/nginx/error.log
Check if there are errors in file paths.
I get this error and change this:
<form method="post">
to this:
<form method="POST">
and it's solved !
Just upper case post make the problem !
I have not any issue with this on 127.0.0.1, but when i use 192.168.x.x address this broke my forms.
Problem seems that you are not handling GET requests appropriately or directly posting the data without first getting the form.
When you first access the page, client will send GET request, in that case you should send html with appropriate form.
Later, user fills up the form and sends POST request with form data.
Your view should be:
def deposit(request,account_num):
if request.method == 'POST':
form_=AccountForm(request.POST or None, instance=account)
if form.is_valid():
#handle form data
return HttpResponseRedirect("/history/" + account_num + "/")
else:
#handle when form not valid
else:
#handle when request is GET (or not POST)
form_=AccountForm(instance=account)
return render_to_response('history.html',
{'account_form': form},
context_instance=RequestContext(request))
Check that chrome's cookies are set with default option for websites. Allow local data to be set (recommended).
I was using Django 1.10 before.So I was facing this problem.
Now I downgraded it to Django 1.9 and it is working fine.
I had the same error, in my case adding method_decorator helps:
from django.views.decorators.csrf import csrf_protect
from django.utils.decorators import method_decorator
method_decorator(csrf_protect)
def post(self, request):
...
Make sure your django session backend is configured properly in settings.py. Then try this,
class CustomMiddleware(object):
def process_request(self,request:HttpRequest):
get_token(request)
Add this middleware in settings.py under MIDDLEWARE_CLASSES or MIDDLEWARE depending on the django version
get_token - Returns the CSRF token required for a POST form. The token is an alphanumeric value. A new token is created if one is not already set.
I have just met once, the solution is to empty the cookies.
And may be changed while debugging SECRET_KEY related.
If you are not using {% csrf_token %} tag in the template you are rendering. Django won't set the csrftoken cookie.
To force django to set the csrftoken cookie, add ensure_csrf_cookie decorator in you view.
from django.views.decorators.csrf import ensure_csrf_cookie
#ensure_csrf_cookie
def myview(request):
In my particular case, the problem is that I was using the Django rest_framework and forgot to add the following decorators to my function:
from rest_framework.decorators import api_view, renderer_classes
#api_view(('POST',))
#renderer_classes((JSONRenderer,))
def handle_web_request(request):
...
Just want to point out my case here as someone might cross the same fields.
Forbidden (CSRF cookie not set.): /main/staff/products/validation/create
HTTP POST /main/staff/products/validation/create 403 [0.01, 127.0.0.1:55940]
This thing was driving me insane... So, by commenting CSRF middleware
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
)
it gave me
POST Method not allowed.
That was my hint, after all.
I was sure Post method was present.
Turns out my url_patterns was leading to another view by a regex bug.
So no matter what I was doing in my view, #csrf_exempt #ensure_crsf_cookie, looking for .as_view()... I was looking at the wrong view.
So, if nothing works, make sure your are actually being sent to the right view.
You can get this error while deploing Django application with NO SSL.
If this is the case then putting an SSL reverse-proxy or SSL-configured Ingress in front of backend will solve the problem.
In my case, setting CSRF_COOKIE_SECURE to False wasn't enough but setting it to Null/ not specifying the parameter worked.
Method 1:
from django.shortcuts import render_to_response
return render_to_response(
'history.html',
RequestContext(request, {
'account_form': form,
})
Method 2:
from django.shortcuts import render
return render(request, 'history.html', {
'account_form': form,
})
Because render_to_response method may case some problem of response cookies.
Clearing my browser's cache fixed this issue for me. I had been switching between local development environments to do the django-blog-zinnia tutorial after working on another project when it happened. At first, I thought changing the order of INSTALLED_APPS to match the tutorial had caused it, but I set these back and was unable to correct it until clearing the cache.
In your view are you using the csrf decorator??
from django.views.decorators.csrf import csrf_protect
#csrf_protect
def view(request, params):
....

Redirecting Django built-in login View to a URL taking user's pk as argument

I am trying to redirect to a URL taking user's pk as argument after successful log-in using Django's built-in login view.
Instead of dynamic {{ next }} variable in my login.html I have a generic landing view of logged-in users;
<input type="submit" value="login" />
<input type="hidden" name="next" value="{% url 'userredirect' %}" />
In my urls.py I have;
url(r'^users/', views.users, name='userredirect'),
url(r'^(?P<pk>\d+)/', UserHome.as_view(), name='userhome'),
and in my views.py I have
#login_required
def users(request):
url = reverse('userhome', kwargs={'pk':request.user.id})
return HttpResponseRedirect(url)
What I am doing here is redirect to a detail view that I have named UserHome on the user model after successful login using 2 redirects as I do not know of a way to redirect to UserHome directly (it takes user's pk as argument). It works and I indeed get redirected to the user's homepage when checking via the browser.
Reference;
The "next" parameter, redirect, django.contrib.auth.login
But when running the below test
def test_page_redirects_to_user_home_on_login(self):
"""
Test to assure that the login page redirects to the user's
home page
"""
username = "someusername"
password = "somepassword"
user = User.objects.create_user(username=username,
password=password)
user.save()
response = self.client.post(reverse("userlogin"),
{"username":username,
"password":password},
follow=True)
assert response.path == self.client.get(reverse("userhome",
kwargs={"pk":user.id}
)
)
I get the below failure
AttributeError: 'HttpResponseNotFound' object has no attribute 'path'
It seems the test client gets no page. Would it be that I am using the userredirect view simply for redirecting and the client do not go ahead and get the UserHome class view to its context.
I'm a newbie to Django/Python. Someone please sort this out for me :).
I look forward either to a way where I can redirect directly from the template for login view to UserHome or a way to rewrite my test.
Hard to say without much more insight in your project. Here are a few possibilities and such.
Response has no path
response indeed has no path, you probably wanted this:
assert response.wsgi_request.path == reverse("userhome", kwargs={"pk":user.id})
Include next in your test
You're simulating data from the login form, but you're omitting the next field.
Add it to the POSTed data:
{"username":username,
"password":password,
"next": '/users/',}
Take a look what's in the response
It might help to see what's in the response in your test. For example:
print(response.redirect_chain)
Perhaps you're not even reaching the login page?
Are you missing LOGIN_URL in your settings.py?
LOGIN_URL = '/login/'
Without it, you'll be redirected to '/accounts/login/', which might be the 404 you're seeing.
Finaly - why? :)
Perhaps you have some special use case, but I'd usually read user's id (a.k.a. pk) from request.user. That way I (for example) can't access example.com/<your_id> and access your homepage. Of course, that might be just what you intend. In that case I'd still have a separate URL for current user, it will probably pay off later. Something like this:
...
url(r'^/', UserHome.as_view(), name='userhome'),
url(r'^(?P<pk>\d+)/', UserHome.as_view(), name='userhome'),
...)
class UserHome(DetailView): # also protect with some LoginRequiredMixin
model = User
def get_object(self, queryset=None):
if queryset is None:
queryset = self.get_queryset()
id = self.kwargs.get('pk', self.request.user.id)
return queryset.filter(id=id).get()
First things first: The error you get is because the line
response = self.client.post(reverse("userlogin"),
{"username":username,
"password":password},
follow=True)
raises a 404 error, hence resonse is a HttpResponseNotFound.
Before testing anything else is it a good practice to first test that your request was successful. Something along the line of:
self.assertEqual(response.status_code, 200)
Also, you are hard-coding url's which goes against DRY and is often the source for trouble (maybe it is the case here).
It would be better to name all your urls:
url(r'^users/', views.users, name='user_redirect'),
and then use this in your template
<input type="hidden" name="next" value="{% url 'user_redirect' %}" />
and this in your view
from django.core.urlresolvers import reverse
#login_required
def users(request):
url = reverse('userhome', kwargs={'pk': request.user.id})
return HttpResponseRedirect(url)
And finally, you are taking an unnecessary step with the redirect. Assuming UserHome is a DetailView on User, you could have this code:
##urls.py
url(r'^users/', UserHome.as_view(), name='userhome')
##views.py
from django.views.generic import DetailView
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
class UserHome(DetailView):
model = User
def get_object(self, queryset=None):
return self.request.user
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(UserHome, self).disatch(*args, **kwargs)
This would also ensure that no user accesses another user's "userhome".
Doing all this should help you find what went wrong with your code. Good luck!

Django return successful/failure after processing POST data

How do I let client side know that Django has processed POST data successfully or not? If yes, it will reset the current views, if not it should raise a notification alert.
def test_page(request):
if request.method == "POST":
newgroup = request.POST["group"]
print "Received result"
# assert validateGroup(newgroup)
t = Trial(group = newgroup, time = timezone.now(), subject = request.user)
t.save()
return render_to_response('interface/test.html',\
context_instance=RequestContext(request))
This is what I did, but definitely there's no try and catch block.
You can use the Django messages framework
Quite commonly in web applications, you need to display a one-time
notification message (also known as “flash message”) to the user after
processing a form or some other types of user input.
https://docs.djangoproject.com/en/1.7/ref/contrib/messages/
Alternatively, you can add an extra variable to your template and display errors in the page.
def test_page(request):
if request.method == "POST":
try:
# form-processing code
except ValueError:
# go back to previous page and show errors
return render(request, 'previous_page.html',
{'errors'=['Problem!']})
# this will be rendered when above exception is not encountered
return render_to_response('interface/test.html',\
context_instance=RequestContext(request))
And in the template (previous_page.html), you can do this:
{% for err in errors %}
<div class="error-box">
{ err }
</div>
{% endfor %}
Where the error-box class highlights your error messages as dismiss-able notifications or however you want to show it.

How can i have aspect oriented programming thing in django python

I am using django and i have the very complex matrix of permissions . Suppose i have 10 set of permission groups like
Permissions = [basic, medium, advanced , very advanced , admin , superadmin , public , etc]
The other is thing is i have 10 Models and all models have different view, edit permissions for each group so in whole the rules becomes in asort of marix of 10 X 10 and more.
I have all the code now in place without permissions. I really don't want to touch the current code at all places for permission checking.
I need the permission checking at following places
The permssions will be based on logged on user permission group
In templates where we will show edit,insert,delete button based on permissions
In views before showing it , a check is made if user can view/update/delete it
Is there any way that before showing any of the view for delete,update,insert the system automatically checks from the permission matrix without writing any code in the view.
Then only thing left will be in the templates that can write if required
I believe you are simply looking for the permissions variable which is provided by Django within the templates
https://docs.djangoproject.com/en/1.5/topics/auth/default/#permissions
{% if perms.foo %}
<p>You have permission to do something in the foo app.</p>
{% if perms.foo.can_vote %}
<p>You can vote!</p>
{% endif %}
{% if perms.foo.can_drive %}
<p>You can drive!</p>
{% endif %}
{% else %}
<p>You don't have permission to do anything in the foo app.</p>
{% endif %}
Example middleware:
from django import http
class PermissionMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
# you should somehow derive this from the view function and/or view args/kwargs
your_object = SomeThing.objects.get(...)
if not request.user.has_perm('name_of_your_object.permission'):
return http.HttpResponseForbidden()
In views, you can use permission decorators!
They look like this:
from django.contrib.auth.decorators import login_required
#login_required
def my_view(request):
...
Or this, for more than a login, i.e. permissions:
from django.contrib.auth.decorators import permission_required
#permission_required('polls.can_vote')
def my_view(request):
...
You can also define your own decorators, if you need more complex scenarios (these are the ones I made for myself):
from django.contrib.auth.decorators import user_passes_test
from django.core.exceptions import PermissionDenied
def group_required(*group_names):
"""Requires user membership in at least one of the groups passed in."""
def in_groups(u):
if u.is_authenticated:
if u.groups.filter(name__in=group_names).exists() | u.is_superuser:
return True
return False
return user_passes_test(in_groups)
def superuser_only(function):
"""Limit view to superusers only."""
def _inner(request, *args, **kwargs):
if not request.user.is_superuser:
raise PermissionDenied
return function(request, *args, **kwargs)
return _inner
def has_group(user, group_name):
return user.groups.filter(name=group_name).exists()
...and use them like this:
#login_required
#group_required('A', 'B')
def my_view(request):
....
For use in templates, you can define your own template tags:
from django import template
register = template.Library()
#register.filter
def multiply(value, arg):
return (value * arg)

Categories

Resources