The following code works locally when I use Django's development server, but I am running into intermittent bugs in production with Nginx and Gunicorn.
views.py
def first_view(request):
if request.method == "POST":
# not using a django form in the template, so need to parse the request POST
# create a dictionary with only strings as values
new_post = {key:val for key,val in request.POST.items() if key != 'csrfmiddlewaretoken'}
request.session['new_post'] = new_mappings # save for use within next view
# more logic here (nothing involving views)
return redirect('second_view')
def second_view(request):
if request.method == 'POST':
new_post = request.session['new_post']
# ... more code below
# render template with form that will eventually post to this view
I will sometimes receive a KeyError after posting to the second view. Based on the documentation on when sessions are saved, it seems like the session variable should be saved since it is modifying the session directly. Also, if I take the sessionid provided the error page's debug panel and access the session via Django's API, I can see the 'new_post' session variable
python manage.py shell
>>> from django.contrib.sessions.backends.db import SessionStore
>>> s = SessionStore(session_key='sessionid_from_debug_panel')
>>> s['new_post']
# dictionary with expected post items
Is there something I'm missing? Thanks in advance for your help!
Ok, I finally figured out the issue.
By default Django uses cached sessions when you create a new project using django-admin startproject project_name_here
In the documentation it warns that caching should only be used in production if using the Memcached cache backend since the local-memory cache backend is NOT multi-process safe. https://docs.djangoproject.com/en/1.11/topics/http/sessions/#using-cached-sessions
The documentation also cautions against local memory caching in the deployment checklist: https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/#caches
I changed the SESSION_ENGINE in settings.py to 'django.contrib.sessions.backends.db' and the error went away. https://docs.djangoproject.com/en/1.11/ref/settings/#session-engine
Hope this is helpful to someone else!
Related
Currently I'm working on a project where I'm using React as frontend and Django as backend. In react i created a login page, where I through axios send files to django, and in this route index i sent all the information from the login page, and define a session variable reqeuset.session['id']=150 in it. But when i call reqeust.session['id'] in a diffrent route, it says that it is type None.
This is the code:
#api_view(['POST'])
def index(request):
data=request.data.get('data')
korisnik = Korisnik.objects.filter(korisnicko_ime=data.get('user'),
if korisnik.exists():
korisnik_json=serializers.serialize('json',korisnik)
request.session['id']=150
print(request.session['id'])
# if not request.session.session_key:
# request.session.create()
return HttpResponse(korisnik_json)
else: return HttpResponse(status=404)
#api_view(['GET'])
def korisnik(request,id):
print(request.session.get('id'))
korisnik=Korisnik.objects.filter(pk=id)
korisnik_json=serializers.serialize('json',korisnik)
return HttpResponse(korisnik_json)
This is the output from python console
Image
Also note, I'm using django restframework. Did anyone, have this problem before, any help is appreciated.
I had faced a similar issue. Please check my answer here.
React Django REST framework session is not persisting/working
Am trying to keep track of AnonymousUsers by their session information (if possible).
In older versions of Django, I could do something like:
def my_view(request):
# in case the user wasn't logged in, create/save a session
if not request.session.session_key:
request.session.save()
# would give me the key and on the next load it would persist
session_key = request.session.session_key
But with 1.6 (and I've been out of the game for a while) this results in a new unique session ID each time the request is put through. There is no persistence. I've tried to do a little reading but am going in circles as I'm out of Django practice.
How do I have a session persist? Do I need to write my own cookie handling?
So, after I started reading through the source code I found myself on the global_settings.py file and found this gem:
SESSION_SAVE_EVERY_REQUEST = True
When I added that to the settings.py file my problems were solved. AnonymousUsers got a session_key. Yipee!
I'm working on a Django site hosted on an Apache server with mod_wsgi.
The site is only on https as we have Apache redirect any http requests to https.
The project I'm working on is called Skittle.
I have a custom user model called SkittleUser which inherits from AbstractBaseUser and is set as the AUTH_USER_MODEL in our settings.py file.
os.environ['HTTPS'] = "on" is set in the wsgi.py file.
SESSION_COOKIE_SECURE = True and CSRF_COOKIE_SECURE = True are both set in settings.py
The issue that we are having right now is that logging in as a user is unreliable.
When you go to the login page, some times it works while other times it doesn't.
Then while browsing the site, you will suddenly lose your session and be kicked down to an anonymous user.
We are currently running our test site here if anybody wants to take a look:
https://skittle.newlinetechnicalinnovations.com/discover/
Our production site is at www.dnaskittle.com but does not yet incorporate user logins as the feature doesn't work.
A test user:
email: test#dnaskittle.com
password: asdf
If the login does not work, you will see in the top right "Welcome, Login" in which case, just try clicking on Login again and use the same credentials.
It may take 5-6 times of doing that process before you will actually get logged in.
You will know it works when you see "Welcome Tester, Logout, My Genomes"
After you are logged in, it may stick for a while, but browsing around to other pages will eventually kick you back off.
There is no consistent amount of pages that you can go through before this happens, and it doesn't happen on any specific page.
Any insights on this would be greatly appreciated.
Also of note, going to the Django admin page (which is not our code, but base django code) has the same issue.
I've gotten this issue sorted out now.
Users can not login while on HTTPS using while using the listed setup.
What I did:
In settings.py add:
SESSION_SAVE_EVERY_REQUEST = True
SESSION_COOKIE_NAME = 'DNASkittle'
I also wiped the current django_sessions database in case that was causing issues with old lingering data.
I did not setup extra middleware or SSLRedirect, and everything is working all ship shape.
It's little longer and complex the SSL system. to handle the session/login properly with https you can set a configuration with the session_cookies.
settings.py:
ENABLE_SSL=False #in the debug mode in the production passed it to True
MIDDLEWARE_CLASSES = (
'commerce.SSLMiddleware.SSLRedirect', #)
# the session here is to use in your views when a user is connected
SESSION_COKIE_NAME='sessionid'
#the module to store sessions data
SESSION_ENGINE='django.contrib.sessions.backends.db'
#age of cookie in seconds (default: 2 weeks)
SESSION_COOKIE_AGE=7776000 # the number of seconds in 90 days
#whether a user's session cookie expires when the web browser is closed
SESSION_EXPIRE_AT_BROWSER_CLOSE=False
#whether the session cookie should be secure (https:// only)
SESSION_COOKIE_SECURE=False
SSLMiddleware is a file you going to define in your project like.
SSLMiddleware.py:
from django.conf import settings
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
SSL='SSL'
class SSLRedirect:
def process_view(self,request,view_func,view_args,view_kwargs):
if SSL in view_kwargs:
secure =view_kwargs[SSL]
del view_kwargs[SSL]
else:
secure=False
if not secure == self._is_secure(request):
return self._redirect(request,secure)
def _is_secure(self,request):
if request.is_secure():
return True
if 'HTTP_X_FORWARD_SSL' in request.META:
return request.META['HTTP_X_FORWARD_SSL'] == 'on'
return False
def _redirect(self,request,secure):
protocol = secure and "https" or "http"
newurl ="%s://%s%s" % (protocol, request, request.get_full_path())
if settings.DEBUG and request.method=="POST":
raise RuntimeError, \
return HttpResponsePermanentRedirect(newurl)
Now in your urls which should handle your logins or connection add the line
urls.py:
from project import settings
urlpatterns += patterns('django.contrib.auth.views',
(r'^login/$','login',{'template_name':'registration/login.html','SSL':settings.ENABLE_SSL},'login'),
Try to adapt this to your code. and don't forget to turn ENABLE_SSL to True
For users login with HTTPS, you have to enable SSL and use code that matches your case to use it. For the user session you can use this:
First check if you have in settings.py:
INSTALLED_APPS= ('django.contrib.sessions',)
and use the request.sessions in your file.py:
request.session['id']='the_id_to_store_in_browser' or 'other_thing'
Here you use request.session like a special SessionStore class which is similar to python dictionary.
In your views.py for example before rendering a template or redirecting test the cookie with the code:
if :
# whatever you want
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
return HttpResponseRedirect(up-to-you)
else:
# whatever you want
request.session.set_test_cookie()
return what-you-want
with this code, the user session will stick a wide, depending on your SESSION_COOKIE_AGE period.
I have a Django app which records users' product choices for both authenticated users. My intention is to use the request.session.session_key variable to associate anonymous data with a user if they decide to register later, a la this post:
Django storing anonymous user data
However, it seems that the session key changes when the user logs in/ registers so the session key can no longer be associated with the user. Is this the correct behaviour of the Django session framework. Is there a solid way to achieve the functionality I'm looking for?
Any help much appreciated.
In settings.py
SESSION_ENGINE = 'youapp.session_backend'
in directory youapp in file session_backend.py
from django.contrib.sessions.backends.db import SessionStore as DbSessionStore
class SessionStore(DbSessionStore):
def cycle_key(self):
pass
And session not changed after login
While the approach suggested by nnmware may work for this particular case, there is a better one.
Instead of just doing nothing inside cycle_key, we should call the super method and then save the session.
Because if you look inside the original cycle_key function you will see that the data from the old session is copied to the new one, but is not actually saved.
In settings.py
SESSION_ENGINE = 'yourapp.session_backend'
Check that SESSION_ENGINE is pointing at a module (.py file), but not to the backend class!
Now, in your 'yourapp/session_backend.py' do the following:
from django.contrib.sessions.backends.db import SessionStore as DbSessionStore
class SessionStore(DbSessionStore):
def cycle_key(self):
super(SessionStore, self).cycle_key()
self.save()
One of the solutions would also be to update old session data in the Session store:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from django.contrib.sessions.backends.db import SessionStore as DbSessionStore
from shop.models.cart import Cart
class SessionStore(DbSessionStore):
def cycle_key(self):
old_session_key = super(SessionStore, self).session_key
super(SessionStore, self).cycle_key()
self.save()
Cart.objects.filter(session_key=old_session_key).update(session_key=self.session_key)
Perhaps this is completely normal behaviour, but I feel like the django_session table is much larger than it should have to be.
First of all, I run the following cleanup command daily so the size is not caused by expired sessions:
DELETE FROM %s WHERE expire_date < NOW()
The numbers:
We've got about 5000 unique visitors (bots excluded) every day.
The SESSION_COOKIE_AGE is set to the default, 2 weeks
The table has a little over 1,000,000 rows
So, I'm guessing that Django also generates session keys for all bots that visits the site and that the bots don't store the cookies so it continuously generates new cookies.
But... is this normal behaviour? Is there a setting so Django won't generate sessions for anonymous users, or atleast... no sessions for users that aren't using sessions?
After a bit of debugging I've managed to trace cause of the problem.
One of my middlewares (and most of my views) have a request.user.is_authenticated() in them.
The django.contrib.auth middleware sets request.user to LazyUser()
Source: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/middleware.py?rev=14919#L13 (I don't see why there is a return None there, but ok...)
class AuthenticationMiddleware(object):
def process_request(self, request):
assert hasattr(request, 'session'), "The Django authentication middleware requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
request.__class__.user = LazyUser()
return None
The LazyUser calls get_user(request) to get the user:
Source: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/middleware.py?rev=14919#L5
class LazyUser(object):
def __get__(self, request, obj_type=None):
if not hasattr(request, '_cached_user'):
from django.contrib.auth import get_user
request._cached_user = get_user(request)
return request._cached_user
The get_user(request) method does a user_id = request.session[SESSION_KEY]
Source: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/init.py?rev=14919#L100
def get_user(request):
from django.contrib.auth.models import AnonymousUser
try:
user_id = request.session[SESSION_KEY]
backend_path = request.session[BACKEND_SESSION_KEY]
backend = load_backend(backend_path)
user = backend.get_user(user_id) or AnonymousUser()
except KeyError:
user = AnonymousUser()
return user
Upon accessing the session sets accessed to true:
Source: http://code.djangoproject.com/browser/django/trunk/django/contrib/sessions/backends/base.py?rev=14919#L183
def _get_session(self, no_load=False):
"""
Lazily loads session from storage (unless "no_load" is True, when only
an empty dict is stored) and stores it in the current instance.
"""
self.accessed = True
try:
return self._session_cache
except AttributeError:
if self._session_key is None or no_load:
self._session_cache = {}
else:
self._session_cache = self.load()
return self._session_cache
And that causes the session to initialize. The bug was caused by a faulty session backend that also generates a session when accessed is set to true...
Is it possible for robots to access any page where you set anything in a user session (even for anonymous users), or any page where you use session.set_test_cookie() (for example Django's default login view in calls this method)? In both of these cases a new session object is created. Excluding such URLs in robots.txt should help.
For my case, I wrongly set SESSION_SAVE_EVERY_REQUEST = True in settings.py without understanding the exact meaning.
Then every request to my django service would generate a session entry, especially the heartbeat test request from upstream load balancers. After several days' running, django_session table turned to a huge one.
Django offers a management command to cleanup these expired sessions!