In web development, databases (also memory cache) are critical in managing data efficiently since their operations are implemented in well-considered data structures and algorithms (and other reasons).

Managing databases is a complex task. Django simplifies it through the Object Relational Mapping ORM system. We don’t explicitly define SQL schemas for relational databases such as SQLite, PostgreSQL, or MySQL. Instead, the SQL schema is auto-generated by reading the model classes defined in models.py. This feature allows developers to focus more on coding in Python rather than managing SQL directly for most common works.

ORM

ORM is a common technique found in other places. While Django comes with its ORM, SQLAlchemy is another popular ORM used in the Python ecosystem.

Django ORM largely handles the common works to use RDBMS. It reduces the complexity of the project and lets coders to focus more on the native objects (Python Django or other similar counterparts) rather than SQL.

A Model (Python class) maps to a table of the database.

An example of a model class in Django:

from django.db import models

class ModelName(models.Model):
    field1 = models.CharField(max_length=200, unique=True,)
    field2 = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
  • Each attribute of the class, Models.Field represents a database field which is a column in the database table. In the example, CharField, TextField, and DateTimeField are Model.Field classes.
  • max_length, auto_now_add are Field options. Not all of them are enforced by the database (side effect inside the database), for example, unique_for_date is a validation constraint in Python. The best reference goes to Django Models and Fields
  • Django will automatically create/update the corresponding SQL table and columns when running the migrations commands. And the running SQL queries are generated. These are migration files.
  • Each row in the table is an instance of the class stored in DB.

Model Relationships

Model classes can be related to each other. Multiple entries belong to an author while other entries belong to a different author. This is a one-to-many relationship.

A record of a User can be related to a record of a User Profile. This is a one-to-one relationship. While a post is being liked by multiple users, a user can like multiple posts. This is a many-to-many relationship.

In relational database design, Entity Relationship is an important process to design the database schema. Django ORM supports the following relationships:

  • One-to-One: Each row in the table is related to only one row in another table.
  • One-to-Many: Each row in the table is related to multiple rows in another table.
  • Many-to-Many: Each row in the table is related to multiple rows in another table and vice versa.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#one-to-many example
class Post(Model):
  pass
class Comment(Model):
  post = models.ForeignKey(Post,
    related_names='comments')

post.comments # access the many-to-ones comments
#if no 'related_name' set, e.g. post.comment_set.all()
entry.<attrSet>.all()

#one-to-one example
class Employee(Model):
  user = models.OneToOneField(auth.models.User,
           on_delete=models.CASCADE)
  department = fields.CharField(max_length='...')

u = User.objects.first()
u.employee.department

#many-to-many example
class Image(Model):
  users_like = models.ManyToManyFields(User,
                   related_name='images_liked',blank=True)
  • Note the highlighted lines are declarations of Field attributes like CharField, but they are foreign keys, one-to-one, or many-to-many relationships.

Note queries hopping over foreign keys are not efficient. Frequent hits to the database are not good for performance, consider using select_related or prefetch_related to reduce the number of queries.

OOP and class inheritance

The composition concept in Object oriented programming applies for similar Models. Meta programming is one of less mentioned features in Python. In Django, it is heavily used.

Children models stem from base Models. Note the differences between the Proxy, Multi-table, and Abstract models. Check Django Model Inheritance

QuerySet

QuerySet API is an important part of ORM to interact with the database with Python. One doesn't want to call Python database libraries to interact with databases. The QuerySet API is designed to interact with Django Model data. It allows CRUD of a row, filtering, sorting, and other operations on the database.

Mostly what SQL can do, QuerySet is designed to do the same. But different databases have different implementations and features. raw() allows SQL statements in Django ORM.

It is a big topic to cover. Here are some examples:

Shell

The shell command allows interacting with data directly from the command line so that one need not to work on the admin site or database interfaces directly.

python manage.py shell # working directory located with manage.py

Inside the shell:

from myapp.models import BlogPost
BlogPost.objects.create(title='My First Post',
     content='This is the content.')

post = BlogPost.objects.get(id=1)
post.title = 'Updated Title'
post.save()

post.delete()

# Many other complex operations that replace SQL statements

The "shell" is a good tool to test snippets and debug the code. It is good for testing models, integration with other non-Django packages and some small algorithms.

Model Form

Form is an interface to facilitate data validation, template context rendered in HTML widgets, instances of Model classes, etc. Model Form is the type of form that manages the fields of the Model class automatically. Form API allows to logic data to/from the users to work correctly.

# forms.py:
from Django import forms

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'content']

# views.py:
def post_create(request):
    form = PostForm(request.POST or None)
    if form.is_valid(): # validation
        form.save()
        return redirect('post-list')
    return render(request, 'post_form.html', {'form': form})

A parallel component to Form is actually DRF (Django Rest Framework) which is a third-party Django package. DRF is for RESTful APIs. Another competitor Ninja + Pydantic looks promising too.

Form should be discussed individually.

Database

Only a few common relational databases are supported by Django native ORM. SQLite is the default database. One may need to look up third-party packages or write custom to use other databases.

Migrating existing data to other databases is a complex task.

Migration

Migrations are a way to propagate changes you make to your models into your database schema. They are crucial for maintaining the integrity of your database.

# Creating Migration files
python manage.py makemigrations <app>
#Applying Migrations
python manage.py migrate

It involves two steps: makemigrations create migration files in the migrations/ directory of the app. migrate applies to the database.

Migration files are like 0001_initial.py, 0002_auto_20210101_0001.py. They are Python files that keep track of proper schema changes. Sometimes, if not used properly, update conflicts occur.

Conclusion

ContentType, and Signals modules are also relevant to Model. They are for external Django app interpolations. Also, remember to write tests on the models' behaviors. Testing allows the discovery of logic inconsistency when updating.

The Django Model is a huge topic. Understanding the interplay among ORM Querysets, Model declarations, Database operations, testing, and other components is crucial for a Django developer.

Your thoughts...