It has been more than a week since the first part of this tutorial series was posted and I’ve been getting positive feedback from several channels. Ironically, it was extremely popular on Hacker News. It was even translated into Chinese.

To be sure, the objective was not to create a full featured clone of any website. The real objective is to learn Django using a medium-sized project that utilises it to the fullest. Many tutorials fall short of bringing together various parts of Django. Compared to a microframework like Flask (which is also great, btw), Django comes with a lot of batteries included. If you are short on time, this makes it ideal for completing an ambitious project like this.

This tutorial would show you how to implement social features like supporting user registration and profile pages. We will leverage Django’s class based views for building CRUD functionality. There is a lot of ground to be covered this time.

As before, there is a text description of the steps if you do not prefer to watch the entire video. There is also a goodies pack with templates and other assets included, which would be required if you are following this tutorial.

This video would be a continuation of the previous video and I recommend watching it. Click on the image below to watch the screencast or scroll down to read the steps.

Enjoyed this tutorial? Then you should sign up for my upcoming book “Building a Social News Site in Django”. It tries to explain in a learn-from-a-friend style how websites are built and gradually tackles advanced topics like testing, security, database migrations and debugging.

Step-by-step Instructions

Here is the text version of the video for people who prefer to read. In part 1, we showed you how to create a private beta-like site to publish rumors about “Man of Steel”.

The outline of Part 2 of the screencast is:

Better branding and templates

Custom login/logout

Sociopath to actually social - django-registrations

Simple registration

User Profiles

Open the goodies pack

So far, the appearance of the website looks a bit bland. Let’s use some assets which are pre-designed for the tutorial.

Download sr-goodies-master.zip to any convenient location. On Linux, you can use the following commands to extract it to the /tmp directory. cd /tmp wget https://github.com/arocks/sr-goodies/archive/master.zip unzip master.zip Explore the extracted files in /tmp/sr-goodies-master Copy the entire static directory from the extracted files to steelrumors/steelrumors . Also, overwrite the extracted sr-goodies-master/templates/base.html template into steelrumors/steelrumors/templates/ cp -R /tmp/sr-goodies-master/static ~/proj/steelrumors/steelrumors/ cp /tmp/sr-goodies-master/templates/base.html ~/proj/steelrumors/steelrumors/templates/

Custom Login page

Add to steelrumors/urls.py : url ( r '^login/$' , 'django.contrib.auth.views.login' , { 'template_name' : 'login.html' }, name = "login" ), url ( r '^logout/$' , 'django.contrib.auth.views.logout_then_login' , name = "logout" ), Add the login/logout URL locations to steelrumors/settings.py : from django.core.urlresolvers import reverse_lazy LOGIN_URL = reverse_lazy ( 'login' ) LOGIN_REDIRECT_URL = reverse_lazy ( 'home' ) LOGOUT_URL = reverse_lazy ( 'logout' ) Now copy login.html from the goodies pack to the templates directory: cp /tmp/sr-goodies-master/templates/login.html ~/proj/steelrumors/steelrumors/templates/ Refresh your browser to view the newly styled pages.

Using django-registrations

We will be using the “simple” backend of django-registrations since it is easy to use and understand.

Currently, the version of django registration on the the Python Package Index doesn’t work well with Django 1.5. So we will use my forked version using pip: pip install git+git://github.com/arocks/django-registration-1.5.git Or if you don’t have git installed, use: pip install https://github.com/arocks/django-registration-1.5/tarball/master Edit settings.py to add registration app to the end of INSTALLED_APPS 'registration' , ) Run syncdb to create the registration models: ./ manage . py syncdb We need to use the registration form template from goodies pack. Since, this is for the registration app we need to create a registration directory under templates : mkdir ~/proj/steelrumors/steelrumors/templates/registration/ cp /tmp/sr-goodies-master/templates/registration/registration_form.html ~/proj/steelrumors/steelrumors/templates/registration/ Add to urls.py : url ( r '^accounts/' , include ( 'registration.backends.simple.urls' )), Visit http://127.0.0.1:8000/accounts/register/ and create a new user. This will throw a “Page not found” error after a user is created.

Create a user’s profile page

Add UserProfile class and its signals to models.py : class UserProfile ( models . Model ): user = models . OneToOneField ( User , unique = True ) bio = models . TextField ( null = True ) def __unicode__ ( self ): return " %s 's profile" % self . user def create_profile ( sender , instance , created , ** kwargs ): if created : profile , created = UserProfile . objects . get_or_create ( user = instance ) # Signal while saving user from django.db.models.signals import post_save post_save . connect ( create_profile , sender = User ) Run syncdb again to create the user profile model: ./manage.py syncdb Add these to admin.py of links app to replace/extend the default admin for User: from django.contrib.auth.admin import UserAdmin from django.contrib.auth import get_user_model ... class UserProfileInline ( admin . StackedInline ): model = UserProfile can_delete = False class UserProfileAdmin ( UserAdmin ): inlines = ( UserProfileInline , ) admin . site . unregister ( get_user_model ()) admin . site . register ( get_user_model (), UserProfileAdmin ) Visit http://127.0.0.1:8000/admin/ and open any user’s details. The bio field should appear in the bottom. Add to views.py of links apps: from django.views.generic import ListView , DetailView from django.contrib.auth import get_user_model from .models import UserProfile .... class UserProfileDetailView ( DetailView ): model = get_user_model () slug_field = "username" template_name = "user_detail.html" def get_object ( self , queryset = None ): user = super ( UserProfileDetailView , self ) . get_object ( queryset ) UserProfile . objects . get_or_create ( user = user ) return user Copy user_detail.html from goodies to steelrumors/templates/ : cp /tmp/sr-goodies-master/templates/user_detail.html \ ~/proj/steelrumors/steelrumors/templates/ Let’s add the urls which failed last time when we tried to create a user. Add to urls.py : from links.views import UserProfileDetailView ... url ( r '^users/(?P<slug>\w+)/$' , UserProfileDetailView . as_view (), name = "profile" ), Now try to create a user and it should work. You should also see the profile page for the newly created user, as well as other users. You probably don’t want to enter these links by hand each time. So let’s edit base.html by adding the lines with a plus sign ‘+’ (omitting the plus sign) below: {% if user.is_authenticated %} <a href=" {% url 'logout' %} ">Logout</a> | + <a href=" {% url 'profile' slug = user.username %} "><b> {{ user.username }} </b></a> {% else %} + <a href=" {% url 'registration_register' %} ">Register</a> | Refresh the browser to see the changes.

Edit your profile details

Add UserProfileEditView class to views.py in links app: from django.views.generic.edit import UpdateView from .models import UserProfile from .forms import UserProfileForm from django.core.urlresolvers import reverse class UserProfileEditView ( UpdateView ): model = UserProfile form_class = UserProfileForm template_name = "edit_profile.html" def get_object ( self , queryset = None ): return UserProfile . objects . get_or_create ( user = self . request . user )[ 0 ] def get_success_url ( self ): return reverse ( "profile" , kwargs = { 'slug' : self . request . user }) Create links/forms.py : from django import forms from .models import UserProfile class UserProfileForm ( forms . ModelForm ): class Meta : model = UserProfile exclude = ( "user" ) Add the profile edit view to urls.py . Protect it with an auth decorator to prevent unlogged users from seeing this view. from django.contrib.auth.decorators import login_required as auth from links.views import UserProfileEditView ... url ( r '^edit_profile/$' , auth ( UserProfileEditView . as_view ()), name = "edit_profile" ), Copy edit_profile.html from goodies to steelrumors/templates/ : cp /tmp/sr-goodies-master/templates/edit_profile.html \ ~/proj/steelrumors/steelrumors Add the following lines to templates/user_detail before the final endblock : {% if object.username == user.username %} <p><a href=' {% url "edit_profile" %} '>Edit my profile</a></p> {% endif %} Now, visit your profile page and try to edit it.

Easter Egg Fun

Add the following lines to user_detail.html before the endblock line:

{% if "zodcat" in object.userprofile.bio %} <style> html { background: #EEE url("/static/img/zod.jpg"); } </style> {% endif %}

Now mention “zodcat” to your bio. You have your easter egg!

We have a much better looking site at the end of this tutorial. While anyone could register to the site, they will not be part of the staff. Hence, you cannot submit links through the admin interface. This will be addressed in the next part.

That concludes Part 2. Follow me on Twitter at @arocks to get updates about upcoming parts.

EDIT: Check out Part 3!

Resources