Redirect to the originally requested page after django registration and email activation

redirectFor my newly-launched, django-based social shopping site Wantbox.com, I have implemented @ubernostrum‘s ubiquitous django-registration 0.7 to handle user registrations.

Recently I completed a Wantbox feature that required me to redirect a new user back to their initially requested, authorization-required page after their registration and email activation. Below are the step-by-steps actions I took to accomplish this.

Step One: Pass the “next” parameter from the login form to the registration form

When a user requests a view adorned with the “@login_required()” decorator, Django automatically checks if that user “is_authenticated()” and redirects them to your login form if they are not. You’ll notice that there is a “?next=/foo/bar/” parameter tacked onto the end of the login page URL so you can send the use back to the requested page upon login.

In my case, the user needs to register, not log in, so my first step was to pass this “next” parameter to the registration form. Under my login form I added this link:

Not a member yet? <a href="{% url registration_register %}?next={{ request.REQUEST.next|urlencode }}">Sign up here</a>

Step Two: Modify the registration form
On your registration form, add a hidden input field to store the “next” parameter that you just passed in from the login form:

<input type="hidden" name="next" id="id_next" value="{{ request.REQUEST.next }}" />

Step Three: Override the “/accounts/registration/” url pattern

Since you’ve added a new registration form field, you’ll need a custom form handler (step five below). But first, you need to override the built-in url pattern to tell it you will be using this custom handler. In “urls.py” add the following:

# django-registration > register
from wantcore.forms import RegistrationFormWithNext
url(r'^accounts/register/$', 'registration.views.register', {'form_class': RegistrationFormWithNext, 'profile_callback': UserProfile.objects.create }, name='registration_register'),

Step Four: Create a table to store the “next” page

In your “models.py” file, create a table to store the user’s initially requested page. Mine looks like this:

class UserNext(models.Model):
   user = models.OneToOneField(User)
   next = models.CharField(max_length=100, null=True, blank=True, default=None)
   create_date = models.DateTimeField(default=datetime.datetime.now)

Don’t forget to sync your database to create this new table.

Step Five: Create a custom registration form handler

Now, create the “RegistrationFormWithNext” form handler to capture and store the user’s initially requested page. I defined mine in a file called “forms.py” and it looks like this:

from registration.forms import RegistrationForm
class RegistrationFormWithNext(RegistrationForm):
   next = forms.CharField(max_length=125, required=False)
   def save(self, *args, **kwargs):
      new_user = super(RegistrationFormWithNext, self).save(*args, **kwargs)
      if self.cleaned_data['next']:
         usernext = UserNext()
         usernext.user = new_user
         usernext.next = self.cleaned_data['next']
         usernext.save()
      return new_user

Step Six: Override the “/accounts/activate/” url and view

Next, override he “activate” url pattern and do a little work after the new user clicks on the activation link in the activation email. First, add the following to your “urls.py” file:

# django-registration > activate
from account.views import pre_activate
url(r'^accounts/activate/(?P<activation_key>\w+)/$', pre_activate, name='registration_activate'),

Here, I am sending the user to my own “pre_activate” view where I will do some work before sending them off to the real django-registration “activate” view.

My “pre_activate” view uses the activation key to look up the user who is activating and then looks in the “UserNext” table to see what their initially requested page was. I then pass this “next” field on to the django-registration “activate” view which, in turn, passes it on to the activation page at “registration/activate.html”.

My “pre_activate” view is in a file called “views.py” in a directory called “account”. It looks like this:

from registration.models import RegistrationProfile
from registration.views import activate
from wantcore.models import UserNext
import logging
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#   pre_activate
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def pre_activate(request, activation_key, template_name='registration/activate.html', extra_context=None):
   username = ""
   next = ""
   try:
      profile = RegistrationProfile.objects.get(activation_key=activation_key)
      usernext = UserNext.objects.get(user=profile.user)
      username = usernext.user.username
      next = usernext.next
   except Exception, e:
      logging.error("error: %s" % e)
   return activate(request, activation_key, template_name, {'next': next, 'username': username})

Step Seven: Modify the login form on the activate page

Upon successful activation, the user is directed to the template “registration/activate.html”. On this page, I’ve added a log in form which pre-fills the user’s username and adds to user’s initially requested page into a hidden “next” field.

My “registration/activate.html” page looks like this:

{% extends "base.html" %}
{% block title %}Account Activation | {% endblock %}
{% block content %}

{% if account %}
<script type="text/javascript">
   $(document).ready(function() {
      document.forms.login.password.focus()
   });
</script>

<h1>Success!</h1>
<div>Your account <strong>{{ username }}</strong> has been successfully activated! Please use the form below to log into Wantbox.</div>
<div>
   <h1>Log In to Wantbox:</h1>
   <form method="post" action="{% url auth_login %}" name="login">
      {% csrf_token %}
      <p><label for="id_username">username:</label> <input id="id_username" type="text" name="username" value="{{ username }}" maxlength="30" /></p>
      <p><label for="id_password">password:</label> <input type="password" name="password" id="id_password" /></p>
      {% if next %}
      <input type="hidden" name="next" value="{{ next }}" />
      {% else %}
      <input type="hidden" name="next" value="/user/" />
      {% endif %}
      <input type="submit" value="Log In" />
   </form>

   <p>Forgot your password? (Really, already???) <a href="{% url auth_password_reset %}">Reset it here</a>.</p>
</div>

{% else %}
<h1>Boooooooooo!</h1>
<p>Account activation failed...</p>
<p>Please contact <a href="mailto:[email protected]">Wantbox support</a> and we'll get your account activated for you!</p>
{% endif %}
{% endblock %}

Now, when a user successfully logs in via this form, they will be redirected to the login-required page that they initially tried to access, even though they have traveled a long journey from a login page, to a registration page, to an email with an activation link and finally to another login form.

If you have questions or suggestions about how this works, please ask in the comments and I’ll try to help.