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 tosteelrumors/steelrumors
. Also, overwrite the extractedsr-goodies-master/templates/base.html
template intosteelrumors/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 thetemplates
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 undertemplates
: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 tomodels.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 tosteelrumors/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 toviews.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 anauth
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 tosteelrumors/templates/
:cp /tmp/sr-goodies-master/templates/edit_profile.html \ ~/proj/steelrumors/steelrumors
-
Add the following lines to
templates/user_detail
before the finalendblock
:{% 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!
Final Comments
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
- Full Source on Github
- Goodies pack on Github