Models
A model is a Python class that describes a table in your database: each attribute is a column, and each object is a row.
A first model
In a project, models go in an app's models.py. Here, the setup lines at the top stand in for a project: they configure Django with an in-memory database, and create_model creates the table, the job migrations normally do.
import django
from django.conf import settings
settings.configure(
INSTALLED_APPS=["django.contrib.contenttypes"],
DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
)
django.setup()
from django.db import connection, models
class Course(models.Model):
title = models.CharField(max_length=100)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=8, decimal_places=2, default=0)
published = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
class Meta:
app_label = "courses" # in a project, the app's folder supplies this
ordering = ["title"]
def __str__(self):
return self.title
with connection.schema_editor() as editor:
editor.create_model(Course)
python = Course.objects.create(title="Python", price=499, published=True)
Course.objects.create(title="SQL", price=299)
print(python.id, python, python.price, python.published)
print(Course.objects.all())- Every model gets an automatic
idprimary key. Course.objects.create(...)inserts a row and returns the object.__str__decides how an object is shown: in the admin, in the shell, and in printed querysets like the last line.Meta.orderingsets the default order for queries.
Common field types
| Field | Stores |
|---|---|
CharField(max_length=...) | short text; max_length is required |
TextField() | long text |
IntegerField(), PositiveIntegerField() | whole numbers |
DecimalField(max_digits, decimal_places) | exact decimals, such as prices |
FloatField() | approximate decimals |
BooleanField() | True or False |
DateField(), DateTimeField() | dates and times |
EmailField(), URLField(), SlugField() | text with validation |
ImageField(), FileField() | uploaded files |
Field options
| Option | Effect |
|---|---|
default=... | the value when none is given |
null=True | the database column may hold NULL |
blank=True | forms may leave the field empty |
unique=True | no two rows may share the value |
choices=... | a fixed set of allowed values |
auto_now_add=True | set to the current time when the row is created |
auto_now=True | set to the current time on every save |
null is about the database; blank is about validation in forms. For text fields, Django's convention is blank=True without null=True, so "empty" is always stored as "".
Choices
import django
from django.conf import settings
settings.configure(
INSTALLED_APPS=["django.contrib.contenttypes"],
DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
)
django.setup()
from django.db import connection, models
class Course(models.Model):
class Level(models.TextChoices):
BEGINNER = "beg", "Beginner"
INTERMEDIATE = "int", "Intermediate"
ADVANCED = "adv", "Advanced"
title = models.CharField(max_length=100)
level = models.CharField(max_length=3, choices=Level.choices, default=Level.BEGINNER)
class Meta:
app_label = "courses"
with connection.schema_editor() as editor:
editor.create_model(Course)
c = Course.objects.create(title="React", level=Course.Level.INTERMEDIATE)
print(c.level, "|", c.get_level_display())
print(Course.objects.filter(level=Course.Level.INTERMEDIATE).count())The database stores the short code, "int", while get_level_display() gives the readable label, "Intermediate". Forms and the admin automatically show a drop-down of the choices.
Relationships
import django
from django.conf import settings
settings.configure(
INSTALLED_APPS=["django.contrib.contenttypes"],
DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
)
django.setup()
from django.db import connection, models
class Course(models.Model):
title = models.CharField(max_length=100)
class Meta:
app_label = "courses"
def __str__(self):
return self.title
class Lesson(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name="lessons")
title = models.CharField(max_length=100)
minutes = models.PositiveIntegerField(default=10)
class Meta:
app_label = "courses"
def __str__(self):
return self.title
with connection.schema_editor() as editor:
editor.create_model(Course)
editor.create_model(Lesson)
python = Course.objects.create(title="Python")
Lesson.objects.create(course=python, title="Variables", minutes=8)
Lesson.objects.create(course=python, title="Loops", minutes=12)
first = Lesson.objects.get(title="Loops")
print(first.course) # follow the link forwards
print(python.lessons.all()) # and backwards, through related_name
print(python.lessons.count(), "lessons")ForeignKeylinks many lessons to one course: a many-to-one relationship.on_delete=models.CASCADEdeletes a course's lessons when the course is deleted.PROTECTrefuses to delete a course that still has lessons, andSET_NULLkeeps them with an empty link.related_name="lessons"names the reverse direction, sopython.lessons.all()lists a course's lessons.
The other two relationships:
class Student(models.Model):
name = models.CharField(max_length=100)
courses = models.ManyToManyField(Course, related_name="students") # many-to-many
class Profile(models.Model):
student = models.OneToOneField(Student, on_delete=models.CASCADE) # one-to-one
bio = models.TextField(blank=True)A student takes many courses and a course has many students, so that's a many-to-many field; Django creates the linking table for you. A one-to-one field is a ForeignKey that allows only one row on each side, which suits extra details about another model.