Basics of Models in Django

We have learned quite a lot of things in the last few chapters. At this point, you should have a pretty good understanding of what Templates and Views are. In this chapter, we will learn about the Model part of the Django MTV architecture.

The Databases and Models #

Today most modern web applications are database driven. And SQL is the de facto language for accessing databases. If you have already done some Web programming then you would probably know in a language like PHP, it is very common to mix PHP code and SQL code. The problem with this approach is that it is messy, very very messy. With Django, we don't that, instead of executing raw SQL queries to create/modify/delete tables we use something called Models.

So What is a Model?

A Model defines fields and behaviors of the data you are storing. In simple words, a Model allows us to do following things:

  1. Create tables and define relationships between them.
  2. Modify tables and relationships between them.
  3. Access data stored in tables using Django ORM (without writing raw SQL).

Don't worry if all this passing over your head. In the next few sections, we are discussing everything in great depth.

Configuring Database #

Before we create models, we first need to configure our database with Django. Open settings.py file and scroll halfway through the page till you find DATABASE variable. If you haven't changed anything it should look like this:

TGDB/django_project/django_project/settings.py

#...
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
#...

The ENGINE specifies the the type of database backend Django is using. In our case we are using SQLite that's why it is set to 'django.db.backends.sqlite3'. Here are some other backends to choose from:

  • django.db.backends.mysql
  • django.db.backends.oracle
  • django.db.backends.postgresql

The NAME specifies the name of the database we are using. As SQLite database consists of a single file, it currently points to the absolute path of the db.sqlite3 file.

As we will be using SQLite database throughout this tutorial you don't need to modify anything here. In fact, we are ready to create models.

However, If we were not using SQLite database then we would need to add some additional settings like USER, PASSWORD, HOST and PORT. For example, here is how you would configure Django to use MySQL database.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'database_name',
        'USER': 'database_user',
        'PASSWORD': 'database_pass',
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}

What data we need? #

Before we code models, let's think about what data do we need to store in our database. As you know, we are creating a blog app. So we would need to store information about the following entities.

Post:

  • title
  • slug
  • content
  • publication date

Author:

  • name
  • email
  • active
  • account creation date
  • last logged in date

Category:

  • name
  • slug

Tag:

  • name
  • slug

Here we have outlined 4 entities Post, Author, Category and Tag. In the context of Database, each of these entities corresponds to a table and each attribute corresponds to a column. And in the context of Django framework, each entity is a model and each attribute is a field.

By default, Django automatically adds a primary key field called id to all models. You can refer to it using id or pk. The key automatically increments every time a new record is added.

You might be wondering what the slug means. In the URL http://example.com/very-important-post/, the slug is very-important-post. As we will see Django can automatically generate slug from the title.

Now let's take a look at what type of relationship exists between these entities.

  1. A post may belong to any one author but an author can write many blog posts. So there exists a one-to-many relationship between author and post.

  2. A post may belong to one category but a category can have multiple posts. So again there is a one-to-many relationship between category and post.

  3. A post may belong to one or more tags. Similarly, each tag may have one or more posts. So there exists a many-to-many relationship between post and tags.

  4. An author can create one or more categories but a category can only belong to one author. So, there is a one-to-many relationship between author and category.

  5. An Author can create one or more tags but a tag can only belong one author. So, again there is a one-to-many relationship between author and tag.

In total, we have 4 one-to-many relationships and 1 many-to-many relationship.

We will see later how to define these relationships inside our models. For now, just remember these relationships exists between our entities.

Here is the updated list of attributes of each entity along with the relationship between them.

Author:

  • name
  • email
  • active
  • account creation date
  • last logged in date

Post:

  • title
  • slug
  • content
  • publication date
  • Foreign key to author
  • Foreign key to category
  • Many-to-many relationship between post and Tag

Category:

  • name
  • slug
  • Foreign key to author

Tag:

  • name
  • slug
  • Foreign key to author

Creating Models #

Open models.py file inside the blog app located at TGDB/django_project/blog. It should look like this:

TGDB/django_project/blog/models.py

from django.db import models

# Create your models here.

Let's start by creating an Author model. A model is just a Python class which inherits from models.Model class. Add the following code to models.py.

TGDB/django_project/blog/models.py

from django.db import models

# Create your models here.


class Author(models.Model):
    pass

Syntactically, Author is a valid model, although it doesn't contain any fields (or attributes), it is still valid. Generally, when we define model we specify the all the fields along with their types. As already said, Django automatically adds a primary key field called id for all models, so you don't need to define it manually. For Author model we want following fields:

  • name
  • email
  • active
  • created_on
  • last_logged_in

Here is the code to add these fields:

TGDB/django_project/blog/models.py

from django.db import models

# Create your models here.


class Author(models.Model):
    name = models.CharField(max_length=100, unique=True)
    email = models.EmailField(unique=True)
    active = models.BooleanField(default=False)
    created_on = models.DateTimeField()
    last_logged_in = models.DateTimeField()

Let's step through the code we have added.

In line 7, we are defining name field of type CharField, think of CharField type as VARCHAR in context of the database. The max_length argument specifies the maximum number of characters this field can store. The unique=True means that value of this field must be unique throughout the table. We can pass unique and max_length arguments to almost all types of fields.

In line 8, here we are defining email field of type EmailField. We want authors to have unique email id's that's why we have passed unique=True to the EmailField() constructor.

In line 9, we are defining active field which designates whether the user is active or not. The type of this field is BooleanField. A BooleanField field can only take two values True or False. You can also pass 1 and 0 to it. The default is a special argument and is used to set the default value for the column. This default value is used in the case when the value of the column is not supplied while creating a new record.

In line 10 and 11, we are defining two fields, created_on and last_logged_in of type DateTimeField. A DateTimeField is used to store date and time and it corresponds to datetime.datetime object in Python.

At this point, you might be thinking "Database already provides various types of data types to store data". So what's the purpose of redefining them in Django?

They serve two important purposes:

  1. They validate data before it is saved into the database.
  2. They also control how to display data in the form.

The first point is discussed in this section. And we will discuss the second point while discussing forms.

Note that both models.Model and model fields (i.e models.CharField, models.EmailField etc) are Python classes. In the context of Python CharField is a string. In the context of database CharField is VARCHAR. CharField is very special field because it is the basis for all the other string-based fields. Django doesn't validate contents of the field of type CharField. In other words, Django doesn't check the values provided to the CharField field. It makes sense if you think about it. As CharField is essentially a string field, it can take almost any type of data. So, 1, mymail@example.com, 100e100, DJANG0, %^%*&^%$# all are valid examples of data.

However, the fields which inherit from CharField do perform Validation. For example, the EmailField. The EmailField is commonly used to store email, if you try to store anything else in it, it will raise an exception.

Django provides many other fields to store different types of data. The following table lists some commonly used fields:

Field Name What it is used for?
CharField A Field for storing small to medium-sized string. It acts as a base for other string-based fields. It doesn't provide any validation
SlugField A CharField to store slug. It only accepts alphabets, numbers and underscores and hyphens. It provides validation.
DateField A Field to store date. It corresponds to Python's datetime.date instance. It doesn't inherit from CharField. It provides validation.
DateTimeField A Field to store date and time. It corresponds to Python's datetime.datetime instance. It provides validation.
EmailField A CharField to store Email address. It verifies whether value entered value is valid email or not
URLField A CharField used to store URL. It provides validation. Note that it is different from SlugField. The SlugField only contains a part of the URL, not the URL itself. It checks whether the entered data is valid or not.
TextField A field to store a large amount of text. Use this field to store the content of blog post. In Python, this field translates to string. In databases, it translates to TEXT field. Like CharField this field doesn't provide any validation
BooleanField A field to store True or False. In the context Python, it translates to boolean True and False. In the context of the database (as we are using SQLite) it is stored as an integer (1)True and (0) False. Remember SQLite has integer type to handle integers.
IntegerField A field to store integer values from -2147483648 to 2147483647. It checks whether the value entered is number or not.
DecimalField A field to store decimal number.
ForeignKey A Field to define a one-to-many relationship.
ManyToManyField A Field to define a many-to-many relationship.

Optional Field Parameters #

The following are some optional field parameters available to all types of field. To use these parameters pass them as keyword arguments to model field class.

blank #

This parameter determines whether the field is required or not. By default, it is set to False, this is the reason why you are required to fill data in every field. If you set blank to True then field can be left empty.

default #

You can pass this parameter to set the default value of a Field.

unique #

If set to True then the value in the field will be unique throughout the table.

help_text #

This parameter is used to specify a help string. A help string describes how to use this field or some other important information regarding the field.

There are many other optional parameters, to view the full list of them check the Django documentation on field options.

Don't get overwhelmed by various field types and their parameters. You don't need to remember all of them. Let's say you want to use URLField and want to know it's parameter. Open your browser and visit https://docs.djangoproject.com/en/1.10/ref/models/fields/. This page contains documentation about every field type and their available options(parameters) in Django 1.10. Search the page for URLField keyword. Scroll down halfway through the page until you find a section describing URLField.

searching_documentation_for_urlfield

URLField documentation tells you following things:

  1. The first line specifies the class signature. In other words, what arguments should be passed to create an instance of URLField.

  2. It is CharField field means it inherits from CharField.

  3. The default form widget for this field is a TextInput - What is this? Earlier in the chapter, I told you that field types in Django are also used to create Forms. So this line tells us that a URLField would be rendered as TextInput (it is a class that represents the input tag i.e <input type="text" ...> ). Don't worry too much about it right now. We will discuss them in detail the upcoming chapters.

  4. The next line tells us we can also pass an optional max_length parameter to URLField to limit the number of characters this field can take. If you don't specify max_length then a default value of 200 will be used.

An astute reader might say but what about the second argument in class signature i.e **options?

The **options refers to the optional keyword arguments that all of the fields can take. We have covered some of them in this section.

Let's say you want to create unique URLField field of length 100. Here is the code to do that:

url = models.URLField(max_length=100, unique=True)

By default, all fields are required. If you don't specify data to a required field an exception will be thrown. To make the URL field optional add blank=True.

url = models.URLField(max_length=100, unique=True, blank=True)

Completing other Models #

Let's add the code for the remaining models.

TGDB/django_project/blog/models.py

from django.db import models

# Create your models here.


class Author(models.Model):
    name = models.CharField(max_length=50)
    email = models.EmailField(unique=True)
    active = models.BooleanField(default=False)
    created_on = models.DateTimeField(auto_now_add=True)
    last_logged_in = models.DateTimeField(auto_now=True)


class Category(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=100, unique=True)


class Tag(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=100, unique=True)


class Post(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    content = models.TextField()
    pub_date = models.DateTimeField(auto_now_add=True)

There is nothing new in above code except for two new parameters in DateTimeField (in Author and Post model). The auto_now parameter when set to True updates the value of the field automatically to current date and time every time an object (i.e an instance of Model) is saved. We use this parameter to commonly store last-modified timestamps. The important thing to note about this parameter is that it only updates the field when Model.save() method is called on the model instance. At this point, we haven't covered the save() method, so just keep this statement in mind, it will make more sense when we will discuss the save() method in Django ORM Basics lesson.

The auto_now_add parameter, when set to True automatically, sets the field value to current date and time when the object is first created.

You can also add these two parameters to the DateField. In that case, auto_now updates the value of the field automatically to current date every time object is saved. And auto_now_add parameter when set to True automatically sets the field value to the current date when the object is first created.

Adding relationship fields to our models #

In this section, we will learn how to add relationships between our models:

Recall that we have outlined the following the relationships between our models.

  1. A one-to-many relationship between Author and Post models.
  2. A one-to-many relationship between Category and Post models.
  3. A many-to-many relationship between Post and Tag models.
  4. A one-to-many relationship between Author and Category models.
  5. A one-to-many relationship between Author and Tag models.

Creating one-to-many relationship is simple, just add ForeignKey on the many side of the relationship and pass the name of the model you want to connect as a first parameter to the ForeignKey constructor.

Open models.py file and add the ForeignKey field to Category, Tag and Post models as follows:

TGDB/django_project/blog/models.py

#...

class Category(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=100, unique=True)
    author = models.ForeignKey(Author)


class Tag(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=100, unique=True)
    author = models.ForeignKey(Author)


class Post(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    content = models.TextField()
    pub_date = models.DateTimeField(auto_now_add=True)
    author = models.ForeignKey(Author)
    category = models.ForeignKey(Category)

#...

Here we have added the four one-to-many relationships:

  1. The first is between Author and Category.
  2. The second is between Author and Tag.
  3. The third is between Post and Author.
  4. The fourth is between Post and Category.

Similarly, creating a many-to-many relationship is easy. In our case, we have a many-to-many relationship between Post and Tag models. To create many-to-many relationship add ManyToManyField field to any one side of the relationship. In other words, you can add ManyToManyField either in the Tag model or Post model. Just like ForeignKey field, it requires you to pass the name of the model you want to connect to.

Here is the code to add a many-to-many relationship between Post and Tag models.

TGDB/django_project/blog/models.py

#...

class Post(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    content = models.TextField()
    pub_date = models.DateTimeField(auto_now_add=True)
    author = models.ForeignKey(Author)
    category = models.ForeignKey(Category)
    tags = models.ManyToManyField(Tag)

#...

At this point, models.py file should look like this:

TGDB/django_project/blog/models.py

from django.db import models

# Create your models here.


class Author(models.Model):
    name = models.CharField(max_length=50)
    email = models.EmailField(unique=True)
    active = models.BooleanField(default=False)
    created_on = models.DateTimeField(auto_now_add=True)
    last_logged_in = models.DateTimeField(auto_now=True)


class Category(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=100, unique=True)
    author = models.ForeignKey(Author)


class Tag(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=100, unique=True)
    author = models.ForeignKey(Author)


class Post(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    content = models.TextField()
    pub_date = models.DateTimeField(auto_now_add=True)
    author = models.ForeignKey(Author)
    category = models.ForeignKey(Category)
    tags = models.ManyToManyField(Tag)

We will continue adding missing elements to models.py file it but for now, it's enough.

We have now created some models. So what's next?

The next step is to create database tables from models. To create tables from models in Django we use something called migration.

Note: To checkout this version of the repository type git checkout 11a.