Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 19

Defining Django Models and Fields

Recommended resources

Hello! Welcome back to our course on Backend Engineering.

In our last session, you learned how Django's Object-Relational Mapper (ORM) acts as a powerful bridge, translating Python classes into database tables. We covered the high-level concept: a models.Model subclass represents a table, its attributes become columns, and the makemigrations/migrate commands bring this structure to life in the database.

Today, we'll move from the "what" to the "how." Our learning outcome is to define Django models with appropriate field types and constraints. This means we'll explore the specific tools Django gives you to define what kind of data each column can hold and what rules that data must follow. For someone with your background, think of this as defining the data types and constraints for the input layer of a machine learning model; ensuring data integrity from the very beginning is crucial for a robust system.

By the end of this lesson, you'll be able to construct a detailed Django model that accurately represents a real-world entity, complete with validation rules and sensible defaults.

1. The Building Blocks: Common Field Types

The most fundamental part of a model is the list of fields it defines. Each field is an instance of a specific class that tells Django what kind of data to expect. Let's start by exploring the most common types.

The Mozilla Developer Network (MDN) guide provides an excellent written overview of these fields and their purposes.

Django Tutorial Part 3: Using models

Let's begin by reading an overview of model fields from the MDN guide, 'Django Tutorial Part 3: Using models'. This will give you a solid foundation for the different types of data you can store.

Please read the subsections 'Common field arguments' and 'Common field types'. You don't need to memorize every argument, but get a feel for the variety of fields like CharField, TextField, IntegerField, DateField, etc., and options like max_length and default.

Now that you have a theoretical overview, let's see these fields in action. Watching how they are implemented and how they appear in the Django admin interface provides valuable practical context.

Field types and options in Django models

This video, 'Field types and options in Django models' by Pyplane, provides a fantastic visual demonstration of many of the fields you just read about. We'll watch it in segments.

Watch from 01:30 to 05:15. This segment covers: BooleanField (for true/false values) CharField (for short strings, note the mandatory max_length) DateField (for dates) DecimalField (for fixed-precision numbers, ideal for currency) EmailField, FileField, ImageField, and IntegerField variants.

Here's a quick summary of the core field types:

Data Category Field Type Use Case Key Argument(s)
Text CharField Short strings (names, titles) max_length (required)
TextField Long-form text (articles, descriptions)
Numbers IntegerField Whole numbers (counts, quantities)
DecimalField Fixed-precision decimal numbers (money) max_digits, decimal_places
FloatField Floating-point numbers (scientific calculations)
Date/Time DateField Dates only auto_now, auto_now_add
DateTimeField Dates and times auto_now, auto_now_add
Boolean BooleanField True/False flags
Other UUIDField Universally Unique Identifiers (for primary keys) primary_key, default
EmailField Stores and validates email addresses max_length
URLField Stores and validates URLs max_length

2. Defining the Rules: Common Field Options (Constraints)

Simply defining a data type is often not enough. You need to enforce rules. Should a field be mandatory? Must its value be unique? Can it have a default value? These questions are answered using field options, which are arguments you pass to the field's constructor.

The official Django documentation is the definitive source for these options.

Models

Let's consult the official Django documentation, 'Models', for precise definitions of the most common field options.

Read the section titled 'Field options'. Focus on understanding the purpose of null, blank, choices, default, help_text, primary_key, and unique.

Let's focus on the most important and sometimes confusing options.

null vs. blank

This is a critical distinction to understand:

  • null=True: This is purely database-related. It means the database is allowed to store a NULL value for this column. For string-based fields like CharField and TextField, Django avoids NULL and uses an empty string ('') instead, so null=True is often unnecessary for them.
  • blank=True: This is validation-related. It means a form (like in the Django admin) will allow this field to be submitted empty.

The two are often used together, for example, on a DateField that is optional: models.DateField(null=True, blank=True). This allows the database to store NULL and allows forms to accept an empty value.

The Pyplane video has an excellent hands-on demonstration showing how adding a new field without null=True or a default requires you to provide a value during migration.

Field types and options in Django models

Let's go back to the Pyplane video to see a practical demonstration of null=True and blank=True.

Watch the segment from 12:36 to 16:03. Pay close attention to the console output when makemigrations is run without null=True. See how adding null=True resolves the database issue, and then how blank=True makes the field optional in the admin form.

choices

This option is perfect for fields with a limited set of possible values, like a status field. It takes a list of tuples, where each tuple is (value_to_store_in_db, human_readable_name). In the admin interface, this will render as a dropdown menu.

Field types and options in Django models

The choices option is best understood by seeing it. Let's see how it's defined and how it looks in the admin.

Watch from 09:57 to 11:06. This part clearly shows how to define a choices tuple and apply it to a CharField.

auto_now vs. auto_now_add

These are incredibly useful for timestamping records:

  • auto_now_add=True: Sets the field to the current timestamp only when the object is first created. Perfect for a created_at field.
  • auto_now=True: Updates the field to the current timestamp every time the object is saved. Perfect for a last_updated_at field.

A field with either of these options set to True will automatically have editable=False (meaning it won't show up in forms) and blank=True.


3. Practical Application: Deconstructing a Model

Let's tie all this together by analyzing a well-structured example. The BookInstance model from the MDN tutorial is a great case study.

Django Tutorial Part 3: Using models

Now, let's read about the models for the 'LocalLibrary' application in the MDN tutorial. We'll focus on the BookInstance model, which uses many of the fields and options we've just discussed.

Scroll down to the 'Defining the LocalLibrary Models' section and read the subsection for the 'BookInstance model'. As you read, try to identify the purpose of each field and option.

Let's break down the BookInstance model code from that article:

import uuid # Required for unique book instances

class BookInstance(models.Model):
    # This sets a UUID as the primary key, overriding Django's default auto-incrementing integer ID.
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, 
                          help_text="Unique ID for this particular book across whole library")
    
    # This is a relationship field, which we'll cover next lesson.
    book = models.ForeignKey('Book', on_delete=models.RESTRICT, null=True) 
    
    imprint = models.CharField(max_length=200)

    # This date is optional (e.g., for available books), so both null and blank are True.
    due_back = models.DateField(null=True, blank=True)

    # A tuple defining the choices for the status field.
    LOAN_STATUS = (
        ('m', 'Maintenance'),
        ('o', 'On loan'),
        ('a', 'Available'),
        ('r', 'Reserved'),
    )

    # The status field uses the choices, has a max_length of 1 (to store 'm', 'o', etc.),
    # is optional in forms (blank=True), and has a default value.
    status = models.CharField(
        max_length=1,
        choices=LOAN_STATUS,
        blank=True,
        default='m',
        help_text='Book availability',
    )

This single model effectively demonstrates UUIDField as a primary_key, the use of null and blank for optional fields, and a CharField with choices and a default value.

Test your understanding!

Let's enhance the Interaction model you designed in the previous lesson for your "AI Career Coach" project.

Modify the model below to include the following fields and constraints:

  1. A session_id field that stores a unique identifier for a conversation session. This should be the primary key for the model.
  2. A status field that can be one of 'Processing', 'Completed', or 'Failed'. It should default to 'Processing'.
  3. A user_rating field that can store an integer from 1 to 5. This field should be optional.
  4. A last_updated field that automatically updates every time the record is saved.
import uuid
from django.db import models

class Interaction(models.Model):
    # Original fields from last lesson
    user_query = models.TextField()
    ai_response = models.TextField()
    timestamp = models.DateTimeField(auto_now_add=True)

    # Add your new fields here

Show answer

Here is a possible solution:

import uuid
from django.db import models

class Interaction(models.Model):
    # 1. New primary key
    session_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    
    # Original fields from last lesson
    user_query = models.TextField()
    ai_response = models.TextField()
    timestamp = models.DateTimeField(auto_now_add=True) # created_at timestamp

    # 4. New 'last_updated' field
    last_updated = models.DateTimeField(auto_now=True)

    # 2. New 'status' field with choices
    STATUS_CHOICES = [
        ('processing', 'Processing'),
        ('completed', 'Completed'),
        ('failed', 'Failed'),
    ]
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='processing')

    # 3. New optional 'user_rating' field
    user_rating = models.IntegerField(null=True, blank=True)

    # Note: We don't need a separate primary key like 'id' anymore because we've defined 'session_id' as the primary key.

Explanation:

  1. session_id: UUIDField is used with primary_key=True to make it the unique identifier. default=uuid.uuid4 ensures a new UUID is generated for each new interaction. editable=False is good practice for primary keys.
  2. last_updated: DateTimeField(auto_now=True) handles the automatic updates on every save.
  3. status: A STATUS_CHOICES tuple is defined and passed to the choices argument of a CharField. A default is also provided.
  4. user_rating: IntegerField is used. null=True, blank=True makes it optional both in the database and in forms.

Conclusion

You have now moved beyond the conceptual and into the practical mechanics of defining your data schema in Django. You have the tools to precisely describe the data your application will store, ensuring a high degree of data integrity right at the model level.

Key Takeaways:

  • Django provides a rich set of field types (CharField, IntegerField, DateTimeField, etc.) to match different kinds of data.
  • Field options (constraints) like max_length, default, and unique allow you to enforce rules on your data.
  • The distinction between null=True (database-level) and blank=True (validation-level) is crucial for defining optional fields.
  • choices is a powerful option for creating fields with a predefined set of values.
  • auto_now and auto_now_add are indispensable for managing timestamps automatically.

Preview of the Next Lesson:
Our models currently exist in isolation. The real power of a relational database comes from connecting data. In our next lesson, we will implement relationships between models. You'll learn how to use ForeignKey, OneToOneField, and ManyToManyField to create a web of interconnected data, which is the foundation for almost any complex backend application.