Designing the blog data schema
We will start designing our blog data schema by defining the data models for our blog. A model is a Python class that subclasses django.db.models.Model
, in which each attribute represents a database field. Django will create a table for each model defined in the models.py
file. When you create a model, Django provides you with a practical API to query objects in the database easily.
First, we will define a Post
model. Add the following lines to the models.py
file of the blog
application:
from django.db import models from django.utils import timezone from django.contrib.auth.models import User class Post(models.Model): STATUS_CHOICES = ( ('draft', 'Draft'), ('published', 'Published'), ) title = models.CharField(max_length=250) slug = models.SlugField(max_length=250, unique_for_date='publish') author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts') body = models.TextField() publish = models.DateTimeField(default=timezone.now) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft') class Meta: ordering = ('-publish',) def __str__(self): return self.title
This is our data model for blog posts. Let's take a look at the fields we just defined for this model:
title
: This is the field for the post title. This field isCharField
, which translates into aVARCHAR
column in the SQL database.slug
: This is a field intended to be used in URLs. A slug is a short label that contains only letters, numbers, underscores, or hyphens. We will use theslug
field to build beautiful, SEO-friendly URLs for our blog posts. We have added theunique_for_date
parameter to this field so that we can build URLs for posts using their publish date andslug
. Django will prevent multiple posts from having the sameslug
for a given date.author
: This field is a foreign key. It defines a many-to-one relationship. We are telling Django that each post is written by a user, and a user can write any number of posts. For this field, Django will create a foreign key in the database using the primary key of the related model. In this case, we are relying on theUser
model of the Django authentication system. Theon_delete
parameter specifies the behavior to adopt when the referenced object is deleted. This is not specific to Django; it is an SQL standard. UsingCASCADE
, we specify that when the referenced user is deleted, the database will also delete its related blog posts. You can take a look at all possible options at https://docs.djangoproject.com/en/2.0/ref/models/fields/#django.db.models.ForeignKey.on_delete. We specify the name of the reverse relationship, fromUser
toPost
, with therelated_name
attribute. This will allow us to access related objects easily. We will learn more about this later.body
: This is the body of the post. This field is a text field, which translates into aTEXT
column in the SQL database.publish
: This datetime indicates when the post was published. We use Django's timezonenow
method as the default value. This returns the current datetime in a timezone-aware format. You can think of it as a timezone-aware version of the standard Pythondatetime.now
method.created
: This datetime indicates when the post was created. Since we are usingauto_now_add
here, the date will be saved automatically when creating an object.updated
: This datetime indicates the last time the post was updated. Since we are usingauto_now
here, the date will be updated automatically when saving an object.status
: This field shows the status of a post. We use achoices
parameter, so the value of this field can only be set to one of the given choices.
Django comes with different types of fields that you can use to define your models. You can find all field types at https://docs.djangoproject.com/en/2.0/ref/models/fields/.
The Meta
class inside the model contains metadata. We tell Django to sort results in the publish
field in descending order by default when we query the database. We specify descending order using the negative prefix. By doing so, posts published recently will appear first.
The __str__()
method is the default human-readable representation of the object. Django will use it in many places, such as the administration site.
Note
If you come from using Python 2.X, note that in Python 3, all strings are natively considered Unicode, and therefore, we only use the __str__()
method. The __unicode__()
method is obsolete.
Activating your application
In order for Django to keep track of our application and be able to create database tables for its models, we have to activate it. To do this, edit the settings.py
file and add blog.apps.BlogConfig
to the INSTALLED_APPS
setting. It should look like this:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog.apps.BlogConfig',
]
The BlogConfig
class is your application configuration. Now Django knows that our application is active for this project and will be able to load its models.
Creating and applying migrations
Now that we have a data model for our blog posts, we will need a database table for it. Django comes with a migration system that tracks the changes done to models and allows to propagate them into the database. The migrate
command applies migrations for all applications listed in INSTALLED_APPS
; it synchronizes the database with the current models and existing migrations.
First, you will need to create an initial migration for our Post
model. In the root directory of your project, run the following command:
python manage.py makemigrations blog
You should get the following output:
Migrations for 'blog': blog/migrations/0001_initial.py - Create model Post
Django just created the 0001_initial.py
file inside the migrations
directory of the blog
application. You can open that file to see how a migration appears. A migration specifies dependencies on other migrations and operations to perform in the database to synchronize it with model changes.
Let's take a look at the SQL code that Django will execute in the database to create the table for our model. The sqlmigrate
command takes migration names and returns their SQL without executing it. Run the following command to inspect the SQL output of our first migration:
python manage.py sqlmigrate blog 0001
The output should look as follows:
BEGIN; -- -- Create model Post -- CREATE TABLE "blog_post" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "title" varchar(250) NOT NULL, "slug" varchar(250) NOT NULL, "body" text NOT NULL, "publish" datetime NOT NULL, "created" datetime NOT NULL, "updated" datetime NOT NULL, "status" varchar(10) NOT NULL, "author_id" integer NOT NULL REFERENCES "auth_user" ("id")); CREATE INDEX "blog_post_slug_b95473f2" ON "blog_post" ("slug"); CREATE INDEX "blog_post_author_id_dd7a8485" ON "blog_post" ("author_id"); COMMIT;
The exact output depends on the database you are using. The preceding output is generated for SQLite. As you can see in the preceding output, Django generates the table names by combining the app name and the lowercase name of the model (blog_post
), but you can also specify a custom database name for your model in the Meta
class of the model using the db_table
attribute. Django creates a primary key automatically for each model, but you can also override this by specifying primary_key=True
in one of your model fields. The default primary key is an id
column, which consists of an integer that is incremented automatically. This column corresponds to the id
field that is automatically added to your models.
Let's sync our database with the new model. Run the following command to apply existing migrations:
python manage.py migrate
You will get an output that ends with the following line:
Applying blog.0001_initial... OK
We just applied migrations for the applications listed in INSTALLED_APPS
, including our blog
application. After applying migrations, the database reflects the current status of our models.
If you edit your models.py
file in order to add, remove, or change fields of existing models, or if you add new models, you will have to create a new migration using the makemigrations
command. The migration will allow Django to keep track of model changes. Then, you will have to apply it with the migrate
command to keep the database in sync with your models.