Вы находитесь на странице: 1из 10

Building a Hacker News clone in Django - Part 2

(User Profiles and Registrations)


(http://arunrocks.com/building-a-hacker-news-
clone-in-django-part-2/)
You are reading a post from a four-part tutorial series
Part 1(/building-a-hacker-news-clone-in-django-part-1/) Part 2(/building-a-hacker-news-clone-in-django-part-2/)
Part 3(/building-a-hacker-news-clone-in-django-part-3/) Part 4(/building-a-hacker-news-clone-in-django-part-4/)
It has been more than a week since the first part of this tutorial
series(/building-a-hacker-news-clone-in-django-part-1/) was posted and Ive
been getting positive feedback from several channels. Ironically, it was
extremely popular on Hacker News(https://news.ycombinator.com/item?
id=5784689). It was even translated into
Chinese(http://www.oschina.net/translate/building-a-hacker-news-clone-in-
django-part-1).
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 Djangos
class based views(https://docs.djangoproject.com/en/dev/topics/class-
based-views/) for building
CRUD(http://en.wikipedia.org/wiki/Create,_read,_update_and_delete)
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(https://github.com/arocks/sr-
goodies/archive/master.zip) 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(/building-a-hacker-
news-clone-in-django-part-1/) and I recommend watching it. Click on the
image below to watch the screencast or scroll down to read the steps.
(http://www.youtube.com/watch?v=NeYKGTSFF6I&hd=1)
Enjoyed this tutorial? Then you should sign up for my upcoming book
Building a Social News Site in Django(https://leanpub.com/social-news-
site-in-django). It tries to explain in a learn-from-a-friend style how websites
BLOG( / ARCHI VE S/ )
ABOUT( HTTP: / / ARUNROCKS. COM/ ABOUT/ )
A RU N RO CKS ( / )
Arun Ravindran
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(http://www.imdb.com/title/tt0770828/).
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. Lets use some assets
which are pre-designed for the tutorial.
1. Download sr-goodies-master.zip(https://github.com/arocks/sr-
goodies/archive/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
2. 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
1. 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"),
2. 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')
3. 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(https://django-
registration.readthedocs.org/en/latest/simple-backend.html) of django-
registrations(https://django-registration.readthedocs.org/en/latest/) since it is
easy to use and understand.
1. Currently, the version of django registration on the the Python
Package Index(https://pypi.python.org/pypi/django-registration)
doesnt 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 dont have git installed, use:
pip install https://github.com/arocks/django-registration-
1.5/tarball/master
2. Edit settings.py to add registration app to the end of INSTALLED_APPS
'registration',
)
3. Run syncdb to create the registration models:
./manage.py syncdb
4. 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/
5. Add to urls.py:
url(r'^accounts/', include('registration.backends.simple.urls')),
Visit
http://127.0.0.1:8000/accounts/register/(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 users profile page
1. 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)
2. Run syncdb again to create the user profile model:
./manage.py syncdb
3. 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/(http://127.0.0.1:8000/admin/) and
open any users details. The bio field should appear in the bottom.
4. 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
5. Copy user_detail.html from goodies to steelrumors/templates/:
cp /tmp/sr-goodies-master/templates/user_detail.html \
~/proj/steelrumors/steelrumors/templates/
6. Lets 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.
7. You probably dont want to enter these links by hand each time. So
lets 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
1. 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})
2. Create links/forms.py:
from django import forms
from .models import UserProfile
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
exclude = ("user")
3. 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"),
4. Copy edit_profile.html from goodies to steelrumors/templates/:
cp /tmp/sr-goodies-master/templates/edit_profile.html \
~/proj/steelrumors/steelrumors
5. 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!
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(http://twitter.com/arocks/) to get updates about upcoming parts.
Resources
Full Source(https://github.com/arocks/steel-rumors/tree/episode2)
on Github
Goodies pack(https://github.com/arocks/sr-
goodies/archive/master.zip) on Github
Building a Hacker News clone in
Django - Part 3 (Comments
and CRUD)
(http://arunrocks.com/building-a-
hacker-news-clone-in-django-part-3/)
Building a Hacker News clone in
Django - Part 1
(http://arunrocks.com/building-a-
hacker-news-clone-in-django-part-1/)
Arun Ravindran(http://arunrocks.com)
Hi! Welcome to ArunRocks, an odd collection of writeups on programming,
travel, gadgets and practically anything under the sun. This state of affairs
could be blamed on the ecelectic interests of your host, Arun Ravindran. He
loves programming in several languages especially Python. In his day job he
works as a Solution Manager at Unisys. Read more...(/about/)
Posted on: Thu 06 June 2013
Tagged: screencast(http://arunrocks.com/tag/screencast.html) /
django(http://arunrocks.com/tag/django.html) /
python(http://arunrocks.com/tag/python.html) /
technical(http://arunrocks.com/tag/technical.html) /
tutorial(http://arunrocks.com/tag/tutorial.html) /
steelrumors(http://arunrocks.com/tag/steelrumors.html)
Share on: Twitter(http://twitter.com/share?
url=http://arunrocks.com/building-a-hacker-news-clone-in-django-part-2/),
Facebook(http://www.facebook.com/sharer.php?
u=http://arunrocks.com/building-a-hacker-news-clone-in-django-part-2/),
Google+(https://plus.google.com/share?url=http://arunrocks.com/building-a-
hacker-news-clone-in-django-part-2/)
Comments
8Comments ArunRocks Login
SortbyOldest Share Favorite
Don'tmissanyfutureposts!
Subscribetoourmailinglist
emailaddress
Subscribe
Jointhediscussion
Reply
tony ayearago
Thankslovingthistutorial.Lookingforwardtoseehowyou
implementthefrontendforvoting.

1
Reply
jackson 10monthsago
IwasalsogettinganintegrityerrorwhenIaddedanewuseratstep
6ofcreatingauser'sprofilepageabove.
Icommentedoutthefollowinglinesinmodels.pyandIcould
createusers:
#fromdjango.db.models.signalsimportpost_save
#post_save.connect(create_profile,sender=User)

Reply
jackson 10monthsago
Underedityourprofilestep3,Ihadtoadd'UserProfileEditView'to
theendofthisline:
fromlinks.viewsimportLinkListView,UserProfileDetailView,
UserProfileEditView

Reply
CJMac 9monthsago
seemore
Hi,I'mrunningintothiserrorandhaven'tbeenabletosolveityet.
Ioriginallyhadtofightwithafewfilesformixingtabsandspaces.
NowthatIhavegotridofthaterror,Inowgetthiserror,ifanyone
couldpointmeintherightdirectionthatwouldbegreat.
"
Nomodulenamedforms
RequestMethod:
GET
RequestURL:
http://127.0.0.1:8000/
DjangoVersion:
1.5.2
ExceptionType:
ImportError

Share
Share
Share
Share
Home(/) Feed(http://arunrocks.com/blog/feed/atom.xml) @arocks(http://twitter.com/arocks)

MarsRoverinPython
andHaskell
3commentsayearago
AkkulThanksfordropping
by!IhavemovedtheL&R
orientationmappingbefore
while(thatwasntsupposedtobe
inthe
RealtimeApplicationsand
willDjangoadapttoit?
21comments7monthsago
HAHAHAHAHAHAHAHHAHAHAAHHAHAHAH
LastjobweusedMeteor.js,
wereadthebook,andwentoutto
buildthe
DecodingGooglesFirst
TweetinPython
1commentayearago
UnderstandingTestDriven
DevelopmentwithDjango
5comments3monthsago
ALSOONARUNROCKS
Reply
Reply
ChrysCantonaOgwara 8monthsago
howdoiputprofilepictures

Reply
ArunRavindran
8monthsago
Author ChrysCantonaOgwara
Ithinkyoucancheckdjangogravatar 2forthis.

Reply
muck 5monthsago
thanksforthetutorial!FYI:i'musingdjango1.6.1andIwas
gettinganimportError'nomodulenamedefaultsfound'whenI
triedtocompletetheUsingDjangoRegistrationstep.So,in
goodies/registration/auth_urls.pyand
registration/backends/simple/urls.pyIchanged"from
django.conf.urls.defaultsimport*"to"fromdjango.conf.urls
importpatterns,url,include"andthisworked.Apparently,from
django.conf.urls.defaultsimport*isnolongerinuseondjango1.6

Reply
ArunRavindran 5monthsago Author muck
Thanksmuckforhighlightingthisproblemandprovidinga
solution.Ishallupdatetheregistrationappsothatitworks
fineinDjango1.6.

WHAT'STHIS?
Share
Share
Share
Share
Share