Models

Using models.py

In Django, the models.py files are where you define your database structure using Python code. It acts as an interface between your app and the database, using Django's built-in ORM system. Each Django app has its own models.py file located inside the app folder. You define tables as Python classes, and Django handles translating them into database tables.

You will use models.py whenever your app needs to store and manage data. Some websites or apps will not need anything in their models.py files.

Setting up models.py

Import Django's model tools to access the models.Model base class you'll use to define each table.

from django.db import models

Define a Model which you can think of like a table.

  • Define a class inheriting from the models.Model base class. This provides key functionality to your model from the start including table creation with migration, query methods, integration with forms, and most importantly giving them ID values automatically in the order of instantiation.
  • Remember to define your model with a capital letter.
  • Add a docstring to describe your model.
  • Define your model fields, these will be the column titles in your table. They take the form of the column title variable before an equal sign, followed by the syntax of models.FieldClass(attributes), where the FieldClasses depend on your requirements for the data (string, integer, Boolean etc) and the attributes consequently depend on the FieldClass. Example of these are outlined below.
  • Define a __str__(self): function that will return the Model as a string, similar to the __repr(self)__ function in Flask. This special method changes how the item appears in admin or shell. Instead of TableName object (1), it will show the instance's name.
  • Define any other Class methods.
class Product(models.Model):
    name = models.CharField(max_length=50, null=False, blank=False)
    in_stock = models.BooleanField(null=False, blank=False, default=False)

    def __str__(self):
        return self.name

Common Field Types

Field Type Usage Example
CharField name = models.CharField(max_length=100)
TextField description = models.TextField()
IntegerField quantity = models.IntegerField()
BooleanField active = models.BooleanField(default=True)
DateField created = models.DateField(auto_now_add=True)
ForeignKey category = models.ForeignKey(Category, on_delete=models.CASCADE)

Run Migrations

After setting up models, Django needs to convert them into actual database tables. You do this using migrations. This will need to be done if you were to update a Model by adding or removing column names. However doing this will not delete existing instances of your Model, so don't worry about that.

python manage.py makemigrations --dry-run
python manage.py makemigrations
python manage.py showmigrations
python manage.py migrate

Register Models in the Admin Panel

To manage your model data through the Django admin interface you must import and register each model you want to see in the admin.py file of the app.

from django.contrib import admin
from .models import ModelName

admin.site.register(ModelName)

Additionally you can customise what fields appear and their order, along with whether they are read only, or if they have filters defining an admin class typically named like ModelNameAdmin.

Here is an example of a Room model that I created for a hotel management webapp and how that model could be seen in the admin.

from django.db import models

class Room(models.Model):
    '''Room model'''
	# Name field for querying: max length of 254 characters, cannot be Null or blank
    name = models.CharField(max_length=254, null=False, blank=False)
	# Sanitised name field for display on the website: max length of 254 characters, cannot be Null of blank
    sanitised_name = models.CharField(max_length=254, null=False, blank=False)
	# Amenities field that is a list of amenities that the room has to offer, it cannot be Null of blank
    amenities = models.JSONField(default=list, null=False, blank=False)
	# A text description field: it has a defined default, so no need to say it cannot be Null of blank
    description = models.TextField(
        default='Room Description',
    )
	# An image url field to add in the url attributes of img elements: max length of 1024 characters as these can be long. This can be Null or blank as it is not required.
    image_url = models.URLField(max_length=1024, null=True, blank=True)
	# Image field to have an actual image saved in the database. It is not required so it can be Null or blank    
    image = models.ImageField(null=True, blank=True)
	# The price to two decimal places, so uses DecimalField: limited to a maximum of 6 digits, 2 decimal places and it cannot be Null of False
    price = models.DecimalField(
        max_digits=6,
        decimal_places=2,
        null=False,
        blank=False
    )
    # Unavailability field which is a list of already booked dates: uses a JSONField as a list, it cannot be Null but it can be blank if the room has not been booked yet
    unavailability = models.JSONField(default=list, null=False, blank=True)

    # __str__(self) function
    def __str__(self):
        '''Returns the room name'''
        return self.name

    # A sanitised name method to display the sanitised name when called
    def get_sanitised_name(self):
        '''Returns the sanitised room name'''
        return self.sanitised_name

Then setting that up in the admin.py file would look like this:

from django.contrib import admin
from .models import Room

class RoomAdmin(admin.ModelAdmin):
    '''Display options for the room instances in Django admin'''
	# Choose which column names to display
    list_display = (
        'id',
        'name',
        'sanitised_name',
        'amenities',
        'description',
        'image_url',
        'image',
        'price',
        'unavailability'
    )

	# Choose how the instances are ordered, e.g. by 'id' number
    ordering = ('id',)
    # Inline editing for selected fields in the list view
    list_editable = ('price',)

    # Make fields searchable
    search_fields = ('name', 'description')
    # Add filters to the sidebar
    list_filter = ('amenities',)
    # Set fields as read-only in the form view
    readonly_fields = ('sanitised_name', 'image_url')

# Register your models here in the from of (ModelName, ModelNameAdmin)
admin.site.register(Room, RoomAdmin)

Accessing Data in views.py

To access data from your database, you need to firstly import the Model from the database at the top of your views.py file. With this imported you can query it in many ways and assign the result of the functions to different variables to save them or pass them in the context to your template.

from .models import ModelName

Basic Queries

Query Type Query Syntax Explanation
Get all records ModelName.objects.all() Returns a QuerySet of all records in the model
Get first record ModelName.objects.first() Returns the first object or None if no record exists
Get last record ModelName.objects.last() Returns the last object or None if no record exists
Get by ID ModelName.objects.get(id=1) Returns a single object, or raises an error if the record does not exist
Filter (first match) ModelName.objects.filter(name='Test').first() Returns the first match based on filter or None if no match
Filter (multiple) ModelName.objects.filter(active=True) Returns a QuerySet of all matching records based on the filter
Check if the record exists ModelName.objects.filter(name='Test').exists() Returns a Boolean indicating if any record matches the filter

Filtering and Lookups

Lookup Type Syntax
Exact match ModelName.objects.filter(field='value')
Case-insensitive match ModelName.objects.filter(field__iexact='value')
Partial match ModelName.objects.filter(field__contains='value')
Case-insensitive partial match ModelName.objects.filter(field__icontains='value')
Greater than ModelName.objects.filter(field__gt=100)
Greater than or equal ModelName.objects.filter(field__gte=100)
Less than ModelName.objects.filter(field__lt=100)
Less than or equal ModelName.objects.filter(field__lte=100)
In a list ModelName.objects.filter(field__in=[1, 2, 3])
Field is null ModelName.objects.filter(field__isnull=True)
Date lookup ModelName.objects.filter(date_field__year=2025)

Complex Queries

Type Syntax
OR condition ModelName.objects.filter(Q(price__gt=50))
AND condition ModelName.objects.filter(Q(a=True) & Q(b=False))
Field-to-field ModelName.objects.filter(stock__lt=F('reorder_level'))
Annotate ModelName.objects.annotate(lower_name=Lower('name'))

The annotate() function is used to add temporary calculated fields to each object in a queryset. These annotated fields can be used in templates, filters, or ordering, just like regular fields, but they exist only in memory, not in the database. For example for each object in the ModelName queryset, to add a temporary field called lower_name that contains the lowercased version of the original name field.

from django.db.models.functions import Lower
ModelName.objects.annotate(lower_name=Lower('name'))

Calculations

Calculation Syntax
Count ModelName.objects.count()
Sum ModelName.objects.aggregate(Sum('price'))
Average ModelName.objects.aggregate(Avg('score'))
Max/Min aggregate(Max('value')), aggregate(Min('value'))

Creating and Updating

Action Syntax
Create & save ModelName.objects.create(name='Example', value=42)
Manual creation obj = ModelName(name='Example')
obj.save()
Update existing object obj = ModelName.objects.get(id=1)
obj.name = 'New'
obj.save()
Bulk update ModelName.objects.filter(active=False).update(active=True)

Deleting

Action Syntax
Delete one ModelName.objects.get(id=1).delete()
Delete many ModelName.objects.filter(status='archived').delete()

Ordering and Slicing

Action Syntax
Order ascending ModelName.objects.order_by('name')
Order descending ModelName.objects.order_by('-created_at')
Limit results ModelName.objects.all()[:5]
Offset + limit (pagination) ModelName.objects.all()[5:10]