Recap of the September Pinax Hangout

On September 17th at noon Eastern time we screencasted our second Pinax Hangout. Here’s a little recap for you.

The purpose of the Pinax Hangouts is to engage more with the Pinax community. We want to let you know what we are working on, answer your questions, and present demos of old and new apps and starter projects. Our host last month was Brian Rosner, who was supported by our co-host Patrick Altman.

As a follow up to our first Pinax Hangout about the pinax-project-account starter project, Brian talked about the django-user-accounts app, which Eldarion uses for a lot of their projects, and which is integrated in most Pinax starter projects. django-user-accounts is just a Django app and can be used in any Django project that needs the functionality it provides. The Pinax starter projects are a good example on how to integrate django-user-accounts into your Django app. Some of the features django-user-accounts offers are:

  • Login
  • Email and username authentication (username authentification is the default)
  • Sign up
  • Email confirmation
  • Sign up tokens for private betas
  • Password resets
  • Account management (updating your account settings, changing your password, account deletion, etc.)
  • Extensible class-based views (enables you to easily customize django-user-accounts for your business logic)
  • Custom user model support
  • Supports Django 1.7 and 1.8, and Python 2.7 and 3

History of django-user-accounts

django-user-accounts came out of the Pinax Project in the earlier days of Pinax. Its original location was pinax.apps.account. It was tightly integrated with other components inside of Pinax and later pulled out of the Pinax ecosystem and made a better member of the Django ecosystem and can now be used on its own. django-user-accounts was originally created because back then a lot of the existing Django user management functionality was a bit scattered. A lot of websites nowadays use user accounts and django-user-accounts provides a solid base to get your site running quickly with user accounts.

Dive in and Demo

Brian demonstrated how django-user-accounts works and what it looks like. You can also find all the steps in the django-user-accounts documentation.

Installation

First create a virtual environment, then pip install pinax-cli, which simplifies the creation of a starter project on your own computer. Next create a new directory for your project and run pinax start account mysite, which creates a new project locally called mysite based on the account project app, which will install Django for you. Then cd into mysite.

Follow these steps next:

pip install -r requirements.txt

python manage.py migrate

python manage.py loaddata sites

python manage.py runserver

Customizing the Sign Up Process

To customize the sign up process, create a views.py file in your mysite app. Then create a new sign up view which will inherit from the view, which exists in django-user-accounts:

import account.views

class SignupView(account.view):
    pass

Make sure to import your new view into urls.py. Add the import statement on line 8:

from.views import SignupView

We want to override just one URL, not all of them so we need to define a URL pattern that comes before the “include URL pattern” since the way URLs are loaded is in the order they are listed in urls.py. On line 15, add the following URL pattern:

url(r“^account/signup/$“, SignupView.as_view(), name="account_signup“)

Start your server again to make sure everything works.

In views.py add the following code:

def update_profile(self, form):
    pass

def after_signup(self, form):
    self.update_profile(form)
    super(SignupView, self).after_signup(form)

We’re overriding the after_signup function. When django-user-accounts does the sign up process, this function gets called after everything has been signed up but right before things get saved to the database.

Create a new model in views.py:

def update_profile(self,form):
    UserProfile.objects.create(
        user=self.created_user
    )

We would like to ask our users to tell us their date of birth before they sign up. In order to do this we need to create a new file called forms.py and add a new field called birth date:

from django import forms
from django.forms.extras.widgets import SelectDateWidget

import account.forms

class SignupForm(account.forms.SignupForm):
    birthdate = forms.DateField(widget=SelectDateWidget(years=range(1910, 1991)))

We import our form in views.py:

from .forms import SignupForm

To hook this into our view, we simply assign form_class:

form_class = SignupForm

In a new file called models.py, create a UserProfile model for it to work:

from django.conf import settings
from django.db import models

class UserProfile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL)
    birthdate = models.DateField()

We’re pulling the date data directly from the form object that’s passed in.

Import your model into views.py:

from .models import UserProfile

and add this line:

birthdate = form.cleaned_data["birthdate“],

Since we made some changes to our models, we need to migrate them before we continue:

python manage.py makemigrations mysite

python manage.py migrate

Start the server again. Now you can see the birthdate field in the sign up form on your website. In order to test if everything works, sign up. After signing up you will get a notification that the user was signed up on the site. In your console you’ll also see that an email was printed out. By default projects have email configured for the console so you can see the email that would have been sent to the user. Copy the URL and paste it into your browser to confirm your email address. All of this functionality is provided through django-user-accounts.

Customizing Confirmation Emails

You can customize all of these emails with the hooksets (default behavior for things that django-user-accounts provides), which are integrated into django-user-accounts. In your project you can provide your own hooks object class that overrides these methods to form the behavior based on your own business logic. If you want to customize the behavior around how django-user-accounts sends out email confirmations you can easily do that by overriding the send_confirmation_email function.

First add hooksets to your project settings:

ACCOUNT_HOOKSET = "account.hooks.AccountDefaultHookSet“

This is the default value. We can also point the path to something in our own project. For our project we’ll use

ACCOUNT_HOOKSET = "mysite.hooks.AccountHookSet“

Then create a new file in your project called hooks.py and add the following code:

from account.hooks import AccountDefaultHookSet

class AccountHookSet(AccountDefaultHookSet):
    pass

The code we just added now provides you with the ability to override these methods. If you have suggestions for other methods you’d like us to integrate into django-user-accounts, please join our Pinax Project Slack channel and let us know!

Email Authentication

django-user-accounts lets you either log in with a username or an email address. Username authentication is the default but we want to change it to email authentication.

Before we start making changes, please note that all account email addresses, meaning all email addresses in your database, should be unique. If they are not all unique, this could cause trouble.

Add the following code to your settings.py file:

ACCOUNT_EMAIL_UNIQUE = True
ACCOUNT_EMAIL_CONFIRMATION_REQUIRED = True

This will require the user to confirm their email address before they can login to the site. We therefore assume that users can access their email.

Next we need to override the login view in views.py. Create your own login view and then redefine the form class that’s used for the login view to be the login email form. This is provided out of the box by django-user-accounts but if you have any other requirements this can be changed.

Add the following code to views.py:

class LoginView(account.views.LoginView):
    form_class = account.forms.LoginEmailForm

Go to urls.py and import your LoginView:

from .views import SignupView, LoginView

Create your URL:

url(r“^account/login/$“, LoginView.as_view(), name="account_login“),

Ensure that you’re using the email authentication backend. If you only want email authentication, you can remove the username authentication, if you leave in both, you have the ability to do both.

The order in which they are listed in the AUTHENTIFICATION_BACKENDS list in settings.py is important because the first one will be tried first so if you want to fall back to email address, you should list email address after username, but if you want to fall back to username you would list username last.

For our app we can remove the username authentication so our line of code should look like this:

AUTHENTIFICATION_BACKENDS = [
    "account.auth_backends.EmailAuthenticationBackend“,
]

To get completely rid of usernames you will have to do a few extra steps, which you can find in the documentation. In our case we’ll do a little bit of magic, by magic meaning we’re going to assume that only one person is ever going to sign up for our site. In views.py add:

def generate_username (self, form):
    username = "<magic>“
    return username

Under the SignupView add

def ACCOUNT_USER_DISPLAY(user):
    return user.email

Please note that ACCOUNT_USER_DISPLAY needs to be defined in your settings.

Next migrate the database and run the server.

It will still ask you for a username during sign up and we might not want that so we will override the sign up constructor in forms.py and then just delete the username. You might also want to change the order of things (we’re not gonna worry about that):

def __init__(self, *args*, **kwargs**):
    super(SignupForm, self).__init__(*args, **kwargs)
    del.self.fields["username“]

Sign up on your website again, confirm your email address (the confirmation email will be printed in the console again). We now have the ability to only login with our email address.

Overriding Templates

When you want to override templates the first thing you need to do is to figure out where the template is because the templates you see on your Pinax website are provided through a theme app, by default it’s the pinax-theme-bootstrap app. It provides all of the templates for interacting with django-user-accounts. If you want to integrate django- user-accounts into your existing Django app, you’ll probably need to provide these templates yourself. Inside of pinax-theme-bootstrap we provide the templates for you and also created the ability to override a template, for example if you want to be able to make your login page or email confirmation page look different.

If you want to override a template, you need to find where the template is in your virtual environment, copy the template file, and put it inside your own project, inside your templates settings underneath account. When Django loads templates it goes in order of the specific loaders that are defined. It uses your project’s template directory first before it goes to the APP_DIRS, which gives you the ability to override any templates at your project level that match the similar naming at an app level.

To make this easier we provided a command called python manage.py copy_from_theme so if you want to customize the login template for example, you would type in python manage.py copy_from_theme —path=account/login.html. Our documentation shows you all the templates that the views in django-user-accounts call. If you needed to override anyone of these you can pass it in like shown above or override the whole project account by typing in python manage.py copy_from_theme —path=account/*.

We should now have all the account templates inside our project, which enables you to modify any of the template code as you desire.

If you missed our September Pinax Hangout, you can watch the video here

If you have any questions, please post them in our Pinax project Slack channel. No matter if beginner, or experienced, we want to help everyone! We would also love contributions to django-user-accounts. You can find the GitHub repo here. Please file issues, make pull requests, etc.

Our next Pinax Hangout will take place on Thursday, October 22nd at noon Eastern time. The topic will be pinax-project-blog. We hope that you will join us for our October Hangout!