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
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/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
If you’re comfortable with databases and want to see the raw SQL that’s being generated by this command, you can take a look at it with the following command:
python manage.py sqlmigrate blog <migration number>
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.
selecting 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 >>> 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.