Skip to content

Creating a blog model in django

A Django model is a source of information and behaviors of your data. It consists of a Python class that subclasses django.db.models.Model. Each model maps to a single database table, where each attribute of the class represents a database field.

Creating the Post model

models
from django.db import models
from django.utils import timezone

class Post(models.Model):
    title = models.CharField(max_length=250)
    slug = models.SlugField(max_length=250)
    body = models.TextField()
    publish = models.DateTimeField(default=timezone.now)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

def__str__(self):
    return self.title

Defining a default sort order

Blog posts are usually displayed in reverse chronological order (from newest to oldest). We will define a default ordering for our model. The default order will apply when obtaining objects from the database when no order is specified in the query.

models.py
from django.db import models
from django.utils import timezone

class Post(models.Model):
    title = models.CharField(max_length=250)
    slug = models.SlugField(max_length=250)
    body = models.TextField()
    publish = models.DateTimeField(default=timezone.now)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

class Meta:
    ordering = ['-publish']

def__str__(self):
    return self.title

We have added a Meta class inside the model. This class defines metadata for the model. We use the ordering attribute to tell Django that it should sort results by the publish field. This ordering will apply by default for database queries when no specific order is provided in the query. We indicate descending order by using a hyphen before the field name, -publish. Posts will be returned in reverse chronological order by default.

Adding a database index

models.py
from django.db import models
from django.utils import timezone

class Post(models.Model):
    title = models.CharField(max_length=250)
    slug = models.SlugField(max_length=250)
    body = models.TextField()
    publish = models.DateTimeField(default=timezone.now)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

class Meta:
    ordering = ['-publish']
    indexes = [
        models.Index(fields=['-publish']),
    ]

def__str__(self):
    return self.title

Danger

Index ordering is not supported on MySQL. If you use MySQL for the database, a descending index will be created as a normal index.

Activating the application

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 the application configuration. Now Django knows that the application is active for this project and will be able to load the application models.

Adding a status field

A common functionality for blogs is to save posts as a draft until ready for publication. We will add a status field to our model that will allow us to manage the status of blog posts. We will be using Draft and Published statuses for posts.

models.py
from django.db import models
from django.utils import timezone

class Post(models.Model):
    class Status(models.TextChoices):
        DRAFT = 'DF', 'Draft'
        PUBLISHED = 'PB', 'Published'

    title = models.CharField(max_length=250)
    slug = models.SlugField(max_length=250)
    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=2,
                            choices=Status.choices,
                            default=Status.DRAFT
                            )
    class Meta:
        ordering = ['-publish']
        indexes = [
        models.Index(fields=['-publish']),
    ]

    def__str__(self):
        return self.title

We have defined the enumeration class Status by subclassing models.TextChoices . The available choices for the post status are DRAFT and PUBLISHED. Their respective values are DF and PB, and their labels or readable names are Draft and Published.

Django provides enumeration types that you can subclass to define choices simply. These are based on the enum object of Python’s standard library. You can read more about enum at https://docs.python.org/3/library/enum.html.

Shell

python manage.py shell

>>> from blog.models import Post

>>> Post.Status.choices
[('DF', 'Draft'), ('PB', 'Published')]

>>> Post.Status.labels
['Draft', 'Published']

>>> Post.Status.values
['DF', 'PB']

>>> Post.Status.names
['DRAFT', 'PUBLISHED']

You can access a specific lookup enumeration member with Post.Status.PUBLISHED and you can access its .name and .value properties as well.

Adding a many-to-one relationship

Posts are always written by an author. We will create a relationship between users and posts that will indicate which user wrote which posts. Django comes with an authentication framework that handles user accounts. The Django authentication framework comes in the django.contrib.auth package and contains a User model.

We will use the User model from the Django authentication framework to create a relationship between users and posts.

models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User

class Post(models.Model):
    class Status(models.TextChoices):
        DRAFT = 'DF', 'Draft'
        PUBLISHED = 'PB', 'Published'

    title = models.CharField(max_length=250)
    slug = models.SlugField(max_length=250)
    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=2,
                            choices=Status.choices,
                            default=Status.DRAFT
                            )
    class Meta:
        ordering = ['-publish']
        indexes = [
        models.Index(fields=['-publish']),
    ]

    def__str__(self):
        return self.title

Reference