Django comes with an ORM – Object Relational Mapper. It allows us to define Python classes instead of database tables and schemas, and interact with the database just like we’d interact with regular code.
Now we can go ahead and start defining our own custom models.
Creating Django models allow us to define our database tables and relationships from Python, and interact with them just like we would interact with Python objects.
I’ve already created a model for Post
. Open practical_blog/blog/models.py
:
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=300)
body = models.TextField()
slug = models.SlugField(null=False, unique=True, max_length=200)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
In order to create the database tables for our custom models, we’ll need to create new migrations for them.
You can think of migrations as a map between user-defined models in code and database tables. The migrations instruct the database on what commands need to be run to either update existing tables as models change, create new tables, or drop tables as the models are deleted.
In order for our custom model to be registered by Django, we’ll need to do two things: 1. Register the app in our project, so that Django knows where to look for models 1. Run a command to make new migrations before running them, so that Django can create database scripts for our custom models.
First, let’s register our app.
If we take a look at practical_blog/blog/apps.py
file, we’ll see the following auto-generated contents:
from django.apps import AppConfig
class BlogConfig(AppConfig):
name = 'blog'
We need to add this class, BlogConfig
, in the blog.apps
module to the INSTALLED_APPS list in our practical_blog/settings.py
file.
Your practical_blog/blog/settings.py
file should now look like this:
INSTALLED_APPS = [
'blog.apps.BlogConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
Every time we change our models, the database needs to be changed with it.
Instructions for how to make those changes for all the databases that Django supports (Postgres, MySQL, Sqlite) are called migrations.
When you make changes to your models, you’ll need to create new instructions with the makemigrations
command, and then run migrate
to apply them to the database.
I’ve already included the initial migrations for our blog app in the repository.
We don’t have any changes at the moment, but if you need to run migrations in the future, run:
(env) $ python manage.py makemigrations blog
(env) $ python manage.py migrate
We can easily work with models on the command line, with the built-in Django shell
command. The shell is different from the Python REPL because it knows about Django context when it starts up.
Start the Django shell with:
(env) $ python manage.py shell
Then, import our Post model. We’ll create a new Post, and save it in the database.
>>> from blog.models import Post
>>> Post.objects.all()
<QuerySet []>
>>> first_post = Post(title="First Post!", body="This is my first blog post.", slug="first-post")
>>> first_post.save()
>>> Post.objects.all()
<QuerySet [<Post: First Post!>]>
We can interact with the data in the database just like if it was an object in Python.
Instead of select
ing from a database, we can use the get()
method to select a single record, or filter()
to select multiple matching records.
>>> my_post = Post.objects.get(id=1)
>>> type(my_post)
<class 'blog.models.Post'>
>>> my_post.title
'First Post!'
>>> my_post.slug
'first-post'
>>> my_post.body
'This is my first blog post.'
>>> my_post.created_at
datetime.datetime(2020, 10, 30, 5, 4, 22, 407523, tzinfo=<UTC>)
Or, we can arbitrarily query the database.
Django even allows us to write queries that examine if values contain others values, how values compare to other values, and a lot more. Review the querysets API reference to see all the options.
Let’s use case-sensitive contains to search for blog posts by slug.
Notice that this returns a QuerySet
, which looks a lot like a list. In fact, we can access objects in a QuerySet by index.
>>> posts = Post.objects.filter(slug__contains="first")
>>> len(posts)
1
>>> type(posts)
<class 'django.db.models.query.QuerySet'>
>>> post = posts[0]
>>> type(post)
<class 'blog.models.Post'>
The Django ORM features a lot of optimizations under the hood, and you don’t need to know a lot about the internal workings of databases to start using them for your application.