Using Models§

Configuring the Database§

Django includes support out of the box for MySQL, PostgreSQL, SQLite3, and Oracle. SQLite3 is included with Python starting with version 2.5, so we’ll use it for our project for simplicity. If you were going to use MySQL, for example, you’d need to add mysql-python to your requirements.txt file.

To enable SQLite as the database, edit the DATABASES definition in addressbook/settings.py. The settings.py file contains the Django configuration for our project. There are some settings that you must specify – like the DATABASES configuration – and others that are optional. Django fills in some defaults when it generates the project scaffolding, and the documentation contains a full list of settings. You can also add your own settings here, if needed.

For SQLite we need to set the engine and then give it a name. The SQLite backend uses the NAME as the filename for the database.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',  # 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'address.db',
        'USER': '',                      # Not used with sqlite3.
        'PASSWORD': '',                  # Not used with sqlite3.
        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}

Note that the database engine is specified as a string, and not a direct reference to the Python object. This is because the settings file needs to be easily importable, without triggering any side effects. You should avoid adding imports to the settings file.

You rarely need to import the settings file directly; Django imports it for you, and makes it available as django.conf.settings. You typically import your settings from django.conf:

from django.conf import settings

Creating a Model§

Django models map (roughly) to a database table, and provide a place to encapsulate business logic. All models subclass the base Model class, and contain field definitions. Let’s start by creating a simple Contact model for our application in contacts/models.py.

from django.db import models


class Contact(models.Model):

    first_name = models.CharField(
        max_length=255,
    )
    last_name = models.CharField(
        max_length=255,

    )

    email = models.EmailField()

    def __str__(self):

        return ' '.join([
            self.first_name,
            self.last_name,
        ])

Django provides a set of fields that map to data types and different validation rules. For example, the EmailField here maps to the same column type as the CharField, but adds validation for the data.

Once you’ve created a model, you need to update your database with the new tables. Django’s syncdb command looks for models that are installed and creates tables for them if needed.

(tutorial)$ python ./manage.py syncdb

Creating tables ...
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_user_permissions
Creating table auth_user_groups
Creating table auth_user
Creating table django_content_type
Creating table django_session
Creating table django_site

...

Our contact table isn’t anywhere to be seen. The reason is that we need to tell the Project to use the Application.

The INSTALLED_APPS setting lists the applications that the project uses. These are listed as strings that map to Python packages. Django will import each and looks for a models module there. Add our Contacts app to the project’s INSTALLED_APPS setting:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Uncomment the next line to enable the admin:
    # 'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    # 'django.contrib.admindocs',
    'contacts',
)

Then run syncdb again:

(tutorial)$ python ./manage.py syncdb
Creating tables ...
Creating table contacts_contact
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)

Note that Django created a table named contacts_contact: by default Django will name your tables using a combination of the application name and model name. You can override that with the model Meta options.

Interacting with the Model§

Now that the model has been synced to the database we can interact with it using the interactive shell.

(tutorial)$ python ./manage.py shell
Python 2.7.3 (default, Aug  9 2012, 17:23:57)
[GCC 4.7.1 20120720 (Red Hat 4.7.1-5)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from contacts.models import Contact
>>> Contact.objects.all()
[]
>>> Contact.objects.create(first_name='Nathan', last_name='Yergler')

>>> Contact.objects.all()
[]
>>> nathan = Contact.objects.get(first_name='Nathan')
>>> nathan

>>> print nathan
Nathan Yergler
>>> nathan.id
1

There are a few new things here. First, the manage.py shell command gives us a interactive shell with the Python path set up correctly for Django. If you try to run Python and just import your application, an Exception will be raised because Django doesn’t know which settings to use, and therefore can’t map Model instances to the database.

Second, there’s this objects property on our model class. That’s the model’s Manager. If a single instance of a Model is analogous to a row in the database, the Manager is analogous to the table. The default model manager provides querying functionality, and can be customized. When we call all() or filter() or the Manager, a QuerySet is returned. A QuerySet is iterable, and loads data from the database as needed.

Finally, there’s this id field that we didn’t define. Django adds an id field as the primary key for your model, unless you specify a primary key.

Writing a Test§

We have one method defined on our model, __str__, and this is a good time to start writing tests. The __str__ method of a model will get used in quite a few places, and it’s entirely conceivable it’d be exposed to end users. It’s worth writing a test so we understand how we expect it to operate. Django creates a tests.py file when it creates the application, so we’ll add our first test to that file in the contacts app.

from contacts.models import Contact
...
class ContactTests(TestCase):
    """Contact model tests."""

    def test_str(self):

        contact = Contact(first_name='John', last_name='Smith')

        self.assertEquals(
            str(contact),
            'John Smith',
        )

You can run the tests for your application using manage.py:

(tutorial)$ python manage.py test

If you run this now, you’ll see that around 420 tests run. That’s surprising, since we’ve only written one. That’s because by default Django runs the tests for all installed applications. When we added the contacts app to our project, there were several Django apps there by default. The extra 419 tests come from those.

If you want to run the tests for a specific app, just specify the app name on the command line:

(tutorial)$ python manage.py test contacts
Creating test database for alias 'default'...
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
Destroying test database for alias 'default'...
$

One other interesting thing to note before moving on is the first and last line of output: “Creating test database” and “Destroying test database”. Some tests need access to a database, and because we don’t want to mingle test data with “real” data (for a variety of reasons, not the least of which is determinism), Django helpfully creates a test database for us before running the tests. Essentially it creates a new database, and runs syncdb on it. If you subclass from Django’s TestCase (which we are), Django also resets any default data after running each TestCase, so that changes in one test won’t break or influence another.

Review§

  • Models define the fields in a table, and can contain business logic.
  • The syncdb manage command creates the tables in your database from models
  • The model Manager allows you to operate on the collection of instances: querying, creating, etc.
  • Write unit tests for methods you add to the model
  • The test manage command runs the unit tests