Models - Django Tutorial




Welcome to Part 2 of the web development with Python and Django tutorial series. In this tutorial, we're going to introduce the concept of Models, which is where the majority of the value of Django comes from.

The first model we will start with is the Tutorial model, since tutorials are the main aspect of PythonProgramming.net. So, what might some attributes of a tutorial be? Obviously we've got the tutorial content, like the writeup itself. Maybe also we'd want to have the tutorial's title specifically, and then maybe the date it was published. That should be enough to start with. Later, we might come up with more things for tutorials, such as maybe what series they are a part of, or the category it falls under, and so on. As mentioned before, with Django, you can easily add more fields to your database with very little struggle, so it's not really as important as it usually is to think ahead. So, let's build the Tutorial model. Each model will be a unique class within your app's models.py file.

Let's open up models.py from main:

mysite/main/models.py
from django.db import models

# Create your models here.

We'll start by defining our Tutorial model here:

from django.db import models


class Tutorial(models.Model):
    tutorial_title = models.CharField(max_length=200)
    tutorial_content = models.TextField()
    tutorial_published = models.DateTimeField('date published')

    def __str__(self):
        return self.tutorial_title

All models will inherit from models.Model. Then, we just define our fields with ...well...fields. Note that different fields are defined in different ways. We expect our title to be fairly short, so we define this as a CharField. These fields correspond to the format of our data in the actual database. You might wonder, what's the difference between a CharField, which we use for the title, and a TextField, which we use for the content itself. In general, we use the CharField for something that does have a limit to the size, and a TextField when we don't have a limit. For all of the fields, see the django model fields documentation.

Finally, we override the the __str__ special method to make it a bit more readable when it's being displayed in string form, which we will see soon.

Okay, any time there has been a change in your models (either a new model, or you modified an existing model), you need to do a migration. There are two steps involved here. First, we run a makemigrations, then we run an actual migrate. We use the manage.py script for this, so let's get it done:

python3 manage.py makemigrations No changes detected

What?

So our migrations will only apply to apps that we've told Django we want "installed." This probably wont be the last time you add an app, build some models, attempt to migrate, and get this message. Hopefully, you wont be confused when it eventually happens to you on your own! So let's go into mysite/mysite/settings.py and add 'main.apps.MainConfig', to INSTALLED_APPS so it should look like:

mysite/mysite/settings.py
INSTALLED_APPS = [
    'main.apps.MainConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

Does ...that exist? Let's check! Go open mysite/main/apps.py:

from django.apps import AppConfig


class MainConfig(AppConfig):
    name = 'main'

Yep!

Okay, let's try to make our migrations again!

python3 manage.py makemigrations
Migrations for 'main':
  main\migrations\0001_initial.py
    - Create model Tutorial

Looks good. What this step actually does is it just builds the code required for the migration, it doesn't actually apply them. If you're curious, you can see all of your migrations by going to the migrations directory of an app. For example, head to mysite/main/migrations. In there, you should see 0001_initial.py, open it up:

0001_initial.py
# Generated by Django 2.1.5 on 2019-01-11 01:35

from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='Tutorial',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('tutorial_title', models.CharField(max_length=200)),
                ('tutorial_content', models.TextField()),
                ('tutorial_published', models.DateTimeField(verbose_name='date published')),
            ],
        ),
    ]

If you know SQL, then you know that's not SQL! If you want to see the exact SQL that will be run, you can also do:

python3 manage.py sqlmigrate main 0001
BEGIN;
--
-- Create model Tutorial
--
CREATE TABLE "main_tutorial" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "tutorial_title" varchar(200) NOT NULL, "tutorial_content" text NOT NULL, "tutorial_published" datetime NOT NULL);
COMMIT;

... but you probably wont be doing that. In general, you will just simple make or modify your models. Run a makemigrations and then run a migrate and you'll be done.

Cool, okay let's actually migrate then!

python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, main, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying main.0001_initial... OK
  Applying sessions.0001_initial... OK

Whoa, that's a lot of stuff! Remember those pending migrations from before that we saw when we ran the server? That was a bunch of them, but we can also see the one for the Tutorial model:

Applying main.0001_initial... OK

We can see that we also did some stuff for admin and auth.

Okay, so what? We can't really see what's so special about any of this. Let's add a tutorial. One quick way for us to do this at the moment is through the shell. We can access the shell through, you might be able to guess...manage.py

python3 manage.py shell
Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)

This gives us a quick way to interact with our website without the need to write up some view and controller just to test something. My main use for this is to do something like test a query like a filter or a get. Once we have many related models, writing the code to properly do what we want doesn't necesaarily work the first time. Rather than printing out, logging, or displaying the data in a view, we can quickly interact via the shell. Let's see a quick example. Let's import our Tutorial model.

>>> from main.models import Tutorial
>>> Tutorial.objects.all()
<QuerySet []>

We can make a new Tutorial object from here quite easily. Recall our attributes were:

  • tutorial_title
  • tutorial_content
  • tutorial_published

Two of those are just strings, one is a date. Let's import timezone:

>>> from django.utils import timezone

Now we can make a new Tutorial object by doing:

>>> new_tutorial = Tutorial(tutorial_title="To be", tutorial_content="or not to be.

That is the question.", tutorial_published=timezone.now())

Now all we need to do to commit this object to our database is .save()

>>> new_tutorial.save()

Now we can do:

>>> Tutorial.objects.all()
<QuerySet []>

So we get this thing called a QuerySet, which we can iterate over like:

>>> for t in Tutorial.objects.all():
...     print(t.tutorial_title)
...
To be

The shell winds up being more useful down the line, however, when we have many more objects and things are far more complex. I wouldn't actually recommend using the shell to actually insert data. Can you imagine writing an entire tutorial via the shell?! Yikes! Instead, you're much more likely to use the admin page for this, which is what we're going to be talking about in the next tutorial!

The next tutorial:





  • Django Web Development with Python Introduction
  • Models - Django Tutorial
  • Admin and Apps - Django Tutorial
  • Views and Templates - Django Tutorial
  • CSS - Django Tutorial
  • User Registration - Django Tutorial
  • Messages - Django Tutorial
  • User Login and Logout - Django Tutorial
  • Foreign Keys with Models - Django Tutorial
  • Working with Foreign Keys - Django Tutorial
  • Dynamic sidebar - Django Tutorial
  • Deploying to a Server - Django Tutorial