Adding Relationships§

We have a basic email address book at this point, but there's other information we might want to track for our contacts. Mailing addresses, for example. A single Contact may have multiple addresses associated with them, so we'll store this in a separate table, allowing us to have multiple addresses for each Contact.

class Address(models.Model):

    contact = models.ForeignKey(Contact)
    address_type = models.CharField(
        max_length=10,
    )

    address = models.CharField(
        max_length=255,
    )
    city = models.CharField(
        max_length=255,
    )
    state = models.CharField(
        max_length=2,
    )
    postal_code = models.CharField(
        max_length=20,
    )

    class Meta:
        unique_together = ('contact', 'address_type',)

Django provides three types of fields for relating objects to each other: ForeignKey for creating one to many relationships, ManyToManyField for relating many to many, and OneToOneField for creating a one to one relationship. You define the relationship in one model, but it's accessible from the other side, as well.

Sync up the database to create the table, and then start the shell so we can explore this.

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

Now that we have the model created, we can again play 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, Address
>>> nathan = Contact.objects.create(first_name='Nathan', email='nathan@yergler.net')
>>> nathan.address_set.all()
[]
>>> nathan.address_set.create(address_type='home',
... city='San Francisco', state='CA', postal_code='94107')
<Address: Address object>
>>> nathan.address_set.create(address_type='college',
... address='354 S. Grant St.', city='West Lafayette', state='IN',
... postal_code='47906')
<Address: Address object>
>>> nathan.address_set.all()
[<Address: Address object>, <Address: Address object>]
>>> nathan.address_set.filter(address_type='college')
<Address: Address object>
>>> Address.objects.filter(contact__first_name='Nathan')
[<Address: Address object>, <Address: Address object>]

As you can see, even though we defined the relationship between Contacts and Addresses on the Address model, Django gives us a way to access things in the reverse direction. We can also use the double underscore notation to filter Addresses or Contacts based on the related objects.

Let's go ahead and add address display to our contacts. We'll add the list of all Addresses to the Contact detail view in contact.html.

{% extends "base.html" %}

{% block content %}

<h1>{{ contact }}</h1>

<p>Email: {{ contact.email }}</p>

<ul>
{% for address in contact.address_set.all %}
   <li>{{ address.address }}<br/>
       {{ address.city }} {{ address.state }}<br/>
       {{ address.postal_code }}
   </li>
{% endfor %}
</ul>

{% endblock %}