Monthly Archives: October 2014

Validating a login page using JQuery Validation: Demo code

Validating a login form with the JQuery Validation plugin. The fields are first validated when the either fields blur or the form is submitted. Any fields found to have an error are then validated onkeyup.

This is a whittled-down version of top example on the main JQuery example page, with two changes: Instead of being hard-coded into the configuration and error messages, the values are set into variables. And both the password and username are checked for both minimum and maximum lengths.

(Try this out on JSFiddle.)

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="utf-8">
   <title>jQuery validation login basic example</title>
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
   <meta name="viewport" content="width=device-width"/>
   <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
   <script src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.13.0/jquery.validate.min.js"></script>
   <script>
      //Configuration
         var minUserLen = 5, maxUserLen = 30;
         var minPassLen = 8, maxPassLen = 4096;
         var usernameMsg = "Username must be between " + minUserLen + " and " +
                           maxUserLen + " characters, inclusive.";
         var passwordMsg = "Password must be between " + minPassLen + " and " +
                           maxPassLen + " characters, inclusive.";
         jQuery.validator.setDefaults({
            debug: true,      //Avoids form submit. Comment when in production.
            success: "valid",
            submitHandler: function() {
               alert("Success! The form was pretend-submitted!");
            }
         });
      $(document).ready(function() {
         // validate signup form on keyup and submit
         $("#signupForm").validate({
            rules: {
               username: {
                  required: true,
                  minlength: minUserLen,
                  maxlength: maxUserLen

               },
               password: {
                  required: true,
                  minlength: minPassLen,
                  maxlength: maxPassLen

               },
            },
            messages: {
               username: {
                  required: "Username required",
                  minlength: usernameMsg,
                  maxlength: usernameMsg
               },
               password: {
                  required: "Password required",
                  minlength: passwordMsg,
                  maxlength: passwordMsg
               }
            }
         });
      });
   </script>
   </head>
<body>
   <p>Validating a login form with the <a href="http://jqueryvalidation.org/">JQuery Validation</a> plugin. The fields are first validated when the form either <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Attribute/textbox.onblur"><code>onBlur</code></a> or when submitted. Any fields found to have an error are then validated <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window.onkeyup"><code>onkeyup</code></a>. This is a whittled-down version of top example on the <a href="http://jqueryvalidation.org/files/demo/">main JQuery example page</a>.</p>

   <form id="signupForm">
      <fieldset>
         <legend>Fake login please</legend>
         <p>
            <label for="username">Username</label>
            <input id="username" name="username" type="text">
         </p>
         <p>
            <label for="password">Password</label>
            <input id="password" name="password" type="password">
         </p>
         <p>
            <input class="submit" type="submit" value="Submit">
         </p>
      </fieldset>
   </form>
</html></body>

The most important applications I use on Windows, for programming and otherwise

VirtuaWin: Virtual desktops. Will be rendered moot by Windows 10.

Regex Buddy for regular expression analysis and diagnostics. WildEdit for making a single replacement in multiple files.

TCC/LE command prompt. An excellent and free cmd-prompt shell. They also have Take Command for tabbed, multiple shell windows, but it’s a hundred dollar license, and I’ve had significant problems with it.

The free Nettalk IRC client, which, along with Stack Overflow, is my primary source of online programming support.

The most important programs for protecting my identity, data, and online privacy.

  • 1-Password: Easily manage ridiculously-random passwords for every website you visit.
  • PWGen: Generate random passwords in bulk, and choose the best among them.
  • AxCrypt: Encryption for private documents (my secrets documents).
  • Peer Block: Prevent millions of suspect computers from communicating with yours, when doing anything private.

Xplorer2: A great Windows Explorer replacement, although I still use Windows Explorer probably a third of the time. Xplorer2 manages multiple windows in tabs, and multiple “sets” of tabs can be managed as well.

SFTP Net Drive Free and PuTTY are how I shell into my remote Digital Ocean Ubuntu box

Sublime Text 3, which I just started using. PhraseExpress for text completion.

Rapid Environment Editor: Environment variable editor and inspector.

ClickCharts Free flowchart and diagram creator.

From fixtures to factories: Updating testing code from manual data fixtures to Fixture Boy

In part one of my Django authentication/testing tutorial, the testing code originally contained a manually-created fixture of test users, including a function to directly save one to the database.

TEST_USER_GIBBERISH = {
    "username": "3u9osuhceo",  "password":  "8um48eut93te",
    "first_name": "xrc4b46id", "last_name": "agc4598pdb9uyfn",
    "birth_year": 1931,        "email":     "oaudetnnao@oth09867.com"}
TEST_USER_KERMIT = {
    "username":   "kermit",    "password":  "timrek",
    "first_name": "Kermit",    "last_name": "The Frog",
    "birth_year": 1955,        "email":     "kermit@muppets.com"}
TEST_USER_FOZZIE = {
    "username":   "fozzie",    "password":  "eizzof",
    "first_name": "Fozzie",    "last_name": "Bear",
    "birth_year": 1976,        "email":     "fozzie@muppets.com"}
TEST_USERS = [TEST_USER_GIBBERISH, TEST_USER_KERMIT, TEST_USER_FOZZIE]

For the purpose of the authentication tutorial, manual fixtures are just fine. In the real world, they should be avoided. As described in Carl Meyer’s excellent talk on Django testing, from PYCON 2012, at 16:20, manually-created fixtures are difficult to maintain, slow to load, and dangerous to share among tests.

Instead, model factories should be used. I’m using Factory Boy. The main benefits of model factories are:

  • When the model changes, the factory automatically detects this, always returning a properly formatted object.
  • Although you must specify defaults for each field, in the definition of each factory, individual tests do not need to provide specific values, expect for only those attributes requiring special values in order to pass. See the top-most example on the main Factory Boy documentation page.
  • Objects can be easily created in memory only, or saved to the database, as required by your tests.

The question is moot.

In addition to fixtures, I also changed it so the user objects are no longer held locally; they are now only created in the database. I thought it was important to have local copies of these objects, so the database values could be compared against them. It’s not (and doing so created problems because the local object was static)-trust Django and Factory Boy to insert them properly. This therefore renders the model tests moot, so the entire ModelsTestCase class has been eliminated, and the file has therefore been renamed to `test__utilities.py`

Below is the original, manual-fixture file in its entirety, test__utilities.py. The updated, Fixture Boy version, is now in part one of the tutorial.

"""
Tests for models.py.

DEPENDS ON TEST:  *nothing* (must not depend on any test_*.py file)
DEPENDED ON TEST: test__profile.py

To run these tests:
  1. source /home/myname/django_files/django_auth_lifecycle_venv/bin/activate
  2. cd /home/myname/django_files/django_auth_lifecycle/
  3. python -Wall manage.py test auth_lifecycle.test__utilities

Running tests documentation:
- https://docs.djangoproject.com/en/1.7/topics/testing/overview/#running-tests

Information on '-Wall' is at the bottom of this section in the official
Django documentation:
- https://docs.djangoproject.com/en/1.7/topics/testing/overview/#running-tests

If the output is too verbose, try it again without '-Wall'.

If a test fails because the test database cannot be created, grant your
database user creation privileges:
- http://dba.stackexchange.com/questions/33285/how-to-i-grant-a-user-account-permission-to-create-databases-in-postgresql

pylint auth_lifecycle.test__utilities > pylint_output.txt
pylint auth_lifecycle.test__view_birth_stats > pylint_output.txt
pylint auth_lifecycle.test__view_user_profile > pylint_output.txt
"""
from django.test                import TestCase
from auth_lifecycle.models      import UserProfile
from django.contrib.auth.models import User
from .models                    import MIN_BIRTH_YEAR

TEST_USER_GIBBERISH = {
    "username": "3u9osuhceo",  "password":  "8um48eut93te",
    "first_name": "xrc4b46id", "last_name": "agc4598pdb9uyfn",
    "birth_year": 1931,        "email":     "oaudetnnao@oth09867.com"}
"""
The main test user used by all tests, containing only gibberish values.

The reason for the gibberish values is to easily and confidently
extract them from the webpage, without having to somehow
delineate them, such as with marker comments like

<!-- UNITRQD-start: email -->email@something.com<!-- UNITRQD-end -->

See this comment, and the entire question-post it's part of:
- http://codereview.stackexchange.com/q/66898/35364#comment121964_66917
"""
TEST_USER_KERMIT = {
    "username":   "kermit",    "password":  "timrek",
    "first_name": "Kermit",    "last_name": "The Frog",
    "birth_year": 1955,        "email":     "kermit@muppets.com"}
TEST_USER_FOZZIE = {
    "username":   "fozzie",    "password":  "eizzof",
    "first_name": "Fozzie",    "last_name": "Bear",
    "birth_year": 1976,        "email":     "fozzie@muppets.com"}
TEST_USERS = [TEST_USER_GIBBERISH, TEST_USER_KERMIT, TEST_USER_FOZZIE]
"""
An array of users for testing purposes only. Each element is a dictionary
containing all non-id attributes in both the User and UserProfile models.

Equal to `[TEST_USER_GIBBERISH, TEST_USER_KERMIT, TEST_USER_FOZZIE]`

Creating the test users in this way allows us to centralize their
attributes, so we don't have hard-coded passwords in multiple places
throughout the testing code, for example.

User model attributes:

https://docs.djangoproject.com/en/1.7/topics/auth/default/#user-objects
"""
def _insert_test_user(test_user):
    """
    Insert and save a single test user.

    Private function for this file only.
    """
    user = User.objects.create_user(
        username=test_user['username'], password=test_user['password'],
        first_name=test_user['first_name'],
        last_name=test_user['last_name'], email=test_user['email'])

    """
    Creating the user with
        user = User(...)
        user.save()
    does not properly hash and salt the password. Although it does save it
    to the database, attempting
        self.client.login(username='theusername', password='thepassword')
    fails (returns False)

    https://docs.djangoproject.com/en/1.7/ref/contrib/auth/#django.contrib.auth.models.UserManager.create_user
    http://stackoverflow.com/questions/26306424/cant-login-a-just-created-user-in-a-django-test
    """

    #The user's id is automatically created by the database. To function
    #as the foreign key, it must be duplicated to the profile.

    profile = UserProfile(user_id=user.id, birth_year=test_user['birth_year'])
    profile.save()

def create_insert_test_users():
    """Insert all <link to TEST_USERS> into the test-database."""
    for  test_user in TEST_USERS:
        #print(test_user)
        _insert_test_user(test_user)

class ModelsTestCase(TestCase):
    """Tests for models.py."""
    def setUp(self_ignored):
        """Insert test users."""
        create_insert_test_users()

    def test_all_demo_users_inserted(self):
        """
        The birth year in the database should equal the local TEST_USERS
        dictionary value.
        """
        for  test_user in TEST_USERS:
            user = User.objects.get(username=test_user['username'])
            self.assertEqual(test_user['birth_year'], user.profile.birth_year)

The full user-authentication lifecycle in Django, with testing at every step — The step-by-step tutorial I wish I had (part three)

In parts one and two of this tutorial, we created the model and the first of our two views: The user’s profile page, which is only viewable to logged-in users. In this post, we’ll create the other half of our trivial website: the “main” page, which contains aggregate information about all website users and, if they happen to be logged in, some extra private info.

This is the final post where we don’t deal with authentication.

[TOC: one, two, three, four, five, six, seven, eight, nine, ten]

screenshot

Create the view

Save the following as
    /home/myname/django_auth_lifecycle/djauth_root/auth_lifecycle/view__main.py

"""
Renders the main page for the user-authentication-lifecycle project,
containing aggregate information about all the website's users, with
additional private information for logged-in users.
"""
from .view__profile        import TMPL_BASE_DIR
from auth_lifecycle.models import UserProfile
from django.db.models      import Avg
from django.shortcuts      import render
from django.template       import RequestContext
def get_avg_birth_year():
    """Returns the average year of birth for all website users."""
    return  UserProfile.objects.all().aggregate(Avg('birth_year'))['birth_year__avg']

def get_rendered(request):
    """
    Displays aggregate information about the birth year of all users on
    this website. The average birth year is publicly displayed. The
    difference between a single user's birth year and the average (and
    a link to their profile page), is only displayed to logged-in
    users.

    General information on aggregation queries:
    - https://docs.djangoproject.com/en/1.7/topics/db/aggregation/"

    It's not used here, but an interesting related post on doing
    GROUP BY in Django:
    - http://blog.roseman.org.uk/2010/05/10/django-aggregation-and-simple-group/"
    """
    context = RequestContext(request)

    #Publicly-viewable information
    avg_birth_year = get_avg_birth_year()
    context['user_count'] = UserProfile.objects.count()
    context['avg_birth_year'] = avg_birth_year

    if  request.user.is_authenticated():
        """
        The user is logged in. Display extra private information. See

        https://docs.djangoproject.com/en/1.7/ref/contrib/auth/#django.contrib.auth.models.AnonymousUser"

        Retrieve the logged-in-only information.

        Interesting discussion about the 80-chars-per-line maximum of
        PEP-8, which the following line violates:
        - http://chat.stackoverflow.com/transcript/message/19414851#19414851
        """
        user_birth_year = UserProfile.objects.get(user_id=request.user.id).birth_year
        context['birth_year_diff_from_avg'] = user_birth_year - avg_birth_year

    """
    else: They're not logged in. While the above information *could* be
          safely passed to the template even if they're not logged in
          (because the template also properly suppresses it), why retrieve
          when we know it's not going to be used?
    """

    return  render(request, TMPL_BASE_DIR + 'main_page.html',
                   context_instance=context)

Create the template

Save the following as
/home/myname/django_auth_lifecycle/djauth_root/auth_lifecycle/templates/auth_lifecycle/main_page.html

{% load i18n %}       {# For the "trans" tag #}
<!DOCTYPE html>
<html lang="en">
    <head>
        <title>{% trans "Main page" %}</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <meta name="viewport" content="width=device-width"/>
    </head>
<body>
    <h2>{% trans "Main page" %}</h2>

    <p>{% trans "This website has" %} {{ user_count }}
    {% trans "users total, with an average birth year of" %} {{ avg_birth_year }}.</p>

    {% if  user.is_authenticated %}
        <h2>{% trans "Private information for logged-in user named" %}
        &quot;{{ user.username }}&quot; <i>{% trans "only" %}</i></h2>

        <p>{% trans "Welcome" %}, {{ user.username }}!
        {% trans "Your birth year is" %} {{ user.profile.birth_year }},
        {% trans "which is" %} {{ birth_year_diff_from_avg }}
        {% trans "years different from average" %}.</p>

        <p>{% trans "View" %}
        <a href="{% url 'user_profile' %}">{% trans "your profile" %}</a>
        {% trans "or ...logout" %}...</p>

    {% else %}
        <p><i><b>...{% trans "Login" %}...</b>
        {% trans "to view your profile, and to compare your birth year to the average" %}.</i></p>
    {% endif %}

</body>
</html>

Create the tests

As in the profile tests, there is a logged-in test and a not-logged-in test. In the main page, however, there is always something displaying, so the sub-tests that check for the public information are placed in a separate function.

Save the following as
    /home/myname/django_auth_lifecycle/djauth_root/auth_lifecycle/test__view_main.py

"""
Tests for the main page.

DEPENDS ON TEST:     test__utilities.py
DEPENDED ON BY TEST: None

To run the tests in this file:
    1. source /home/myname/django_files/django_auth_lifecycle/djauth_venv/bin/activate
    2. cd /home/myname/django_files/django_auth_lifecycle/djauth_root/
    3. python -Wall manage.py test auth_lifecycle.test__view_main

See the top of <link to .test__utilities> for more information.
"""
from .models                  import UserProfile
from .test__utilities         import assert_attr_val_in_content
from .test__utilities         import create_insert_test_users, UserFactory
from .test__utilities         import debug_test_user, login_get_next_user
from .view__main              import get_avg_birth_year
from django.core.urlresolvers import reverse
from django.test              import TestCase

def _get_page_response(test_instance):
    """
    Returns the response object for the main page.

    Private function for this file only.
    """
    return  test_instance.client.get(reverse('main_page'))

def _subtest_public_info_exists(test_instance, content_str):
    """
    Publicly-accessible information should be somewhere on the page.

    Private function for this file only.
    """
    #The number of users and average birth year exist
    test_instance.assertTrue(str(UserProfile.objects.count()) in content_str)
    test_instance.assertTrue(str(get_avg_birth_year()) in content_str)

class MainPageTestCase(TestCase):
    """Tests for the main page."""
    def setUp(self_ignored):
        """Insert test users."""
        create_insert_test_users()

    def test_page_responds_with_200(self):
        """
        The main birth-stats page should give a 200 response at all
        times.
        """
        response = _get_page_response(self)
        self.assertEqual(200, response.status_code)

    def test_content_not_logged_in(self):
        """Public information should be somewhere on the page."""

        self.client.logout()

        content_str = str(_get_page_response(self).content)

        #print("content_str: " + content_str)

        _subtest_public_info_exists(self, content_str)

        #Logged-in information should not be displaying
        self.assertTrue(UserFactory().username not in content_str)

    def test_logged_in_users(self):
        """
        In addition to public information, private content for logged in
        users should also be somewhere on the page.
        """

        #Test the first two users
        for  n in range(2):
            test_user = login_get_next_user(self)
            content_str = str(_get_page_response(self).content)

            #print("content_str: " + str(content_str))

            _subtest_public_info_exists(self, content_str)

            #The test user's attributes should be somewhere on the page.
            birth_year = test_user.profile.birth_year
            assert_attr_val_in_content(self, 'username', test_user.username, content_str)
            assert_attr_val_in_content(self, 'birth_year', birth_year, content_str)

            #The difference between the user's birth year and the average
            #should be somewhere on the page.
            difference = birth_year - get_avg_birth_year()
            self.assertTrue(str(difference) in content_str)

Configure the urls

Add the main page element to the patterns list in
    /home/myname/django_auth_lifecycle/djauth_root/auth_lifecycle/urls.py

url(r'^$', 'auth_lifecycle.view__main.get_rendered', name='main_page'),

The only difference is the addition of the 'main_page' line.

Run all the tests we’ve created so far

  1. source /home/myname/django_auth_lifecycle/djauth_venv/bin/activate
  2. cd /home/myname/django_auth_lifecycle/djauth_root/
  3. python -Wall manage.py test

Output:

/home/myname/django_auth_lifecycle/djauth_venv/lib/python3.4/imp.py:32: PendingDeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
  PendingDeprecationWarning)
Creating test database for alias 'default'...
.....
----------------------------------------------------------------------
Ran 5 tests in 2.127s

OK
Destroying test database for alias 'default'...

(The deprecation warning refers to something outside of our code, so we’re going to ignore it. Our tests passed.)

Insert some data into the database

(If data already exists, delete it via the admin app (http://my.website/admin)–just don’t delete the ‘admin’ superuser!)

We’ve already created the code to insert users and their profiles into the database. It’s the create_insert_test_users function in test__utilities.py, as used by every test. So let’s use it.

$ source /home/myname/django_auth_lifecycle/djauth_venv/bin/activate
$ cd /home/myname/django_auth_lifecycle/djauth_root/
$ python manage.py shell
>>> from django.contrib.auth.models import User
>>> from auth_lifecycle.models import UserProfile
>>> User.objects.all()
[<User: admin>]
>>> UserProfile.objects.all()
[<UserProfile: admin>]
>>> from auth_lifecycle.test__utilities import create_insert_test_users
>>> create_insert_test_users()
>>> User.objects.all()
[<User: admin>, <User: 3u9osuhceo>, <User: kermit>, <User: fozzie>]
>>> UserProfile.objects.all()
[<UserProfile: admin>, <UserProfile: 3u9osuhceo>, <UserProfile: kermit>, <UserProfile: fozzie>
>>> exit()

Give it a try!

We can finally take a look at our website in a web browser:

  1. Start the webserver:
    • For Nginx/Gunicorn (may be executed in or out of the virtualenv):
      1. sudo service nginx start
      2. sudo /home/myname/django_auth_lifecycle/djauth_venv/bin/gunicorn -c /home/myname/django_auth_lifecycle/djauth_venv/gunicorn_config.py django_auth_lifecycle.wsgi:application     (the contents of the config file)
    • For the development-only Django server:
      1. Make sure you’re in the virtualenv:
            source /home/myname/django_auth_lifecycle/djauth_venv/bin/activate
      2. cd /home/myname/django_auth_lifecycle/djauth_root/
      3. python manage.py runserver
    • Open the browser and load the page:
          http-colon-slash-slashmy.website/auth_lifecycle/

This displays the main page (screenshot at the top of this post), with public information only. Attempting to go to
    http-colon-slash-slashmy.website/auth_lifecycle/user_profile
redirects you right back to the main page. If you really want to see the profile page before the login view exists, take a look at the workaround in the comments of view__profile.get_rendered.

In the next post, we’ll create the beginning of a login page.

[TOC: one, two, three, four, five, six, seven, eight, nine, ten]

…to be continued…

(cue cliffhanger segue music)

The full user-authentication lifecycle in Django, with testing at every step — The step-by-step tutorial I wish I had (part two)

In part one, we created the model for the "trivial website" in our authentication-and-testing tutorial. In this second part, we’ll create the first of our two views: The user’s profile page, which is only viewable to logged-in users. We’ll also be writing our first substantial tests.

[TOC: one, two, three, four, five, six, seven, eight, nine, ten]

screenshot

This displays all (non-password) fields in the default Django User model and the only field in our UserProfile model: birth year. If no user is logged in, this redirects to the login page (which will be created in a future post).

Warning: The website cannot be viewed, and the tests can’t be run (successfully!), until the end of part three. Patience, Grasshopper.

The view

Save the following as
    /home/myname/django_auth_lifecycle/djauth_root/auth_lifecycle/view__profile.py

"""Renders web pages for the user-authentication-lifecycle project."""
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers       import reverse_lazy
from django.shortcuts               import render
from django.template                import RequestContext

TMPL_BASE_DIR = 'auth_lifecycle/'
"""
The root of the (relative) template paths used in this file, as exists
in

/home/myname/django_files/django_auth_lifecycle/auth_lifecycle/templates/

Equal to 'auth_lifecycle/'

This rendundant 'auth_lifecycle' directory is recommended, because it
allows the templates to be 'namespaced'.
- https://docs.djangoproject.com/en/1.7/topics/http/urls/#url-namespaces
- https://docs.djangoproject.com/en/1.7/topics/http/urls/#topics-http-reversing-url-namespaces
"""

PROFILE_LOGGED_OUT_REDIRECT_URL_NAME='main_page'
"""
TEMPORARY VALUE, only while the login view does not exist. Once it's
created, change this to 'login', and eliminate this comment. This
is also used by the tests.

The 'main_page' will be created in the next post. The 'login' page
will be created in the post after that.
"""
@login_required(login_url=reverse_lazy(PROFILE_LOGGED_OUT_REDIRECT_URL_NAME))
def get_rendered(request):
    """
    Displays information unique to the logged-in user.

    This blindly passes the request context back to the template.

    Before the login functionality exists, you can only view page in a
    browser by commenting out the 'login_required' decorator, and adding
    following three lines before the return:

    from django.contrib.auth import authenticate, login
    user = authenticate(username='admin', password='admin')
    login(request, user)

    - https://docs.djangoproject.com/en/1.7/topics/auth/default/#how-to-log-a-user-in
    - https://docs.djangoproject.com/en/1.7/topics/auth/default/#django.contrib.auth.decorators.login_required

    Regarding `reverse_lazy`:
    - http://stackoverflow.com/questions/26446718/decorated-view-causing-a-viewdoesnotexist-error
    """
    return  render(request, TMPL_BASE_DIR + 'user_profile.html',
                   context_instance=RequestContext(request))

The template

Save the following as
/home/myname/django_auth_lifecycle/djauth_root/auth_lifecycle/templates/auth_lifecycle/user_profile.html

{% load i18n %}       {# For the "trans" tag #}
<!DOCTYPE html>
<html lang="en">
    <head>
        <title>{% trans "My profile" %}</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <meta name="viewport" content="width=device-width"/>
    </head>
<body>
    <h1>{% trans "My profile" %}</h1>

    <ul>
        <li>{% trans "Username" %}: {{ user.username }}</li>
        <li>{% trans "Email" %}: {{ user.email }}</li>
        <li>{% trans "Name" %}: {{ user.first_name }} {{ user.last_name }}</li>
        <li>{% trans "Year of birth" %}: {{ user.profile.birth_year }}</li>
    </ul>

    <h2>{% trans "Available actions" %}</h2>
    <ul>
        <li>{% trans "Go back to the" %} <a href="{% url 'main_page' %}">{% trans "main page" %}</a></li>
        <li>...{% trans "Logout" %}...</li>
        <li>...{% trans "Change-your-password link here" %}...</li>
    </ul>
</body>
</html>

Translation (internationalization) in all templates will be done by the i18n tag, which gives us access to the trans tag.

Our first substantial tests

When no user is logged in, the profile page should redirect them to the login page. Temporarily, until the login page is created, they’ll instead be redirected to the main page (which will be created in the next post, at which point we can actually run the tests).

When a user is logged in, all their private attributes, aside from the password, should exist somewhere in the rendered html. Test users are defined in test__utilities.py at the bottom of part one.

Save the following as
    /home/myname/django_auth_lifecycle/djauth_root/auth_lifecycle/test__view_profile.py

(The login_get_test_user used in future tests. And a reminder: The tests cannot be run and the website can’t be viewed, until the end of part three.)

"""
Tests for the user-profile view.

DEPENDS ON TEST:     test__utilities.py
DEPENDED ON BY TEST: None

To run the tests in this file:
    1. source /home/myname/django_files/django_auth_lifecycle/djauth_venv/bin/activate
    2. cd /home/myname/django_files/django_auth_lifecycle/djauth_root/
    3. python -Wall manage.py test auth_lifecycle.test__view_profile

See the top of <link to .test__utilities> for more information.
"""
from .test__utilities         import assert_attr_val_in_content
from .test__utilities         import create_insert_test_users, debug_test_user
from .test__utilities         import login_get_next_user, TEST_PASSWORD
from .test__utilities         import UserFactory
from .view__profile           import PROFILE_LOGGED_OUT_REDIRECT_URL_NAME
from django.core.urlresolvers import reverse
from django.test              import TestCase

def _subtest_next_logged_in_user(test_instance):
    """
    All private information for a single *already-logged-in* test user,
    should be displayed somewhere on the page.

    Private function for this file only.
    """

    #Log in and get the test user, and assert the login succeeded.
    test_user = login_get_next_user(test_instance)

    #The page should load successfully.
    response = test_instance.client.get(reverse('user_profile'))
    test_instance.assertEqual(200, response.status_code)

    #Convert bytes to string.
    content_str = str(response.content)

    #test_user's attributes should exist somewhere on the page
    assert_attr_val_in_content(test_instance, 'username', test_user.username, content_str)
    assert_attr_val_in_content(test_instance, 'birth_year', test_user.profile.birth_year, content_str)
    assert_attr_val_in_content(test_instance, 'email', test_user.email, content_str)
    assert_attr_val_in_content(test_instance, 'first_name', test_user.first_name, content_str)
    assert_attr_val_in_content(test_instance, 'last_name', test_user.last_name, content_str)

class ProfilePageTestCase(TestCase):
    """Tests for the user profile page."""
    def setUp(self_ignored):
        """Insert test users."""
        create_insert_test_users()

    def test_profile_redirects_when_not_logged_in(self):
        """
        Page should redirect when a non-logged-in user attempts to access
        it.
        """
        self.client.logout()
        #"follow=True" is required because we're testing a redirect.
        #- https://code.djangoproject.com/ticket/10971
        response = self.client.get(reverse('user_profile'), follow=True)

        #http://stackoverflow.com/questions/7949089/how-to-find-the-location-url-in-a-django-response-object/22073938#22073938

        #Exactly one redirect expected
        self.assertTrue(len(response.redirect_chain) == 1)

        #Get the first (which, since there's exactly one, could also be
        #'[-1]')
        last_url, status_code = response.redirect_chain[0]
        expected_url_mid = reverse(PROFILE_LOGGED_OUT_REDIRECT_URL_NAME) + '?next='
        self.assertTrue(expected_url_mid in last_url)

        #http://en.wikipedia.org/wiki/HTTP_302
        self.assertEqual(status_code, 302)

    def test_profile_logged_in(self):
        """
        Private information for logged-in users should be somewhere
        on the page.
        """

        #Test the first two users
        for  n in range(2):
            _subtest_next_logged_in_user(self)

Configure the urls

Replace the contents of
    /home/myname/django_auth_lifecycle/djauth_root/django_auth_lifecycle/urls.py
with

"""Passes browser requests to the proper view, based on the url."""
from django.conf.urls import patterns, include, url
from django.contrib   import admin

urlpatterns = patterns('',
    #Adding the namespace attribute to this element would force all
    #references to be prefixed with "auth_lifecycle:". In the template:
    #    {% url 'auth_lifecycle:url_name' %}
    #Elsewhere:
    #    reverse('auth_lifecycle:url_name')
    #
    #- https://docs.djangoproject.com/en/1.7/topics/http/urls/#url-namespaces
    #- https://docs.djangoproject.com/en/1.7/topics/http/urls/#topics-http-reversing-url-namespaces
    #
    #(Unrelated note: If this were a multi-line comment, it would cause
    # a syntax error.)
    url(r'^auth_lifecycle/', include('auth_lifecycle.urls')),
        # namespace="auth_lifecycle")),
    url(r'^admin/', include(admin.site.urls)),
)

Save the following as
    /home/myname/django_auth_lifecycle/djauth_root/auth_lifecycle/urls.py
with

from django.conf.urls import patterns, url
urlpatterns = patterns('',
    url(r'^user_profile/$', 'auth_lifecycle.view__profile.get_rendered',
        name='user_profile'),
)

In the next post, we’ll implement and test the second of two views: The main, publicly-available, aggregate-information page. And finally, we’ll actually use and test our trivial demo website. Then it’s onto authentication!

[TOC: one, two, three, four, five, six, seven, eight, nine, ten]

A final reminder to backup your files.

…to be continued…

(cue cliffhanger segue music)

The full user-authentication lifecycle in Django, with testing at every step — The step-by-step tutorial I wish I had (part one)

This tutorial demonstrates the entire user-authentication lifecycle in Django, with testing at every step:

  1. Create an account, both with and without email confirmation
  2. Login, including a forgot-my-password reset (via email), and a keep-me-logged-in checkbox
  3. A page viewable when logged out, but containing extra information when logged-in
  4. A page viewable only when logged in: Your user “profile”.
  5. Change your password
  6. Logout
  7. Delete your account

[The chapters on creating and deleting an account (chapters nine and ten), and changing-your-password (chapter eight) are not yet written. Resetting your password is in chapter seven.]

[TOC: one, two, three, four, five, six, seven, eight, nine, ten]

The goal of this tutorial is to require as trivial a website as possible, before proceeding onto authentication. In contrast, the How To Tango With Django tutorial does not address authentication until chapter eight, and Mike Hibbert’s video tutorial doesn’t talk about it until chapter nine. It’s not possible to start in the middle, as they both expect that all previous steps were followed. (Other available tutorials.)

Warnings:

  • The testing code increases the amount of code, and the number of steps in this tutorial, by a lot. So creating the demo website is more substantial than I just implied. However, if you did skip the testing–which you shouldn’t–implementing the website itself would be much faster. Were proper testing in either the Tango or Hibbert tutorials, they would be a whole lot longer.
  • The one type of testing I can’t demonstrate in this tutorial is end-user simulation/integration testing with Selenium. My webserver is text-only. This leaves the JavaScript portions of this tutorial, such as client-side make-sure-the-passwords-match verification, untested.

The trivial website: Screenshots

Before doing authentication, we need to create something. Let’s take a look at that something before actually building it. It will contain these two simple views:

screenshot

The main "aggregate" page which is publicly viewable, but contains extra information for logged-in users, including a link to their private profile page:

screenshot

The profile page, which displays every non-password field in the User model, plus the one extra field that makes up the entirety of our model: birth year.

Installation

Here is my setup. The only differences for this tutorial are:

  • The virtualenv base directory is
        /home/myname/django_auth_lifecycle/djauth_venv/
  • The project base directory is
        /home/myname/django_auth_lifecycle/djauth_root/

The reason for this structure is so that the entire django_auth_lifecycle directory can be a Git repository. It is also where I place non-Django files, such as some scripts, some personal-only files, the wordpress posts, including their java builders. In actuality, each post is its own repository. When one part is done–or if there’s a bug–all changes are copied over to all future parts. Attempting to have all posts in one monster repository is impossible, given my current beginner-level Git skills.

The steps I took, which you will need to tailor to your environment:

  1. mkdir -p /home/myname/django_auth_lifecycle/djauth_venv/
  2. sudo virtualenv -p /usr/bin/python3.4 /home/myname/django_auth_lifecycle/djauth_venv/
  3. source /home/myname/django_auth_lifecycle/djauth_venv/bin/activate
  4. sudo /home/myname/django_auth_lifecycle/djauth_venv/bin/pip install django
  5. sudo /home/myname/django_auth_lifecycle/djauth_venv/bin/pip install gunicorn
  6. sudo /home/myname/django_auth_lifecycle/djauth_venv/bin/pip install psycopg2     (this step has a lot of output)
  7. sudo chown -R myname /home/myname/django_auth_lifecycle/

Install a new Django project

  1. Start your virtualenv:
        source /home/myname/django_auth_lifecycle/djauth_venv/bin/activate     (exit it with deactivate)
  2. Create the project directory:
        mkdir /home/myname/django_auth_lifecycle/djauth_root/
  3. Create the project (this is a long command that belongs on a single line) :
        django-admin.py startproject django_auth_lifecycle /home/myname/django_auth_lifecycle/djauth_root

  4. Create the sub-application:
    1. cd /home/myname/django_auth_lifecycle/djauth_root/
    2. python manage.py startapp auth_lifecycle

     
    This and the previous command create the following (items unused by this tutorial are omitted):

    $ tree /home/myname/django_auth_lifecycle/djauth_root/
    +-- auth_lifecycle
    |   +-- admin.py
    |   +-- models.py
    |   +-- views.py
    +-- django_auth_lifecycle
    |   +-- settings.py
    |   +-- urls.py
    +-- manage.py
  5. In
        /home/myname/django_auth_lifecycle/djauth_root/django_auth_lifecycle/settings.py

    1. Add 'auth_lifecycle' to INSTALLED_APPS
    2. Configure your database by overwriting the current value with
      DATABASES = {
          'default': {
              'ENGINE': 'django.db.backends.postgresql_psycopg2',
              'NAME': 'database_name_here',
              'USER': 'database_username_here',
              'PASSWORD': 'database_user_password_goes_here',
              'HOST': "localhost",  # Empty for localhost through domain sockets or
                                    # '127.0.0.1' for localhost through TCP.
              'PORT': '',           # Set to empty string for default.
          }
      }
  6. If you were not yet prompted to create a superuser, do it now:
    1. cd /home/myname/django_auth_lifecycle/djauth_root/
    2. python manage.py createsuperuser

    The rest of this tutorial expects the superuser’s username and password to both be "admin"

The model

The only thing in our model is the user’s year-of-birth, which will be stored in a UserProfile model that is linked to the default Django User model. Although I’ve added in some validation, a simpler alternative without it is below.

Replace the contents of
    /home/myname/django_auth_lifecycle/djauth_root/auth_lifecycle/models.py
with

"""
Defines a single extra user-profile field for the user-authentication
lifecycle demo project:
    - Birth year, which must be between <link to MIN_BIRTH_YEAR> and
   <link to MAX_BIRTH_YEAR>, inclusive.
"""
from datetime                   import datetime
from django.contrib.auth.models import User
from django.core.exceptions     import ValidationError
from django.db                  import models

OLDEST_EVER_AGE     = 127  #:Equal to `127`
YOUNGEST_ALLOWED_IN_SYSTEM_AGE = 13   #:Equal to `13`
MAX_BIRTH_YEAR      = datetime.now().year - YOUNGEST_ALLOWED_IN_SYSTEM_AGE
"""Most recent allowed birth year for (youngest) users."""
MIN_BIRTH_YEAR      = datetime.now().year - OLDEST_EVER_AGE
"""Most distant allowed birth year for (oldest) users."""

def _validate_birth_year(birth_year_str):
    """Validator for <link to UserProfile.birth_year>, ensuring the
        selected year is between <link to OLDEST_EVER_AGE> and
        <link to MAX_BIRTH_YEAR>, inclusive.
        Raises:
            ValidationError: When the selected year is invalid.

        - https://docs.djangoproject.com/en/1.7/ref/validators/

        I am a recovered Hungarian Notation junkie (I come from Java). I
        stopped using it long before I started with Python. In this
        particular function, however, because of the necessary cast, it's
        appropriate.
    """
    birth_year_int = -1
    try:
        birth_year_int = int(str(birth_year_str).strip())
    except TypeError:
        raise ValidationError(u'"{0}" is not an integer'.format(birth_year_str))

    if  not (MIN_BIRTH_YEAR <= birth_year_int <= MAX_BIRTH_YEAR):
        message = (u'{0} is an invalid birth year.'
                   u'Must be between {1} and {2}, inclusive')
        raise ValidationError(message.format(
            birth_year_str, MIN_BIRTH_YEAR, MAX_BIRTH_YEAR))
    #It's all good.

class UserProfile(models.Model):
    """Extra information about a user: Birth year.

    ---NOTES---

    Useful related SQL:
    - `select id from auth_user where username <> 'admin';`
    - `select * from auth_lifecycle_userprofile where user_id=(x,x,...);`
    """
    # This line is required. Links UserProfile to a User model instance.
    user = models.OneToOneField(User, related_name="profile")

    # The additional attributes we wish to include.
    birth_year = models.IntegerField(
        blank=True,
        verbose_name="Year you were born",
        validators=[_validate_birth_year])

    # Override the __str__() method to return out something meaningful
    def __str__(self):
        return self.user.username

Register it into the admin app by replacing the contents of
    /home/myname/django_auth_lifecycle/djauth_root/auth_lifecycle/admin.py
with

from django.contrib import admin
from .models import UserProfile

admin.site.register(UserProfile)

and then sync it to the database:

  1. source /home/myname/django_auth_lifecycle/djauth_venv/bin/activate
  2. cd /home/myname/django_auth_lifecycle/djauth_root/
  3. python manage.py makemigrations
  4. python manage.py migrate

The same model with no validation:

"""
Defines a single extra user-profile field for the user-authentication
lifecycle demo project: Birth year. There is no validation on this field.
"""
from django.contrib.auth.models import User
from django.db                  import models

class UserProfile(models.Model):
    """
    Extra information about a user: Birth year.

    ---NOTES---

    Useful related SQL:
    - `select id from auth_user where username <> 'admin';`
    - `select * from auth_lifecycle_userprofile where user_id=(x,x,...);`
    """
    # This line is required. Links UserProfile to a User model instance.
    user = models.OneToOneField(User, related_name="profile")

    # The additional attributes we wish to include.
    birth_year = models.IntegerField(
        blank=True,
        verbose_name="Year you were born")

    # Override the __str__() method to return out something meaningful
    def __str__(self):
        return self.user.username

Utilities needed by future tests

There’s nothing to test yet. However, we can already create some very useful utilities for future tests: creating test-users in bulk, logging a user in, finding specific text in the html, and debugging. This is also a good spot to place some more generic testing documentation.

The tests require Factory Boy to create its demo data (instead of creating it manually). To install it:

  1. source /home/myname/django_auth_lifecycle/djauth_venv/bin/activate
  2. sudo /home/myname/django_auth_lifecycle/djauth_venv/bin/pip install factory_boy

Save the following as
    /home/myname/django_auth_lifecycle/djauth_root/auth_lifecycle/test__utilities.py

"""
Utilities used by testing code throughout the authentication-lifecycle and
testing tutorial.

DEPENDS ON TEST:  *nothing* (must not depend on any test_*.py file)
DEPENDED ON TEST: test__profile.py

--- Generic information on running tests ---

To run a single test:
    1. source /home/myname/django_files/django_auth_lifecycle/djauth_venv/bin/activate
    2. cd /home/myname/django_files/django_auth_lifecycle/djauth_root/
    3. python -Wall manage.py test auth_lifecycle.test__file_name

To run all tests:
    python -Wall manage.py test auth_lifecycle

Running tests documentation:
- https://docs.djangoproject.com/en/1.7/topics/testing/overview/#running-tests

Information on '-Wall' is at the bottom of that same section. If the
output is too verbose, try it again without '-Wall'.

If a test fails because the test database cannot be created, grant your
database user creation privileges:
- http://dba.stackexchange.com/questions/33285/how-to-i-grant-a-user-account-permission-to-create-databases-in-postgresql

pylint auth_lifecycle.test__utilities > pylint_output.txt
pylint auth_lifecycle.test__view_birth_stats > pylint_output.txt
pylint auth_lifecycle.test__view_user_profile > pylint_output.txt
"""
from .models                    import MIN_BIRTH_YEAR
from auth_lifecycle.models      import UserProfile
from django.contrib.auth.models import User
from django.test                import TestCase
import factory

TEST_USER_COUNT = 5
"""The number of test users to create. Equal to `5`."""
TEST_PASSWORD = 'password123abc'
"""The password shared by all test users. Equal to `'password123abc'`."""

class UserProfileFactory(factory.django.DjangoModelFactory):
    """
    Creates `UserProfile`-s, where each user has a unique birth year,
    starting with <link to .models.MIN_BIRTH_YEAR>.

    *Warning*: Creating more than
        MAX_BIRTH_YEAR - MIN_BIRTH_YEAR
     users will cause a ValidationError.
    """
    #Uncommenting this line would allow you to directly create a
    #UserProfile, which would then automatically create a User.
    #- Docs: http://factoryboy.readthedocs.org/en/latest/reference.html#subfactory
    #user = factory.SubFactory('auth_lifecycle.test__utilities.UserFactory', profile=None)
    class Meta:
        model = UserProfile

    #factory.Sequence always starts at one. This starts it at
    #MIN_BIRTH_YEAR.
    #http://factoryboy.readthedocs.org/en/latest/reference.html#sequence
    #http://stackoverflow.com/questions/15402256/how-to-pass-in-a-starting-sequence-number-to-a-django-factoryboy-factory
    birth_year = factory.Sequence(lambda n: n + MIN_BIRTH_YEAR - 1)

class UserFactory(factory.django.DjangoModelFactory):
    """
    Creates `User`-s and its corresponding `UserProfile`-s. Each user has
    the same attributes, but with a unique sequence number, starting with
    one.

    See <link to TEST_PASSWORD>.
    """
    class Meta:
        model = User
    #Automatically create a profile when the User is created.
    #- Docs: http://factoryboy.readthedocs.org/en/latest/reference.html?highlight=subfactory#relatedfactory
    profile = factory.RelatedFactory(UserProfileFactory, 'user')

    username = factory.Sequence(lambda n: 'test_username{}'.format(n))
    first_name = factory.Sequence(lambda n: 'test_first_name{}'.format(n))
    last_name = factory.Sequence(lambda n: 'test_last_name{}'.format(n))
    email = factory.Sequence(lambda n: 'test_email{}@example.com'.format(n))

    #http://factoryboy.readthedocs.org/en/latest/reference.html#postgenerationmethodcall
    #See Django mention at the bottom of that documentation section.
    password = factory.PostGenerationMethodCall('set_password', TEST_PASSWORD)

def create_insert_test_users():
    """
    Insert <link to TEST_USER_COUNT> test users into the database. I don't
    understand why, but even though this is called for every test, via
    `setUp`, this does *not* create more than `TEST_USER_COUNT` users.
    Use the debugging statements to prove this.
    """

    #print('a User.objects.count()=' + str(User.objects.count()))

    #http://factoryboy.readthedocs.org/en/latest/reference.html?highlight=create#factory.create_batch
    UserFactory.create_batch(TEST_USER_COUNT)

    #print('b User.objects.count()=' + str(User.objects.count()))

def login_get_next_user(test_instance):
    """
    Log in the next test user, assert it succeeded, and return the `User`
    object.
    """
    test_instance.client.logout()

    test_user = UserFactory()
    #debug_test_user(test_user, prefix='Attempting to login:')

    did_login_succeed = test_instance.client.login(
        username=test_user.username,
        password=TEST_PASSWORD)
    test_instance.assertTrue(did_login_succeed)

    return  test_user

def assert_attr_val_in_content(
        test_instance, attribute_name, expected_value, page_content_str):
    """A specific attribute should be somewhere in the html."""
    #print('assert_attr_val_in_content: expected_value=' + expected_value)
    test_instance.assertTrue(str(expected_value) in page_content_str)

def debug_test_user(test_user, prefix=''):
    """
    Print all user attributes to standard out, except password.

    Parameters:
    - prefix: Defaults to `''`. If not the empty string, printed before
    the user information
    """
    if  prefix is not '':
        print(prefix)

    profile = test_user.profile
    print('test_user.id=' + str(test_user.id))
    print('   username=' + test_user.username + ', password=' + TEST_PASSWORD)
    print('   first_name=' + test_user.first_name + ', last_name=' + test_user.last_name)
    print('   email=' + test_user.email)
    print('   profile=' + str(profile))
    print('      profile.birth_year=' + str(profile.birth_year))

That’s it for now.

In the next post, well implement and test the first of two views: The private user-profile page.

[TOC: one, two, three, four, five, six, seven, eight, nine, ten]

At this point, it would be a good idea to backup your files.

…to be continued…

(cue cliffhanger segue music)

Increasing the minimum word size for the Sublime Text 3 plugin All Autocomplete

All Autocomplete is an ST3 plugin that I first thought was sort of a gimmick, but turns out to very useful. Normally automatic autocompletion (meaning not coming from snippets or autocompletion files) uses words only in the currently-open document. All Autocomplete uses all open documents.

A disadvantage, however, is that it includes words with three characters or more which, for my taste, clutters up the popups. But this minimum is directly in the plugin code, not a preference-file.

To increase this value from three to five, first extract them with the PackageResourceViewer plugin (or just unzip it… *.sublime-package files are just renamed zip files). This creates the directory

C:\Users\jeffy\AppData\Roaming\Sublime Text 3\Packages\All Autocomplete\

In that directory, open all_views_completions.py and change

MIN_WORD_SIZE = 3

to

MIN_WORD_SIZE = 5

This directory overrides the package file. You could also re-zip this back into the file, and delete the directory.

The bad thing about this setting being directly in the code, instead of in a configuration file, is that this change must be made each time AllAutocomplete is updated.