Creating My Own Blogging Engine Part 2

Continuing from part 1, Let's get the model sorted out.

Model

To keep things simple, a blog post is made up of the following data elements:

  • ID
  • Title
  • Slug
  • Category
  • Status
  • Publication Date
  • Content

To keep things simple, I plan on using the django admin to edit posts.

Each post has a status of either Draft or Published. The slug will be created on save. And each post can have one category. (Later on I'll decide I want to use tags as well, but for now, just a category). The category model is simple.

In $ blog/models.py

class Category(models.Model):
    category = models.CharField(max_length=50)
    slug = models.SlugField(max_length=50, blank=True)

    def __str__(self):
                return self.category

Now to the fun part, the Post:

class Post(models.Model):
    DRAFT = 'DR'
    PUBLISHED = 'PU'
    STATUS_CHOICES = (
    (DRAFT, 'Draft'),
    (PUBLISHED, 'Published'),
    )

    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200,unique=True,
            allow_unicode=True)
    category = models.ForeignKey(
        'Category',
        on_delete=models.CASCADE,
        blank=True,
        null=True,
    )
    status = models.CharField(
    max_length=2,
    choices=STATUS_CHOICES,
    default=DRAFT,
    ) 
    pub_date = models.DateTimeField('date published',
            blank=True, null=True)
    content = models.TextField()
    meta_title = models.CharField(max_length=200,blank=True)
    meta_description = models.TextField(blank=True)
    meta_keywords = models.CharField(max_length=200,blank=True)

    def get_absolute_url(self):
        from django.urls import reverse
        return reverse('detail', kwargs={'slug': self.slug})

    def __str__(self):
                return self.title

A couple of key parts here. The slug field is a SlugField, a specific django field type. The slug is generated through the admin. Setting that up is pretty easy.

In $ blog/admin.py:

class PostAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("title",)}
    ...

class CategoryAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("category",)}

admin.site.register(Post, PostAdmin)
admin.site.register(Category, CategoryAdmin)

This nice feature fills in the slug as the title is typed in the admin.

Slug field in Admin

Slug field in Admin

At the top of the class, we declare the status choices, then apply the choices to the status field. These choices will be used to help filter querysets.

While setting up the model, I decided to add a few 'meta' fields to store SEO related data for each post.

Once the model is setup, it's time to get the database running and do some migrations.

I haven't used Postgresql much over the years, tending to choose MySql or SQLite, but there is good python integration, and Postgresql seems to be the preferred database for python development.

The django app is connected to the database through an adapter. The recommended one is psycopg2.

Installing Postgresql is easy enough, just follow the install recommendations for your operating system.

Adding the connector is equally easy. It's key, though, to make sure you are working in the correct environment.

Terminal Screenshot

Terminal Screenshot

Run the pip command to install psycopg2:

$ pip install psycopg2

DigitalOcean has a great walkthrough of getting started with PostgreSQL.

Now that the database and user is setup, it's time to add the settings to the project, and do some migrations. In the settings.py file add or update the database settings.

# Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases

DATABASES = {
        "default": {
            # Ends with "postgresql_psycopg2", "mysql", "sqlite3" or "oracle".
            "ENGINE": "django.db.backends.postgresql_psycopg2",
            # DB name or path to database file if using sqlite3.
            "NAME": "database_name",
            # Not used with sqlite3.
            "USER": "Username",
            # Not used with sqlite3.
            "PASSWORD": "Password",
            # Set to empty string for localhost. Not used with sqlite3.
            "HOST": "localhost",
            # Set to empty string for default. Not used with sqlite3.
            "PORT": "5432",
            }
        }

Now that we have the database connection setup, we can migrate the blog and category model and test things out. To setup the migrations we can use the makemigrations function. This creates a migration file in the app/migrations folder. This is a very powerful feature, and it's worth taking some time to read through the django docs about migrations.

$ python manage.py makemigrations
Screenshot makemigrations

Screenshot makemigrations

Assuming we don't have any typos or other problems, the migrate command will run the migration script and in this case create the database tables and relations.

$ python manage.py migrate
Screenshot migrate

Screenshot migrate

Pro-Tip: Type

$ python manage.py

to see all of the commands available. In fact, in a future part, we'll use this command structure to automate a daily notification.

Screenshot python manage.py

Screenshot python manage.py

Now that the model is built, it's time to attach the models to the admin and test things out.

from django.contrib import admin 
from .models import Category, Post 

class PostAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("title",)} 

class CategoryAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("category",)} 

admin.site.register(Post, PostAdmin)
admin.site.register(Category, CategoryAdmin)

Adding in the PostAdmin class let's us add the prepopulated_fields functionality. Let's check it out.

$ python manage.py runserver
Admin Screenshot

Admin Screenshot

This is what makes django so popular. Some simple setup and you have a working administration screen that allows adding, updating, and deleting posts and categories.

Admin Screenshot

Admin Screenshot

Admin Screenshot

Admin Screenshot

Awesome stuff! In the next post, I'll work on adding some features to the admin, and creating the first views.

Enjoy!