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 defacto language of accessing databases. If you have already done some Web programming then you would probably know in 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 relationship between them.
  2. Modify tables and relationship between them.
  3. Access data stored in tables using Django ORM (without creating raw SQL queries).

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 half way through the page till you find DATABASE variable. If you haven't changed anything it should look like this:

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

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

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 to 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, lets 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/,
very-important-post is a slug. 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 categories 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 tags 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 one to many relationship between author and category.

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

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 relationship exists between our entities.

Here is the updated list of attributes of each entities along with 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 inside blog app located at TGDB/django_project/blog. It should look like this:

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.

from django.db import models

# Create your models here.


class Author(models.Model):
    pass

Syntactically, Author is a valid model, although it doesn't contains any fields (or attributes) but 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:

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()

Lets step through the code we have added.

In line 7, we are defining name field of type models.CharField, think of CharField type as VARCHAR in context of database. max_length specifies the maximum number of characters this field can store. unique=True means that value of this field must be unique throughout the table. unique and max_length parameters belongs to some of those few parameters which can be used on almost all types of fields.

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

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

In line 10 and 11, we are defining two fields named created_on and last_logged_in of type DateTimeField. A DateTimeField is used to store date and time and it corresponds to 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 purpose:

  1. They validate data before data is saved to databases.
  2. They also control how to display data in form.

The first point is discussed in this section. And we will discuss 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 inherits 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 has many other fields to store different types of data. Here are some of them:

Field Name What it is used for ?
CharField A Field to store small and medium sized string. It acts as a base for most other 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 represented by Python's datetime.date instance. It doesn't inherit from CharField. Django ensures that data provided to DateField are valid dates only
DateTimeField A Field to store date and time represented by Python's datetime.datetime instance. It provides validation too.
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 contains only part of the URL not the URL itself. It checks whether the entered data is valid or not.
TextField A field to store large amount of text. Use this field to store 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 database (as we are using SQLite) it is stored as integer (1)True and (0) False. Remember SQLite3 has integer type to handle integers.
IntegerField A field to store integer values from -2147483648 to 2147483647. It checks wheter value entered is number or not.
DecimalField A field to store decimal number.
ForeignKey A Field to define one-to-many relations.
ManyToManyField A Field to define many-to-many relations.

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 default value of a Field.

unique #

If True then the value this 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 full list of them check the Django documentation on field options.

At this point 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 you browser and visit https://docs.djangoproject.com/en/1.10/ref/models/fields/. This page contains documentation about every field type and their options(parameters) available in Django 1.10. Search this page for URLField keyword. Scroll down halfway through the page until you find a section describing URLField.

searching_documentation_for_urlfield

URLField documention tells you following things:

  1. First line specifies the class signature i.e 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 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 to represent text input i.e <input type="text" ...> tag). 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 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 the Optional Field Options section above.

Lets 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 other models.

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_new parameter when set to True updates the value of the field automatically to current date and time every time 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, 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 current date when the object is first created.

Adding relationship fields to our models #

Models we have defined above doesn't contain any relationships between them. In this section we will learn how to add relationships:

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

  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 categories but a category can have multiple posts. So there exists a one to many relationship between category and post.

  3. A post may belong to one or more tags. Similarly each tags 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 one to many relationship between author and category.

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

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

Open models.py file in the blog app and add the ForeignKey field to Category, Tag and Post model class as follows:

...

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)

...

Note: Code is truncated to save space.

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 model. To create many-to-many relationship add ManyToManyField field to any one side of the realtionship i.e you can add ManyToManyField to either in Tag model or Post model. Just like ForeignKey field it requires you to pass name of the model you want to connect to.

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

...

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:

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 model in Django we use something called migration.