It feels like ages since part 2 was out. Meanwhile, the movie Man of Steel has actually been released. So there is really no need to check for rumors about what that movie is all about. Probably the industry is now abuzz with rumours about its sequel instead.
In this tutorial, I would be showing how to use features like comments and CRUD views which are integral to a social site. You can choose to watch the video or read the step by step description below or follow both. The goodies pack which was introduced in the last part has been updated and would be used again to save time to create templates.
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 2, we showed you how to create a beta-like site to publish rumors about “Man of Steel” where users can sign-up and create their own profiles.
The outline of Part 3 of the screencast is:
- Comments framework
- Create/Read/Update/Delete of a Link
- Pagination
Get the goodies pack again
The goodies pack has changed since the last tutorial, so I would recommend downloading it again.
-
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
Pagination
So far we have been seeing just the first page of the list of links. But we are using Django’s ListView
which provides pagination. Let’s implement a simple ‘Next’ link to visit the next page.
-
Add the following snippet to
steelrumors/templates/links/link_list.html
just before{% endblock %}
:{% if is_paginated %} <div class="pagination"> {% if page_obj.has_next %} <a href="?page={{ page_obj.next_page_number }}">More »</a> {% endif %} </div> {% endif %}
-
In the same template file we’ll need to change the first
<ol>
tag to ensure that the line numbers in page 2 and later appear correctly. Replace that line with<ol>
with these lines:{% if is_paginated %} <ol start="{{ page_obj.start_index }}"> {% else %} <ol> {% endif %}
Now you can visit every page and read every submitted link!
CRUD - Create and Read Links
-
We have been using the admin interface to create/update/delete links. But this is only accessible to staff members. To allow users to submit links, we will need a new form, a new view class (generic CBV) and a new template with a form.
Add this new ModelForm to `links/forms.py`: from .models import Link ... class LinkForm(forms.ModelForm): class Meta: model = Link exclude = ("submitter", "rank_score")
-
Time to implement the “C” of CRUD by importing
CreateView
and the form you just created:Add the `LinkCreateView` class to `links/views.py`: from django.views.generic.edit import CreateView from .forms import LinkForm ... class LinkCreateView(CreateView): model = Link form_class = LinkForm def form_valid(self, form): f = form.save(commit=False) f.rank_score = 0.0 f.submitter = self.request.user f.save() return super(CreateView, self).form_valid(form)
-
Copy
link_form.html
from goodies tosteelrumors/templates/links/link_form.html
:cp /tmp/sr-goodies-master/templates/links/link_form.html \ ~/proj/steelrumors/steelrumors/templates/links/
-
Add this view in
steelrumours/urls.py
:from links.views import LinkCreateView url(r'^link/create/$', auth(LinkCreateView.as_view()), name='link_create'),
Visit http://127.0.0.1:8000/link/create/ and submit a new link. Remember, you’ll need to be logged in to submit links.
-
If you try to submit a link, you will see an error message asking you to “Either provide a url or define a
get_absolute_url
method on the Model.” So let’s define theget_absolute_url
method.Add the following method in the
Link
class:from django.core.urlresolvers import reverse ... def get_absolute_url(self): return reverse("link_detail", kwargs={"pk": str(self.id)})
-
Create a DetailView in
links/views.py
. This is the “R” of CRUD.from django.views.generic import DetailView ... class LinkDetailView(DetailView): model = Link
-
Copy
link_detail.html
from goodies tosteelrumors/templates/links/link_detail.html
:cp /tmp/sr-goodies-master/templates/links/link_detail.html \ ~/proj/steelrumors/templates/links/
-
Add this detail view in
steelrumours/urls.py
:from links.views import LinkDetailView url(r'^link/(?P<pk>\d+)/$', LinkDetailView.as_view(), name='link_detail'),
Try submitting the add link form again and it should take you to the detail page, without error.
-
For convenience, let’s add the urls to these new views in our template so that users can easily find them. Add only the line with a + sign to
base.html
(remove the + sign):{% if user.is_authenticated %} + <a href="{% url 'link_create' %}">Submit Link</a> | <a href="{% url 'logout' %}">Logout</a> |
Make the following change (changed line starts with a + sign) to
steelrumors/templates/links/link_list.html
:{% for link in object_list %} <li> [{{ link.votes }}] + <a href="{% url 'link_detail' pk=link.pk %}"> <b>{{ link.title }}</b>
Refresh the steelrumours site on your browser and check if all the links work correctly.
CRUD - Update and Delete
-
The remaining two views for Update and Delete are straight forward and we can add them together. We are going to reuse the LinkForm, so let’s start with views.
Add these view classes to
links/views.py
:from django.core.urlresolvers import reverse_lazy from django.views.generic.edit import UpdateView from django.views.generic.edit import DeleteView ... class LinkUpdateView(UpdateView): model = Link form_class = LinkForm class LinkDeleteView(DeleteView): model = Link success_url = reverse_lazy("home")
-
Copy
link_confirm_delete.html
from goodies tosteelrumors/templates/links/link_confirm_delete.html
:cp /tmp/sr-goodies-master/templates/links/link_confirm_delete.html \ ~/proj/steelrumors/templates/links/
-
Add these views in
steelrumours/urls.py
:from links.views import LinkUpdateView from links.views import LinkDeleteView url(r'^link/update/(?P<pk>\d+)/$', auth(LinkUpdateView.as_view()), name='link_update'), url(r'^link/delete/(?P<pk>\d+)/$', auth(LinkDeleteView.as_view()), name='link_delete'),
-
Finally, for convenience, add these lines (with + sign) to
steelrumors/templates/links/link_detail.html
:<h2><a href="{{ object.link }}">{{ object.title }}</a></h2> + {% if object.submitter == user %} + <a href="{% url 'link_update' pk=object.pk %}">Edit</a> | + <a href="{% url 'link_delete' pk=object.pk %}">Delete</a> + {% endif %}
Now, you can create, read, update and delete Link objects. Try it!
Enabling Comments
-
We are going to add comments to the link detail pages using the built-in Django comments framework. First add this applications in
steelrumors/settings.py
:INSTALLED_APPS = ( 'django.contrib.admin', + 'django.contrib.comments',
Run syndb to create the tables required by the comments app:
./manage.py syncdb
-
Copy the new
link_detail.html
page from the goodies pack:cp /tmp/sr-goodies-master/templates/links/link_detail2.html \ ~/proj/steelrumors/templates/links/link_detail.html
-
We need to show comment counts in the front page itself. So add the following lines to
steelrumors/templates/links/link_list.html
at the beginning and middle of the template:{% extends "base.html" %} + {% load comments %} ... <a href="{% url 'link_detail' pk=link.pk %}"> <b>{{ link.title }}</b> + {% get_comment_count for link as comment_count %} + {{ comment_count }} comment{{ comment_count|pluralize }} </a>
-
Add this line to
steelrumours/urls.py
for wiring up the comments app:url(r'^comments/', include('django.contrib.comments.urls')),
Now, open any link detail page and have fun writing comments!
Fun with Random Gossip
Add a mixin class called RandomGossipMixin
in links/views.py
before LinkListView
class:
from django.contrib.comments.models import Comment
...
class RandomGossipMixin(object):
def get_context_data(self, **kwargs):
context = super(RandomGossipMixin, self).get_context_data(**kwargs)
context[u"randomquip"] = Comment.objects.order_by('?')[0]
return context
Change the class declaration of LinkListView
to include this mixin as a base class:
class LinkListView(RandomGossipMixin, ListView):
Add the following lines to steelrumors/templates/links/link_list.html
before the endblock
line:
<blockquote style="background-color: #ddd; padding: 4px; border-radius: 10px; margin: 10px 0; color: #666; font-size: smaller; text-shadow: rgba(255,255,255,0.8) 1px 1px 0;">
{{ randomquip.comment|truncatechars:140 }}
</blockquote>
Now refresh the home page and enjoy a random comment appear at the bottom of the page.
Final Comments
We have a lot more feature-complete social news site at this point. Users can actually submit links and comment about them. In the next and concluding part, we will cover writing mixins and ranking algorithms in Django. With this users will be able to vote and influence the ranking of links.
That concludes Part 3. Follow me on Twitter at @arocks to get updates about upcoming parts.
EDIT: Check out Part 4!
Resources
- Full Source on Github
- Goodies pack on Github