Sessions in Django

In the last chapter Cookies in Django, we have learned how cookies allow us to store data in browser easily. Although no doubt cookies are useful, they have following problems.

  1. An attacker can modify contents of a cookie that could potentially break your application
  2. We can’t store sensitive data.
  3. We can only store a limited amount of data in cookies. Most browsers don’t allow a cookie to store more than 4KB of data. Breaking data into multiple cookies causes too much overhead in each request. Further, you can’t even rely on a number of cookies allowed by the browser for each domain.

We can overcome these problems easily using Sessions. This is how session work:

When we use sessions the data is not stored directly in the browser instead it is stored in the server. Django creates a unique random string called session id or SID and associates SID with the data. The server then sends a cookie named sessionid containing SID as value to the browser. On requesting a page, the browser sends the request along with the cookie with SID to the server. Django then uses this SID to retrieve session data and makes it accessible in your code. SID generated by Django is a 32 characters long random string, so it is almost impossible to guess by an attacker.

Setting Up Sessions

In Django, sessions are implemented using middleware. Open settings.py file and locate MIDDLEWARE list variable. If it has 'django.contrib.sessions.middleware.SessionMiddleware' as one of the elements then you are good to go. If you can’t see it, just add it now to the MIDDLEWARE list. At this point, the MIDDLEWARE list should look like this:

TGDB/django_project/django_project/settings.py

The 'django.contrib.sessions.middleware.SessionMiddleware' is responsible for generating unique SID.

Django provides an app called 'django.contrib.sessions' whose role is to store the session data into the database. So make sure you have 'django.contrib.sessions' in the INSTALLED_APPS list. If for some reason you don’t have 'django.contrib.sessions' in INSTALLED_APPS, add it right now and run python manage.py migrate command. This command will create the necessary table to store the session data.

TGDB/django_project/django_project/settings.py

Testing Sessions

We have already discussed that users can configure their browsers to not accept any cookie. As a result, Django provides some convenience methods to check cookies support in the browser. The request.sessions object provides the following three methods to check cookies support in the browser.

Method What it does?
set_test_cookie() sets the cookie in the browser
test_cookie_worked() returns True if browser accepted the cookie(a browser accepted the cookie means, it has sent the cookie to the server in the next request), Otherwise False
delete_test_cookie() delete the test cookie

Let’s try them!

Open blog app’s views.py file and add following two views at the end of the file.

TGDB/django_project/blog/views.py

The test_session() view sets the cookie and test_delete() checks whether the browser accepted the cookie or not.

Add the following two URL patterns at the beginning of urlpatterns list in blog’s urls.py.

TGDB/django_project/blog/urls.py

Point your browser to http://127.0.0.1:8000/test-session/ and you should see a page like this:

Open Developer Tools hitting Ctrl+Shift+J and visit Application tab again. If your browser accepted the cookie it should look like this:

Now visit http://127.0.0.1:8000/test-delete/, if everything went fine, you would see “Cookie test passed” response.

On the other hand, if you get “Cookie test failed” message. Check the browser settings and allow websites to save cookies.

Reading and Writing Session Data

To read and write session data we use session attribute of the request object. The session attribute acts like a dictionary.

Here is how we can save, read, and delete session data.

Open blog app’s views.py and add the following three views at the end of the file.

TGDB/django_project/blog/views.py

here is what happen when save_session_data() view is called.

  1. 'django.contrib.sessions.middleware.SessionMiddleware' middleware creates a new random session id or SID and associates the session data with it.
  2. 'django.contrib.sessions.middleware.SessionMiddleware' uses 'django.contrib.sessions' app to store the session data in the database
  3. At last, a cookie named sessionid with a random value(i.e SID) generated in step 1, is sent to the browser.
  4. From now on, the browser would send this sessionid cookie with every request to the server, allowing Python code to access session data in views using request.session

Before we test this code add the following URL pattern to the blog’s urls.py file:

TGDB/django_project/blog/urls.py

Visit http://127.0.0.1:8000/save-session-data/ and you should get the following output.

At this point, the browser should have a cookie named sessionid with a random SID.

To access session data visit http://127.0.0.1:8000/access-session-data/ and you should get the following output:

To delete the session data visit http://127.0.0.1:8000/delete-session-data/.

Visit http://127.0.0.1:8000/access-session-data/ again and you should see “No session data”.

It is important to note that deleting session data from the database doesn’t delete the session cookie in the browser. As a result, if you visit http://127.0.0.1:8000/save-session-data/, Django will reuse the SID sent by the browser to associate the session data with it again.

Modifying Session Data

Here comes one of the most important thing you need to remember while using sessions:

Django saves the session data into the database only when it is modified.

By modified, Django means following actions:

1. Adding new key-value pair to request.session dictionary.

2. Changing the value of an existing key.

3. Deleting the a key-value pair.

Unfortunately, the following is not a modification.

But why ? Here we are not modifying request.session dictionary instead we are modifying request.session['items_to_buy'].

If you want Django to save these changes to the database, you have to set modified attribute of session object to True.

To change this default behavior permanently, set SESSION_SAVE_EVERY_REQUEST to True in settings.py. When SESSION_SAVE_EVERY_REQUEST is set to True Django will save session data to the database on every request, even if you don’t modify session data at all.

Another important point worth mentioning here is that Django sends the session cookie to the browser only when session data is modified, in the process also updates the cookie expiry time.

If SESSION_SAVE_EVERY_REQUEST is set to True, Django will send the session cookie on every request.

Sessions API

Django provides an API which allows you to access session data outside of views.

The long string passed to the get() method is SID. You can copy it either from the browser or session_key column from the django_session table. By default, session data is stored in the django_session table. It consists of three columns as follows:

Column Explanation
session_key To store unique random session id or SID
session_data Django stores the session data in the encoded format. To get the data the raw data use get_decoded() method of the session object
expire_date expiration date of the session cookie

Once you have access to session object you can use it to query for other information.

As you can see, get_decoded() method returns an empty dictionary because in my case there is no data associated with the given SID. Had there been data associated with the SID the get_decoded() would have returned a dictionary like this:

Setting Expiration Time for Session Cookie

We can control session cookies expiration time by setting the following two variables in settings.py file:

Variable Name Explanation
SESSION_COOKIE_AGE This variable is used to set cookie expiration time in seconds. By default, it is set to 1209600 seconds or 2 weeks. If SESSION_EXPIRE_AT_BROWSER_CLOSE is not set then Django uses this variable to set cookie expiration time. Here is how you can set session cookie expiration time to 5 days: SESSION_COOKIE_AGE = 3600*24*5
SESSION_EXPIRE_AT_BROWSER_CLOSE , This variable controls whether to expire the session cookie when the user closes the browser. By default it is set to False. If set to True, session cookie lasts until the browser is closed, irrespective of the value of SESSION_COOKIE_AGE.

Clearing Sessions

Session data accumulates pretty fast especially if your site relies on sessions for different functionalities. It is a good idea to search for expired sessions regularly clean them before they start cluttering up your tables. Fortunately, Django provides clearsessions command to clear expired sessions from the tables.

Let’s conclude this chapter by creating a small project – The lousy login form.

The Lousy Login

Sessions are commonly used to create login systems. The motive of this section is to show you how login systems are implemented using sessions.

Open blog’s views.py and add lousy_login(), lousy_secret() and lousy_logout() views to the end of the file.

TGDB/django_project/blog/views.py

Next create three new templates lousy_login.html, lousy_secret_page.html and lousy_logout.html with the following content.

TGDB/django_project/blog/templates/blog/lousy_login.html

TGDB/django_project/blog/templates/blog/lousy_secret_page.html

TGDB/django_project/blog/templates/blog/lousy_logout.html

Finally, add the following three URL patterns at the beginning of urlpatterns list in blog’s urls.py.

TGDB/django_project/blog/urls.py

Open the browser and visit http://127.0.0.1:8000/lousy-secret/, you will be redirected to http://127.0.0.1:8000/lousy-login/ because currently we are not authorized to view this page.

Type wrong a username/password combination or just hit “Submit” without entering anything and you would get “Error wrong username/password” error above the form as follows:

Enter correct username/password (i.e root and pass) and hit submit, this time you will be redirected to http://127.0.0.1:8000/lousy-secret/.

Click the logout link to logout from the page and this time you will be redirected to http://127.0.0.1:8000/lousy-logout/.

Obviously, this is a lousy way of logging in users. Nonetheless, it perfectly describes the role of sessions in creating login systems. Django has an authentication framework which makes logging in user into your app pretty easily. Django authentication framework is discussed in the next chapter.

Note: To checkout this version of the repository type git checkout 28a.

Leave a Comment