Django-registration is cool, but often you want to add some more fields to the Registration Form. At first, it doesn’t quite jump out at you how to do this. This blog post is all about how to add some extra fields into django-registration while having to write the least code. We’ll go through all the steps just because its nice to have completness. Let’s see what we can do.

I used easy_install django-registration, and it installed this egg file to my site-packages: django_registration-0.7-py2.5.egg

Steps:
1. Create your own app, ours is called ‘mynewapp’ (which is a very poor name, by the way), and add it to your settings.py file in the regular way by registering it under INSTALLED_APPS

2. In your app, create a model in your models.py file using this method: http://www.djangobook.com/en/1.0/chapter12/#cn222 and make sure to set the AUTH_PROFILE_MODULE in your settings.py file.

This model is the ‘extended’ fields that you want to see in the form, such as user ‘location’, ‘address’, or ‘favorite stripper’ to name a few example.

It should look something like this and should be in models.py:

from django.db import models
from django.contrib.auth.models import User
class ZProfile(models.Model):
user = models.ForeignKey(User, unique=True)
favorite_band = models.CharField(max_length=200, blank=True)

In our case, we created a class model called ZProfile. Then, we added this in our settings.py file:

AUTH_PROFILE_MODULE = "mynewapp.ZProfile"

Note: you MUST put ‘appname.ClassName’ NOT like ‘projectname.appname.modulename’ or anything like that. Our class is ZProfile (inside a module named something like zforms.py or something), our app is mynewapp.

3. Now create a new registration form for yourself. You can create a new python file, and name it like newform.py.In it, define new RegistrationFormZ that extends RegistrationForm; this should have the new fields you want to add, and also it should override the save() method of RegistrationForm (its superclass).
Something like this:

from django import forms
from registration.forms import RegistrationForm
from django.utils.translation import ugettext_lazy as _
from registrationz.models import ZProfile
from registration.models import RegistrationProfile
attrs_dict = { 'class': 'required' }
class RegistrationFormZ(RegistrationForm):
band = forms.CharField(widget=forms.TextInput(attrs=attrs_dict))
def save(self, profile_callback=None):
new_user = RegistrationProfile.objects.create_inactive_user(username=self.cleaned_data['username'],
password=self.cleaned_data['password1'],
email=self.cleaned_data['email'])
new_profile = ZProfile(user=new_user, favorite_band=self.cleaned_data['band'])
new_profile.save()
return new_user

So what we see is that when a new user account is created, so too do we create our profile account. Nice! But we’re not done yet.

We have to tell django-registration to use our new form, RegistrationFormZ, instead of the default RegistrationForm. Django-registration is well designed in that it lets just simply pass this in as a keyword argument to the ‘register’ view, specifying which registration form we want to use. This is done using url patterns. So, let’s create a urls.py file WITHIN OUR APP (i.e. within the app we created earlier) that looks something like ‘registration.urls’ (django-registration default). We only have to change the url pattern that maps to the register view, with this line:

url(r'^register/$',
register,
{'form_class' : RegistrationFormZ},
name='registration_register'),

Here is the whole of the urls.py inside my app, with this change:

from django.conf.urls.defaults import *
from django.views.generic.simple import direct_to_template
from django.contrib.auth import views as auth_views

from registration.views import activate
from registration.views import register
from mynewapp.registrationz.formsz import RegistrationFormZ

urlpatterns = patterns('',
# Activation keys get matched by w+ instead of the more specific
# [a-fA-F0-9]{40} because a bad activation key should still get to the view;
# that way it can return a sensible "invalid key" message instead of a
# confusing 404.
url(r'^activate/(?Pw+)/$',
activate,
name='registration_activate'),
url(r'^login/$',
auth_views.login,
{'template_name': 'registration/login.html'},
name='auth_login'),
url(r'^logout/$',
auth_views.logout,
{'template_name': 'registration/logout.html'},
name='auth_logout'),
url(r'^password/change/$',
auth_views.password_change,
name='auth_password_change'),
url(r'^password/change/done/$',
auth_views.password_change_done,
name='auth_password_change_done'),
url(r'^password/reset/$',
auth_views.password_reset,
name='auth_password_reset'),
url(r'^password/reset/confirm/(?P[0-9A-Za-z]+)-(?P.+)/$',
auth_views.password_reset_confirm,
name='auth_password_reset_confirm'),
url(r'^password/reset/complete/$',
auth_views.password_reset_complete,
name='auth_password_reset_complete'),
url(r'^password/reset/done/$',
auth_views.password_reset_done,
name='auth_password_reset_done'),
url(r'^register/$',
register,
{'form_class' : RegistrationFormZeta},
name='registration_register'),
url(r'^register/complete/$',
direct_to_template,
{'template': 'registration/registration_complete.html'},
name='registration_complete'),
)

Now, you need to make sure to include this urls.py file in your PROJECT urls.py.

So go to your project’s global urls.py, and add these lines:

(r'^accounts/', include('myproject.mynewapp.urls')),
(r'^$', direct_to_template, { 'template': 'index.html' }, 'index'),

The first line simply includes the URL file that we created in our app; the second line uses direct_to_template notation, which means don’t do any djangoey stuff (i.e. dont’ redirect to a view) instead just show a plain static template. The second line is only necessary if you used those template files I provided a link to earlier (on the other blog), and you want to show your index.html when people go to your site.

Now, run python manage.py syncdb, and it should update the correct models. Now when you create a user account, it will created a profile for him/her that is accessible by calling the get_profile() method (because we created a ForeignKey relationship between our user and the profile account, remember?). This profile will have all the fields you defined, in our case, it just has one field, favorite_band (which I really don’t even have, sadly).

One thing, is that while the ‘management’ script to clean up expired users accounts works for RegistrationProfiles or whatever, you will probably have to modify this if you to say you also want to delete the user’s accompanying ZProfile as well when it is checking for expired accounts.

Hope this was helpful! Feel free to leave me a comment or question and I will try to reply.