Let's Take Notes With Django - Part 2

Let's Take Notes With Django - Part 2

part of the Startproject To Deployment - Django Tutorial series

show all in series

back to index

We're building a Django project called DjangoNote that will let us store private notes online. It's fun!

In part one we laid the foundation to begin building our project. In part 2 we're going to breathe life into things with a few models.

Let's head to djangonote_project/djangonote/notes and open up models.py. We're greeted with a little boilerplate to get us started:

# /djangonote_project/djangonote/notes/models.py
from django.db import models
# Create your models here.

Our next step is to figure out what models we'll need to define. We decided in part one that, to start, our application is going to have one goal: allow a single logged-in user to store text notes online. It'd probably be pretty nice to be able to categorize these notes, too - to differentiate between a grocery list, a to-do list, and an appointment reminder for example. Let's start with two models, Note and Tag.

# /djangonote_project/djangonote/notes/forms.py
from django.db import models
# Create your models here.

class Note(models.Model):
label = models.CharField(max_length=200)
body = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
tags = models.ManyToManyField('Tag', related_name='notes')

def __unicode__(self):
return self.label

class Tag(models.Model):
label = models.CharField(max_length=200)

def __unicode__(self):
return self.label

Now we're getting somewhere! The documentation for the various modelfields is pretty extensive, but let's take a minute to talk about the ones we're using here.

We also defined a __unicode__ method for each model that returns self.label. This defines the default unicode representation of any instance of one of our models - label seems like a good label to use.

Now that we've got our models defined, we have enough to work with to get a database up and running. On our first run, this is a three-step process:


  1. manage.py makemigrations

  2. manage.py migrate

  3. manage.py createsuperuser

With those three commands run, our initial sqlite database is ready to rock and roll. Following the prompts after running createsuperuser creates our database's superuser - these credentials will also be what we use to log into our web app and store our notes.

While we're working with our models, let's think about how we'll be interacting with them. We'll want to be able to create and update our notes and tags, and the easiest way to do this is going to be through a modelform. Modelforms give us a quick and easy way to interact with our database through the models we've defined. Let's create a file called forms.py inside our notes directory. Open it up and make some magic:

# /djangonote_project/djangonote/notes/forms.py
from django import forms
from notes.models import Note, Tag

class NoteForm(forms.ModelForm):
class Meta:
model = Note
fields = ('label', 'body', 'tags')

class TagForm(forms.ModelForm):
class Meta:
model = Tag
fields = ('label',)

Earlier I said I wasn't a fan of class-based views because it makes too much of our process "automagic" - and while I stand by that, class-based modelforms are a different story. Forms are how we'll give our website the information it needs to write things to our database, so we want to make sure the info we're feeding it is sanitized and formatted properly. ModelForms take care of much of that for us, making working with our database info quick and easy. All we have to do is define a form class that inherits from forms.ModelForm, describe what model it's related to in the Meta block with model=ModelName, and determine what model fields we want to be able to edit with our form. As you can see, we're going to use our form to manipulate the only field on the Tag model ('label'), and we'll work with everything but the timestamp on the Note model.

Regardless, when defining what fields are available, we explicitly define the included fields. There are two other ways of defining the fields we'll use on a given modelform, but they both have a problem to be aware of:


  1. Using __all__: You can simply tell your modelform that you want to work with every field on a given model by naming __all__ instead of defining specific fields. Don't do this. One day you're going to go back to your model and add a field, SuperSecretIdentifierKey, that is a super secret identifier key that only you as the site administrator should ever see. And because you forgot to exclude that field from your modelform, your users are going to be able to read its value and edit it using the form you left open.
  2. Defining fields exclusively instead of inclusively: This creates the same problem as __all___. Take our Note modelform for instance - instead of saying "we explicitly want to include these three fields," we could have just said "we want to exclude this one field." Easier, right? But again, if we ever update this model we run the risk of forgetting to update the associated form, and that could leave a security hole. Better to keep things explicit and define what we do want included.


Okay, we've got our database set and we're ready to interact with it. It's time to start writing some views - we'll get to it in part 3!

Continue to Part 3


<< back to blog index