The talks app

Now we can get to the meat of our project, the talks app.

startapp

To start our app, we can to tell Django to give us some boilerplate right away with python manage.py startapp talks. We want app names to be plural, generally, as they usually concern themselves with multiple model instances and work around them.

This will give us a structure similar to:

/talks
├── __init__.py
├── admin.py
├── models.py
├── tests.py
├── views.py

We’ll start off with the models.py in here.

Note

I’ll be giving paths relative to the talks/ directory from here on out, so be sure to adjust them in your head as needed. Most text editors seem to offer a fuzzy file finder now, so editing should be fairly painless.

TalkList model

We want to be able to organize our talks into lists, things like “Attend or Else” and “Watch Online”. So the first thing we should probably have is a model for the list. Open up models.py and add the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 from django.contrib.auth.models import User
 from django.core.urlresolvers import reverse
 from django.db import models
 from django.template.defaultfilters import slugify


 class TalkList(models.Model):
     user = models.ForeignKey(User, related_name='lists')
     name = models.CharField(max_length=255)
     slug = models.SlugField(max_length=255, blank=True)

     class Meta:
         unique_together = ('user', 'name')

     def __unicode__(self):
         return self.name

     def save(self, *args, **kwargs):
         self.slug = slugify(self.name)
         super(TalkList, self).save(*args, **kwargs)

     def get_absolute_url(self):
         return reverse('talks:lists:detail', kwargs={'slug': self.slug})

Our model ties TalkList instances to a user, makes sure each list has a unique name per user, runs slugify on the name so we can use it in our URL, and provides an URL to get an individual list. But, what about that URL? What’s with the colons in it?

URLs

To make our URLs work like that, we need to set up three things and bring in a couple of namespaces.

First, let’s make a placeholder in our views.py file.

from django.http import HttpResponse
from django.views import generic


class TalkListDetailView(generic.View):
    def get(self, request, *args, **kwargs):
        return HttpResponse('A talk list')

Now, we need to create urls.py inside talks/ and add the followng:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
 from __future__ import absolute_import

 from django.conf.urls import patterns, url, include

 from . import views


 lists_patterns = patterns(
     '',
     url(r'^$', views.TalkListDetailView.as_view(), name='detail'),
 )

 urlpatterns = patterns(
     '',
     url(r'^lists/', include(lists_patterns, namespace='lists')),
 )

This line sets up an internal namespace of lists for all of our TalkList-specific URLs. Now we need to add the talks namespace that our get_absolute_url mentioned.

Open up survivalguide/urls.py and add:

url(r'^talks/', include('talks.urls', namespace='talks')),

This sets up the talks namespace.

south

Now, before we actually create the model, we should add south into the mix. pip install south will install it and we need to add 'south' to our INSTALLED_APPS. Since south has a model of its own, we also need to run python manage.py syncdb again to add it.

We should now add our 'talks' app to INSTALLED_APPS and, instead of running syncdb, we should run python manage.py schemamigration --initial talks. south will create a migration that generates our database table and put it in the migrations/ directory. Then we apply it with python manage.py migrate.

Note

python manage.py schemamigration is a really long command to have to type repeatedly, so I recommend creating a shell alias for it to save yourself some time.

Warning

Django 1.7 introduces an internal migration tool much like south. This tutorial does not cover that tool. While south will likely work with Django 1.7, you should use the new tool instead.

Default list

Now that we have a model and a database table, let’s make our SignUpView automatically create a default list for everyone. Open survivalguide/views.py and change SignUpView to match this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
[...]
from talks.models import TalkList
[...]

 class SignUpView(views.AnonymousRequiredMixin, views.FormValidMessageMixin,
                  generic.CreateView):
     form_class = RegistrationForm
     form_valid_message = "Thanks for signing up! Go ahead and login."
     model = User
     success_url = reverse_lazy('login')
     template_name = 'accounts/signup.html'

     def form_valid(self, form):
         resp = super(SignUpView, self).form_valid(form)
         TalkList.objects.create(user=self.object, name='To Attend')
         return resp