DEV Community

Cover image for Structure - Django ORM Working - Part 1
Kracekumar
Kracekumar

Posted on

Structure - Django ORM Working - Part 1

Django ORM hides a lot of complexity while developing the web application. The data model declaration and querying pattern are simplified, whereas it's structured differently behind the scenes. The series of blog posts will explain Django ORM working(not just converting Python code to SQL), model declaration, querying (manager, queryset), supporting multiple drivers, writing custom queries, migrations etc...

Consider a model definition from the Django tutorial.

from django.db import models


class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
Enter fullscreen mode Exit fullscreen mode

The Question and Choice model class derives from models.Model. Inheriting Model signals Django at run-time the class is a database model. Question model(later converted to a table) contains two extra class variables, question_text and pub_date, which will be two columns in the table. Their type is indicated by creating an instance of the respective type of fields, here models.CharField, models.DateTimeField. A similar work applies to the Choice model.

Querying

Let's display all the instances of Question (all the rows in the table - polls_question).

>>> Question.objects.all()
<QuerySet []>
Enter fullscreen mode Exit fullscreen mode

As expected, the table is empty, and the method call returns empty result. The two things to notice are objects.all() and QuerySet. All the data entry in and out of the database through Django ORM happens through the object's interface. All the results are wrapped inside the QuerySet, even empty ones.

From where does the objects instance come?

>>> class Foo:
...     pass
...
>>> class Bar(Foo):
...     pass
...
>>> Bar.mro()
[<class 'Bar'>, <class 'Foo'>, <class 'object'>]
Enter fullscreen mode Exit fullscreen mode

Every Python class has a Method Resolution Order, which determines the behavior of the class/method invocation. In the above example, the Bar class inherits the Foo class. The mro method returns the order from child to patent class, Bar inherits Foo, Foo inherits object. As you can see, the Foo class has no inherited class in the code, but the mro method says Foo inherits object. All the Python class inherits from the object; hence mro method returns object as the last parent in the list.

>>> Question.mro()
[<class 'polls.models.Question'>, 
<class 'django.db.models.base.Model'>, 
<class 'object'>]
Enter fullscreen mode Exit fullscreen mode

MRO for Question is clear and returns result as expected.

>>> Bar
<class 'Bar'>
>>> Question
<class 'polls.models.Question'>
>>> Question.objects
<django.db.models.manager.Manager object at 0x10bd7f1c0>
>>> Question.objects.mro()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'Manager' object has no attribute 'mro'
>>> Question.objects.__class__.mro()
[<class 'django.db.models.manager.Manager'>, <class 'django.db.models.manager.BaseManagerFromQuerySet'>, <class 'django.db.models.manager.BaseManager'>, <class 'object'>]
Enter fullscreen mode Exit fullscreen mode

The representation of Question.objects is different from the representation of Bar and Question classes. As the name indicates, objects is an instance of the Manager. The objects, the instance of Manager class, inherits BaseManagerFromQuerySet and BaseManager.

>>> Choice
<class 'polls.models.Choice'>
>>> Choice.objects
<django.db.models.manager.Manager object at 0x10bd7f0a0>
>>> Question.objects
<django.db.models.manager.Manager object at 0x10bd7f1c0>
>>> Choice.objects is Question.objects
False
>>> Choice.objects == Question.objects
True
Enter fullscreen mode Exit fullscreen mode

What? Even though the instance uses different id but equality test returns True.

# It's decalared in manager class

def __eq__(self, other):
        return (
            isinstance(other, self.__class__) and
            self._constructor_args == other._constructor_args
        )
Enter fullscreen mode Exit fullscreen mode

The logic for checking the equality has two pieces - both operands should be of the same type and their constructor args are same. In this case(behind the scenes), both the managers were called with empty arguments.

>>> Question.objects._constructor_args
((), {})
>>> Choice.objects._constructor_args
((), {})
Enter fullscreen mode Exit fullscreen mode

In the next post, I'll cover how models.*Field works.

Summary

  1. The Django model inherits model.Model and all the class variables initialized with model.*Field automatically behaves like a column in the table.
  2. The interactions to the database happen through ModelManager via objects attribute.

Notes:

Discussion (0)