Designing the blog data schema

We will start by defining the initial 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 offers you a practical API to query 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 modelsfrom 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,
                               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 basic 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 is CharField, which translates into a VARCHAR column in the SQL database.
  • slug: This is a field intended to be used in URLs. A slug is a short label containing only letters, numbers, underscores, or hyphens. We will use the slug field to build beautiful, SEO-friendly URLs for our blog posts. We have added the unique_for_date parameter to this field so we can build URLs for posts using the date and slug of the post. Django will prevent from multiple posts having the same slug for the same date.
  • author: This field is ForeignKey. This field defines a many-to-one relationship. We are telling Django that each post is written by a user and a user can write several 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 the User model of the Django authentication system. We specify the name of the reverse relationship, from User to Post, with the related_name attribute. We are going to learn more about this later.
  • body: This is the body of the post. This field is TextField, which translates into a TEXT column in the SQL database.
  • publish: This datetime indicates when the post was published. We use Django's timezone now method as default value. This is just a timezone-aware datetime.now.
  • created: This datetime indicates when the post was created. Since we are using auto_now_add here, the date will be saved automatically when creating an object.
  • updated: This datetime indicates the last time the post has been updated. Since we are using auto_now here, the date will be updated automatically when saving an object.
  • status: This is a field to show the status of a post. We use a choices parameter, so the value of this field can only be set to one of the given choices.

As you can see, Django comes with different types of fields that you can use to define your models. You can find all field types in https://docs.djangoproject.com/en/1.8/ref/models/fields/.

The class Meta inside the model contains metadata. We are telling Django to sort results by the publish field in descending order by default when we query the database. We specify descending order by using the negative prefix.

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 Python 2.X, note that in Python 3 all strings are natively considered unicode, therefore we only use the __str__() method. The __unicode__() method is obsolete.

Since we are going to deal with datetimes, we will install the pytz module. This module provides timezone definitions for Python and is required by SQLite to work with datetimes. Open the shell and install pytz with the following command:

pip install pytz

Django comes with support for timezone-aware datetimes. You can activate/deactivate time zone support with the USE_TZ setting in the settings.py file of your project. This setting is set to True when you create a new project using the startproject management command.

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 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',
)

Now Django knows that our application is active for this project and will be able to introspect its models.

Creating and applying migrations

Let's create a data table for our model in the database. Django comes with a migration system to track the changes you do to your models and 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 migrations.

First, we need to create a migration for the new model we just created. From the root directory of your project, enter this command:

python manage.py makemigrations blog

You should get the following output:

Migrations for 'blog':
  0001_initial.py:
    - Create model Post

Django just created a file 0001_initial.py inside the migrations directory of the blog application. You can open that file to see how a migration looks like.

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 running it. Run the following command to inspect its output:

python manage.py sqlmigrate blog 0001

The output should look as follows:

BEGIN;
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_2dbcba41" ON "blog_post" ("slug");
CREATE INDEX "blog_post_4f331e2f" ON "blog_post" ("author_id");
COMMIT;

The exact output depends on the database you are using. The output above is generated for SQLite. As you can see, Django generates the table names by combining the app name and the lowercase name of the model (blog_post), but you can also specify them in the Meta class of the models using the db_table attribute. Django creates a primary key automatically for each model but you can also override this specifying primary_key=True on one of your model fields.

Let's sync our database with the new model. Run the following command to apply existing migrations:

python manage.py migrate

You will get the following 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 make 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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset