An object-relational mapper (ORM) makes it easy for developers to be productive by reducing the need for extensive knowledge of relational databases. ORMs can remove details from database access and replace a query with a chain of a method call, simplifying the process of working with strings of code.
Gaining an understanding of the ORM in the Django QuerySet can strengthen your skills and help you be a better developer.
In today’s tutorial, we will create a basic example of an app with an ORM. The app will consist of models and your Django shell, and we will use it to perform queries in the QuerySet. Following along with this tutorial can help you understand the material and prepare you for a San Francisco data class or elsewhere.
Using a Django Shell
If you have experience using the Python shell, you should be able to easily use the Django shell. Keep in mind that the Django shell loads your project’s specific parameters and settings that only work in the framework, which allows you to work with your project instead of around it. It also allows you to work with the settings module inside your project. To run it, first create a new virtualenv, or virtual environment, then use this command:
python manage.py shell
This tutorial will use Python 3, but you can use another version if you prefer. To create the virtual environment, run the following command:
$ mkvirtualenv -p $(which python3) QuerySets Already using interpreter /usr/bin/python3 Using base prefix '/usr' New python executable in /home/jrb/.virtualenvs/QuerySets/bin/python3 Also creating executable in /home/jrb/.virtualenvs/QuerySets/bin/python Installing setuptools, pkg_resources, pip, wheel...done.
Now, we will install both IPython and Django using this command:
(QuerySets) $ pip install django ipython
To create your project, use this command:
(QuerySets) $ django-admin.py startproject QuerySets (QuerySets) $ cd QuerySets/ (QuerySets) $ ./manage.py startapp qs
Once you have installed these on your machine, you can update your QuerySets/settings.py to add “qs” to your INSTALLED_APPS list at the end. Run this command to set up your database:
(QuerySets) jrb@caktus025:~/caktus/QuerySets$ ./manage.py makemigrations qs Migrations for 'qs': qs/migrations/0001_initial.py: - Create model MainModel - Create model OnlyOne - Create model RelatedModel - Add field one to mainmodel (QuerySets) jrb@caktus025:~/caktus/QuerySets$ ./manage.py migrate ...
Now, open a new IPython session by running the Python manage.py shell. Keep track of how many queries are sent to your database by using CaptureQueriesContext to keep track of your queries. Here is the code to install the Django QuerySet manager:
class CaptureQueriesContext(object): def __init__(self, connection): self.connection = connection @property def captured_queries(self): return self.connection.queries[self.initial_queries:self.final_queries] def __enter__(self): self.force_debug_cursor = self.connection.force_debug_cursor self.connection.force_debug_cursor = True self.initial_queries = len(self.connection.queries_log) self.final_queries = None request_started.disconnect(reset_queries) return self def __exit__(self, exc_type, exc_value, traceback): self.connection.force_debug_cursor = self.force_debug_cursor request_started.connect(reset_queries) if exc_type is not None: return self.final_queries = len(self.connection.queries_log)
By looking at the code above, you can see that this context manager references the connection to your database, known as a self.connection. A self.connection can set or unset the connection’s flag. We can still use a test suite. Test it using this code:
In : from django.test.utils import CaptureQueriesContext In : from django.db import connection In : from qs import models In : with CaptureQueriesContext(connection) as context: ...: print(models.MainModel.objects.all()) ...: <QuerySet > In : print(context.initial_queries, context.final_queries) 0 1
When we run the test suite in Django QuerySet, you can tell that there are no queries in the beginning. However, the code issues a query to your database, so after the code has finished running, we end up with one.
We can now add data to the Django QuerySet to explore how it works. First, populate the name fields by running the following code:
In : import random In : import string In : def random_name(): ...: return ''.join(random.choice(string.ascii_letters) for i in range(16 ...: )) ...: In : random_name() Out: 'nRtybzKaSZWjHOBZ'
This code will allow us to add objects to the Django QuerySet name fields:
In : with CaptureQueriesContext(connection) as context: ...: models.OnlyOne.objects.bulk_create([ ...: models.OnlyOne(name=random_name()) ...: for i in range(5) ...: ]) ...: models.MainModel.objects.bulk_create([ ...: models.MainModel(name=random_name(), one_id=i + 1) ...: for i in range(5) ...: ]) ...: models.RelatedModel.objects.bulk_create([ ...: models.RelatedModel(name=random_name(), main_id=i + 1) ...: for i in range(5) ...: for x in range(7) ...: ]) ...: In : print(context.final_queries - context.initial_queries) 6
Exploring the Django QuerySet
Let’s take a look at the QuerySet to learn more about it. First, create a Django QuerySet and place it in one of your variables. Run this code to see what happens:
In : with CaptureQueriesContext(connection) as context: ...: qs = models.MainModel.objects.all() ...: In : print(context.final_queries - context.initial_queries) 0 In : print(context.captured_queries) 
As you can see, a Django QuerySet was not sent to your database. Even though it has all the information needed to be populated from your database, it will not do so until the Django QuerySet requires the information. On their own, Django QuerySets will not trigger any queries.
Even if you chain a Django QuerySet from another QuerySet, Django will still not send any queries. If you wanted to send a Django QuerySet to your database, you could use a non-QuerySet-returning approach, like .count().
A Django QuerySet will go to your database whenever it needs concrete results. These types of results include implicitly or explicitly looping. Here is an example of when a concrete result is needed:
In : with CaptureQueriesContext(connection) as context: ...: for m in models.MainModel.objects.all(): ...: obj = m ...: r = repr(models.OnlyOne.objects.all()) ...: l = len(models.RelatedModel.objects.all()) ...: list_main = list(models.MainModel.objects.all()) ...: b = bool(models.OnlyOne.objects.all()) ...: print(context.final_queries - context.initial_queries) ...: 5