The Condon Project: Part 2

Published on:
May 26, 2017

In Part 1 of this series, I talked about setting up a database and virtual environment. Today will be all about the data models I used for different parts of Chris’s website. If you’re unfamiliar with any of the terms I throw around, the official Django tutorial will get you up to speed.

What’s a Data Model, Anyway?


Back in my CS50x review, I talked about the “Model, View, Controller”(MVC) pattern for software development. Django web apps also follow this pattern. Any object you want rendered on screen that can be edited or interacted with needs to be represented as a row in a table in a database. Django comes with built-in object-relational mapping (ORM) for turning your python classes into database tables, so all you have to do is decide what properties each class should have.

A simple example is a basic home page. Assuming the page has a headline, a subheadline, and a link to something more interesting your model might look like this:

from django.db import models


class HomePage (models.Model):

    headline = models.CharField(max_length=40)
    sub_headline = models.CharField(max_length=50)
    link_text = models.CharField(max_length=20)

Using the example above will make a table in a database that has three character fields (columns that store characters how they are entered). The headline will be limited to 40 characters, the sub headline 50, and the text in the link to 20 (people have short attention spans). Modeling everything you want to be editable in your database is step 1 in creating a web app.

Is it Ever Really That Simple?


The home page above is probably the simplest functional example of how to model an object with Django’s ORM. A slightly more useful example is in how I modeled Chris's Contact page. This page is split into two objects. The first is a set of social media links that will populate as a row of icons:

class Contact(models.Model):

    class Meta:
        verbose_name_plural = 'Contact Page'

    title = models.CharField(max_length=30)
    facebook = models.URLField(blank=True)
    youtube = models.URLField(blank=True)
    twitter = models.URLField(blank=True)
    pub_date = models.DateField(default=timezone.now)

    def __str__(self):
        return self.title

Heads up. This part is going to be a little Object-Oriented. The blank=True parameters being passed to those URLField instances is overriding a property of the parent class. Django URLFields are not allowed to be blank by default, but they built in a way to override that. Here Chris can add a link to the social media sites he chooses, and anything left blank will just get skipped by my template (more on templates in a future entry).

The second model I used for his contact page was for the form to send him an email:

# Django Imports
from django import forms
from django.core.mail import EmailMessage


# create a contact form to send email to my email address
class ContactForm(forms.Form):
    
    name = forms.CharField(max_length=100, required=True)
    email = forms.EmailField(max_length=50, required=True)
    subject = forms.CharField(max_length=100, required=True)
    message = forms.CharField(widget=forms.Textarea, required=True)
    cc_myself = forms.BooleanField(required=False)

    def send_email(self):
        
        # EmailMessage class with data from form
        contact_email = EmailMessage(subject=self.cleaned_data['subject'],
                                     to=['*******@*****.com'],
                                     body='Sender Name: {} \nSender Email: {}\n\n {}'.format(
                                         self.cleaned_data['name'],
                                         self.cleaned_data['email'],
                                         self.cleaned_data['message']
                                     ))

        # adds cc line if applicable
        cc_myself = self.cleaned_data['cc_myself']
        if cc_myself:
            contact_email.cc = [self.cleaned_data['email']]

        # send email
        contact_email.send()

This one is a bit trickier still. In addition to having properties to display, the form also has the ability to do something. Send_mail is a “method” that I can use later in different sections of my code. In the “View” layer—another blog entry to look forward to—I tell my app to call this method any time a visitor hits the “submit” button at the bottom of the contact form.

Object Relationships


Nothing in the world exists in isolation; even bull elephants meet up with the herd once per season. It’s the same way with objects in programming. The entire relational database paradigm is built around objects being able to relate to one another. In a world where a new iPhone comes out every year, the relational database has survived since 1970.

A real-world example is Chris’s “Albums” page. Each album has tracks, but I won’t know in advance how many tracks are on each album. This means they really need to be separate tables in my database, and have separate python classes defining them. Check out the class for tracks:

class AlbumTrack (models.Model):

    class Meta:
        verbose_name='Track'
        verbose_name_plural='Tracks'

    def __str__(self):
        return self.title

    title = models.CharField(max_length=50)
    album = models.ForeignKey(Album, related_name='tracks')
    composer = models.ForeignKey(Composer, related_name='pieces')
    composition_date = models.DateField(default=timezone.now, blank=True, null=True)
    audio_sample = AudioFileField(max_upload_size=10485760)

The ForeignKey field allows each new AlbumTrack to reference an Album that already exists. Having composers be represented separately means that each composer only needs to have their data stored once.

In a perfectly normalized database every cell in every table would be unique and objects would relate to each other accordingly to make that happen. In addition to keeping storage space down, storing data in this way keeps this app flexible should Chris at some point in the future decides he wants separate pages dedicated to certain tracks or specific composers.

What Now?


Now that I have all this data neatly modeled, it would be nice if someone could see it. That’s where the “View” part of the MVC model comes in. More on that next time. Until then, I need some kind of goofy catchphrase to end these super-dry blog entries!