<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title>Real Python</title>
  <link href="https://realpython.com/atom.xml" rel="self"/>
  <link href="https://realpython.com/"/>
  <updated>2019-04-11T14:00:00+00:00</updated>
  <id>https://realpython.com/</id>
  <author>
    <name>Real Python</name>
  </author>

  
    <entry>
      <title>Python String Formatting Tips &amp; Best Practices</title>
      <id>https://realpython.com/courses/python-string-formatting-tips-best-practices/</id>
      <link href="https://realpython.com/courses/python-string-formatting-tips-best-practices/"/>
      <updated>2019-04-11T14:00:00+00:00</updated>
      <summary>Learn the four main approaches to string formatting in Python, as well as their strengths and weaknesses. You&#39;ll also get a simple rule of thumb for how to pick the best general purpose string formatting approach in your own programs.</summary>
      <content type="html">
        &lt;p&gt;Learn the four main approaches to string formatting in Python, as well as their strengths and weaknesses. You&amp;rsquo;ll also get a simple rule of thumb for how to pick the best general purpose string formatting approach in your own programs.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>How to Create an Index in Django Without Downtime</title>
      <id>https://realpython.com/create-django-index-without-downtime/</id>
      <link href="https://realpython.com/create-django-index-without-downtime/"/>
      <updated>2019-04-10T14:00:00+00:00</updated>
      <summary>In this step-by-step Python tutorial, you&#39;ll get a solid understanding of the limitations of Django migrations by tackling a well known problem: creating an index in Django with no downtime.</summary>
      <content type="html">
        &lt;p&gt;Managing database migrations is a great challenge in any software project. Luckily, as of version 1.7, Django comes with a built-in migration framework. The framework is very powerful and useful in managing change in databases. But the flexibility provided by the framework required some compromises. To understand the limitations of Django migrations, you are going to tackle a well known problem: creating an index in Django with no downtime.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this tutorial, you&amp;rsquo;ll learn:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How and when Django generates new migrations&lt;/li&gt;
&lt;li&gt;How to inspect the commands Django generates to execute migrations&lt;/li&gt;
&lt;li&gt;How to safely modify migrations to fit your needs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This intermediate-level tutorial is designed for readers who are already familiar with Django migrations. For an introduction to that topic, check out &lt;a href=&quot;https://realpython.com/django-migrations-a-primer/&quot;&gt;Django Migrations: A Primer&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-django-resources-2&quot; data-focus=&quot;false&quot;&gt;Click here to get free access to additional Django tutorials and resources&lt;/a&gt; you can use to deepen your Python web development skills.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-problem-with-creating-an-index-in-django-migrations&quot;&gt;The Problem With Creating an Index in Django Migrations&lt;/h2&gt;
&lt;p&gt;A common change that usually becomes necessary when the data stored by your application grows is adding an index. Indexes are used to speed up queries and make your app feel fast and responsive.&lt;/p&gt;
&lt;p&gt;In most databases, adding an index requires an exclusive lock on the table. An exclusive lock prevents data modification (DML) operations such as &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;INSERT&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt;, while the index is created.&lt;/p&gt;
&lt;p&gt;Locks are obtained implicitly by the database when executing certain operations. For example, when a user logs into your app, Django will update the &lt;code&gt;last_login&lt;/code&gt; field in the &lt;code&gt;auth_user&lt;/code&gt; table. To perform the update, the database will first have to obtain a lock on the row. If the row is currently being locked by another connection, then you might get a &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/exceptions/#database-exceptions&quot;&gt;database exception&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Locking a table might pose a problem when it&amp;rsquo;s necessary to keep the system available during migrations. The bigger the table, the longer it can take to create the index. The longer it takes to create the index, the longer the system is unavailable or unresponsive to users.&lt;/p&gt;
&lt;p&gt;Some database vendors provide a way to create an index without locking the table. For example, to create an index in PostgreSQL without locking a table, you can use the &lt;a href=&quot;https://www.postgresql.org/docs/current/sql-createindex.html&quot;&gt;&lt;code&gt;CONCURRENTLY&lt;/code&gt;&lt;/a&gt; keyword:&lt;/p&gt;
&lt;div class=&quot;highlight sql&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CONCURRENTLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In Oracle, there is an &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/CREATE-INDEX.html&quot;&gt;&lt;code&gt;ONLINE&lt;/code&gt;&lt;/a&gt; option to allow DML operations on the table while the index is created:&lt;/p&gt;
&lt;div class=&quot;highlight sql&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ix&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ONLINE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When generating migrations, Django will not use these special keywords. Running the migration as is will make the database acquire an exclusive lock on the table and prevent DML operations while the index is created.&lt;/p&gt;
&lt;p&gt;Creating an index concurrently has some caveats. It&amp;rsquo;s important to understand the issues specific to your database backend in advance. For example, &lt;a href=&quot;https://www.postgresql.org/docs/current/sql-createindex.html#SQL-CREATEINDEX-CONCURRENTLY&quot;&gt;one caveat in PostgreSQL&lt;/a&gt; is that creating an index concurrently takes longer because it requires an additional table scan.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this tutorial, you&amp;rsquo;ll use Django migrations to create an index on a large table, without causing any downtime.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; To follow this tutorial, it is recommended that you use a PostgreSQL backend, Django 2.x, and Python 3.&lt;/p&gt;
&lt;p&gt;It is possible to follow along with other database backends as well. In places where SQL features unique to PostgreSQL are used, change the SQL to match your database backend.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;re going to use a made up &lt;code&gt;Sale&lt;/code&gt; model in an app called &lt;code&gt;app&lt;/code&gt;. In a real life situation, models such as &lt;code&gt;Sale&lt;/code&gt; are the main tables in the database, and they will usually be very big and store a lot of data:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# models.py&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Sale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sold_at&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTimeField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;auto_now_add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;charged_amount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To create the table, generate the initial migration and apply it:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py makemigrations
&lt;span class=&quot;go&quot;&gt;Migrations for &amp;#39;app&amp;#39;:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  app/migrations/0001_initial.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - Create model Sale&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage migrate
&lt;span class=&quot;go&quot;&gt;Operations to perform:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Apply all migrations: app&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Running migrations:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying app.0001_initial... OK&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After a while, the sales table becomes very big, and users start to complain about slowness. While monitoring the database, you noticed that a lot of queries use the &lt;code&gt;sold_at&lt;/code&gt; column. To speed things up, you decide that you need an index on the column.&lt;/p&gt;
&lt;p&gt;To add an index on &lt;code&gt;sold_at&lt;/code&gt;, you make the following change to the model:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# models.py&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Sale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sold_at&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTimeField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;auto_now_add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;db_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;charged_amount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PositiveIntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you run this migration as it is, then Django will create the index on the table, and it will be locked until the index is completed. It can take a while to create an index on a very large table, and you want to avoid downtime.&lt;/p&gt;
&lt;p&gt;On a local development environment with a small dataset and very few connections, this migration might feel instantaneous. However, on large datasets with many concurrent connections, obtaining a lock and creating the index can take a while.&lt;/p&gt;
&lt;p&gt;In the next steps, you are going to modify migrations created by Django to create the index without causing any downtime.&lt;/p&gt;
&lt;h2 id=&quot;fake-migration&quot;&gt;Fake Migration&lt;/h2&gt;
&lt;p&gt;The first approach is to create the index manually. You are going to generate the migration, but you are not going to actually let Django apply it. Instead, you will run the SQL manually in the database and then make Django think the migration completed.&lt;/p&gt;
&lt;p&gt;First, generate the migration:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py makemigrations --name add_index_fake
&lt;span class=&quot;go&quot;&gt;Migrations for &amp;#39;app&amp;#39;:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  app/migrations/0002_add_index_fake.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - Alter field sold_at on sale&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Use the &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/django-admin/#django-admin-sqlmigrate&quot;&gt;&lt;code&gt;sqlmigrate&lt;/code&gt; command&lt;/a&gt; to view the SQL Django will use to execute this migration:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py sqlmigrate app &lt;span class=&quot;m&quot;&gt;0002&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;BEGIN;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;-- Alter field sold_at on sale&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;CREATE INDEX &amp;quot;app_sale_sold_at_b9438ae4&amp;quot; ON &amp;quot;app_sale&amp;quot; (&amp;quot;sold_at&amp;quot;);&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;COMMIT;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You want to create the index without locking the table, so you need to modify the command. Add the &lt;code&gt;CONCURRENTLY&lt;/code&gt; keyword and execute in the database:&lt;/p&gt;
&lt;div class=&quot;highlight psql&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;app=#&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONCURRENTLY&lt;/span&gt; &lt;span class=&quot;s s-Name&quot;&gt;&amp;quot;app_sale_sold_at_b9438ae4&amp;quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;s s-Name&quot;&gt;&amp;quot;app_sale&amp;quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s s-Name&quot;&gt;&amp;quot;sold_at&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;CREATE INDEX&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice that you executed the command without the &lt;code&gt;BEGIN&lt;/code&gt; and &lt;code&gt;COMMIT&lt;/code&gt; parts. Omitting these keywords will execute the commands without a database transaction. We will discuss database transactions later in the article.&lt;/p&gt;
&lt;p&gt;After you executed the command, if you try to apply migrations, then you will get the following error:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py migrate

&lt;span class=&quot;go&quot;&gt;Operations to perform:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Apply all migrations: app&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Running migrations:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying app.0002_add_index_fake...Traceback (most recent call last):&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  File &amp;quot;venv/lib/python3.7/site-packages/django/db/backends/utils.py&amp;quot;, line 85, in _execute&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    return self.cursor.execute(sql, params)&lt;/span&gt;

&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;go&quot;&gt;psycopg2.ProgrammingError: relation &amp;quot;app_sale_sold_at_b9438ae4&amp;quot; already exists&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Django complains that the index already exists, so it can&amp;rsquo;t proceed with the migration. You just created the index directly in the database, so now you need to make Django think that the migration was already applied.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How to Fake a Migration&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Django provides a built-in way of marking migrations as executed, without actually executing them. To use this option, set the &lt;code&gt;--fake&lt;/code&gt; flag when applying the migration:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py migrate --fake
&lt;span class=&quot;go&quot;&gt;Operations to perform:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Apply all migrations: app&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Running migrations:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying app.0002_add_index_fake... FAKED&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Django didn&amp;rsquo;t raise an error this time. In fact, Django didn&amp;rsquo;t really apply any migration. It just marked it as executed (or &lt;code&gt;FAKED&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Here are some issues to consider when faking migrations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The manual command must be equivalent to the SQL generated by Django:&lt;/strong&gt; You need to make sure the command you execute is equivalent to the SQL generated by Django. Use &lt;code&gt;sqlmigrate&lt;/code&gt; to produce the SQL command. If the commands do not match, then you might end up with inconsistencies between the database and the models state.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Other unapplied migrations will also be faked:&lt;/strong&gt; When you have multiple unapplied migrations, they will all be faked. Before you apply migrations, it&amp;rsquo;s important to make sure only the migrations you want to fake are unapplied. Otherwise, you might end up with inconsistencies. Another option is to specify the exact migration you want to fake.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Direct access to the database is required:&lt;/strong&gt; You need to run the SQL command in the database. This is not always an option. Also, executing commands directly in a production database is dangerous and should be avoided when possible.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Automated deployment processes might need adjustments:&lt;/strong&gt; If you &lt;a href=&quot;https://realpython.com/automating-django-deployments-with-fabric-and-ansible/&quot;&gt;automated the deployment process&lt;/a&gt; (using CI, CD, or other automation tools), then you might need to alter the process to fake migrations. This is not always desirable.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cleanup&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Before moving on to the next section, you need to bring the database back to its state right after the initial migration. To do that, migrate back to the initial migration:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py migrate &lt;span class=&quot;m&quot;&gt;0001&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Operations to perform:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Target specific migration: 0001_initial, from app&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Running migrations:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Rendering model states... DONE&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Unapplying app.0002_add_index_fake... OK&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Django unapplied the changes made in the second migration, so now it&amp;rsquo;s safe to also delete the file:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; rm app/migrations/0002_add_index_fake.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To make sure you did everything right, inspect the migrations:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py showmigrations app
&lt;span class=&quot;go&quot;&gt;app&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0001_initial&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The initial migration was applied, and there are no unapplied migrations.&lt;/p&gt;
&lt;h2 id=&quot;execute-raw-sql-in-migrations&quot;&gt;Execute Raw SQL in Migrations&lt;/h2&gt;
&lt;p&gt;In the previous section, you executed SQL directly in the database and faked the migration. This gets the job done, but there is a better solution.&lt;/p&gt;
&lt;p&gt;Django provides a way to execute raw SQL in migrations using &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/migration-operations/#runsql&quot;&gt;&lt;code&gt;RunSQL&lt;/code&gt;&lt;/a&gt;. Let&amp;rsquo;s try to use it instead of executing the command directly in the database.&lt;/p&gt;
&lt;p&gt;First, generate a new empty migration:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py makemigrations app --empty --name add_index_runsql
&lt;span class=&quot;go&quot;&gt;Migrations for &amp;#39;app&amp;#39;:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  app/migrations/0002_add_index_runsql.py&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, edit the migration file and add a &lt;code&gt;RunSQL&lt;/code&gt; operation:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# migrations/0002_add_index_runsql.py&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;atomic&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;dependencies&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;app&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;0001_initial&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;operations&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunSQL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;            &lt;span class=&quot;s1&quot;&gt;&amp;#39;CREATE INDEX &amp;quot;app_sale_sold_at_b9438ae4&amp;quot; &amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;            &lt;span class=&quot;s1&quot;&gt;&amp;#39;ON &amp;quot;app_sale&amp;quot; (&amp;quot;sold_at&amp;quot;);&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you run the migration, you will get the following output:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py migrate
&lt;span class=&quot;go&quot;&gt;Operations to perform:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Apply all migrations: app&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Running migrations:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying app.0002_add_index_runsql... OK&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is looking good, but there is a problem. Let&amp;rsquo;s try to generate migrations again:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py makemigrations --name leftover_migration
&lt;span class=&quot;go&quot;&gt;Migrations for &amp;#39;app&amp;#39;:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  app/migrations/0003_leftover_migration.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - Alter field sold_at on sale&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Django generated the same migration again. Why did it do that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cleanup&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Before we can answer that question, you need to clean up and undo the changes you made to the database. Start by deleting the last migration. It was not applied, so it&amp;rsquo;s safe to delete:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; rm app/migrations/0003_leftover_migration.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, list the migrations for the &lt;code&gt;app&lt;/code&gt; app:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py showmigrations app
&lt;span class=&quot;go&quot;&gt;app&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0001_initial&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0002_add_index_runsql&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The third migration is gone, but the second is applied. You want to get back to the state right after the initial migration. Try to migrate back to the initial migration as you did in the previous section:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py migrate app &lt;span class=&quot;m&quot;&gt;0001&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Operations to perform:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Target specific migration: 0001_initial, from app&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Running migrations:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Rendering model states... DONE&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Unapplying app.0002_add_index_runsql...Traceback (most recent call last):&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;NotImplementedError: You cannot reverse this operation&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Django is unable to reverse the migration.&lt;/p&gt;
&lt;h2 id=&quot;reverse-migration-operation&quot;&gt;Reverse Migration Operation&lt;/h2&gt;
&lt;p&gt;To reverse a migration, Django executes an opposite action for every operation. In this case, the reverse of adding an index is to drop it. As you&amp;rsquo;ve already seen, when a migration is reversible, you can unapply it. Just like you can use &lt;code&gt;checkout&lt;/code&gt; in Git, you can reverse a migration if you execute &lt;code&gt;migrate&lt;/code&gt; to an earlier migration.&lt;/p&gt;
&lt;p&gt;Many built-in migration operations already define a reverse action. For example, the reverse action for adding a field is to drop the corresponding column. The reverse action for creating a model is to drop the corresponding table.&lt;/p&gt;
&lt;p&gt;Some migration operations are not reversible. For example, there is no reverse action for removing a field or deleting a model, because once the migration was applied, the data is gone.&lt;/p&gt;
&lt;p&gt;In the previous section, you used the &lt;code&gt;RunSQL&lt;/code&gt; operation. When you tried to reverse the migration, you encountered an error. According to the error, one of the operations in the migration cannot be reversed. Django is unable to reverse raw SQL by default. Because Django has no knowledge of what was executed by the operation, it cannot generate an opposite action automatically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How to Make a Migration Reversible&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For a migration to be reversible, all the operations in it must be reversible. It&amp;rsquo;s not possible to reverse part of a migration, so a single non-reversible operation will make the entire migration non-reversible.&lt;/p&gt;
&lt;p&gt;To make a &lt;code&gt;RunSQL&lt;/code&gt; operation reversible, you must provide SQL to execute when the operation is reversed. The reverse SQL is provided in the &lt;code&gt;reverse_sql&lt;/code&gt; argument.&lt;/p&gt;
&lt;p&gt;The opposite action to adding an index is to drop it. To make your migration reversible, provide the &lt;code&gt;reverse_sql&lt;/code&gt; to drop the index:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# migrations/0002_add_index_runsql.py&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;atomic&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;dependencies&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;app&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;0001_initial&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;operations&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunSQL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s1&quot;&gt;&amp;#39;CREATE INDEX &amp;quot;app_sale_sold_at_b9438ae4&amp;quot; &amp;#39;&lt;/span&gt;
            &lt;span class=&quot;s1&quot;&gt;&amp;#39;ON &amp;quot;app_sale&amp;quot; (&amp;quot;sold_at&amp;quot;);&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;reverse_sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;DROP INDEX &amp;quot;app_sale_sold_at_b9438ae4&amp;quot;;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now try to reverse the migration:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py showmigrations app
&lt;span class=&quot;go&quot;&gt;app&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0001_initial&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0002_add_index_runsql&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py migrate app &lt;span class=&quot;m&quot;&gt;0001&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Operations to perform:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Target specific migration: 0001_initial, from app&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Running migrations:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Rendering model states... DONE&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;go&quot;&gt; Unapplying app.0002_add_index_runsql... OK&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py showmigrations app
&lt;span class=&quot;go&quot;&gt;app&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [X] 0001_initial&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; [ ] 0002_add_index_runsql&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The second migration was reversed, and the index was dropped by Django. Now it&amp;rsquo;s safe to delete the migration file:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; rm app/migrations/0002_add_index_runsql.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It&amp;rsquo;s always a good idea to provide &lt;code&gt;reverse_sql&lt;/code&gt;. In situations where reversing a raw SQL operation does not require any action, you can mark the operation as reversible using the special sentinel &lt;code&gt;migrations.RunSQL.noop&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunSQL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;...&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Your forward SQL here&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;reverse_sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunSQL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;noop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;understand-model-state-and-database-state&quot;&gt;Understand Model State and Database State&lt;/h2&gt;
&lt;p&gt;In your previous attempt to create the index manually using &lt;code&gt;RunSQL&lt;/code&gt;, Django generated the same migration over and over again even though the index was created in the database. To understand why Django did that, you first need to understand how Django decides when to generate new migrations.&lt;/p&gt;
&lt;h3 id=&quot;when-django-generates-a-new-migration&quot;&gt;When Django Generates a New Migration&lt;/h3&gt;
&lt;p&gt;In the process of generating and applying migrations, Django syncs between the state of the database and the state of the models. For example, when you add a field to a model, Django adds a column to the table. When you remove a field from the model, Django removes the column from the table.&lt;/p&gt;
&lt;p&gt;To sync between the models and the database, Django maintains a state that represents the models. To sync the database with the models, Django generates migration operations. Migration operations translate to a vendor specific SQL that can be executed in the database. When all migration operations are executed, the database and the models are expected to be consistent.&lt;/p&gt;
&lt;p&gt;To get the state of the database, Django aggregates the operations from all past migrations. When the aggregated state of the migrations is not consistent with the state of the models, Django generates a new migration.&lt;/p&gt;
&lt;p&gt;In the previous example, you created the index using raw SQL. Django did not know you created the index because you didn&amp;rsquo;t use a familiar migration operation.&lt;/p&gt;
&lt;p&gt;When Django aggregated all the migrations and compared them with the state of the models, it found that an index was missing. This is why, even after you created the index manually, Django still thought it was missing and generated a new migration for it.&lt;/p&gt;
&lt;h3 id=&quot;how-to-separate-database-and-state-in-migrations&quot;&gt;How to Separate Database and State in Migrations&lt;/h3&gt;
&lt;p&gt;Since Django is unable to create the index the way you want it to, you want to provide your own SQL but still let Django know you created it.&lt;/p&gt;
&lt;p&gt;In other words, you need to execute something in the database and provide Django with the migration operation to sync its internal state. To do that, Django provides us with a special migration operation called &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/migration-operations/#separatedatabaseandstate&quot;&gt;&lt;code&gt;SeparateDatabaseAndState&lt;/code&gt;&lt;/a&gt;. This operation is not well known and should be reserved for special cases such as this one.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s much easier to edit migrations than write them from scratch, so start by generating a migration the usual way:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py makemigrations --name add_index_separate_database_and_state

&lt;span class=&quot;go&quot;&gt;Migrations for &amp;#39;app&amp;#39;:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  app/migrations/0002_add_index_separate_database_and_state.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - Alter field sold_at on sale&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the contents of the migration generated by Django, same as before:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# migrations/0002_add_index_separate_database_and_state.py&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;dependencies&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;app&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;0001_initial&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;operations&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AlterField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;model_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sale&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sold_at&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTimeField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;auto_now_add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;db_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Django generated an &lt;code&gt;AlterField&lt;/code&gt; operation on the field &lt;code&gt;sold_at&lt;/code&gt;. The operation will create an index and update the state. We want to keep this operation but provide a different command to execute in the database.&lt;/p&gt;
&lt;p&gt;Once again, to get the command, use the SQL generated by Django:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py sqlmigrate app &lt;span class=&quot;m&quot;&gt;0002&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;BEGIN;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;-- Alter field sold_at on sale&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;--&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;CREATE INDEX &amp;quot;app_sale_sold_at_b9438ae4&amp;quot; ON &amp;quot;app_sale&amp;quot; (&amp;quot;sold_at&amp;quot;);&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;COMMIT;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Add the &lt;code&gt;CONCURRENTLY&lt;/code&gt; keyword in the appropriate place:&lt;/p&gt;
&lt;div class=&quot;highlight sql&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CONCURRENTLY&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&amp;quot;app_sale_sold_at_b9438ae4&amp;quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&amp;quot;app_sale&amp;quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&amp;quot;sold_at&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, edit the migration file and use &lt;code&gt;SeparateDatabaseAndState&lt;/code&gt; to provide your modified SQL command for execution:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# migrations/0002_add_index_separate_database_and_state.py&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;dependencies&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;app&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;0001_initial&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;operations&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SeparateDatabaseAndState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;state_operations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AlterField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;model_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sale&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sold_at&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTimeField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;auto_now_add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;db_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;database_operations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunSQL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;                    CREATE INDEX CONCURRENTLY &amp;quot;app_sale_sold_at_b9438ae4&amp;quot;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;                    ON &amp;quot;app_sale&amp;quot; (&amp;quot;sold_at&amp;quot;);&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;                &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reverse_sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;                    DROP INDEX &amp;quot;app_sale_sold_at_b9438ae4&amp;quot;;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;                &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The migration operation &lt;code&gt;SeparateDatabaseAndState&lt;/code&gt; accepts 2 lists of operations:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;state_operations&lt;/strong&gt; are operations to apply on the internal model state. They do not effect the database.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;database_operations&lt;/strong&gt; are operations to apply to the database.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You kept the original operation generated by Django in &lt;code&gt;state_operations&lt;/code&gt;. When using &lt;code&gt;SeparateDatabaseAndState&lt;/code&gt;, this is what you will usually want to do. Notice that the &lt;code&gt;db_index=True&lt;/code&gt; argument is provided to the field. This migration operation will let Django know that there is an index on the field.&lt;/p&gt;
&lt;p&gt;You used the SQL generated by Django and added the &lt;code&gt;CONCURRENTLY&lt;/code&gt; keyword. You used the special action &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/migration-operations/#runsql&quot;&gt;&lt;code&gt;RunSQL&lt;/code&gt;&lt;/a&gt; to execute raw SQL in the migration.&lt;/p&gt;
&lt;p&gt;If you try to run the migration, you will get the following output:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py migrate app
&lt;span class=&quot;go&quot;&gt;Operations to perform:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Apply all migrations: app&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Running migrations:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying app.0002_add_index_separate_database_and_state...Traceback (most recent call last):&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  File &amp;quot;/venv/lib/python3.7/site-packages/django/db/backends/utils.py&amp;quot;, line 83, in _execute&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    return self.cursor.execute(sql)&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;go&quot;&gt;psycopg2.InternalError: CREATE INDEX CONCURRENTLY cannot run inside a transaction block&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;non-atomic-migrations&quot;&gt;Non-Atomic Migrations&lt;/h2&gt;
&lt;p&gt;In SQL, &lt;code&gt;CREATE&lt;/code&gt;, &lt;code&gt;DROP&lt;/code&gt;, &lt;code&gt;ALTER&lt;/code&gt;, and &lt;code&gt;TRUNCATE&lt;/code&gt; operations are referred to as &lt;strong&gt;Data Definition Language&lt;/strong&gt; (DDL). In databases that support transactional DDL, &lt;a href=&quot;https://wiki.postgresql.org/wiki/Transactional_DDL_in_PostgreSQL:_A_Competitive_Analysis#Transactional_DDL&quot;&gt;such as PostgreSQL&lt;/a&gt;, Django executes migrations inside a database transaction by default. However, according to the error above, PostgreSQL cannot create an index concurrently inside a transaction block.&lt;/p&gt;
&lt;p&gt;To be able to create an index concurrently within a migration, you need to tell Django to not execute the migration in a database transaction. To do that, you mark the migration as &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/howto/writing-migrations/#non-atomic-migrations&quot;&gt;non-atomic&lt;/a&gt; by setting &lt;code&gt;atomic&lt;/code&gt; to &lt;code&gt;False&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# migrations/0002_add_index_separate_database_and_state.py&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;atomic&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dependencies&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;app&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;0001_initial&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;operations&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SeparateDatabaseAndState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;state_operations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AlterField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;model_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sale&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;sold_at&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTimeField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;auto_now_add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;db_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;database_operations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunSQL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;                    CREATE INDEX CONCURRENTLY &amp;quot;app_sale_sold_at_b9438ae4&amp;quot;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;                    ON &amp;quot;app_sale&amp;quot; (&amp;quot;sold_at&amp;quot;);&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;                &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;reverse_sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;                    DROP INDEX &amp;quot;app_sale_sold_at_b9438ae4&amp;quot;;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;                &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After you marked the migration as non-atomic, you can run the migration:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py migrate app
&lt;span class=&quot;go&quot;&gt;Operations to perform:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Apply all migrations: app&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Running migrations:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying app.0002_add_index_separate_database_and_state... OK&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You just executed the migration without causing any downtime.&lt;/p&gt;
&lt;p&gt;Here are some issues to consider when you&amp;rsquo;re using &lt;code&gt;SeparateDatabaseAndState&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Database operations must be equivalent to state operations:&lt;/strong&gt; Inconsistencies between the database and model state can cause a lot of trouble. A good starting point is to keep the operations generated by Django in &lt;code&gt;state_operations&lt;/code&gt; and edit the output of &lt;code&gt;sqlmigrate&lt;/code&gt; to use in &lt;code&gt;database_operations&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Non atomic migrations cannot rollback in case of error:&lt;/strong&gt; If there is an error during the migration, then you won&amp;rsquo;t be able to rollback. You would have to either rollback the migration or complete it manually. It&amp;rsquo;s a good idea to keep the operations executed inside a non-atomic migration to a minimum. If you have additional operations in the migration, move them to a new migration.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Migration might be vendor specific:&lt;/strong&gt; The SQL generated by Django is specific to the database backend used in the project. It might work with other database backends, but that is not guaranteed. If you need to support multiple database backends, you need to make some adjustments to this approach.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You started this tutorial with a large table and a problem. You wanted to make your app faster for your users, and you wanted to do that without causing them any downtime.&lt;/p&gt;
&lt;p&gt;By the end of the tutorial, you managed to generate and safely modify a Django migration to achieve this goal. You tackled different problems along the way and managed to overcome them using built-in tools provided by the migrations framework.&lt;/p&gt;
&lt;p&gt;In this tutorial, you learned the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How Django migrations work internally using model and database state, and when new migrations are generated&lt;/li&gt;
&lt;li&gt;How to execute custom SQL in migrations using the &lt;code&gt;RunSQL&lt;/code&gt; action&lt;/li&gt;
&lt;li&gt;What reversible migrations are, and how to make a &lt;code&gt;RunSQL&lt;/code&gt; action reversible&lt;/li&gt;
&lt;li&gt;What atomic migrations are, and how to change the default behavior according to your needs&lt;/li&gt;
&lt;li&gt;How to safely execute complex migrations in Django&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The separation between model and database state is an important concept. Once you understand it, and how to utilize it, you can overcome many limitations of the built-in migration operations. Some use cases that come to mind include adding an index that was already created in the database and providing vendor specific arguments to DDL commands.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Running Python Scripts</title>
      <id>https://realpython.com/courses/running-python-scripts/</id>
      <link href="https://realpython.com/courses/running-python-scripts/"/>
      <updated>2019-04-09T14:00:00+00:00</updated>
      <summary>This step-by-step course will guide you through a series of ways to run Python scripts, depending on your environment, platform, needs, and skills as a programmer.</summary>
      <content type="html">
        &lt;p&gt;One of the most important skills you need to build as a Python developer is to be able to run Python scripts and code. This is going to be the only way for you to know if your code works as you planned. It&amp;rsquo;s even the only way of knowing if your code works at all!&lt;/p&gt;
&lt;p&gt;This step-by-step course will guide you through a series of ways to run Python scripts, depending on your environment, platform, needs, and skills as a programmer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You&amp;rsquo;ll learn how to run Python scripts by using:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The operating system command-line or terminal&lt;/li&gt;
&lt;li&gt;The Python interactive mode&lt;/li&gt;
&lt;li&gt;The IDE or text editor you like best&lt;/li&gt;
&lt;li&gt;The file manager of your system, by double-clicking on the icon of your script&lt;/li&gt;
&lt;/ul&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Python REST APIs With Flask, Connexion, and SQLAlchemy – Part 3</title>
      <id>https://realpython.com/flask-connexion-rest-api-part-3/</id>
      <link href="https://realpython.com/flask-connexion-rest-api-part-3/"/>
      <updated>2019-04-08T14:00:00+00:00</updated>
      <summary>In Part 3 of this series, you&#39;ll learn how to add relationships to the database created in Part 2 and extend the API to support CRUD operations on those relationships using SQLAlchemy and Marshmallow.</summary>
      <content type="html">
        &lt;p&gt;In &lt;a href=&quot;https://realpython.com/flask-connexion-rest-api-part-2/&quot;&gt;Part 2&lt;/a&gt; of this series, you added the ability to save changes made through the REST API to a database using &lt;a href=&quot;https://www.sqlalchemy.org/&quot;&gt;SQLAlchemy&lt;/a&gt; and learned how to &lt;a href=&quot;https://en.wikipedia.org/wiki/Serialization&quot;&gt;serialize&lt;/a&gt; that data for the REST API using &lt;a href=&quot;https://marshmallow.readthedocs.io/en/3.0/&quot;&gt;Marshmallow&lt;/a&gt;. Connecting the REST API to a database so that the application can make changes to existing data and create new data is great and makes the application much more useful and robust. &lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s only part of the power a database offers, however. An even more powerful feature is the &lt;strong&gt;R&lt;/strong&gt; part of &lt;strong&gt;RDBMS&lt;/strong&gt; systems: &lt;strong&gt;relationships&lt;/strong&gt;. In a database, a relationship is the ability to connect two or more tables together in a meaningful way. In this article, you&amp;rsquo;ll learn how to implement relationships and turn your &lt;code&gt;Person&lt;/code&gt; database into a mini-blogging web application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this article, you&amp;rsquo;ll learn:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why more than one table in a database is useful and important&lt;/li&gt;
&lt;li&gt;How tables are related to each other&lt;/li&gt;
&lt;li&gt;How SQLAlchemy can help you manage relationships&lt;/li&gt;
&lt;li&gt;How relationships help you build a mini-blogging application&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;who-this-article-is-for&quot;&gt;Who This Article Is For&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://realpython.com/flask-connexion-rest-api/&quot;&gt;Part 1&lt;/a&gt; of this series guided you through building a REST API, and &lt;a href=&quot;https://realpython.com/flask-connexion-rest-api-part-2/&quot;&gt;Part 2&lt;/a&gt; showed you how to connect that REST API to a database. &lt;/p&gt;
&lt;p&gt;This article expands your programming tool belt further. You&amp;rsquo;ll learn how to create hierarchical data structures represented as &lt;a href=&quot;https://en.wikipedia.org/wiki/One-to-many_(data_model)&quot;&gt;one-to-many&lt;/a&gt; relationships by SQLAlchemy. In addition, you&amp;rsquo;ll extend the REST API you&amp;rsquo;ve already built to provide CRUD (Create, Read, Update, and Delete) support for the elements in this hierarchical structure. &lt;/p&gt;
&lt;p&gt;The web application presented in Part 2 will have its HTML and JavaScript files modified in major ways to create a more fully functional mini-blogging application. You can review the final version of the code from Part 2 in the &lt;a href=&quot;https://github.com/realpython/materials/tree/master/flask-connexion-rest-part-2/version_1&quot;&gt;GitHub repository for that article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hang on as you get started creating relationships and your mini-blogging application!&lt;/p&gt;
&lt;h2 id=&quot;additional-dependencies&quot;&gt;Additional Dependencies&lt;/h2&gt;
&lt;p&gt;There are no new Python dependencies beyond what was required for the Part 2 article. However, you will be using two new JavaScript modules in the web application to makes things easier and more consistent. The two modules are the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://handlebarsjs.com/&quot;&gt;Handlebars.js&lt;/a&gt;&lt;/strong&gt; is a templating engine for JavaScript, much like &lt;a href=&quot;https://realpython.com/primer-on-jinja-templating/&quot;&gt;Jinja2&lt;/a&gt; for Flask.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://momentjs.com/&quot;&gt;Moment.js&lt;/a&gt;&lt;/strong&gt; is a datetime parsing and formatting module that makes displaying UTC timestamps easier. &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You don&amp;rsquo;t have to download either of these, as the web application will get them directly from the &lt;a href=&quot;https://www.cloudflare.com/&quot;&gt;Cloudflare CDN&lt;/a&gt; (&lt;a href=&quot;https://en.wikipedia.org/wiki/Content_delivery_network&quot;&gt;Content Delivery Network&lt;/a&gt;), as you&amp;rsquo;re already doing for the jQuery module. &lt;/p&gt;
&lt;h2 id=&quot;people-data-extended-for-blogging&quot;&gt;People Data Extended for Blogging&lt;/h2&gt;
&lt;p&gt;In Part 2, the &lt;code&gt;People&lt;/code&gt; data existed as a dictionary in the &lt;code&gt;build_database.py&lt;/code&gt; Python code. This is what you used to populate the database with some initial data. You&amp;rsquo;re going to modify the &lt;code&gt;People&lt;/code&gt; data structure to give each person a list of notes associated with them. The new &lt;code&gt;People&lt;/code&gt; data structure will look like this:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Data to initialize database with&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;PEOPLE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;fname&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Doug&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;lname&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Farrell&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;notes&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Cool, a mini-blogging application!&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2019-01-06 22:17:54&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;This could be useful&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2019-01-08 22:17:54&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Well, sort of useful&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2019-03-06 22:17:54&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;fname&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Kent&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;lname&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Brockman&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;notes&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;s2&quot;&gt;&amp;quot;I&amp;#39;m going to make really profound observations&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s2&quot;&gt;&amp;quot;2019-01-07 22:17:54&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;s2&quot;&gt;&amp;quot;Maybe they&amp;#39;ll be more obvious than I thought&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s2&quot;&gt;&amp;quot;2019-02-06 22:17:54&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;fname&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Bunny&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;lname&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Easter&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;notes&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Has anyone seen my Easter eggs?&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2019-01-07 22:47:54&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;I&amp;#39;m really late delivering these!&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2019-04-06 22:17:54&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Each person in the &lt;code&gt;People&lt;/code&gt; dictionary now includes a key called &lt;code&gt;notes&lt;/code&gt;, which is associated with a list containing tuples of data. Each tuple in the &lt;code&gt;notes&lt;/code&gt; list represents a single &lt;strong&gt;note&lt;/strong&gt; containing the content and a timestamp. The timestamps are initialized (rather than dynamically created) to demonstrate ordering later on in the REST API.&lt;/p&gt;
&lt;p&gt;Each single person is associated with multiple notes, and each single note is associated with only one person. This hierarchy of data is known as a &lt;a href=&quot;https://en.wikipedia.org/wiki/One-to-many_(data_model)&quot;&gt;one-to-many&lt;/a&gt; relationship, where a single parent object is related to many child objects. You&amp;rsquo;ll see how this one-to-many relationship is managed in the database with SQLAlchemy.&lt;/p&gt;
&lt;h2 id=&quot;brute-force-approach&quot;&gt;Brute Force Approach&lt;/h2&gt;
&lt;p&gt;The database you built stored the data in a table, and a table is a two-dimensional array of rows and columns. Can the &lt;code&gt;People&lt;/code&gt; dictionary above be represented in a single table of rows and columns? It can be, in the following way, in your &lt;code&gt;person&lt;/code&gt; database table. Unfortunately to include all of the actual data in the example creates a scroll bar for the table, as you&amp;rsquo;ll see below:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;
&lt;table class=&quot;table table-hover&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;person_id&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;lname&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;fname&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;timestamp&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;content&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;note_timestamp&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Farrell&lt;/td&gt;
&lt;td&gt;Doug&lt;/td&gt;
&lt;td&gt;2018-08-08 21:16:01&lt;/td&gt;
&lt;td&gt;Cool, a mini-blogging application!&lt;/td&gt;
&lt;td&gt;2019-01-06 22:17:54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Farrell&lt;/td&gt;
&lt;td&gt;Doug&lt;/td&gt;
&lt;td&gt;2018-08-08 21:16:01&lt;/td&gt;
&lt;td&gt;This could be useful&lt;/td&gt;
&lt;td&gt;2019-01-08 22:17:54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Farrell&lt;/td&gt;
&lt;td&gt;Doug&lt;/td&gt;
&lt;td&gt;2018-08-08 21:16:01&lt;/td&gt;
&lt;td&gt;Well, sort of useful&lt;/td&gt;
&lt;td&gt;2019-03-06 22:17:54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Brockman&lt;/td&gt;
&lt;td&gt;Kent&lt;/td&gt;
&lt;td&gt;2018-08-08 21:16:01&lt;/td&gt;
&lt;td&gt;I&amp;rsquo;m going to make really profound observations&lt;/td&gt;
&lt;td&gt;2019-01-07 22:17:54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Brockman&lt;/td&gt;
&lt;td&gt;Kent&lt;/td&gt;
&lt;td&gt;2018-08-08 21:16:01&lt;/td&gt;
&lt;td&gt;Maybe they&amp;rsquo;ll be more obvious than I thought&lt;/td&gt;
&lt;td&gt;2019-02-06 22:17:54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Easter&lt;/td&gt;
&lt;td&gt;Bunny&lt;/td&gt;
&lt;td&gt;2018-08-08 21:16:01&lt;/td&gt;
&lt;td&gt;Has anyone seen my Easter eggs?&lt;/td&gt;
&lt;td&gt;2019-01-07 22:47:54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Easter&lt;/td&gt;
&lt;td&gt;Bunny&lt;/td&gt;
&lt;td&gt;2018-08-08 21:16:01&lt;/td&gt;
&lt;td&gt;I&amp;rsquo;m really late delivering these!&lt;/td&gt;
&lt;td&gt;2019-04-06 22:17:54&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;The above table would actually work. All the data is represented, and a single person is associated with a collection of different notes. &lt;/p&gt;
&lt;h3 id=&quot;advantages&quot;&gt;Advantages&lt;/h3&gt;
&lt;p&gt;Conceptually, the above table structure has the advantage of being relatively simple to understand. You could even make the case that the data could be persisted to a flat file instead of a database.&lt;/p&gt;
&lt;p&gt;Because of the two-dimensional table structure, you could store and use this data in a spreadsheet. Spreadsheets have been pressed into service as data storage quite a bit.&lt;/p&gt;
&lt;h3 id=&quot;disadvantages&quot;&gt;Disadvantages&lt;/h3&gt;
&lt;p&gt;While the above table structure would work, it has some real disadvantages. &lt;/p&gt;
&lt;p&gt;In order to represent the collection of notes, all the data for each person is repeated for every unique note, the person data is therefore redundant. This isn&amp;rsquo;t such a big deal for your person data as there aren&amp;rsquo;t that many columns. But imagine if a person had many more columns. Even with large disk drives, this can get to be a storage concern if you&amp;rsquo;re dealing with millions of rows of data.&lt;/p&gt;
&lt;p&gt;Having redundant data like this can lead to maintenance issues as time goes by. For example, what if the Easter Bunny decided a change of name was a good idea. In order to do this, every record containing the Easter Bunny&amp;rsquo;s name would have to be updated in order to keep the data consistent. This kind of work against the database can lead to data inconsistency, particularly if the work is done by a person running a SQL query by hand. &lt;/p&gt;
&lt;p&gt;Naming columns becomes awkward. In the table above, there is a &lt;code&gt;timestamp&lt;/code&gt; column used to track the creation and update time of a person in the table. You also want to have similar functionality for the creation and update time for a note, but because &lt;code&gt;timestamp&lt;/code&gt; is already used, a contrived name of &lt;code&gt;note_timestamp&lt;/code&gt; is used.&lt;/p&gt;
&lt;p&gt;What if you wanted to add additional one-to-many relationships to the &lt;code&gt;person&lt;/code&gt; table? For example, to include a person&amp;rsquo;s children or phone numbers. Each person could have multiple children and multiple phone numbers. This could be done relatively easily to the Python &lt;code&gt;People&lt;/code&gt; dictionary above by adding &lt;code&gt;children&lt;/code&gt; and &lt;code&gt;phone_numbers&lt;/code&gt; keys with new lists containing the data. &lt;/p&gt;
&lt;p&gt;However, representing those new one-to-many relationships in your &lt;code&gt;person&lt;/code&gt; database table above becomes significantly more difficult. Every new one-to-many relationship increases the number of rows necessary to represent it for every single entry in the child data dramatically. In addition, the problems associated with data redundancy get bigger and more difficult to handle.&lt;/p&gt;
&lt;p&gt;Lastly, the data you&amp;rsquo;d get back from the above table structure wouldn&amp;rsquo;t be very Pythonic: it would be just a big list of lists. SQLAlchemy wouldn&amp;rsquo;t be able to help you very much because the relationship isn&amp;rsquo;t there.&lt;/p&gt;
&lt;h2 id=&quot;relational-database-approach&quot;&gt;Relational Database Approach&lt;/h2&gt;
&lt;p&gt;Based on what you&amp;rsquo;ve seen above, it becomes clear that trying to represent even a moderately complex dataset in a single table becomes unmanageable pretty quickly. Given that, what alternative does a database offer? This is where the &lt;strong&gt;R&lt;/strong&gt; part of &lt;strong&gt;RDBMS&lt;/strong&gt; databases comes into play. Representing relationships removes the disadvantages outlined above. &lt;/p&gt;
&lt;p&gt;Instead of trying to represent hierarchical data in a single table, the data is broken up into multiple tables, with a mechanism to relate them to one another. The tables are broken along collection lines, so for your &lt;code&gt;People&lt;/code&gt; dictionary above, this means there will be a table representing people and another representing notes. This brings back your original &lt;code&gt;person&lt;/code&gt; table, which looks like this:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;
&lt;table class=&quot;table table-hover&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;person_id&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;lname&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;fname&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;timestamp&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Farrell&lt;/td&gt;
&lt;td&gt;Doug&lt;/td&gt;
&lt;td&gt;2018-08-08 21:16:01.888444&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Brockman&lt;/td&gt;
&lt;td&gt;Kent&lt;/td&gt;
&lt;td&gt;2018-08-08 21:16:01.889060&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Easter&lt;/td&gt;
&lt;td&gt;Bunny&lt;/td&gt;
&lt;td&gt;2018-08-08 21:16:01.886834&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;To represent the new note information, you&amp;rsquo;ll create a new table called &lt;code&gt;note&lt;/code&gt;. (Remember our singular table naming convention.) The table looks like this:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;
&lt;table class=&quot;table table-hover&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;note_id&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;person_id&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;content&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;timestamp&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Cool, a mini-blogging application!&lt;/td&gt;
&lt;td&gt;2019-01-06 22:17:54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;This could be useful&lt;/td&gt;
&lt;td&gt;2019-01-08 22:17:54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Well, sort of useful&lt;/td&gt;
&lt;td&gt;2019-03-06 22:17:54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;I&amp;rsquo;m going to make really profound observations&lt;/td&gt;
&lt;td&gt;2019-01-07 22:17:54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Maybe they&amp;rsquo;ll be more obvious than I thought&lt;/td&gt;
&lt;td&gt;2019-02-06 22:17:54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Has anyone seen my Easter eggs?&lt;/td&gt;
&lt;td&gt;2019-01-07 22:47:54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;I&amp;rsquo;m really late delivering these!&lt;/td&gt;
&lt;td&gt;2019-04-06 22:17:54&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Notice that, like the &lt;code&gt;person&lt;/code&gt; table, the &lt;code&gt;note&lt;/code&gt; table has a unique identifier called &lt;code&gt;note_id&lt;/code&gt;, which is the primary key for the &lt;code&gt;note&lt;/code&gt; table. One thing that isn&amp;rsquo;t obvious is the inclusion of the &lt;code&gt;person_id&lt;/code&gt; value in the table. What is that used for? This is what creates the relationship to the &lt;code&gt;person&lt;/code&gt; table. Whereas &lt;code&gt;note_id&lt;/code&gt; is the primary key for the table, &lt;code&gt;person_id&lt;/code&gt; is what&amp;rsquo;s known as a &lt;a href=&quot;https://en.wikipedia.org/wiki/Foreign_key&quot;&gt;foreign key&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The foreign key gives each entry in the &lt;code&gt;note&lt;/code&gt; table the primary key of the &lt;code&gt;person&lt;/code&gt; record it&amp;rsquo;s associated with. Using this, SQLAlchemy can gather all the notes associated with each person by connecting the &lt;code&gt;person.person_id&lt;/code&gt; primary key to the &lt;code&gt;note.person_id&lt;/code&gt; foreign key, creating a relationship.&lt;/p&gt;
&lt;h3 id=&quot;advantages_1&quot;&gt;Advantages&lt;/h3&gt;
&lt;p&gt;By breaking the data set into two tables, and introducing the concept of a foreign key, you&amp;rsquo;ve made the data a little more complex to think about, you have resolved the disadvantages of a single table representation. SQLAlchemy will help you encode the increased complexity fairly easily.&lt;/p&gt;
&lt;p&gt;The data is no longer redundant in the database. There is only one person entry for each person you want to store in the database. This solves the storage concern immediately and dramatically simplifies the maintenance concerns.&lt;/p&gt;
&lt;p&gt;If the Easter Bunny still wanted to change names, then you&amp;rsquo;d only have to change a single row in the &lt;code&gt;person&lt;/code&gt; table, and anything else related to that row (like the &lt;code&gt;note&lt;/code&gt; table) would immediately take advantage of the change. &lt;/p&gt;
&lt;p&gt;Column naming is more consistent and meaningful. Because person and note data exist in separate tables, the creation and update timestamp can be named consistently in both tables, as there is no conflict for names across tables.&lt;/p&gt;
&lt;p&gt;In addition, you&amp;rsquo;d no longer have to create permutations of each row for new one-to-many relationships you might want to represent. Take our &lt;code&gt;children&lt;/code&gt; and &lt;code&gt;phone_numbers&lt;/code&gt; example from earlier. Implementing this would require &lt;code&gt;child&lt;/code&gt; and &lt;code&gt;phone_number&lt;/code&gt; tables. Each table would contain a foreign key of &lt;code&gt;person_id&lt;/code&gt; relating it back to the &lt;code&gt;person&lt;/code&gt; table.&lt;/p&gt;
&lt;p&gt;Using SQLAlchemy, the data you&amp;rsquo;d get back from the above tables would be more immediately useful, as what you&amp;rsquo;d get is an object for each person row. That object has named attributes equivalent to the columns in the table. One of those attributes is a Python list containing the related note objects.&lt;/p&gt;
&lt;h3 id=&quot;disadvantages_1&quot;&gt;Disadvantages&lt;/h3&gt;
&lt;p&gt;Where the brute force approach was simpler to understand, the concept of foreign keys and relationships make thinking about the data somewhat more abstract. This abstraction needs to be thought about for every relationship you establish between tables. &lt;/p&gt;
&lt;p&gt;Making use of relationships means committing to using a database system. This is another tool to install, learn, and maintain above and beyond the application that actually uses the data.&lt;/p&gt;
&lt;h2 id=&quot;sqlalchemy-models&quot;&gt;SQLAlchemy Models&lt;/h2&gt;
&lt;p&gt;To use the two tables above, and the relationship between them, you&amp;rsquo;ll need to create SQLAlchemy models that are aware of both tables and the relationship between them. Here&amp;rsquo;s the SQLAlchemy &lt;code&gt;Person&lt;/code&gt; model from Part 2, updated to include a relationship to a collection of &lt;code&gt;notes&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;__tablename__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;person&amp;#39;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;person_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;primary_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;lname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;fname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;utcnow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;onupdate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;utcnow&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;notes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relationship&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;s1&quot;&gt;&amp;#39;Note&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;backref&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;person&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;cascade&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;all, delete, delete-orphan&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;single_parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;order_by&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;desc(Note.timestamp)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Lines 1 to 8 of the above Python class look exactly like what you created before in Part 2. Lines 9 to 16 create a new attribute in the &lt;code&gt;Person&lt;/code&gt; class called &lt;code&gt;notes&lt;/code&gt;. This new &lt;code&gt;notes&lt;/code&gt; attributes is defined in the following lines of code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 9:&lt;/strong&gt; Like the other attributes of the class, this line creates a new attribute called &lt;code&gt;notes&lt;/code&gt; and sets it equal to an instance of an object called &lt;code&gt;db.relationship&lt;/code&gt;. This object creates the relationship you&amp;rsquo;re adding to the &lt;code&gt;Person&lt;/code&gt; class and is created with all of the parameters defined in the lines that follow.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 10:&lt;/strong&gt; The string parameter &lt;code&gt;&#39;Note&#39;&lt;/code&gt; defines the SQLAlchemy class that the &lt;code&gt;Person&lt;/code&gt; class will be related to. The &lt;code&gt;Note&lt;/code&gt; class isn&amp;rsquo;t defined yet, which is why it&amp;rsquo;s a string here. This is a forward reference and helps handle problems that the order of definitions could cause when something is needed that isn&amp;rsquo;t defined until later in the code. The &lt;code&gt;&#39;Note&#39;&lt;/code&gt; string allows the &lt;code&gt;Person&lt;/code&gt; class to find the &lt;code&gt;Note&lt;/code&gt; class at runtime, which is after both &lt;code&gt;Person&lt;/code&gt; and &lt;code&gt;Note&lt;/code&gt; have been defined.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 11:&lt;/strong&gt; The &lt;code&gt;backref=&#39;person&#39;&lt;/code&gt; parameter is trickier. It creates what&amp;rsquo;s known as a backwards reference in &lt;code&gt;Note&lt;/code&gt; objects. Each instance of a &lt;code&gt;Note&lt;/code&gt; object will contain an attribute called &lt;code&gt;person&lt;/code&gt;. The &lt;code&gt;person&lt;/code&gt; attribute references the parent object that a particular &lt;code&gt;Note&lt;/code&gt; instance is associated with. Having a reference to the parent object (&lt;code&gt;person&lt;/code&gt; in this case) in the child can be very useful if your code iterates over notes and has to include information about the parent. This happens surprisingly often in display rendering code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 12:&lt;/strong&gt; The &lt;code&gt;cascade=&#39;all, delete, delete-orphan&#39;&lt;/code&gt; parameter determines how to treat note object instances when changes are made to the parent &lt;code&gt;Person&lt;/code&gt; instance. For example, when a &lt;code&gt;Person&lt;/code&gt; object is deleted, SQLAlchemy will create the SQL necessary to delete the &lt;code&gt;Person&lt;/code&gt; from the database. Additionally, this parameter tells it to also delete all the &lt;code&gt;Note&lt;/code&gt; instances associated with it. You can read more about these options in the &lt;a href=&quot;https://docs.sqlalchemy.org/en/latest/orm/cascades.html#delete&quot;&gt;SQLAlchemy documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 13:&lt;/strong&gt; The &lt;code&gt;single_parent=True&lt;/code&gt; parameter is required if &lt;code&gt;delete-orphan&lt;/code&gt; is part of the previous &lt;code&gt;cascade&lt;/code&gt; parameter. This tells SQLAlchemy not to allow orphaned &lt;code&gt;Note&lt;/code&gt; instances (a &lt;code&gt;Note&lt;/code&gt; without a parent &lt;code&gt;Person&lt;/code&gt; object) to exist because each &lt;code&gt;Note&lt;/code&gt; has a single parent.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 14:&lt;/strong&gt; The &lt;code&gt;order_by=&#39;desc(Note.timestamp)&#39;&lt;/code&gt; parameter tells SQLAlchemy how to sort the &lt;code&gt;Note&lt;/code&gt; instances associated with a &lt;code&gt;Person&lt;/code&gt;. When a &lt;code&gt;Person&lt;/code&gt; object is retrieved, by default the &lt;code&gt;notes&lt;/code&gt; attribute list will contain &lt;code&gt;Note&lt;/code&gt; objects in an unknown order. The SQLAlchemy &lt;code&gt;desc(...)&lt;/code&gt; function will sort the notes in descending order from newest to oldest. If this line was instead &lt;code&gt;order_by=&#39;Note.timestamp&#39;&lt;/code&gt;, SQLAlchemy would default to using the &lt;code&gt;asc(...)&lt;/code&gt; function, and sort the notes in ascending order, oldest to newest. &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that your &lt;code&gt;Person&lt;/code&gt; model has the new &lt;code&gt;notes&lt;/code&gt; attribute, and this represents the one-to-many relationship to &lt;code&gt;Note&lt;/code&gt; objects, you&amp;rsquo;ll need to define a SQLAlchemy model for a &lt;code&gt;Note&lt;/code&gt;: &lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Note&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;__tablename__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;note&amp;#39;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;note_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;primary_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;person_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ForeignKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;person.person_id&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nullable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;utcnow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;onupdate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;utcnow&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;Note&lt;/code&gt; class defines the attributes making up a note as seen in our sample &lt;code&gt;note&lt;/code&gt; database table from above. The attributes are defined here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 1&lt;/strong&gt; creates the &lt;code&gt;Note&lt;/code&gt; class, inheriting from &lt;code&gt;db.Model&lt;/code&gt;, exactly as you did before when creating the &lt;code&gt;Person&lt;/code&gt; class.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 2&lt;/strong&gt; tells the class what database table to use to store &lt;code&gt;Note&lt;/code&gt; objects.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 3&lt;/strong&gt; creates the &lt;code&gt;note_id&lt;/code&gt; attribute, defining it as an integer value, and as the primary key for the &lt;code&gt;Note&lt;/code&gt; object.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 4&lt;/strong&gt; creates the &lt;code&gt;person_id&lt;/code&gt; attribute, and defines it as the foreign key, relating the &lt;code&gt;Note&lt;/code&gt; class to the &lt;code&gt;Person&lt;/code&gt; class using the &lt;code&gt;person.person_id&lt;/code&gt; primary key. This, and the &lt;code&gt;Person.notes&lt;/code&gt; attribute, are how SQLAlchemy knows what to do when interacting with &lt;code&gt;Person&lt;/code&gt; and &lt;code&gt;Note&lt;/code&gt; objects.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 5&lt;/strong&gt; creates the &lt;code&gt;content&lt;/code&gt; attribute, which contains the actual text of the note. The &lt;code&gt;nullable=False&lt;/code&gt; parameter indicates that it&amp;rsquo;s okay to create new notes that have no content.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 6&lt;/strong&gt; creates the &lt;code&gt;timestamp&lt;/code&gt; attribute, and exactly like the &lt;code&gt;Person&lt;/code&gt; class, this contains the creation or update time for any particular &lt;code&gt;Note&lt;/code&gt; instance.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;initialize-the-database&quot;&gt;Initialize the Database&lt;/h2&gt;
&lt;p&gt;Now that you&amp;rsquo;ve updated the &lt;code&gt;Person&lt;/code&gt; and created the &lt;code&gt;Note&lt;/code&gt; models, you&amp;rsquo;ll use them to rebuild the testing database &lt;code&gt;people.db&lt;/code&gt;. You&amp;rsquo;ll do this by updating the &lt;code&gt;build_database.py&lt;/code&gt; code from Part 2. Here&amp;rsquo;s what the code will look like:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Note&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Data to initialize database with&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PEOPLE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;fname&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Doug&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;lname&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Farrell&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;notes&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Cool, a mini-blogging application!&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2019-01-06 22:17:54&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;This could be useful&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2019-01-08 22:17:54&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Well, sort of useful&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2019-03-06 22:17:54&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;fname&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Kent&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;lname&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Brockman&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;notes&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22 &lt;/span&gt;                &lt;span class=&quot;s2&quot;&gt;&amp;quot;I&amp;#39;m going to make really profound observations&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23 &lt;/span&gt;                &lt;span class=&quot;s2&quot;&gt;&amp;quot;2019-01-07 22:17:54&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24 &lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;25 &lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;26 &lt;/span&gt;                &lt;span class=&quot;s2&quot;&gt;&amp;quot;Maybe they&amp;#39;ll be more obvious than I thought&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;27 &lt;/span&gt;                &lt;span class=&quot;s2&quot;&gt;&amp;quot;2019-02-06 22:17:54&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;28 &lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;29 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;30 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;31 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;32 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;fname&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Bunny&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;33 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;lname&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Easter&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;34 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;notes&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;35 &lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Has anyone seen my Easter eggs?&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2019-01-07 22:47:54&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;36 &lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;I&amp;#39;m really late delivering these!&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;2019-04-06 22:17:54&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;37 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;38 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;39 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;40 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;41 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Delete database file if it exists currently&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;42 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;people.db&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;43 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;people.db&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;44 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;45 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Create the database&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;46 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;47 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;48 &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Iterate over the PEOPLE structure and populate the database&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;49 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;person&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PEOPLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;50 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;lname&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;fname&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;51 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;52 &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Add the notes for the person&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;53 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;note&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;notes&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;54 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;note&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;55 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;notes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;56 &lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;Note&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;57 &lt;/span&gt;                &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;58 &lt;/span&gt;                &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strptime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;%Y-%m-&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; %H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;59 &lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;60 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;61 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;62 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;63 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The code above came from Part 2, with a few changes to create the one-to-many relationship between &lt;code&gt;Person&lt;/code&gt; and &lt;code&gt;Note&lt;/code&gt;. Here are the updated or new lines added to the code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 4&lt;/strong&gt; has been updated to import the &lt;code&gt;Note&lt;/code&gt; class defined previously.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Lines 7 to 39&lt;/strong&gt; contain the updated &lt;code&gt;PEOPLE&lt;/code&gt; dictionary containing our person data, along with the list of notes associated with each person. This data will be inserted into the database.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Lines 49 to 61&lt;/strong&gt; iterate over the &lt;code&gt;PEOPLE&lt;/code&gt; dictionary, getting each &lt;code&gt;person&lt;/code&gt; in turn and using it to create a &lt;code&gt;Person&lt;/code&gt; object. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 53&lt;/strong&gt; iterates over the &lt;code&gt;person.notes&lt;/code&gt; list, getting each &lt;code&gt;note&lt;/code&gt; in turn.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 54&lt;/strong&gt; unpacks the &lt;code&gt;content&lt;/code&gt; and &lt;code&gt;timestamp&lt;/code&gt; from each &lt;code&gt;note&lt;/code&gt; tuple.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 55 to 60&lt;/strong&gt; creates a &lt;code&gt;Note&lt;/code&gt; object and appends it to the person notes collection using &lt;code&gt;p.notes.append()&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 61&lt;/strong&gt; adds the &lt;code&gt;Person&lt;/code&gt; object &lt;code&gt;p&lt;/code&gt; to the database session.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Line 63&lt;/strong&gt; commits all of the activity in the session to the database. It&amp;rsquo;s at this point that all of the data is written to the &lt;code&gt;person&lt;/code&gt; and &lt;code&gt;note&lt;/code&gt; tables in the &lt;code&gt;people.db&lt;/code&gt; database file.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can see that working with the &lt;code&gt;notes&lt;/code&gt; collection in the &lt;code&gt;Person&lt;/code&gt; object instance &lt;code&gt;p&lt;/code&gt; is just like working with any other list in Python. SQLAlchemy takes care of the underlying one-to-many relationship information when the &lt;code&gt;db.session.commit()&lt;/code&gt; call is made.&lt;/p&gt;
&lt;p&gt;For example, just like a &lt;code&gt;Person&lt;/code&gt; instance has its primary key field &lt;code&gt;person_id&lt;/code&gt; initialized by SQLAlchemy when it&amp;rsquo;s committed to the database, instances of &lt;code&gt;Note&lt;/code&gt; will have their primary key fields initialized. In addition, the &lt;code&gt;Note&lt;/code&gt; foreign key &lt;code&gt;person_id&lt;/code&gt; will also be initialized with the primary key value of the &lt;code&gt;Person&lt;/code&gt; instance it&amp;rsquo;s associated with.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example instance of a &lt;code&gt;Person&lt;/code&gt; object before the &lt;code&gt;db.session.commit()&lt;/code&gt; in a kind of pseudocode:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Person (
    person_id = None
    lname = &amp;#39;Farrell&amp;#39;
    fname = &amp;#39;Doug&amp;#39;
    timestamp = None
    notes = [
        Note (
            note_id = None
            person_id = None
            content = &amp;#39;Cool, a mini-blogging application!&amp;#39;
            timestamp = &amp;#39;2019-01-06 22:17:54&amp;#39;
        ),
        Note (
            note_id = None
            person_id = None
            content = &amp;#39;This could be useful&amp;#39;
            timestamp = &amp;#39;2019-01-08 22:17:54&amp;#39;
        ),
        Note (
            note_id = None
            person_id = None
            content = &amp;#39;Well, sort of useful&amp;#39;
            timestamp = &amp;#39;2019-03-06 22:17:54&amp;#39;
        )
    ]
)
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here&amp;rsquo;s the example &lt;code&gt;Person&lt;/code&gt; object after the &lt;code&gt;db.session.commit()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Person (
    person_id = 1
    lname = &amp;#39;Farrell&amp;#39;
    fname = &amp;#39;Doug&amp;#39;
    timestamp = &amp;#39;2019-02-02 21:27:10.336&amp;#39;
    notes = [
        Note (
            note_id = 1
            person_id = 1
            content = &amp;#39;Cool, a mini-blogging application!&amp;#39;
            timestamp = &amp;#39;2019-01-06 22:17:54&amp;#39;
        ),
        Note (
            note_id = 2
            person_id = 1
            content = &amp;#39;This could be useful&amp;#39;
            timestamp = &amp;#39;2019-01-08 22:17:54&amp;#39;
        ),
        Note (
            note_id = 3
            person_id = 1
            content = &amp;#39;Well, sort of useful&amp;#39;
            timestamp = &amp;#39;2019-03-06 22:17:54&amp;#39;
        )
    ]
)
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The important difference between the two is that the primary key of the &lt;code&gt;Person&lt;/code&gt; and &lt;code&gt;Note&lt;/code&gt; objects has been initialized. The database engine took care of this as the objects were created because of the auto-incrementing feature of primary keys discussed in Part 2.&lt;/p&gt;
&lt;p&gt;Additionally, the &lt;code&gt;person_id&lt;/code&gt; foreign key in all the &lt;code&gt;Note&lt;/code&gt; instances has been initialized to reference its parent. This happens because of the order in which the &lt;code&gt;Person&lt;/code&gt; and &lt;code&gt;Note&lt;/code&gt; objects are created in the database. &lt;/p&gt;
&lt;p&gt;SQLAlchemy is aware of the relationship between &lt;code&gt;Person&lt;/code&gt; and &lt;code&gt;Note&lt;/code&gt; objects. When a &lt;code&gt;Person&lt;/code&gt; object is committed to the &lt;code&gt;person&lt;/code&gt; database table, SQLAlchemy gets the &lt;code&gt;person_id&lt;/code&gt; primary key value. That value is used to initialize the foreign key value of &lt;code&gt;person_id&lt;/code&gt; in a &lt;code&gt;Note&lt;/code&gt; object before it&amp;rsquo;s committed to the database.&lt;/p&gt;
&lt;p&gt;SQLAlchemy takes care of this database housekeeping work because of the information you passed when the &lt;code&gt;Person.notes&lt;/code&gt; attribute was initialized with the &lt;code&gt;db.relationship(...)&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;In addition, the &lt;code&gt;Person.timestamp&lt;/code&gt; attribute has been initialized with the current timestamp.&lt;/p&gt;
&lt;p&gt;Running the &lt;code&gt;build_database.py&lt;/code&gt; program from the command line (in the &lt;a href=&quot;https://realpython.com/python-virtual-environments-a-primer/&quot;&gt;virtual environment&lt;/a&gt; will re-create the database with the new additions, getting it ready for use with the web application. This command line will rebuild the database:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python build_database.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;build_database.py&lt;/code&gt; utility program doesn&amp;rsquo;t output any messages if it runs successfully. If it throws an exception, then an error will be printed on the screen.&lt;/p&gt;
&lt;h2 id=&quot;update-rest-api&quot;&gt;Update REST API&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ve updated the SQLAlchemy models and used them to update the &lt;code&gt;people.db&lt;/code&gt; database. Now it&amp;rsquo;s time to update the REST API to provide access to the new notes information. Here&amp;rsquo;s the REST API you built in Part 2:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;
&lt;table class=&quot;table table-hover&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;HTTP Verb&lt;/th&gt;
&lt;th&gt;URL Path&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Create&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/api/people&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URL to create a new person&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/api/people&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URL to read a collection of people&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/api/people/{person_id}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URL to read a single person by &lt;code&gt;person_id&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PUT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/api/people/{person_id}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URL to update an existing person by &lt;code&gt;person_id&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/api/people/{person_id}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URL to delete an existing person by &lt;code&gt;person_id&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;The REST API above provides HTTP URL paths to collections of things, and to the things themselves. You can get a list of people or interact with a single person from that list of people. This path style refines what&amp;rsquo;s returned in a left-to-right manner, getting more granular as you go. &lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll continue this left-to-right pattern to get more granular and access the notes collections. Here&amp;rsquo;s the extended REST API you&amp;rsquo;ll create in order to provide notes to the mini-blog web application:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;
&lt;table class=&quot;table table-hover&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;HTTP Verb&lt;/th&gt;
&lt;th&gt;URL Path&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Create&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/api/people/{person_id}/notes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URL to create a new note&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/api/people/{person_id}/notes/{note_id}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URL to read a single person&amp;rsquo;s single note&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PUT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;api/people/{person_id}/notes/{note_id}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URL to update a single person&amp;rsquo;s single note&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;api/people/{person_id}/notes/{note_id}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URL to delete a single person&amp;rsquo;s single note&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/api/notes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URL to get all notes for all people sorted by &lt;code&gt;note.timestamp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;There are two variations in the &lt;code&gt;notes&lt;/code&gt; part of the REST API compared to the convention used in the &lt;code&gt;people&lt;/code&gt; section:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;There is no URL defined to get all the &lt;code&gt;notes&lt;/code&gt; associated with a person, only a URL to get a single note. This would have made the REST API complete in a way, but the web application you&amp;rsquo;ll create later doesn&amp;rsquo;t need this functionality. Therefore, it&amp;rsquo;s been left out.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There is the inclusion of the last URL &lt;code&gt;/api/notes&lt;/code&gt;. This is a convenience method created for the web application. It will be used in the mini-blog on the home page to show all the notes in the system. There isn&amp;rsquo;t a way to get this information readily using the REST API pathing style as designed, so this &lt;strong&gt;shortcut&lt;/strong&gt; has been added.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As in Part 2, the REST API is configured in the &lt;a href=&quot;https://github.com/realpython/materials/blob/master/flask-connexion-rest-part-3/swagger.yml&quot;&gt;&lt;code&gt;swagger.yml&lt;/code&gt;&lt;/a&gt; file.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The idea of designing a REST API with a path that gets more and more granular as you move from left to right is very useful. Thinking this way can help clarify the relationships between different parts of a database. Just be aware that there are realistic limits to how far down a hierarchical structure this kind of design should be taken. &lt;/p&gt;
&lt;p&gt;For example, what if the &lt;code&gt;Note&lt;/code&gt; object had a collection of its own, something like comments on the notes. Using the current design ideas, this would lead to a URL that went something like this: &lt;code&gt;/api/people/{person_id}/notes/{note_id}/comments/{comment_id}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;There is no practical limit to this kind of design, but there is one for usefulness. In actual use in real applications, a long, multilevel URL like that one is hardly ever needed. A more common pattern is to get a list of intervening objects (like notes) and then use a separate API entry point to get a single comment for an application use case.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;implement-the-api&quot;&gt;Implement the API&lt;/h2&gt;
&lt;p&gt;With the updated REST API defined in the &lt;code&gt;swagger.yml&lt;/code&gt; file, you&amp;rsquo;ll need to update the implementation provided by the Python modules. This means updating existing module files, like &lt;code&gt;models.py&lt;/code&gt; and &lt;code&gt;people.py&lt;/code&gt;, and creating a new module file called &lt;code&gt;notes.py&lt;/code&gt; to implement support for &lt;code&gt;Notes&lt;/code&gt; in the extended REST API.&lt;/p&gt;
&lt;h3 id=&quot;update-response-json&quot;&gt;Update Response JSON&lt;/h3&gt;
&lt;p&gt;The purpose of the REST API is to get useful JSON data out of the database. Now that you&amp;rsquo;ve updated the SQLAlchemy &lt;code&gt;Person&lt;/code&gt; and created the &lt;code&gt;Note&lt;/code&gt; models, you&amp;rsquo;ll need to update the Marshmallow schema models as well. As you may recall from Part 2, Marshmallow is the module that translates the SQLAlchemy objects into Python objects suitable for creating JSON strings. &lt;/p&gt;
&lt;p&gt;The updated and newly created Marshmallow schemas are in the &lt;code&gt;models.py&lt;/code&gt; module, which are explained below, and look like this:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PersonSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ma&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ModelSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;sqla_session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;notes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Nested&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;PersonNoteSchema&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;many&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PersonNoteSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ma&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ModelSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;    This class exists to get around a recursion issue&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;note_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;person_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NoteSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ma&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ModelSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Note&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;sqla_session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Nested&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;NotePersonSchema&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NotePersonSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ma&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ModelSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24 &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;    This class exists to get around a recursion issue&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;25 &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;26 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;person_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;27 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;lname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;28 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;fname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;29 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are some interesting things going on in the above definitions. The &lt;code&gt;PersonSchema&lt;/code&gt; class has one new entry: the &lt;code&gt;notes&lt;/code&gt; attribute defined in line 5. This defines it as a nested relationship to the &lt;code&gt;PersonNoteSchema&lt;/code&gt;. It will default to an empty list if nothing is present in the SQLAlchemy &lt;code&gt;notes&lt;/code&gt; relationship. The &lt;code&gt;many=True&lt;/code&gt; parameter indicates that this is a one-to-many relationship, so Marshmallow will serialize all the related &lt;code&gt;notes&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;PersonNoteSchema&lt;/code&gt; class defines what a &lt;code&gt;Note&lt;/code&gt; object looks like as Marshmallow serializes the &lt;code&gt;notes&lt;/code&gt; list. The &lt;code&gt;NoteSchema&lt;/code&gt; defines what a SQLAlchemy &lt;code&gt;Note&lt;/code&gt; object looks like in terms of Marshmallow. Notice that it has a &lt;code&gt;person&lt;/code&gt; attribute. This attribute comes from the SQLAlchemy &lt;code&gt;db.relationship(...)&lt;/code&gt; definition parameter &lt;code&gt;backref=&#39;person&#39;&lt;/code&gt;. The &lt;code&gt;person&lt;/code&gt; Marshmallow definition is nested, but because it doesn&amp;rsquo;t have the &lt;code&gt;many=True&lt;/code&gt; parameter, there is only a single &lt;code&gt;person&lt;/code&gt; connected. &lt;/p&gt;
&lt;p&gt;The &lt;code&gt;NotePersonSchema&lt;/code&gt; class defines what is nested in the &lt;code&gt;NoteSchema.person&lt;/code&gt; attribute.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You might be wondering why the &lt;code&gt;PersonSchema&lt;/code&gt; class has its own unique &lt;code&gt;PersonNoteSchema&lt;/code&gt; class to define the &lt;code&gt;notes&lt;/code&gt; collection attribute. By the same token, the &lt;code&gt;NoteSchema&lt;/code&gt; class has its own unique &lt;code&gt;NotePersonSchema&lt;/code&gt; class to define the &lt;code&gt;person&lt;/code&gt; attribute. You may be wondering whether the &lt;code&gt;PersonSchema&lt;/code&gt; class could be defined this way:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PersonSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ma&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ModelSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;sqla_session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;notes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Nested&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;NoteSchema&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;many&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Additionally, couldn&amp;rsquo;t the &lt;code&gt;NoteSchema&lt;/code&gt; class be defined using the &lt;code&gt;PersonSchema&lt;/code&gt; to define the &lt;code&gt;person&lt;/code&gt; attribute? A class definition like this would each refer to the other, and this causes a recursion error in Marshmallow as it will cycle from &lt;code&gt;PersonSchema&lt;/code&gt; to &lt;code&gt;NoteSchema&lt;/code&gt; until it runs out of stack space. Using the unique schema references breaks the recursion and allows this kind of nesting to work.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;people&quot;&gt;People&lt;/h3&gt;
&lt;p&gt;Now that you&amp;rsquo;ve got the schemas in place to work with the one-to-many relationship between &lt;code&gt;Person&lt;/code&gt; and &lt;code&gt;Note&lt;/code&gt;, you need to update the &lt;code&gt;person.py&lt;/code&gt; and create the &lt;code&gt;note.py&lt;/code&gt; modules in order to implement a working REST API.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;people.py&lt;/code&gt; module needs two changes. The first is to import the &lt;code&gt;Note&lt;/code&gt; class, along with the &lt;code&gt;Person&lt;/code&gt; class at the top of the module. Then only &lt;code&gt;read_one(person_id)&lt;/code&gt; needs to change in order to handle the relationship. That function will look like this:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;read_one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;    This function responds to a request for /api/people/{person_id}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;    with one matching person from people&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;    :param person_id:   Id of person to find&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;    :return:            person matching id&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Build the initial query&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;person_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;outerjoin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Note&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;one_or_none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Did we find a person?&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;person&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19 &lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# Serialize the data for the response&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;person_schema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PersonSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;person_schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24 &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Otherwise, nope, didn&amp;#39;t find that person&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;25 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;26 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Person not found for Id: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{person_id}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The only difference is line 12: &lt;code&gt;.outerjoin(Note)&lt;/code&gt;. An outer join (&lt;a href=&quot;https://en.wikipedia.org/wiki/Join_(SQL)#Left_outer_join&quot;&gt;left outer join&lt;/a&gt; in SQL terms) is necessary for the case where a user of the application has created a new &lt;code&gt;person&lt;/code&gt; object, which has no &lt;code&gt;notes&lt;/code&gt; related to it. The outer join ensures that the SQL query will return a &lt;code&gt;person&lt;/code&gt; object, even if there are no &lt;code&gt;note&lt;/code&gt; rows to join with.&lt;/p&gt;
&lt;p&gt;At the start of this article, you saw how person and note data could be represented in a single, flat table, and all of the disadvantages of that approach. You also saw the advantages of breaking that data up into two tables, &lt;code&gt;person&lt;/code&gt; and &lt;code&gt;note&lt;/code&gt;, with a relationship between them.&lt;/p&gt;
&lt;p&gt;Until now, we&amp;rsquo;ve been working with the data as two distinct, but related, items in the database. But now that you&amp;rsquo;re actually going to use the data, what we essentially want is for the data to be joined back together. This is what a &lt;a href=&quot;https://en.wikipedia.org/wiki/Join_(SQL)&quot;&gt;database join&lt;/a&gt; does. It combines data from two tables together using the primary key to foreign key relationship. &lt;/p&gt;
&lt;p&gt;A join is kind of a boolean &lt;code&gt;and&lt;/code&gt; operation because it only returns data if there is data in both tables to combine. If, for example, a &lt;code&gt;person&lt;/code&gt; row exists but has no related &lt;code&gt;note&lt;/code&gt; row, then there is nothing to join, so nothing is returned. This isn&amp;rsquo;t what you want for &lt;code&gt;read_one(person_id)&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;This is where the outer join comes in handy. It&amp;rsquo;s a kind of boolean &lt;code&gt;or&lt;/code&gt; operation. It returns &lt;code&gt;person&lt;/code&gt; data even if there is no associated &lt;code&gt;note&lt;/code&gt; data to combine with. This is the behavior you want for &lt;code&gt;read_one(person_id)&lt;/code&gt; to handle the case of a newly created &lt;code&gt;Person&lt;/code&gt; object that has no notes yet.&lt;/p&gt;
&lt;p&gt;You can see the complete &lt;a href=&quot;https://github.com/realpython/materials/blob/master/flask-connexion-rest-part-3/people.py&quot;&gt;&lt;code&gt;people.py&lt;/code&gt;&lt;/a&gt; in the article repository.&lt;/p&gt;
&lt;h3 id=&quot;notes&quot;&gt;Notes&lt;/h3&gt;
&lt;p&gt;You&amp;rsquo;ll create a &lt;code&gt;notes.py&lt;/code&gt; module to implement all the Python code associated with the new note related REST API definitions. In many ways, it works like the &lt;code&gt;people.py&lt;/code&gt; module, except it must handle both a &lt;code&gt;person_id&lt;/code&gt; and a &lt;code&gt;note_id&lt;/code&gt; as defined in the &lt;code&gt;swagger.yml&lt;/code&gt; configuration file. As an example, here is &lt;code&gt;read_one(person_id, note_id)&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;read_one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;note_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;    This function responds to a request for&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;    /api/people/{person_id}/notes/{note_id}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;    with one matching note for the associated person&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;    :param person_id:       Id of person the note is related to&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;    :param note_id:         Id of the note&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;    :return:                json string of note contents&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Query the database for the note&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;note&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;Note&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Note&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;person_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Note&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;note_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;note_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;one_or_none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;17 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;18 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19 &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Was a note found?&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;note&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;note_schema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NoteSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;note_schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;25 &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Otherwise, nope, didn&amp;#39;t find that note&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;26 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;27 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Note not found for Id: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{note_id}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The interesting parts of the above code are lines 12 to 17:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Line 13&lt;/strong&gt; begins a query against the &lt;code&gt;Note&lt;/code&gt; SQLAlchemy objects and joins to the related &lt;code&gt;Person&lt;/code&gt; SQLAlchemy object comparing &lt;code&gt;person_id&lt;/code&gt; from both &lt;code&gt;Person&lt;/code&gt; and &lt;code&gt;Note&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Line 14&lt;/strong&gt; filters the result down to the &lt;code&gt;Note&lt;/code&gt; objects that has a &lt;code&gt;Person.person_id&lt;/code&gt; equal to the passed in &lt;code&gt;person_id&lt;/code&gt; parameter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Line 15&lt;/strong&gt; filters the result further to the &lt;code&gt;Note&lt;/code&gt; object that has a &lt;code&gt;Note.note_id&lt;/code&gt; equal to the passed in &lt;code&gt;note_id&lt;/code&gt; parameter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Line 16&lt;/strong&gt; returns the &lt;code&gt;Note&lt;/code&gt; object if found, or &lt;code&gt;None&lt;/code&gt; if nothing matching the parameters is found.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can check out the complete &lt;a href=&quot;https://github.com/realpython/materials/blob/master/flask-connexion-rest-part-3/notes.py&quot;&gt;&lt;code&gt;notes.py&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;updated-swagger-ui&quot;&gt;Updated Swagger UI&lt;/h2&gt;
&lt;p&gt;The Swagger UI has been updated by the action of updating the &lt;code&gt;swagger.yml&lt;/code&gt; file and creating the URL endpoint implementations. Below is a screenshot of the updated UI showing the Notes section with the &lt;code&gt;GET /api/people/{person_id}/notes/{note_id}&lt;/code&gt; expanded:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/swagger_ui_notes-part-3.bb51184454db.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border &quot; src=&quot;https://files.realpython.com/media/swagger_ui_notes-part-3.bb51184454db.png&quot; width=&quot;1010&quot; height=&quot;970&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/swagger_ui_notes-part-3.bb51184454db.png&amp;amp;w=252&amp;amp;sig=7b37d3bcc0f224e9355abebb035342969b5b6479 252w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/swagger_ui_notes-part-3.bb51184454db.png&amp;amp;w=505&amp;amp;sig=aa31c4b5ba908b76dbe7e4ce8a99bb5e87f6a422 505w, https://files.realpython.com/media/swagger_ui_notes-part-3.bb51184454db.png 1010w&quot; sizes=&quot;75vw&quot; alt=&quot;Swagger UI with notes part 3&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;mini-blogging-web-application&quot;&gt;Mini-Blogging Web Application&lt;/h2&gt;
&lt;p&gt;The web application has been substantially changed to show its new purpose as a mini-blogging application. It has three pages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The home page (&lt;code&gt;localhost:5000/&lt;/code&gt;)&lt;/strong&gt;, which shows all of the blog messages (notes) sorted from newest to oldest &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The people page (&lt;code&gt;localhost:5000/people&lt;/code&gt;)&lt;/strong&gt;, which shows all the people in the system, sorted by last name, and also allows the user to create a new person and update or delete an existing one&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The notes page (&lt;code&gt;localhost:5000/people/{person_id}/notes&lt;/code&gt;)&lt;/strong&gt;, which shows all the notes associated with a person, sorted from newest to oldest, and also allows the user to create a new note and update or delete an existing one&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;navigation&quot;&gt;Navigation&lt;/h3&gt;
&lt;p&gt;There are two buttons on every page of the application:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The &lt;em&gt;Home&lt;/em&gt; button&lt;/strong&gt; will navigate to the home screen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The &lt;em&gt;People&lt;/em&gt; button&lt;/strong&gt; navigates to the &lt;code&gt;/people&lt;/code&gt; screen, showing all people in the database. &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These two buttons are present on every screen in the application as a way to get back to a starting point.&lt;/p&gt;
&lt;h3 id=&quot;home-page&quot;&gt;Home Page&lt;/h3&gt;
&lt;p&gt;Below is a screenshot of the home page showing the initialized database contents:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/flask_connexion_rest_home_page_part_3.049c149d887b.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border &quot; src=&quot;https://files.realpython.com/media/flask_connexion_rest_home_page_part_3.049c149d887b.png&quot; width=&quot;1122&quot; height=&quot;415&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/flask_connexion_rest_home_page_part_3.049c149d887b.png&amp;amp;w=280&amp;amp;sig=cb3490b5c8499e04033d2309c66ea94171c3c000 280w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/flask_connexion_rest_home_page_part_3.049c149d887b.png&amp;amp;w=561&amp;amp;sig=49f96b84b9e39cf38844afcad3a21e56ecb9be8d 561w, https://files.realpython.com/media/flask_connexion_rest_home_page_part_3.049c149d887b.png 1122w&quot; sizes=&quot;75vw&quot; alt=&quot;Flask Connexion Rest Home Page Part 3&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The functionality of this page works like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Double-clicking on a person&amp;rsquo;s name will take the user to the &lt;code&gt;/people/{person_id}&lt;/code&gt; page, with the editor section filled in with the person&amp;rsquo;s first and last names and the update and reset buttons enabled.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Double-clicking on a person&amp;rsquo;s note will take the user to the &lt;code&gt;/people/{person_id}/notes/{note_id}&lt;/code&gt; page, with the editor section filled in with the note&amp;rsquo;s contents and the &lt;em&gt;Update&lt;/em&gt; and &lt;em&gt;Reset&lt;/em&gt; buttons enabled.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;people-page&quot;&gt;People Page&lt;/h3&gt;
&lt;p&gt;Below is a screenshot of the people page showing the people in the initialized database:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/flask_connexion_rest_people_page_part_3.1ed575f1eeef.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border &quot; src=&quot;https://files.realpython.com/media/flask_connexion_rest_people_page_part_3.1ed575f1eeef.png&quot; width=&quot;1117&quot; height=&quot;399&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/flask_connexion_rest_people_page_part_3.1ed575f1eeef.png&amp;amp;w=279&amp;amp;sig=7b4111145b525d22d4dd815453ced53dd9ec9e55 279w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/flask_connexion_rest_people_page_part_3.1ed575f1eeef.png&amp;amp;w=558&amp;amp;sig=e18ac9a3d7693475ffe17d09d4bbed8b3f8dc52a 558w, https://files.realpython.com/media/flask_connexion_rest_people_page_part_3.1ed575f1eeef.png 1117w&quot; sizes=&quot;75vw&quot; alt=&quot;Flask Connexion Rest People Page Part 3&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The functionality of this page works like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Single-clicking on a person&amp;rsquo;s name will populate the editor section of the page with the person&amp;rsquo;s first and last name, disabling the &lt;em&gt;Create&lt;/em&gt; button, and enabling the &lt;em&gt;Update&lt;/em&gt; and &lt;em&gt;Delete&lt;/em&gt; buttons. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Double clicking on a person&amp;rsquo;s name will navigate to the notes pages for that person.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The functionality of the editor works like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If the first and last name fields are empty, the &lt;em&gt;Create&lt;/em&gt; and &lt;em&gt;Reset&lt;/em&gt; buttons are enabled. Entering a new name in the fields and clicking &lt;em&gt;Create&lt;/em&gt; will create a new person and update the database and re-render the table below the editor. Clicking &lt;em&gt;Reset&lt;/em&gt; will clear the editor fields.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the first and last name fields have data, the user navigated here by double-clicking the person&amp;rsquo;s name from the home screen. In this case, the &lt;em&gt;Update&lt;/em&gt;, &lt;em&gt;Delete&lt;/em&gt;, and &lt;em&gt;Reset&lt;/em&gt; buttons are enabled. Changing the first or last name and clicking &lt;em&gt;Update&lt;/em&gt; will update the database and re-render the table below the editor. Clicking &lt;em&gt;Delete&lt;/em&gt; will remove the person from the database and re-render the table.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;notes-page&quot;&gt;Notes Page&lt;/h3&gt;
&lt;p&gt;Below is a screenshot of the notes page showing the notes for a person in the initialized database:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/flask_connexion_rest_notes_page_part_3.7f146b443cd9.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border &quot; src=&quot;https://files.realpython.com/media/flask_connexion_rest_notes_page_part_3.7f146b443cd9.png&quot; width=&quot;1121&quot; height=&quot;452&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/flask_connexion_rest_notes_page_part_3.7f146b443cd9.png&amp;amp;w=280&amp;amp;sig=a7d9b4813bf1a5fe11fc4cdae7273c46f0ee12b3 280w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/flask_connexion_rest_notes_page_part_3.7f146b443cd9.png&amp;amp;w=560&amp;amp;sig=70be712d19df46579563beb0a077b72810b9cc77 560w, https://files.realpython.com/media/flask_connexion_rest_notes_page_part_3.7f146b443cd9.png 1121w&quot; sizes=&quot;75vw&quot; alt=&quot;Flask Connexion Rest Notes Page Part 3&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The functionality of this page works like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Single-clicking on a note will populate the editor section of the page with the notes content, disabling the &lt;em&gt;Create&lt;/em&gt; button, and enabling the &lt;em&gt;Update&lt;/em&gt; and &lt;em&gt;Delete&lt;/em&gt; buttons. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;All other functionality of this page is in the editor section.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The functionality of the editor works like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If the note content field is empty, then the &lt;em&gt;Create&lt;/em&gt; and &lt;em&gt;Reset&lt;/em&gt; buttons are enabled. Entering a new note in the field and clicking &lt;em&gt;Create&lt;/em&gt; will create a new note and update the database and re-render the table below the editor. Clicking &lt;em&gt;Reset&lt;/em&gt; will clear the editor fields.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the note field has data, the user navigated here by double-clicking the person&amp;rsquo;s note from the home screen. In this case, the &lt;em&gt;Update&lt;/em&gt;, &lt;em&gt;Delete&lt;/em&gt;, and &lt;em&gt;Reset&lt;/em&gt; buttons are enabled. Changing the note and clicking &lt;em&gt;Update&lt;/em&gt; will update the database and re-render the table below the editor. Clicking &lt;em&gt;Delete&lt;/em&gt; will remove the note from the database and re-render the table.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;web-application&quot;&gt;Web Application&lt;/h3&gt;
&lt;p&gt;This article is primarily focused on how to use SQLAlchemy to create relationships in the database, and how to extend the REST API to take advantage of those relationships. As such, the code for the web application didn&amp;rsquo;t get much attention. When you look at the web application code, keep an eye out for the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Each page of the application is a fully formed &lt;a href=&quot;https://en.wikipedia.org/wiki/Single-page_application&quot;&gt;single page web application&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Each page of the application is driven by JavaScript following an &lt;a href=&quot;https://realpython.com/the-model-view-controller-mvc-paradigm-summarized-with-legos/&quot;&gt;MVC&lt;/a&gt; (Model/View/Controller) style of responsibility delegation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The HTML that creates the pages takes advantage of the &lt;a href=&quot;http://jinja.pocoo.org/docs/2.10/templates/#template-inheritance&quot;&gt;Jinja2 inheritance functionality&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The hardcoded JavaScript table creation has been replaced by using the &lt;a href=&quot;https://handlebarsjs.com/&quot;&gt;Handlebars.js&lt;/a&gt; templating engine.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The timestamp formating in all of the tables is provided by &lt;a href=&quot;https://momentjs.com/&quot;&gt;Moment.js&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can find the following code in the repository for this article:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/realpython/materials/tree/master/flask-connexion-rest-part-3/templates&quot;&gt;HTML for the web application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/realpython/materials/tree/master/flask-connexion-rest-part-3/static/css&quot;&gt;CSS for the web application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/realpython/materials/tree/master/flask-connexion-rest-part-3/static/js&quot;&gt;JavaScript for the web application&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of the example code for this article is available in the &lt;a href=&quot;https://github.com/realpython/materials/tree/master/flask-connexion-rest-part-3&quot;&gt;GitHub repository&lt;/a&gt; for this article. This contains all of the code related to this article, including all of the web application code.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Congratulations are in order for what you&amp;rsquo;ve learned in this article! Knowing how to build and use database relationships gives you a powerful tool to solve many difficult problems. There are other relationship besides the one-to-many example from this article. Other common ones are &lt;a href=&quot;https://en.wikipedia.org/wiki/One-to-one_(data_model)&quot;&gt;one-to-one&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Many-to-many_(data_model)&quot;&gt;many-to-many&lt;/a&gt;, and &lt;a href=&quot;https://www.ibm.com/support/knowledgecenter/en/SSWU4L/Data/imc_Data/Data_q_a_watson_assistant/What_is_a_many-to-one_relationship.html&quot;&gt;many-to-one&lt;/a&gt;. All of them have a place in your toolbelt, and SQLAlchemy can help you tackle them all!&lt;/p&gt;
&lt;p&gt;For more information about databases, you can check out &lt;a href=&quot;https://realpython.com/tutorials/databases/&quot;&gt;these tutorials&lt;/a&gt;. You can also &lt;a href=&quot;https://realpython.com/flask-by-example-part-2-postgres-sqlalchemy-and-alembic/&quot;&gt;set up Flask&lt;/a&gt; to use SQLAlchemy. You can check out &lt;a href=&quot;https://realpython.com/the-model-view-controller-mvc-paradigm-summarized-with-legos/&quot;&gt;Model-View-Controller (MVC)&lt;/a&gt; more information about the pattern used in the web application JavaScript code.&lt;/p&gt;
&lt;p&gt;In Part 4 of this series, you&amp;rsquo;ll focus on the HTML, CSS, and JavaScript files used to create the web application.&lt;/p&gt;
&lt;div class=&quot;container py-3 series-nav mb-3&quot;&gt;
  &lt;div class=&quot;row justify-content-between&quot;&gt;
    &lt;div class=&quot;col-12 col-md-3 text-left text-muted ml-1&quot;&gt;&lt;a href=&quot;https://realpython.com/flask-connexion-rest-api-part-2/&quot;&gt; «&amp;nbsp;Part 2: Database Persistence&lt;/a&gt;&lt;/div&gt;
    &lt;div class=&quot;col-12 col-md-3 text-center text-muted&quot;&gt;&lt;a href=&quot;#&quot;&gt;Part 3: Database Relationships&lt;/a&gt;&lt;/div&gt;
    &lt;div class=&quot;col-12 col-md-3 text-right text-muted mr-1&quot;&gt;&lt;a &gt;Part 4: Coming Soon!&amp;nbsp;»&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Python Development in Visual Studio Code (Setup Guide)</title>
      <id>https://realpython.com/courses/python-development-visual-studio-code-setup-guide/</id>
      <link href="https://realpython.com/courses/python-development-visual-studio-code-setup-guide/"/>
      <updated>2019-04-04T14:00:00+00:00</updated>
      <summary>Learn how to set up Visual Studio Code for Python development. By following examples, you&#39;ll cover everything from  installing and configuring VSCode, to running tests and debugging Python apps.</summary>
      <content type="html">
        &lt;p&gt;Learn how to set up Visual Studio Code for Python development. By following examples, you&amp;rsquo;ll cover everything from  installing and configuring VSCode, to running tests and debugging Python apps. Make the most of this powerful Python IDE by following our complete setup guide.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>What Is Pip? A Guide for New Pythonistas</title>
      <id>https://realpython.com/what-is-pip/</id>
      <link href="https://realpython.com/what-is-pip/"/>
      <updated>2019-04-03T14:00:00+00:00</updated>
      <summary>What is pip? In this beginner-friendly tutorial, you&#39;ll learn how to use pip, the standard package manager for Python, so that you can install and manage additional packages that are not part of the Python standard library.</summary>
      <content type="html">
        &lt;p&gt;What is &lt;code&gt;pip&lt;/code&gt;? &lt;a href=&quot;https://pip.pypa.io/en/stable/&quot;&gt;&lt;code&gt;pip&lt;/code&gt;&lt;/a&gt; is the standard package manager for &lt;a href=&quot;https://www.python.org/&quot;&gt;Python&lt;/a&gt;. It allows you to install and manage additional packages that are not part of the &lt;a href=&quot;https://docs.python.org/3/py-modindex.html&quot;&gt;Python standard library&lt;/a&gt;. This tutorial is an introduction to &lt;code&gt;pip&lt;/code&gt; for new Pythonistas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this tutorial, you&amp;rsquo;ll learn about:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Installing additional packages not included with the standard Python distribution&lt;/li&gt;
&lt;li&gt;Finding packages published to the &lt;a href=&quot;https://pypi.org/&quot;&gt;Python Package Index&lt;/a&gt; (&lt;a href=&quot;https://pypi.org/&quot;&gt;PyPI&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Managing requirements for your scripts and applications&lt;/li&gt;
&lt;li&gt;Uninstalling packages and their dependencies&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you&amp;rsquo;ll see, the Python community is very active and has created some neat alternatives to &lt;code&gt;pip&lt;/code&gt; that you&amp;rsquo;ll learn about later in this tutorial.&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-mastery-course&quot; data-focus=&quot;false&quot;&gt;5 Thoughts On Python Mastery&lt;/a&gt;, a free course for Python developers that shows you the roadmap and the mindset you&#39;ll need to take your Python skills to the next level.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;getting-started-with-pip&quot;&gt;Getting Started With &lt;code&gt;pip&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;So, what is pip? &lt;code&gt;pip&lt;/code&gt; is a package manager for Python. That means it&amp;rsquo;s a tool that allows you to install and manage additional libraries and dependencies that are not distributed as part of the standard library. &lt;/p&gt;
&lt;p&gt;Package management is so important that &lt;code&gt;pip&lt;/code&gt; has been included with the Python installer since versions &lt;code&gt;3.4&lt;/code&gt; for Python 3 and &lt;code&gt;2.7.9&lt;/code&gt; for Python 2, and it&amp;rsquo;s used by many Python projects, which makes it an essential tool for every Pythonista.&lt;/p&gt;
&lt;p&gt;The concept of a package manager might be familiar to you if you are coming from other languages. &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript&quot;&gt;JavaScript&lt;/a&gt; uses &lt;a href=&quot;https://www.npmjs.com/&quot;&gt;&lt;code&gt;npm&lt;/code&gt;&lt;/a&gt; for package management, &lt;a href=&quot;https://www.ruby-lang.org/en/&quot;&gt;Ruby&lt;/a&gt; uses &lt;a href=&quot;https://rubygems.org/&quot;&gt;gem&lt;/a&gt;, and &lt;a href=&quot;https://dotnet.microsoft.com/languages&quot;&gt;.NET&lt;/a&gt; use &lt;a href=&quot;https://www.nuget.org/&quot;&gt;NuGet&lt;/a&gt;. In Python, &lt;code&gt;pip&lt;/code&gt; has become the standard package manager.&lt;/p&gt;
&lt;p&gt;The Python installer installs &lt;code&gt;pip&lt;/code&gt;, so it should be ready for you to use, unless you installed an old version of Python. You can verify that &lt;code&gt;pip&lt;/code&gt; is available by running the following command in your console:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip --version

&lt;span class=&quot;go&quot;&gt;pip 18.1 from C:\Python37\lib\site-packages\pip (python 3.7)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You should see a similar output displaying the &lt;code&gt;pip&lt;/code&gt; version, as well as the location and version of Python. If you are using an old version of Python that does not include &lt;code&gt;pip&lt;/code&gt;, then you can install it by following the instructions for your system in the &lt;a href=&quot;https://pip.pypa.io/en/stable/installing/&quot;&gt;&lt;code&gt;pip&lt;/code&gt; installation documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You probably want to follow the examples in this tutorial inside a virtual environment to avoid installing packages to the global Python installation. You can learn about virtual environments in &lt;a href=&quot;https://realpython.com/python-virtual-environments-a-primer/&quot;&gt;Python Virtual Environments: A Primer&lt;/a&gt;. The &lt;a href=&quot;https://realpython.com/python-virtual-environments-a-primer/#using-virtual-environments&quot;&gt;Using Virtual Environments&lt;/a&gt; section of that article explains the basics of creating new virtual environments.&lt;/p&gt;
&lt;h2 id=&quot;installing-packages-with-pip&quot;&gt;Installing Packages With &lt;code&gt;pip&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Python is considered a &lt;a href=&quot;https://www.python.org/dev/peps/pep-0206/#id3&quot;&gt;batteries included&lt;/a&gt; language. This means that the &lt;a href=&quot;https://docs.python.org/3/py-modindex.html&quot;&gt;Python standard library&lt;/a&gt; includes an extensive set of packages and modules to help developers with their scripts and applications.&lt;/p&gt;
&lt;p&gt;At the same time, Python has a very active community that contributes an even bigger set of packages that can help you with your development needs. These packages are published to the &lt;a href=&quot;https://pypi.org/&quot;&gt;Python Package Index&lt;/a&gt;, also known as &lt;a href=&quot;https://pypi.org/&quot;&gt;PyPI&lt;/a&gt; (pronounced &lt;em&gt;Pie Pea Eye&lt;/em&gt;). PyPI hosts an extensive collection of packages that include development frameworks, tools, and libraries. &lt;/p&gt;
&lt;p&gt;Many of these packages simplify Python development by providing friendly interfaces to functionality that already exists in the standard library. For example, you can write a script that retrieves the contents of a web page using only the standard libraries included with Python:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# In using-http.py&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;cgi&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;http.client&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;www.google.com&amp;#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HTTPSConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getresponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;content_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cgi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse_header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;encoding&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;charset&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Response returned: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{response.status}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{response.reason}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;)&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Body:&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this script, you import &lt;code&gt;cgi&lt;/code&gt; and &lt;code&gt;http.client&lt;/code&gt;, both of which are included in the Python standard library. You create an &lt;code&gt;HTTPSConnection&lt;/code&gt; object specifying the server and invoke its &lt;code&gt;.request()&lt;/code&gt; and &lt;code&gt;.getresponse()&lt;/code&gt; to retrieve a response.&lt;/p&gt;
&lt;p&gt;From the response, you can retrieve the &lt;code&gt;Content-Type&lt;/code&gt; header and parse it using the &lt;code&gt;cgi&lt;/code&gt; module to extract the charset in which the page is encoded.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.python.org/3/library/cgi.html#functions&quot;&gt;&lt;code&gt;cgi.parse_header()&lt;/code&gt;&lt;/a&gt; returns a tuple with a main value and a dictionary of parameters. For example, the &lt;code&gt;Content-Type&lt;/code&gt; header might contain a value like &lt;code&gt;text/html; charset=ISO-8859-1&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;The tuple will contain the string &lt;code&gt;text/html&lt;/code&gt; as the first element, and the second element will be a dictionary in the form &lt;code&gt;{&#39;charset&#39;: &#39;ISO-8859-1&#39;}&lt;/code&gt;. Because you only care about the &lt;code&gt;charset&lt;/code&gt; parameter, you can ignore the beginning of the tuple using an underscore: &lt;code&gt;_, params = cgi.parse_header(content_type)&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;a href=&quot;https://dbader.org/blog/meaning-of-underscores-in-python&quot;&gt;The Meaning of Underscores in Python&lt;/a&gt; explains how to use underscores to unpack values from a tuple.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Once you have the encoding of the page, you can read the response and decode it into text. You can run the example in the console to see how it works:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python using-http.py

&lt;span class=&quot;go&quot;&gt;Response returned: 200 (OK)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Body:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;!doctype html&amp;gt;&amp;lt;html itemscope=&amp;quot;&amp;quot; itemtype=&amp;quot;http://schema.org/WebPage&amp;quot; &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;lang=&amp;quot;en&amp;quot;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta content=&amp;quot;Search the world&amp;#39;s information, including &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;webpages, images, videos and more. Google has many special features to help you &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;find exactly what you&amp;#39;re looking for.&amp;quot; name=&amp;quot;description&amp;quot;&amp;gt;&amp;lt;meta content=&amp;quot;noodp&amp;quot; &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;name=&amp;quot;robots&amp;quot;&amp;gt;... Additional Output Omitted&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This seems like a lot of work for a small script that retrieves the contents of a web page. Fortunately, there is a Python package that simplifies HTTP requests and provides a nice interface to do exactly what you want.&lt;/p&gt;
&lt;h3 id=&quot;basic-package-installation&quot;&gt;Basic Package Installation&lt;/h3&gt;
&lt;p&gt;PyPI hosts a very popular library to perform HTTP requests called &lt;a href=&quot;https://pypi.org/project/requests/&quot;&gt;&lt;code&gt;requests&lt;/code&gt;&lt;/a&gt;. You can learn all about it in its official &lt;a href=&quot;http://docs.python-requests.org/en/master/&quot;&gt;documentation site&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;The first step is to install the &lt;code&gt;requests&lt;/code&gt; package into your environment. You can learn about &lt;code&gt;pip&lt;/code&gt; supported commands by running it with &lt;code&gt;help&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip &lt;span class=&quot;nb&quot;&gt;help&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Usage:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  pip &amp;lt;command&amp;gt; [options]&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Commands:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  install                     Install packages.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  download                    Download packages.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  uninstall                   Uninstall packages.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  freeze                      Output installed packages in requirements format.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  list                        List installed packages.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  show                        Show information about installed packages.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  check                       Verify installed packages have compatible &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              dependencies.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  config                      Manage local and global configuration.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  search                      Search PyPI for packages.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  wheel                       Build wheels from your requirements.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  hash                        Compute hashes of package archives.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  completion                  A helper command used for command completion.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  help                        Show help for commands.&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;General Options:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  -h, --help                  Show help.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --isolated                  Run pip in an isolated mode, ignoring environment &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              variables and user configuration.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  -v, --verbose               Give more output. Option is additive, and can be &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              used up to 3 times.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  -V, --version               Show version and exit.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  -q, --quiet                 Give less output. Option is additive, and can be &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              used up to 3 times (corresponding to WARNING, &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              ERROR, and CRITICAL logging levels).&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --log &amp;lt;path&amp;gt;                Path to a verbose appending log.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --proxy &amp;lt;proxy&amp;gt;             Specify a proxy in the form &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              [user:passwd@]proxy.server:port.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --retries &amp;lt;retries&amp;gt;         Maximum number of retries each connection should &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              attempt (default 5 times).&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --timeout &amp;lt;sec&amp;gt;             Set the socket timeout (default 15 seconds).&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --exists-action &amp;lt;action&amp;gt;    Default action when a path already exists: &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              (s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort).&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --trusted-host &amp;lt;hostname&amp;gt;   Mark this host as trusted, even though it does &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              not have valid or any HTTPS.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --cert &amp;lt;path&amp;gt;               Path to alternate CA bundle.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --client-cert &amp;lt;path&amp;gt;        Path to SSL client certificate, a single file &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              containing the private key and the certificate in &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              PEM format.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --cache-dir &amp;lt;dir&amp;gt;           Store the cache data in &amp;lt;dir&amp;gt;.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --no-cache-dir              Disable the cache.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --disable-pip-version-check&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              Don&amp;#39;t periodically check PyPI to determine &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              whether a new version of pip is available for &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              download. Implied with --no-index.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, &lt;code&gt;pip&lt;/code&gt; provides an &lt;code&gt;install&lt;/code&gt; command to install packages. You can run it to install the &lt;code&gt;requests&lt;/code&gt; package:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install requests

&lt;span class=&quot;go&quot;&gt;Looking in indexes: https://pypi.org/simple&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Collecting requests&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Using cached &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  https://files.pythonhosted.org/packages/7d/e3/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  20f3d364d6c8e5d2353c72a67778eb189176f08e873c9900e10c0287b84b/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  requests-2.21.0-py2.py3-none-any.whl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Collecting chardet&amp;lt;3.1.0,&amp;gt;=3.0.2 (from requests)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Using cached https://files.pythonhosted.org/packages/bc/a9/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  chardet-3.0.4-py2.py3-none-any.whl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Collecting idna&amp;lt;2.9,&amp;gt;=2.5 (from requests)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Using cached https://files.pythonhosted.org/packages/14/2c/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  cd551d81dbe15200be1cf41cd03869a46fe7226e7450af7a6545bfc474c9/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  idna-2.8-py2.py3-none-any.whl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Collecting urllib3&amp;lt;1.25,&amp;gt;=1.21.1 (from requests)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Using cached https://files.pythonhosted.org/packages/62/00/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  ee1d7de624db8ba7090d1226aebefab96a2c71cd5cfa7629d6ad3f61b79e/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  urllib3-1.24.1-py2.py3-none-any.whl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Collecting certifi&amp;gt;=2017.4.17 (from requests)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Using cached https://files.pythonhosted.org/packages/9f/e0/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  accfc1b56b57e9750eba272e24c4dddeac86852c2bebd1236674d7887e8a/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  certifi-2018.11.29-py2.py3-none-any.whl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Installing collected packages: chardet, idna, urllib3, certifi, requests&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Successfully installed certifi-2018.11.29 chardet-3.0.4 idna-2.8 &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  requests-2.21.0 urllib3-1.24.1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;You are using pip version 18.1, however version 19.0.1 is available.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;You should consider upgrading via the &amp;#39;python -m pip install --upgrade pip&amp;#39; &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  command.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You should see an output similar to the one above. You use &lt;code&gt;pip&lt;/code&gt; with an &lt;code&gt;install&lt;/code&gt; command followed by the name of the package you want to install. &lt;code&gt;pip&lt;/code&gt; looks for the package in PyPI, calculates its dependencies, and installs them to insure &lt;code&gt;requests&lt;/code&gt; will work.&lt;/p&gt;
&lt;p&gt;You can also see that the current environment is using &lt;code&gt;pip&lt;/code&gt; version &lt;code&gt;18.1&lt;/code&gt;, but version &lt;code&gt;19.0.1&lt;/code&gt; is available. It also shows the command you should use to update &lt;code&gt;pip&lt;/code&gt;, so let&amp;rsquo;s do that:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -m pip install --upgrade pip

&lt;span class=&quot;go&quot;&gt;Looking in indexes: https://pypi.org/simple&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Collecting pip&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Downloading https://files.pythonhosted.org/packages/46/dc/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  7fd5df840efb3e56c8b4f768793a237ec4ee59891959d6a215d63f727023/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  pip-19.0.1-py2.py3-none-any.whl (1.4MB)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    100% |████████████████████████████████| 1.4MB 2.0MB/s&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Installing collected packages: pip&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Found existing installation: pip 18.1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    Uninstalling pip-18.1:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;      Successfully uninstalled pip-18.1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Successfully installed pip-19.0.1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice that you use &lt;code&gt;python -m&lt;/code&gt; to update &lt;code&gt;pip&lt;/code&gt;. The &lt;code&gt;-m&lt;/code&gt; switch tells Python to run a module as an executable. This is necessary because in order for you to update &lt;code&gt;pip&lt;/code&gt;, the old version has to be uninstalled before installing the new version, and removing it while running the tool can cause errors.&lt;/p&gt;
&lt;p&gt;When you run &lt;code&gt;pip&lt;/code&gt; as a module, Python loads the module in memory and allows the package to be removed while it is being used. You can run packages as if they were scripts if the package provides a &lt;a href=&quot;https://docs.python.org/3/library/__main__.html&quot;&gt;top-level script&lt;/a&gt; &lt;code&gt;__main__.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now that you have installed &lt;code&gt;requests&lt;/code&gt; and upgraded &lt;code&gt;pip&lt;/code&gt;, you can use the &lt;code&gt;list&lt;/code&gt; command to see the packages installed in your environment:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip list

&lt;span class=&quot;go&quot;&gt;Package    Version&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;---------- ----------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;certifi    2018.11.29&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;chardet    3.0.4&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;idna       2.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;pip        19.0.1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;requests   2.21.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;setuptools 40.6.2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;urllib3    1.24.1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, &lt;code&gt;pip&lt;/code&gt; has been upgraded to version &lt;code&gt;19.0.1&lt;/code&gt; (the latest version at the moment), and &lt;code&gt;requests&lt;/code&gt; version &lt;code&gt;2.21.0&lt;/code&gt; has been installed.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;pip install &amp;lt;package&amp;gt;&lt;/code&gt; command always looks for the latest version of the package and installs it. It also searches for dependencies listed in the package metadata and installs those dependencies to insure that the package has all the requirements it needs. &lt;/p&gt;
&lt;p&gt;As you can see, multiple packages were installed. You can look at the package metadata by using the &lt;code&gt;show&lt;/code&gt; command in &lt;code&gt;pip&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip show requests

&lt;span class=&quot;go&quot;&gt;Name: requests&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Version: 2.21.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Summary: Python HTTP for Humans.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Home-page: http://python-requests.org&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Author: Kenneth Reitz&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Author-email: me@kennethreitz.org&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;License: Apache 2.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Location: py37\lib\site-packages&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Requires: certifi, chardet, idna, urllib3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Required-by:&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The metadata lists &lt;code&gt;certifi&lt;/code&gt;, &lt;code&gt;chardet&lt;/code&gt;, &lt;code&gt;idna&lt;/code&gt;, and &lt;code&gt;urllib3&lt;/code&gt; as dependencies, and you can see they were also installed.&lt;/p&gt;
&lt;p&gt;With the &lt;code&gt;requests&lt;/code&gt; package installed, you can modify the example above and see how easy it is to retrieve the contents of a web page:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# In using-requests.py&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;https://www.google.com&amp;#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Response returned: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{response.status_code}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{response.reason}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can import the &lt;code&gt;requests&lt;/code&gt; package as any other standard package because it is now installed in your environment. &lt;/p&gt;
&lt;p&gt;As you can see, &lt;code&gt;requests.get()&lt;/code&gt; handles the HTTP connection for you and returns a response object similar to the original example but with some interface improvements. &lt;/p&gt;
&lt;p&gt;You don&amp;rsquo;t have to deal with the encoding of the page because &lt;code&gt;requests&lt;/code&gt; will handle that for you in most situations. Still, &lt;code&gt;requests&lt;/code&gt; provides a flexible interface to handle special cases through the &lt;a href=&quot;http://docs.python-requests.org/en/master/user/quickstart/#response-content&quot;&gt;&lt;code&gt;requests.Response&lt;/code&gt;&lt;/a&gt; object.&lt;/p&gt;
&lt;h3 id=&quot;using-requirement-files&quot;&gt;Using Requirement Files&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;pip install&lt;/code&gt; command always installs the latest published version of a package, but sometimes, you may want to install a specific version that you know works with your code. &lt;/p&gt;
&lt;p&gt;You want to create a specification of the dependencies and versions you used to develop and test your application, so there are no surprises when you use the application in production.&lt;/p&gt;
&lt;p&gt;Requirement files allow you to specify exactly which packages and versions should be installed. Running &lt;code&gt;pip help&lt;/code&gt; shows that there is a &lt;code&gt;freeze&lt;/code&gt; command that outputs the installed packages in requirements format. You can use this command, redirecting the output to a file to generate a requirements file:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip freeze &amp;gt; requirements.txt
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; cat requirements.txt

&lt;span class=&quot;go&quot;&gt;certifi==2018.11.29&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;chardet==3.0.4&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;idna==2.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;requests==2.21.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;urllib3==1.24.1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;freeze&lt;/code&gt; command dumps all the packages and their versions to standard output, so you can redirect the output to a file that can be used to install the exact requirements into another system. The convention is to name this file &lt;code&gt;requirements.txt&lt;/code&gt;, but you can give it any name you want.&lt;/p&gt;
&lt;p&gt;When you want to replicate the environment in another system, you can run &lt;code&gt;pip install&lt;/code&gt; specifying the requirements file using the &lt;code&gt;-r&lt;/code&gt; switch:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install -r requirements.txt

&lt;span class=&quot;go&quot;&gt;Looking in indexes: https://pypi.org/simple&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Collecting certifi==2018.11.29 (from -r .\requirements.txt (line 1))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Using cached https://files.pythonhosted.org/packages/9f/e0/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  accfc1b56b57e9750eba272e24c4dddeac86852c2bebd1236674d7887e8a/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  certifi-2018.11.29-py2.py3-none-any.whl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Collecting chardet==3.0.4 (from -r .\requirements.txt (line 2))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Using cached https://files.pythonhosted.org/packages/bc/a9/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  chardet-3.0.4-py2.py3-none-any.whl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Collecting idna==2.8 (from -r .\requirements.txt (line 3))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Using cached https://files.pythonhosted.org/packages/14/2c/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  cd551d81dbe15200be1cf41cd03869a46fe7226e7450af7a6545bfc474c9/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  idna-2.8-py2.py3-none-any.whl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Collecting requests==2.21.0 (from -r .\requirements.txt (line 4))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Using cached https://files.pythonhosted.org/packages/7d/e3/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  20f3d364d6c8e5d2353c72a67778eb189176f08e873c9900e10c0287b84b/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  requests-2.21.0-py2.py3-none-any.whl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Collecting urllib3==1.24.1 (from -r .\requirements.txt (line 5))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Using cached https://files.pythonhosted.org/packages/62/00/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  ee1d7de624db8ba7090d1226aebefab96a2c71cd5cfa7629d6ad3f61b79e/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  urllib3-1.24.1-py2.py3-none-any.whl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Installing collected packages: certifi, chardet, idna, urllib3, requests&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Successfully installed certifi-2018.11.29 chardet-3.0.4 idna-2.8 &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  requests-2.21.0 urllib3-1.24.1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The versions of the packages will match those listed in &lt;code&gt;requirements.txt&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip list

&lt;span class=&quot;go&quot;&gt;Package    Version&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;---------- ----------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;certifi    2018.11.29&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;chardet    3.0.4&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;idna       2.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;pip        19.0.1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;requests   2.21.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;setuptools 40.6.2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;urllib3    1.24.1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can submit the &lt;code&gt;requirements.txt&lt;/code&gt; file into source control and use it to create the exact environment in other machines.&lt;/p&gt;
&lt;h3 id=&quot;fine-tuning-requirements&quot;&gt;Fine-Tuning Requirements&lt;/h3&gt;
&lt;p&gt;The problem with &lt;strong&gt;hardcoding&lt;/strong&gt; the versions of your packages and their dependencies is that packages are updated frequently with bug and security fixes, and you probably want to leverage those as soon as they are published.&lt;/p&gt;
&lt;p&gt;The requirements file format allows you to specify dependency versions using logical operators that give you a bit of flexibility to insure packages are updated, but still define the base versions of a package.&lt;/p&gt;
&lt;p&gt;Open the &lt;code&gt;requirements.txt&lt;/code&gt; file in your favorite &lt;a href=&quot;https://realpython.com/python-ides-code-editors-guide/&quot;&gt;editor&lt;/a&gt; and make the following changes:&lt;/p&gt;
&lt;div class=&quot;highlight pyreq&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;certifi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2018.11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;29&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;chardet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;idna&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.8&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.21&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;urllib3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.24&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can change the logical operator to &lt;code&gt;&amp;gt;=&lt;/code&gt; to tell &lt;code&gt;pip&lt;/code&gt; to install an exact or greater version that has been published. When you set a new environment using the &lt;code&gt;requirments.txt&lt;/code&gt; file, &lt;code&gt;pip&lt;/code&gt; looks for the latest version that satisfies the requirement and installs it. You can upgrade the packages in your requirements file by running the &lt;code&gt;install&lt;/code&gt; command with the &lt;code&gt;--upgrade&lt;/code&gt; switch:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install --upgrade -r requirements.txt

&lt;span class=&quot;go&quot;&gt;Looking in indexes: https://pypi.org/simple&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Requirement already up-to-date: certifi==2018.11.29 in \py37\lib\site-packages &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  (from -r .\requirements.txt (line 1)) (2018.11.29)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Requirement already up-to-date: chardet==3.0.4 in \py37\lib\site-packages &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  (from -r .\requirements.txt (line 2)) (3.0.4)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Requirement already up-to-date: idna==2.8 in \py37\lib\site-packages &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  (from -r .\requirements.txt (line 3)) (2.8)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Requirement already up-to-date: requests==2.21.0 in \py37\lib\site-packages &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  (from -r .\requirements.txt (line 4)) (2.21.0)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Requirement already up-to-date: urllib3==1.24.1 in \py37\lib\site-packages &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  (from -r .\requirements.txt (line 5)) (1.24.1)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Nothing was upgraded because you have the latest versions, but if a new version was published for a listed package, then the package would&amp;rsquo;ve been upgraded.&lt;/p&gt;
&lt;p&gt;In an ideal world, new versions of packages would be backwards compatible and would never introduce new bugs. Unfortunately, new versions can introduce changes that will break your application. The requirements file syntax supports additional &lt;a href=&quot;https://www.python.org/dev/peps/pep-0440/#version-specifiers&quot;&gt;version specifiers&lt;/a&gt; to fine-tune your requirements.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s say that a new version &lt;code&gt;3.0&lt;/code&gt; of &lt;code&gt;requests&lt;/code&gt; is published but introduces an incompatible change that breaks your application. You can modify the requirements file to prevent &lt;code&gt;3.0&lt;/code&gt; or higher from being installed:&lt;/p&gt;
&lt;div class=&quot;highlight pyreq&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;certifi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2018.11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;29&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;chardet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;idna&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.8&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.21&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;urllib3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.24&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Changing the version specifier for the &lt;code&gt;requests&lt;/code&gt; package ensures that any version greater or equal to &lt;code&gt;3.0&lt;/code&gt; does not get installed. The &lt;code&gt;pip&lt;/code&gt; documentation provides all the information about the &lt;a href=&quot;https://pip.pypa.io/en/stable/reference/pip_install/#requirements-file-format&quot;&gt;requirements file format&lt;/a&gt;, and you can consult it to learn more about it.&lt;/p&gt;
&lt;h3 id=&quot;production-vs-development-dependencies&quot;&gt;Production vs Development Dependencies&lt;/h3&gt;
&lt;p&gt;Not all packages that you install during the development of your applications are going to be application dependencies. There are many packages published to PyPI that are development tools or libraries that you want to leverage during the development process.&lt;/p&gt;
&lt;p&gt;As an example, you&amp;rsquo;ll probably want to unit test your application, so you need a unit test framework. A popular framework for unit testing is &lt;a href=&quot;https://docs.pytest.org/en/latest/&quot;&gt;&lt;code&gt;pytest&lt;/code&gt;&lt;/a&gt;. You want to install it in your development environment, but you do not want it in your production environment because it isn&amp;rsquo;t an application dependency.&lt;/p&gt;
&lt;p&gt;You create a second requirements file (&lt;code&gt;requirements_dev.txt&lt;/code&gt;) to list additional tools to set up a development environment:&lt;/p&gt;
&lt;div class=&quot;highlight pyreq&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# In requirements_dev.txt&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pytest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;4.2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This requires you to use &lt;code&gt;pip&lt;/code&gt; to install both requirement files: &lt;code&gt;requirements.txt&lt;/code&gt; and &lt;code&gt;requirements_dev.txt&lt;/code&gt;. Fortunately, pip allows you to specify additional parameters within a requirements file. You can modify &lt;code&gt;requirements_dev.txt&lt;/code&gt; to also install the requirements from the production &lt;code&gt;requirements.txt&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&quot;highlight pyreq&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# In requirements_dev.txt&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;txt&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pytest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;4.2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice that you are using the exact same &lt;code&gt;-r&lt;/code&gt; switch to install the production &lt;code&gt;requirements.txt&lt;/code&gt; file. The &lt;a href=&quot;https://pip.pypa.io/en/stable/reference/pip_install/#requirements-file-format&quot;&gt;requirements file format&lt;/a&gt; allows you to specify additional arguments right on a requirements file.&lt;/p&gt;
&lt;h3 id=&quot;freezing-requirements-for-production&quot;&gt;Freezing Requirements for Production&lt;/h3&gt;
&lt;p&gt;You created the production and development requirement files and added them to source control. The files use flexible version specifiers to ensure that you leverage bug fixes published by your dependencies. You are also testing your application and are ready to deploy it to production.&lt;/p&gt;
&lt;p&gt;You probably want to ensure that the versions of the dependencies you deploy to production are the exact same versions you used in your integration pipeline or build process because you know all the tests pass and the application works.&lt;/p&gt;
&lt;p&gt;The current version specifiers don&amp;rsquo;t guarantee that the same versions will be deployed to production, so you want to freeze the production requirements as you saw earlier.&lt;/p&gt;
&lt;p&gt;You create a clean production virtual environment and install the production requirements using the &lt;code&gt;requirements.txt&lt;/code&gt; file. Once the requirements are installed, you can freeze the specific versions, dumping the output to a &lt;code&gt;requirements_lock.txt&lt;/code&gt; file that you use in production. The &lt;code&gt;requirements_lock.txt&lt;/code&gt; file will contain exact versions specifiers and can be used to replicate the environment.&lt;/p&gt;
&lt;h2 id=&quot;finding-packages-to-use&quot;&gt;Finding Packages to Use&lt;/h2&gt;
&lt;p&gt;As you become a more experienced Pythonista, there&amp;rsquo;ll be a set of packages that you&amp;rsquo;ll know by heart and that you&amp;rsquo;ll use in most of your applications. The &lt;code&gt;requests&lt;/code&gt; and &lt;code&gt;pytest&lt;/code&gt; packages are good candidates to become useful tools in your Python toolbox.&lt;/p&gt;
&lt;p&gt;There will be times though when you will need to solve a different problem, and you will want to look for a different tool or library that can help you with it. As you can see above, &lt;code&gt;pip help&lt;/code&gt; shows that there is a &lt;code&gt;search&lt;/code&gt; command that looks for packages published to PyPI.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see how this command can help us:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip &lt;span class=&quot;nb&quot;&gt;help&lt;/span&gt; search

&lt;span class=&quot;go&quot;&gt;Usage:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  pip search [options] &amp;lt;query&amp;gt;&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Description:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Search for PyPI packages whose name or summary contains &amp;lt;query&amp;gt;.&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Search Options:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  -i, --index &amp;lt;url&amp;gt;           Base URL of Python Package Index &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              (default https://pypi.org/pypi)&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;General Options:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  -h, --help                  Show help.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --isolated                  Run pip in an isolated mode, ignoring environment &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              variables and user configuration.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  -v, --verbose               Give more output. Option is additive, and can be &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              used up to 3 times.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  -V, --version               Show version and exit.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  -q, --quiet                 Give less output. Option is additive, and can be &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              used up to 3 times (corresponding to WARNING, &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              ERROR, and CRITICAL logging levels).&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --log &amp;lt;path&amp;gt;                Path to a verbose appending log.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --proxy &amp;lt;proxy&amp;gt;             Specify a proxy in the form &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              [user:passwd@]proxy.server:port.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --retries &amp;lt;retries&amp;gt;         Maximum number of retries each connection should &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              attempt (default 5 times).&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --timeout &amp;lt;sec&amp;gt;             Set the socket timeout (default 15 seconds).&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --exists-action &amp;lt;action&amp;gt;    Default action when a path already exists: &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              (s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort).&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --trusted-host &amp;lt;hostname&amp;gt;   Mark this host as trusted, even though it does &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              not have valid or any HTTPS.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --cert &amp;lt;path&amp;gt;               Path to alternate CA bundle.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --client-cert &amp;lt;path&amp;gt;        Path to SSL client certificate, a single file &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              containing the private key and the certificate in &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              PEM format.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --cache-dir &amp;lt;dir&amp;gt;           Store the cache data in &amp;lt;dir&amp;gt;.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --no-cache-dir              Disable the cache.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --disable-pip-version-check&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              Don&amp;#39;t periodically check PyPI to determine &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              whether a new version of pip is available for &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                              download. Implied with --no-index.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  --no-color                  Suppress colored output&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The command takes a set of options listed above and a &lt;code&gt;&amp;lt;query&amp;gt;&lt;/code&gt;. The query is just a string to search for and will match packages and their descriptions.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can use &lt;code&gt;pip help &amp;lt;command&amp;gt;&lt;/code&gt; to retrieve additional information about a supported command.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Let&amp;rsquo;s say your application needs to access a service that is using &lt;a href=&quot;https://oauth.net/2/&quot;&gt;OAuth2&lt;/a&gt; for authorization. Ideally, there is a library that works with &lt;code&gt;requests&lt;/code&gt; or with a similar interface that can help us. Let&amp;rsquo;s search PyPI for it using &lt;code&gt;pip&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip search requests oauth

&lt;span class=&quot;go&quot;&gt;requests-oauth (0.4.1)             - Hook for adding Open Authentication &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                                     support to Python-requests HTTP library.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;oauth (1.0.1)                      - Library for OAuth version 1.0a.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;pmr2.oauth (0.6.1)                 - OAuth PAS Plugin, OAuth 1.0 provider for &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                                     Plone.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;oauth-proxy (1.0.5)                - OAuth HTTP proxy&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;django-oauth (1.1)                 - Support of OAuth in Django.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;intuit-oauth (1.2.0)               - Intuit OAuth Client&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;brubeck-oauth (0.1.11)             - Brubeck OAuth module&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;guillotina-oauth (2.0.0)           - guillotina oauth support&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;httpie-oauth (1.0.2)               - OAuth plugin for HTTPie.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;paytm-oauth (0.2)                  - Consumer for paytm oauth&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;plurk-oauth (0.9.2)                - Plurk OAuth API&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;oauth-flow (1.0.3)                 - Authenticate and make calls to OAuth 1.0, &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                                     OAuth 2.0 services&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;... Additional Output Omitted&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The search term yields quite an extensive collection of packages. Some of them seem specific to a service or technology like &lt;code&gt;django-oauth&lt;/code&gt;. Others look promising, like &lt;code&gt;requests-oauth&lt;/code&gt;. Unfortunately, there isn&amp;rsquo;t much information other than a brief description.&lt;/p&gt;
&lt;p&gt;Most of the time, you want to search for packages directly in the &lt;a href=&quot;https://pypi.org/&quot;&gt;PyPI&lt;/a&gt; website. PyPI provides search capabilities for its index and a way to filter results by the metadata exposed in the package, like framework, topic, development status, and so on. &lt;/p&gt;
&lt;p&gt;A search for the same terms in PyPI yields a lot of results, but you can filter them by different categories. For example, you can expand the &lt;em&gt;Intended Audience&lt;/em&gt; and select &lt;em&gt;Developers&lt;/em&gt; since you want a library that helps you with developing your application. Also, you probably want a package that is stable and production-ready. You can expand the &lt;em&gt;Development Status&lt;/em&gt; category and select &lt;em&gt;Production/Stable&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/search_results.1151d72a4b9b.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border &quot; src=&quot;https://files.realpython.com/media/search_results.1151d72a4b9b.png&quot; width=&quot;1473&quot; height=&quot;1471&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/search_results.1151d72a4b9b.png&amp;amp;w=368&amp;amp;sig=32d42daff402d9580b436e990f95d6e7df56aa04 368w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/search_results.1151d72a4b9b.png&amp;amp;w=736&amp;amp;sig=542bcb234bf41daed37fb0ced65cf2527f16b0f3 736w, https://files.realpython.com/media/search_results.1151d72a4b9b.png 1473w&quot; sizes=&quot;75vw&quot; alt=&quot;PyPi Search Results for Requests OAuth&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can apply additional filters and tweak the search terms until you find the package that you are looking for.&lt;/p&gt;
&lt;p&gt;The results provide a link to the package page, which contains more information and hopefully some documentation. Let&amp;rsquo;s take a look at the information for &lt;code&gt;requests-oauth2&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/request_oauth_page.a5c341a27a69.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border &quot; src=&quot;https://files.realpython.com/media/request_oauth_page.a5c341a27a69.png&quot; width=&quot;1474&quot; height=&quot;886&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/request_oauth_page.a5c341a27a69.png&amp;amp;w=368&amp;amp;sig=9004835381d13f007cf24fa1c54b1d1058a9fcbc 368w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/request_oauth_page.a5c341a27a69.png&amp;amp;w=737&amp;amp;sig=d02dbc0511342565f109618fa63d91e919569b2b 737w, https://files.realpython.com/media/request_oauth_page.a5c341a27a69.png 1474w&quot; sizes=&quot;75vw&quot; alt=&quot;PyPi Package Page for Requests OAuth 2&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The project page provides more information, and it seems to have a link to the project homepage. The link takes you to the &lt;a href=&quot;https://github.com/maraujop/requests-oauth2&quot;&gt;project repository&lt;/a&gt; on GitHub. There, you can see some more information about the project and some usage examples.&lt;/p&gt;
&lt;p&gt;Finding the original source code repository can be an invaluable resource. There, you can find some hints about the status of the project by looking at the date of the latest commits, number of pull request and open issues, and so forth.&lt;/p&gt;
&lt;p&gt;Another option to find a package is to Google it. Widely used Python libraries will show up at the top of google searches, and you should be able to find a link to the package in PyPI or its source code repository.&lt;/p&gt;
&lt;p&gt;Finding the right package may take some time and research, but it will also speed up your development process once you find it.&lt;/p&gt;
&lt;h2 id=&quot;uninstalling-packages&quot;&gt;Uninstalling Packages&lt;/h2&gt;
&lt;p&gt;Once in a while, you will have to uninstall a package. You either found a better library to replace it, or it is something you don&amp;rsquo;t really need. Uninstalling packages can be a bit tricky.&lt;/p&gt;
&lt;p&gt;Notice that, when you installed &lt;code&gt;requests&lt;/code&gt;, &lt;code&gt;pip&lt;/code&gt; installed other dependencies too. The more packages you install, the bigger the chances that multiple packages depend on the same dependency. This is where the &lt;code&gt;show&lt;/code&gt; command in &lt;code&gt;pip&lt;/code&gt; comes in handy.&lt;/p&gt;
&lt;p&gt;Before you uninstall a package, make sure you run the &lt;code&gt;show&lt;/code&gt; command for that package:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip show requests

&lt;span class=&quot;go&quot;&gt;Name: requests&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Version: 2.21.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Summary: Python HTTP for Humans.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Home-page: http://python-requests.org&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Author: Kenneth Reitz&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Author-email: me@kennethreitz.org&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;License: Apache 2.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Location: c:\users\isaac\projects\virtualenv\py37\lib\site-packages&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Requires: urllib3, certifi, chardet, idna&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Required-by:&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice the last two fields &lt;code&gt;Requires&lt;/code&gt; and &lt;code&gt;Required-by&lt;/code&gt;. The &lt;code&gt;show&lt;/code&gt; command tells us that &lt;code&gt;requests&lt;/code&gt; requires &lt;code&gt;urllib3&lt;/code&gt;, &lt;code&gt;certifi&lt;/code&gt;, &lt;code&gt;chardet&lt;/code&gt;, and &lt;code&gt;idna&lt;/code&gt;. You probably want to uninstall those two. You can also see that &lt;code&gt;requests&lt;/code&gt; is not required by any other package, so it is safe to uninstall it.&lt;/p&gt;
&lt;p&gt;You should run the &lt;code&gt;show&lt;/code&gt; command against all of the &lt;code&gt;requests&lt;/code&gt; dependencies to make sure that no other libraries also depend on them. Once you understand the dependency order of the packages you want to uninstall, you can remove them using the &lt;code&gt;uninstall&lt;/code&gt; command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip uninstall certifi

&lt;span class=&quot;go&quot;&gt;Uninstalling certifi-2018.11.29:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Would remove:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    \py37\lib\site-packages\certifi-2018.11.29.dist-info\*&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    \py37\lib\site-packages\certifi\*&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Proceed (y/n)? y&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Successfully uninstalled certifi-2018.11.29&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Uninstalling a package shows you the files that will be removed and will ask for confirmation. If you are sure you want to remove the package because you&amp;rsquo;ve checked its dependencies and know that nothing else is using it, you can pass a &lt;code&gt;-y&lt;/code&gt; switch to suppress the file list and confirmation:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip uninstall urllib3 -y

&lt;span class=&quot;go&quot;&gt;Uninstalling urllib3-1.24.1:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Successfully uninstalled urllib3-1.24.1&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip uninstall chardet -y

&lt;span class=&quot;go&quot;&gt;Uninstalling chardet-3.0.4:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Successfully uninstalled chardet-3.0.4&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip uninstall idna -y

&lt;span class=&quot;go&quot;&gt;Uninstalling idna-2.8:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Successfully uninstalled idna-2.8&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip uninstall requests -y

&lt;span class=&quot;go&quot;&gt;Uninstalling requests-2.21.0:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Successfully uninstalled requests-2.21.0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can specify all the packages you want to uninstall in a single call: &lt;code&gt;pip uninstall -y urllib3 chardet idna requests&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can also uninstall all the packages listed in a requirements file by providing the &lt;code&gt;-r &amp;lt;requirments file&amp;gt;&lt;/code&gt; option. The command will ask confirmation for each individual package, but you can suppress it, if you know what you&amp;rsquo;re doing, with the &lt;code&gt;-y&lt;/code&gt; switch:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip uninstall -r requirements.txt -y

&lt;span class=&quot;go&quot;&gt;Uninstalling certifi-2018.11.29:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Successfully uninstalled certifi-2018.11.29&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Uninstalling chardet-3.0.4:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Successfully uninstalled chardet-3.0.4&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Uninstalling idna-2.8:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Successfully uninstalled idna-2.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Uninstalling requests-2.21.0:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Successfully uninstalled requests-2.21.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Uninstalling urllib3-1.24.1:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Successfully uninstalled urllib3-1.24.1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Remember to always check the dependencies of packages you want to uninstall. You probably want to uninstall all its dependencies, but uninstalling a package that is being used by others will break your application.&lt;/p&gt;
&lt;h2 id=&quot;alternatives-to-pip&quot;&gt;Alternatives to &lt;code&gt;pip&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;pip&lt;/code&gt; is an essential tool for all Pythonistas, and it is used by many applications and projects for package management. This tutorial has helped you with the basics, but the Python community is very active in providing great tools and libraries for other developers to use. These include other alternatives to &lt;code&gt;pip&lt;/code&gt; that try to simplify and improve package management.&lt;/p&gt;
&lt;p&gt;In this section, you&amp;rsquo;ll learn about other package management tools available for Python.&lt;/p&gt;
&lt;h3 id=&quot;conda-does-it-all&quot;&gt;Conda Does It All&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://conda.io/en/latest/&quot;&gt;Conda&lt;/a&gt; is a package, dependency, and environment manager for many languages including Python. In fact, its origin comes from &lt;a href=&quot;https://www.anaconda.com/&quot;&gt;Anaconda&lt;/a&gt;, which started as a data science package for Python.&lt;/p&gt;
&lt;p&gt;Conda is widely used for data science and machine learning applications, and uses its own &lt;a href=&quot;https://repo.continuum.io/&quot;&gt;index&lt;/a&gt; to host compatible packages. &lt;/p&gt;
&lt;p&gt;Conda not only allows you to manage package dependencies, but it also manages virtual environments for your applications, installs compatible Python distributions, and packages your application for deployment to production.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://realpython.com/python-windows-machine-learning-setup/&quot;&gt;Setting Up Python for Machine Learning on Windows&lt;/a&gt; is a great introduction to Conda that explores package and environment management. The only Windows-specific information is around installation, so it&amp;rsquo;s still relevant if you use a different OS platform.&lt;/p&gt;
&lt;h3 id=&quot;pipenv&quot;&gt;Pipenv&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://pipenv.readthedocs.io/en/latest/&quot;&gt;Pipenv&lt;/a&gt; is another package management tool that &amp;ldquo;aims to bring the best of all packaging worlds&amp;rdquo; to Python. It&amp;rsquo;s gaining a lot of traction among the Python community because it merges virtual environment and package management in a single tool.&lt;/p&gt;
&lt;p&gt;It also solves some of the most common hiccups you will run into when manually managing dependencies through &lt;code&gt;pip&lt;/code&gt;, like versions of packages, separating development and production dependencies, and locking versions for production.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://realpython.com/pipenv-guide/&quot;&gt;Pipenv: A Guide to the New Python Packaging Tool&lt;/a&gt; is a great start to learn about Pipenv and its approach to package management. Even though the article is tagged as &lt;code&gt;intermediate&lt;/code&gt;, the author does a great job of guiding the reader that the article is accessible to anyone starting with Python.&lt;/p&gt;
&lt;h3 id=&quot;poetry&quot;&gt;Poetry&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://poetry.eustace.io/&quot;&gt;Poetry&lt;/a&gt; is another &lt;code&gt;pip&lt;/code&gt; alternative that is gaining a lot of traction. Like Pipenv, it simplifies package version management and separates development vs production dependencies, and it works by isolating those dependencies into a virtual environment.&lt;/p&gt;
&lt;p&gt;If you are coming from &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript&quot;&gt;JavaScript&lt;/a&gt; and &lt;a href=&quot;https://www.npmjs.com/&quot;&gt;npm&lt;/a&gt;, then Poetry will look very familiar. It goes beyond package management, helping you build distributions for your applications and libraries and deploying them to PyPI. &lt;a href=&quot;https://realpython.com/pypi-publish-python-package/&quot;&gt;How to Publish an Open-Source Python Package to PyPI&lt;/a&gt; has a good &lt;a href=&quot;https://realpython.com/pypi-publish-python-package/#poetry&quot;&gt;introduction to Poetry&lt;/a&gt; and can help you get started.&lt;/p&gt;
&lt;h2 id=&quot;conclusion-what-is-pip&quot;&gt;Conclusion: What Is &lt;code&gt;pip&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;This tutorial answered the question, what is pip? You&amp;rsquo;ve seen that &lt;code&gt;pip&lt;/code&gt; is a package manager for Python, used by many projects to manage dependencies. It&amp;rsquo;s included with the Python installer, which makes it an essential tool for all Pythonistas to know how to use. &lt;/p&gt;
&lt;p&gt;Python provides an extensive standard library suitable for developing all sorts of applications, but the active Python community provides an even larger set of tools and libraries that speed up Python application development. &lt;/p&gt;
&lt;p&gt;These tools and libraries are published to the &lt;a href=&quot;https://pypi.org/&quot;&gt;Python Package Index&lt;/a&gt; (PyPI), and &lt;code&gt;pip&lt;/code&gt; allows developers to install them in their application environments.&lt;/p&gt;
&lt;p&gt;In this tutorial, you&amp;rsquo;ve learned about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Installing new packages using &lt;code&gt;pip&lt;/code&gt; in the command line and with requirement files&lt;/li&gt;
&lt;li&gt;Managing dependencies, separating development and production requirements, and creating a locked requirements file&lt;/li&gt;
&lt;li&gt;Finding packages through &lt;code&gt;pip&lt;/code&gt; and PyPI&lt;/li&gt;
&lt;li&gt;Evaluating package dependencies before uninstalling a package and how &lt;code&gt;pip&lt;/code&gt; uninstalls packages&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition, you&amp;rsquo;ve learned about the importance of keeping dependencies up to date and alternatives to &lt;code&gt;pip&lt;/code&gt; that can help you manage those dependencies.&lt;/p&gt;
&lt;p&gt;Feel free to reach out in the comments section below with any questions you might have, and you can always get more information at the &lt;a href=&quot;https://pip.pypa.io/en/stable/&quot;&gt;&lt;code&gt;pip&lt;/code&gt; documentation page&lt;/a&gt;.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Idiomatic Pandas: Tricks &amp; Features You May Not Know</title>
      <id>https://realpython.com/courses/idiomatic-pandas-tricks-features-you-may-not-know/</id>
      <link href="https://realpython.com/courses/idiomatic-pandas-tricks-features-you-may-not-know/"/>
      <updated>2019-04-02T14:00:00+00:00</updated>
      <summary>In this course you&#39;ll see how to use some lesser-used but idiomatic Pandas capabilities that lend your code better readability, versatility, and speed.</summary>
      <content type="html">
        &lt;p&gt;Pandas is a foundational library for analytics, data processing, and data science. It&amp;rsquo;s a huge project with tons of optionality and depth.&lt;/p&gt;
&lt;p&gt;In this course you&amp;rsquo;ll see how to use some lesser-used but idiomatic Pandas capabilities that lend your code better readability, versatility, and speed.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Get Started With Django Part 1: Build a Portfolio App</title>
      <id>https://realpython.com/get-started-with-django-1/</id>
      <link href="https://realpython.com/get-started-with-django-1/"/>
      <updated>2019-04-01T14:00:00+00:00</updated>
      <summary>In this step-by-step tutorial, you&#39;ll learn the basics of creating powerful web applications with Django, a Python web framework. You&#39;ll build a portfolio application to showcase your web development projects, complete with a fully functioning blog.</summary>
      <content type="html">
        &lt;p&gt;Django is a fully featured Python web framework that can be used to build complex web applications. In this tutorial, you&amp;rsquo;ll jump in and learn &lt;a href=&quot;https://realpython.com/tutorials/django/&quot;&gt;Django&lt;/a&gt; by example. You&amp;rsquo;ll follow the steps to create a fully functioning web application and, along the way, learn some of the most important features of the framework and how they work together.&lt;/p&gt;
&lt;p&gt;In later posts in this series, you&amp;rsquo;ll see how to build more complex websites using even more of Django&amp;rsquo;s features than you&amp;rsquo;ll cover in this tutorial.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;By the end of this tutorial, you will be able to:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Understand what Django is and why it&amp;rsquo;s a great web framework&lt;/li&gt;
&lt;li&gt;Understand the architecture of a Django site and how it compares with other frameworks&lt;/li&gt;
&lt;li&gt;Set up a new Django project and app&lt;/li&gt;
&lt;li&gt;Build a Personal Portfolio Website with Django&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;#&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-django-resources-learing-guide&quot; data-focus=&quot;false&quot;&gt;Click here to get access to a free Django Learning Resources Guide (PDF)&lt;/a&gt; that shows you tips and tricks as well as common pitfalls to avoid when building Python + Django web applications.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;why-you-should-learn-django&quot;&gt;Why You Should Learn Django&lt;/h2&gt;
&lt;p&gt;There are endless web development frameworks out there, so why should you learn Django over any of the others? First of all, it&amp;rsquo;s written in Python, one of the most readable and beginner-friendly programming languages out there.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This tutorial assumes an intermediate knowledge of the Python language. If you&amp;rsquo;re new to programming with Python, check out some of our &lt;a href=&quot;https://realpython.com/tutorials/basics/&quot;&gt;beginner tutorials&lt;/a&gt; or the &lt;a href=&quot;https://realpython.com/products/real-python-course/&quot;&gt;introductory course&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The second reason you should learn Django is the scope of its features. If you need to build a website, you don&amp;rsquo;t need to rely on any external libraries or packages if you choose Django. This means that you don&amp;rsquo;t need to learn how to use anything else, and the syntax is seamless as you&amp;rsquo;re using only one framework.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also the added benefit that you don&amp;rsquo;t need to worry that updating one library or framework will render others that you&amp;rsquo;ve installed useless.&lt;/p&gt;
&lt;p&gt;If you do find yourself needing to add extra features, there are a range of external libraries that you can use to enhance your site.&lt;/p&gt;
&lt;p&gt;One of the great things about the Django framework is its &lt;a href=&quot;https://www.djangoproject.com/&quot;&gt;in-depth documentation&lt;/a&gt;. It has detailed documentation on every aspect of Django and also has great examples and even a tutorial to get you started.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also a fantastic community of Django developers, so if you get stuck there&amp;rsquo;s almost always a way forward by either checking the docs or &lt;a href=&quot;https://stackoverflow.com/questions/tagged/django&quot;&gt;asking the community&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Django is a high-level web application framework with loads of features. It&amp;rsquo;s great for anyone new to web development due to its fantastic documentation, and particularly if you&amp;rsquo;re also familiar with Python.&lt;/p&gt;
&lt;h2 id=&quot;the-structure-of-a-django-website&quot;&gt;The Structure of a Django Website&lt;/h2&gt;
&lt;p&gt;A Django website consists of a single &lt;strong&gt;project&lt;/strong&gt; that is split into separate &lt;strong&gt;apps&lt;/strong&gt;. The idea is that each app handles a self-contained function that the site needs to perform. As an example, imagine an application like Instagram. There are several different functions that need to be performed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;User management&lt;/strong&gt;: Login, logout, register, and so on&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The image feed&lt;/strong&gt;: Uploading, editing, and displaying images&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Private messaging&lt;/strong&gt;: Private messages between users and notifications&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are each separate pieces of functionality, so if this were a Django site, then each piece of functionality should be a different Django app inside a single Django project.&lt;/p&gt;
&lt;p&gt;The Django project holds some configurations that apply to the project as a whole, such as project settings, URLs, shared templates and static files. Each application can have its own database and has its own functions to control how the data is displayed to the user in HTML templates.&lt;/p&gt;
&lt;p&gt;Each application also has its own URLs as well as its own HTML templates and static files, such as JavaScript and CSS.&lt;/p&gt;
&lt;p&gt;Django apps are structured so that there is a separation of logic. It supports the Model-View-Controller Pattern, which is the architecture on which most web frameworks are built. The basic principle is that in each application there are three separate files that handle the three main pieces of logic separately:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; defines the data structure. This is usually a database and is the base layer to an application.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;View&lt;/strong&gt; displays some or all of the data to the user with HTML and CSS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Controller&lt;/strong&gt; handles how the database and the view interact.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to learn more about the MVC pattern, then check out &lt;a href=&quot;https://realpython.com/the-model-view-controller-mvc-paradigm-summarized-with-legos/&quot;&gt;Model-View-Controller (MVC) Explained – With Legos&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In Django, the architecture is slightly different. Although based upon the MVC pattern, Django handles the controller part itself. There&amp;rsquo;s no need to define how the database and views interact. It&amp;rsquo;s all done for you!&lt;/p&gt;
&lt;p&gt;The pattern Django utilizes is called the Model-View-Tempalate (MVT) pattern. The view and template in the MVT pattern make up the view in the MVC pattern. All you need to do is add some URL configurations to map the views to, and Django handles the rest!&lt;/p&gt;
&lt;p&gt;A Django site starts off as a project and is built up with a number of applications that each handle separate functionality. Each app follows the Model-View-Template pattern. Now that you&amp;rsquo;re familiar with the structure of a Django site, let&amp;rsquo;s have a look at what you&amp;rsquo;re going to build!&lt;/p&gt;
&lt;h2 id=&quot;what-youre-going-to-build&quot;&gt;What You&amp;rsquo;re Going to Build&lt;/h2&gt;
&lt;p&gt;Before you get started with any web development project, it&amp;rsquo;s a good idea to come up with a plan of what you&amp;rsquo;re going to build. In this tutorial, we are going to build an application with the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A fully functioning blog&lt;/strong&gt;: If you&amp;rsquo;re looking to demonstrate your coding ability, a blog is a great way to do that. In this application, you will be able to create, update, and delete blog posts. Posts will have categories that can be used to sort them. Finally, users will be able to leave comments on posts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A portfolio of your work&lt;/strong&gt;: You can showcase previous &lt;a href=&quot;https://realpython.com/tutorials/web-dev/&quot;&gt;web development&lt;/a&gt; projects here. You&amp;rsquo;ll build a gallery style page with clickable links to projects that you&amp;rsquo;ve completed.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Before you get started, you can pull down the &lt;a href=&quot;https://github.com/realpython/materials/tree/master/rp-portfolio&quot;&gt;source code&lt;/a&gt; and follow along with the tutorial.&lt;/p&gt;
&lt;p&gt;If you prefer to follow along by writing the code yourself, don&amp;rsquo;t worry. I&amp;rsquo;ve referenced the relevant parts of the source code throughout so you can refer back to it.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;We won&amp;rsquo;t be using any external Python libraries in this tutorial. One of the great things about Django is that it has so many features that you don&amp;rsquo;t need to rely on external libraries. However, we will add &lt;a href=&quot;https://getbootstrap.com/&quot;&gt;Bootstrap 4&lt;/a&gt; styling in the templates.&lt;/p&gt;
&lt;p&gt;By building these two apps, you&amp;rsquo;ll learn the basics of Django models, view functions, forms, templates, and the Django admin page. With knowledge of these features, you&amp;rsquo;ll be able to go away and build loads more applications. You&amp;rsquo;ll also have the tools to learn even more and build sophisticated Django sites.&lt;/p&gt;
&lt;h2 id=&quot;hello-world&quot;&gt;Hello, World!&lt;/h2&gt;
&lt;p&gt;Now that you know the structure of a Django application, and what you are about to build, we&amp;rsquo;re going to go through the process of creating an application in Django. You&amp;rsquo;ll extend this later into your personal portfolio application.&lt;/p&gt;
&lt;h3 id=&quot;set-up-your-development-environment&quot;&gt;Set Up Your Development Environment&lt;/h3&gt;
&lt;p&gt;Whenever you are starting a new web development project, it&amp;rsquo;s a good idea to first set up your development environment. Create a new directory for your project to live in, and &lt;code&gt;cd&lt;/code&gt; into it:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mkdir rp-portfolio
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; rp-portfolio
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once your inside the main directory, it&amp;rsquo;s a good idea to create a &lt;a href=&quot;https://realpython.com/python-virtual-environments-a-primer/&quot;&gt;virtual environment&lt;/a&gt; to manage dependencies. There are many different ways to set up virtual environments, but here you&amp;rsquo;re going to use &lt;code&gt;venv&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python3 -m venv venv
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command will create a folder &lt;code&gt;venv&lt;/code&gt; in your working directory. Inside this directory, you&amp;rsquo;ll find several files including a copy of the Python standard library. Later, when you install new dependencies, they will also be stored in this directory. Next, you need to activate the virtual environment by running the following command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt; venv/bin/activate
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you&amp;rsquo;re not using bash shell, you might need to use a &lt;a href=&quot;https://docs.python.org/3/library/venv.html#creating-virtual-environments&quot;&gt;different command&lt;/a&gt; to activate your virtual environment. For example, on windows you need this command:&lt;/p&gt;
&lt;div class=&quot;highlight doscon&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;C:\&amp;gt;&lt;/span&gt; venv\Scripts\activate.bat
&lt;/pre&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;p&gt;You&amp;rsquo;ll know that your virtual environment has been activated, because your console prompt in the terminal will change. It should look something like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;(venv) $&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Your virtual environment directory doesn&amp;rsquo;t have to be called &lt;code&gt;venv&lt;/code&gt;. If you want to create one under a different name, for example &lt;code&gt;my_venv&lt;/code&gt;, just replace with the second &lt;code&gt;venv&lt;/code&gt; with &lt;code&gt;my_venv&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then, when activating your virtual environment, replace &lt;code&gt;venv&lt;/code&gt; with &lt;code&gt;my_venv&lt;/code&gt; again. The prompt will also now be prefixed with &lt;code&gt;(my_venv)&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Now that you&amp;rsquo;ve created a virtual environment, it&amp;rsquo;s time to install Django. You can do this using &lt;code&gt;pip&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;(venv) $&lt;/span&gt; pip install Django
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once you&amp;rsquo;ve set up the virtual environment and installed Django, you can now dive in to creating the application.&lt;/p&gt;
&lt;h3 id=&quot;create-a-django-project&quot;&gt;Create a Django Project&lt;/h3&gt;
&lt;p&gt;As you saw in the previous section, a Django web application is made up of a project and its constituent apps. Making sure you&amp;rsquo;re in the &lt;code&gt;rp_portfolio&lt;/code&gt; directory, and you&amp;rsquo;ve activated your virtual environment, run the following command to create the project:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; django-admin startproject personal_portfolio
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will create a new directory &lt;code&gt;personal_portfolio&lt;/code&gt;. If you &lt;code&gt;cd&lt;/code&gt; into this new directory you&amp;rsquo;ll see &lt;em&gt;another&lt;/em&gt; directory called &lt;code&gt;personal_portfolio&lt;/code&gt; and a file called &lt;code&gt;manage.py&lt;/code&gt;. Your directory structure should look something like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;rp-portfolio/
│
├── personal_portfolio/
│   ├── personal_portfolio/
│   │   ├── __init__.py
│   │   ├── settings.py
│   │   ├── urls.py
│   │   └── wsgi.py
│   │
│   └── manage.py
│
└── venv/
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Most of the work you do will be in that first &lt;code&gt;personal_portfolio&lt;/code&gt; directory. To save having to &lt;code&gt;cd&lt;/code&gt; through several directories each time you come to work on your project, it can be helpful to reorder this slightly by moving all the files up a directory. While you&amp;rsquo;re in the &lt;code&gt;rp-portfolio&lt;/code&gt; directory, run the following commands:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mv personal_portfolio/manage.py ./
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mv personal_portfolio/personal_portfolio/* personal_portfolio
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; rm -r personal_portfolio/personal_portfolio/
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You should end up with something like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;rp-portfolio/
│
├── personal_portfolio/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
│
├── venv/
│
└── manage.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once your file structure is set up, you can now start the server and check that your set up was successful. In the console, run the following command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py runserver
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, in your browser go to &lt;code&gt;localhost:8000&lt;/code&gt;, and you should see the following:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-12-09_at_17.58.16.20be0c5d3f1e.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Screenshot_2018-12-09_at_17.58.16.20be0c5d3f1e.png&quot; width=&quot;2332&quot; height=&quot;1600&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-09_at_17.58.16.20be0c5d3f1e.png&amp;amp;w=583&amp;amp;sig=e1f6932faf00b971880a614f48267fb9259b9948 583w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-09_at_17.58.16.20be0c5d3f1e.png&amp;amp;w=1166&amp;amp;sig=4ffc2fd1351ae277e120dc5f29821f4cd544a11a 1166w, https://files.realpython.com/media/Screenshot_2018-12-09_at_17.58.16.20be0c5d3f1e.png 2332w&quot; sizes=&quot;75vw&quot; alt=&quot;Initial view of Django site&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Congratulations, you&amp;rsquo;ve created a Django site! The source code for this part of the tutorial can be found on &lt;a href=&quot;https://github.com/realpython/materials/tree/a639f1c2f85032334fbb4dca88f3e8dc88397f6d/rp-portfolio&quot;&gt;GitHub&lt;/a&gt;. The next step is to create apps so that you can add views and functionality to your site.&lt;/p&gt;
&lt;h3 id=&quot;create-a-django-application&quot;&gt;Create a Django Application&lt;/h3&gt;
&lt;p&gt;For this part of the tutorial, we&amp;rsquo;ll create an app called &lt;code&gt;hello_world&lt;/code&gt;, which you&amp;rsquo;ll subsequently delete as its not necessary for our personal portfolio site.&lt;/p&gt;
&lt;p&gt;To create the app, run the following command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py startapp hello_world
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will create another directory called &lt;code&gt;hello_world&lt;/code&gt; with several files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;__init__.py&lt;/code&gt;&lt;/strong&gt; tells Python to treat the directory as a Python package.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;admin.py&lt;/code&gt;&lt;/strong&gt; contains settings for the Django admin pages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;apps.py&lt;/code&gt;&lt;/strong&gt; contains settings for the application configuration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;models.py&lt;/code&gt;&lt;/strong&gt; contains a series of classes that Django&amp;rsquo;s ORM converts to database tables.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;tests.py&lt;/code&gt;&lt;/strong&gt; contains test classes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;views.py&lt;/code&gt;&lt;/strong&gt; contains functions and classes that handle what data is displayed in the HTML templates.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once you&amp;rsquo;ve created the app, you need to install it in your project. In &lt;code&gt;rp-portfolio/settings.py&lt;/code&gt;, add the following line of code under &lt;code&gt;INSTALLED_APPS&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.admin&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.auth&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.contenttypes&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.sessions&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.messages&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.staticfiles&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;s1&quot;&gt;&amp;#39;hello_world&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That line of code means that your project now knows that the app you just created exists. The next step is to create a view so that you can display something to a user.&lt;/p&gt;
&lt;h3 id=&quot;create-a-view&quot;&gt;Create a View&lt;/h3&gt;
&lt;p&gt;Views in Django are a collection of functions or classes inside the &lt;code&gt;views.py&lt;/code&gt; file in your app directory. Each function or class handles the logic that gets processed each time a different URL is visited.&lt;/p&gt;
&lt;p&gt;Navigate to the &lt;code&gt;views.py&lt;/code&gt; file in the &lt;code&gt;hello_world&lt;/code&gt; directory. There&amp;rsquo;s already a line of code in there that imports &lt;code&gt;render()&lt;/code&gt;. Add the following code:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.shortcuts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hello_world&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;hello_world.html&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this piece of code, you&amp;rsquo;ve defined a &lt;strong&gt;view function&lt;/strong&gt; called &lt;code&gt;hello_world()&lt;/code&gt;. When this function is called, it will render an HTML file called &lt;code&gt;hello_world.html&lt;/code&gt;. That file doesn&amp;rsquo;t exist yet, but we&amp;rsquo;ll create it soon.&lt;/p&gt;
&lt;p&gt;The view function takes one argument, &lt;code&gt;request&lt;/code&gt;. This object is an &lt;code&gt;HttpRequestObject&lt;/code&gt; that is created whenever a page is loaded. It contains information about the request, such as the method, which can take several values including &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now that you&amp;rsquo;ve created the view function, you need to create the HTML template to display to the user. &lt;code&gt;render()&lt;/code&gt; looks for HTML templates inside a directory called &lt;code&gt;templates&lt;/code&gt; inside your app directory. Create that directory and subsequently a file named &lt;code&gt;hello_world.html&lt;/code&gt; inside it:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mkdir hello_world/templates/
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; touch hello_world/templates/hello_world.html
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Add the following lines of HTML to your file:&lt;/p&gt;
&lt;div class=&quot;highlight htmldjango&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;Hello, World!&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You&amp;rsquo;ve now created a function to handle your views and templates to display to the user. The final step is to hook up your URLs so that you can visit the page you&amp;rsquo;ve just created. Your project has a module called &lt;code&gt;urls.py&lt;/code&gt; in which you need to include a URL configuration for the &lt;code&gt;hello_world&lt;/code&gt; app. Inside &lt;code&gt;personal_portfolio/urls.py&lt;/code&gt;, add the following:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.contrib&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;admin&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.urls&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;include&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;urlpatterns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;admin/&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;hello_world.urls&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This looks for a module called &lt;code&gt;urls.py&lt;/code&gt; inside the &lt;code&gt;hello_world&lt;/code&gt; application and registers any URLs defined there. Whenever you visit the root path of your URL (&lt;code&gt;localhost:8000&lt;/code&gt;), the &lt;code&gt;hello_world&lt;/code&gt; application&amp;rsquo;s URLs will be registered. The &lt;code&gt;hello_world.urls&lt;/code&gt; module doesn&amp;rsquo;t exist yet, so you&amp;rsquo;ll need to create it:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; touch hello_world/urls.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Inside this module, we need to import the path object as well as our app&amp;rsquo;s &lt;code&gt;views&lt;/code&gt; module. Then we want to create a list of URL patterns that correspond to the various view functions. At the moment, we have only created one view function, so we need only create one URL:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.urls&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;hello_world&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;views&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;urlpatterns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;views&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hello_world&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;hello_world&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, when you restart the server and visit &lt;code&gt;localhost:8000&lt;/code&gt;, you should be able to see the HTML template you created:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-12-09_at_17.57.22.f3c9ea711bd4.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Screenshot_2018-12-09_at_17.57.22.f3c9ea711bd4.png&quot; width=&quot;2332&quot; height=&quot;1600&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-09_at_17.57.22.f3c9ea711bd4.png&amp;amp;w=583&amp;amp;sig=ce7e968bfed6b86f9e943ed3eda22be6390cb04b 583w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-09_at_17.57.22.f3c9ea711bd4.png&amp;amp;w=1166&amp;amp;sig=f49cdefd0aa57477917d8d7afb68a6d8d9e4dda7 1166w, https://files.realpython.com/media/Screenshot_2018-12-09_at_17.57.22.f3c9ea711bd4.png 2332w&quot; sizes=&quot;75vw&quot; alt=&quot;Hello, World! view of Django site&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Congratulations, again! You&amp;rsquo;ve created your first Django app and hooked it up to your project. Don&amp;rsquo;t forget to check out the &lt;a href=&quot;https://github.com/realpython/materials/tree/754bf43a150b818f73e6508fd937eefee997cc6c/rp-portfolio&quot;&gt;source code&lt;/a&gt; for this section and the previous one. The only problem now is that it doesn&amp;rsquo;t look very nice. In the next section, we&amp;rsquo;re going to add bootstrap styles to your project to make it prettier!&lt;/p&gt;
&lt;h3 id=&quot;add-bootstrap-to-your-app&quot;&gt;Add Bootstrap to Your App&lt;/h3&gt;
&lt;p&gt;If you don&amp;rsquo;t add any styling, then the app you create isn&amp;rsquo;t going to look too nice. Instead of going into CSS styling with this tutorial, we&amp;rsquo;ll just cover how to add bootstrap styles to your project. This will allow us to improve the look of the site without too much effort.&lt;/p&gt;
&lt;p&gt;Before we get started with the Bootstrap styles, we&amp;rsquo;ll create a base template that we can import to each subsequent view. This template is where we&amp;rsquo;ll subsequently add the Bootstrap style imports.&lt;/p&gt;
&lt;p&gt;Create another directory called &lt;code&gt;templates&lt;/code&gt;, this time inside &lt;code&gt;personal_portfolio&lt;/code&gt;, and a file called &lt;code&gt;base.html&lt;/code&gt;, inside the new directory:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mkdir personal_portfolio/templates/
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; touch personal_portfolio/templates/base.html
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We create this additional templates directory to store HTML templates that will be used in every Django app in the project. As you saw previously, each Django project can consist of multiple apps that handle separated logic, and each app contains its own &lt;code&gt;templates&lt;/code&gt; directory to store HTML templates related to the application.&lt;/p&gt;
&lt;p&gt;This application structure works well for the back end logic, but we want our entire site to look consistent on the front end. Instead of having to import Bootstrap styles into every app, we can create a template or set of templates that are shared by all the apps. As long as Django knows to look for templates in this new, shared directory it can save a lot of repeated styles.&lt;/p&gt;
&lt;p&gt;Inside this new file (&lt;code&gt;personal_portfolio/templates/base.html&lt;/code&gt;), add the following lines of code:&lt;/p&gt;
&lt;div class=&quot;highlight htmldjango&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;page_content&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endblock&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, in &lt;code&gt;hello_world/templates/hello_world.html&lt;/code&gt;, we can extend this base template:&lt;/p&gt;
&lt;div class=&quot;highlight htmldjango&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;base.html&amp;quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;page_content&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;Hello, World!&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endblock&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What happens here is that any HTML inside the &lt;code&gt;page_content&lt;/code&gt; block gets added inside the same block in &lt;code&gt;base.html&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To install Bootstrap in your app, you&amp;rsquo;ll use the &lt;a href=&quot;https://getbootstrap.com/docs/4.1/getting-started/introduction/#quick-start&quot;&gt;Bootstrap CDN&lt;/a&gt;. This is a really simple way to install Bootstrap that just involves adding a few lines of code to &lt;code&gt;base.html&lt;/code&gt;. Check out the &lt;a href=&quot;https://github.com/realpython/materials/blob/1ea78b8bb71e685c476d6fd98e829b6ad0a42123/rp-portfolio/personal_portfolio/templates/base.html&quot;&gt;source code&lt;/a&gt; to see how to add the CDN links to your project.&lt;/p&gt;
&lt;p&gt;All future templates that we create will extend &lt;code&gt;base.html&lt;/code&gt; so that we can include Bootstrap styling on every page without having to import the styles again.&lt;/p&gt;
&lt;p&gt;Before we can see our new styled application, we need to tell our Django project that &lt;code&gt;base.html&lt;/code&gt; exists. The default settings register &lt;code&gt;template&lt;/code&gt; directories in each app, but not in the project directory itself. In &lt;code&gt;personal_portfolio/settings.py&lt;/code&gt;, update &lt;code&gt;TEMPLATES&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TEMPLATES&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;BACKEND&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;django.template.backends.django.DjangoTemplates&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;DIRS&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;personal_portfolio/templates/&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;APP_DIRS&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;OPTIONS&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&amp;quot;context_processors&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                &lt;span class=&quot;s2&quot;&gt;&amp;quot;django.template.context_processors.debug&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s2&quot;&gt;&amp;quot;django.template.context_processors.request&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s2&quot;&gt;&amp;quot;django.contrib.auth.context_processors.auth&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s2&quot;&gt;&amp;quot;django.contrib.messages.context_processors.messages&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, when you visit &lt;code&gt;localhost:8000&lt;/code&gt;, you should see that the page has been formatted with slightly different styling:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-12-09_at_19.46.23.cd4783f33899.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Screenshot_2018-12-09_at_19.46.23.cd4783f33899.png&quot; width=&quot;2332&quot; height=&quot;1600&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-09_at_19.46.23.cd4783f33899.png&amp;amp;w=583&amp;amp;sig=04f4551691b0ef74a52f0b8961c9d178fbaab226 583w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-09_at_19.46.23.cd4783f33899.png&amp;amp;w=1166&amp;amp;sig=8a6be1d612ccc4a6fac967f15d65546c1dd56177 1166w, https://files.realpython.com/media/Screenshot_2018-12-09_at_19.46.23.cd4783f33899.png 2332w&quot; sizes=&quot;75vw&quot; alt=&quot;Hello, World! view of Django site with Bootstrap styles&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Whenever you want create templates or import scripts that you intend to use in all your Django apps inside a project, you can add them to this project-level directory and extend them inside your app templates.&lt;/p&gt;
&lt;p&gt;Adding templates is the last stage to building your &lt;em&gt;Hello, World!&lt;/em&gt; Django site. You learned how the Django templating engine works and how to create project-level templates that can be shared by all the apps inside your Django project.&lt;/p&gt;
&lt;p&gt;In this section, you learned how to create a simple &lt;em&gt;Hello, World!&lt;/em&gt; Django site by creating a project with a single app. In the next section, you&amp;rsquo;ll create another application to showcase web development projects, and you&amp;rsquo;ll learn all about models in Django!&lt;/p&gt;
&lt;p&gt;The source code for this section can be found on &lt;a href=&quot;https://github.com/realpython/materials/tree/1ea78b8bb71e685c476d6fd98e829b6ad0a42123/rp-portfolio/hello_world&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;showcase-your-projects&quot;&gt;Showcase Your Projects&lt;/h2&gt;
&lt;p&gt;Any web developer looking to create a portfolio needs a way to show off projects they have worked on. That&amp;rsquo;s what you&amp;rsquo;ll be building now. You&amp;rsquo;ll create another Django app called &lt;code&gt;projects&lt;/code&gt; that will hold a series of sample projects that will be displayed to the user. Users can click on projects and see more information about your work.&lt;/p&gt;
&lt;p&gt;Before we build the &lt;code&gt;projects&lt;/code&gt; app, let&amp;rsquo;s first delete the &lt;code&gt;hello_world&lt;/code&gt; application. All you need to do is delete the &lt;code&gt;hello_world&lt;/code&gt; directory and remove the line &lt;code&gt;&quot;hello_world&quot;,&lt;/code&gt; from &lt;code&gt;INSTALLED_APPS&lt;/code&gt; in &lt;code&gt;settings.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.admin&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.auth&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.contenttypes&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.sessions&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.messages&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.staticfiles&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;s1&quot;&gt;&amp;#39;hello_world&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Delete this line&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, you need to remove the URL path created in &lt;code&gt;personal_portfolio/urls.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.contrib&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;admin&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.urls&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;include&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;urlpatterns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;admin/&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;hello_world.urls&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Delete this line&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that you&amp;rsquo;ve removed the &lt;code&gt;hello_world&lt;/code&gt; app, we can create the &lt;code&gt;projects&lt;/code&gt; app. Making sure you&amp;rsquo;re in the &lt;code&gt;rp-portfolio&lt;/code&gt; directory, run the following command in your console:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py startapp projects
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will create a directory named &lt;code&gt;projects&lt;/code&gt;. The files created are the same as those created when we set up the &lt;code&gt;hello_world&lt;/code&gt; application. In order to hook up our app, we need to add it into &lt;code&gt;INSTALLED_APPS&lt;/code&gt; in &lt;code&gt;settings.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.admin&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.auth&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.contenttypes&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.sessions&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.messages&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;django.contrib.staticfiles&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;s1&quot;&gt;&amp;#39;projects&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Check out the &lt;a href=&quot;https://github.com/realpython/materials/tree/4de6ebd8baebec950357d4f479fcc0de962e6ae4/rp-portfolio&quot;&gt;source code&lt;/a&gt; for this section on GitHub. We&amp;rsquo;re not going to worry about URLs for this application just yet. Instead, we&amp;rsquo;re going to focus on building a &lt;code&gt;Project&lt;/code&gt; model.&lt;/p&gt;
&lt;h3 id=&quot;projects-app-models&quot;&gt;Projects App: Models&lt;/h3&gt;
&lt;p&gt;If you want to store data to display on a website, then you&amp;rsquo;ll need a database. Typically, if you want to create a database with tables and columns within those tables, you&amp;rsquo;ll need to use SQL to manage the database. But when you use Django, you don&amp;rsquo;t need to learn a new language because it has a built-in Object Relational Mapper (ORM).&lt;/p&gt;
&lt;p&gt;An ORM is a program that allows you to create classes that correspond to database tables. Class attributes correspond to columns, and instances of the classes correspond to rows in the database. So, instead of learning a whole new language to create our database and its tables, we can just write some Python classes.&lt;/p&gt;
&lt;p&gt;When you&amp;rsquo;re using an ORM, the classes you build that represent database tables are referred to as &lt;strong&gt;models&lt;/strong&gt;. In Django, they live in the &lt;code&gt;models.py&lt;/code&gt; module of each Django app.&lt;/p&gt;
&lt;p&gt;In your projects app, you&amp;rsquo;ll only need one table to store the different projects you&amp;rsquo;ll display to the user. That means you&amp;rsquo;ll only need to create one model in &lt;code&gt;models.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The model you&amp;rsquo;ll create will be called &lt;code&gt;Project&lt;/code&gt; and will have the following fields:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;title&lt;/code&gt;&lt;/strong&gt; will be a short string field to hold the name of your project.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;description&lt;/code&gt;&lt;/strong&gt; will be a larger string field to hold a longer piece of text.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;technology&lt;/code&gt;&lt;/strong&gt; will be a string field, but its contents will be limited to a select number of choices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;image&lt;/code&gt;&lt;/strong&gt; will be an image field that holds the file path where the image is stored.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To create this model, we&amp;rsquo;ll create a new class in &lt;code&gt;models.py&lt;/code&gt; and add the following in our fields:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;technology&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FilePathField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;/img&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Django models come with many &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/models/fields/&quot;&gt;built-in model field types&lt;/a&gt;. We&amp;rsquo;ve only used three in this model. &lt;code&gt;CharField&lt;/code&gt; is used for short strings and specifies a maximum length.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;TextField&lt;/code&gt; is similar to &lt;code&gt;CharField&lt;/code&gt; but can be used for longer form text as it doesn&amp;rsquo;t have a maximum length limit. Finally, &lt;code&gt;FilePathField&lt;/code&gt; also holds a string but must point to a file path name.&lt;/p&gt;
&lt;p&gt;Now that we&amp;rsquo;ve created our &lt;code&gt;Project&lt;/code&gt; class, we need Django to create the database. By default, the Django ORM creates databases in SQLite, but you can use other databases that use the SQL language, such as PostgreSQL or MySQL, with the Django ORM.&lt;/p&gt;
&lt;p&gt;To start the process of creating our database, we need to create a &lt;strong&gt;migration&lt;/strong&gt;. A migration is a file containing a &lt;code&gt;Migration&lt;/code&gt; class with rules that tell Django what changes need to be made to the database. To create the migration, type the following command in the console, making sure you&amp;rsquo;re in the &lt;code&gt;rp-portfolio&lt;/code&gt; directory:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py makemigrations projects
&lt;span class=&quot;go&quot;&gt;Migrations for &amp;#39;projects&amp;#39;:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  projects/migrations/0001_initial.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    - Create model Project&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You should see that a file &lt;code&gt;projects/migrations/0001_initial.py&lt;/code&gt; has been created in the projects app. Check out that file in the &lt;a href=&quot;https://github.com/realpython/materials/blob/0091ee5421f8107e8629f1f22687ff224850b889/rp-portfolio/projects/migrations/0001_initial.py&quot;&gt;source code&lt;/a&gt; to make sure your migration is correct.&lt;/p&gt;
&lt;p&gt;Now that you&amp;rsquo;ve create a migration file, you need to apply the migrations set out in the migrations file and create your database using the &lt;code&gt;migrate&lt;/code&gt; command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py migrate projects
&lt;span class=&quot;go&quot;&gt;Operations to perform:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Apply all migrations: projects&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Running migrations:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  Applying projects.0001_initial... OK&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; When running both the &lt;code&gt;makemigrations&lt;/code&gt; and &lt;code&gt;migrate&lt;/code&gt; commands, we added &lt;code&gt;projects&lt;/code&gt; to our command. This tells Django to only look at models and migrations in the &lt;code&gt;projects&lt;/code&gt; app. Django comes with several models already created.&lt;/p&gt;
&lt;p&gt;If you run &lt;code&gt;makemigrations&lt;/code&gt; and &lt;code&gt;migrate&lt;/code&gt; without the &lt;code&gt;projects&lt;/code&gt; flag, then all migrations for all the default models in your Django projects will be created and applied. This is not a problem, but for the purposes of this section, they are not needed.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You should also see that a file called &lt;code&gt;db.sqlite3&lt;/code&gt; has been created in the root of your project. Now your database is set up and ready to go. You can now create rows in your table that are the various projects you want to show on your portfolio site.&lt;/p&gt;
&lt;p&gt;To create instances of our &lt;code&gt;Project&lt;/code&gt; class, we&amp;rsquo;re going to have to use the Django shell. The Django shell is similar to the Python shell but allows you to access the database and create entries. To access the Django shell, we use another Django management command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py shell
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once you&amp;rsquo;ve accessed the shell, you&amp;rsquo;ll notice that the command prompt will change from &lt;code&gt;$&lt;/code&gt; to &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt;. You can then import your models:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;projects.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Project&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We&amp;rsquo;re first going to create a new project with the following attributes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;name&lt;/code&gt;:&lt;/strong&gt; &lt;code&gt;My First Project&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;description&lt;/code&gt;:&lt;/strong&gt; &lt;code&gt;A web development project.&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;technology&lt;/code&gt;:&lt;/strong&gt; &lt;code&gt;Django&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;image&lt;/code&gt;:&lt;/strong&gt; &lt;code&gt;img/project1.png&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To do this, we create an instance of the Project class in the Django shell:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;My First Project&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;A web development project.&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;technology&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Django&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;img/project1.png&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This creates a new entry in your projects table and saves it to the database. Now you have created a project that you can display on your portfolio site.&lt;/p&gt;
&lt;p&gt;The final step in this section is to create two more sample projects:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;My Second Project&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Another web development project.&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;technology&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Flask&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;img/project2.png&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;My Third Project&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;A final development project.&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;technology&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Django&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;img/project3.png&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Well done for reaching the end of this section! You now know how to create models in Django and build migration files so that you can translate these model classes into database tables. You&amp;rsquo;ve also used the Django shell to create three instances of your model class.&lt;/p&gt;
&lt;p&gt;In the next section, we&amp;rsquo;ll take these three projects you created and create a view function to display them to users on a web page. You can find the &lt;a href=&quot;https://github.com/realpython/materials/tree/0091ee5421f8107e8629f1f22687ff224850b889/rp-portfolio&quot;&gt;source code&lt;/a&gt; for this section of the tutorial on GitHub.&lt;/p&gt;
&lt;h3 id=&quot;projects-app-views&quot;&gt;Projects App: Views&lt;/h3&gt;
&lt;p&gt;Now you&amp;rsquo;ve created the projects to display on your portfolio site, you&amp;rsquo;ll need to create view functions to send the data from the database to the HTML templates.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;projects&lt;/code&gt; app, you&amp;rsquo;ll create two different views:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;An index view that shows a snippet of information about each project&lt;/li&gt;
&lt;li&gt;A detail view that shows more information on a particular topic&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&amp;rsquo;s start with the index view, as the logic is slightly simpler. Inside &lt;code&gt;views.py&lt;/code&gt;, you&amp;rsquo;ll need to import the &lt;code&gt;Project&lt;/code&gt; class from &lt;code&gt;models.py&lt;/code&gt; and create a function &lt;code&gt;project_index()&lt;/code&gt; that renders a template called &lt;code&gt;project_index.html&lt;/code&gt;. In the body of this function, you&amp;rsquo;ll make a Django ORM query to select all objects in the &lt;code&gt;Project&lt;/code&gt; table:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.shortcuts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;projects.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Project&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;project_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;projects&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;        &lt;span class=&quot;s1&quot;&gt;&amp;#39;projects&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;projects&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;project_index.html&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There&amp;rsquo;s quite a lot going on in this code block, so let&amp;rsquo;s break it down.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;line 6&lt;/strong&gt;, you perform a &lt;strong&gt;query&lt;/strong&gt;. A query is simply a command that allows you to create, retrieve, update, or delete objects (or rows) in your database. In this case, you&amp;rsquo;re retrieving all objects in the &lt;code&gt;projects&lt;/code&gt; table.&lt;/p&gt;
&lt;p&gt;A database query returns a collection of all objects that match the query, known as a &lt;strong&gt;Queryset&lt;/strong&gt;. In this case, you want all objects in the table, so it will return a collection of all projects.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;line 7&lt;/strong&gt; of the code block above, we define a dictionary &lt;code&gt;context&lt;/code&gt;. The dictionary only has one entry &lt;code&gt;projects&lt;/code&gt; to which we assign our Queryset containing all projects. The context dictionary is used to send information to our template. Every view function you create needs to have a context dictionary.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;line 10&lt;/strong&gt;, &lt;code&gt;context&lt;/code&gt; is added as an argument to &lt;code&gt;render()&lt;/code&gt;. Any entries in the &lt;code&gt;context&lt;/code&gt; dictionary are available in the template, as long as the &lt;code&gt;context&lt;/code&gt; argument is passed to &lt;code&gt;render()&lt;/code&gt;. You&amp;rsquo;ll need to create a context dictionary and pass it to &lt;code&gt;render&lt;/code&gt; in each view function you create.&lt;/p&gt;
&lt;p&gt;We also render a template named &lt;code&gt;project_index.html&lt;/code&gt;, which doesn&amp;rsquo;t exist yet. Don&amp;rsquo;t worry about that for now. You&amp;rsquo;ll create the templates for these views in the next section.&lt;/p&gt;
&lt;p&gt;Next, you&amp;rsquo;ll need to create the &lt;code&gt;project_detail()&lt;/code&gt; view function. This function will need an additional argument: the id of the project that&amp;rsquo;s being viewed.&lt;/p&gt;
&lt;p&gt;Otherwise, the logic is similar:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;project_detail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;        &lt;span class=&quot;s1&quot;&gt;&amp;#39;project&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;project_detail.html&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In &lt;strong&gt;line 14&lt;/strong&gt;, we perform another query. This query retrieves the project with primary key, &lt;code&gt;pk&lt;/code&gt;, equal to that in the function argument. We then assign that project in our &lt;code&gt;context&lt;/code&gt; dictionary, which we pass to &lt;code&gt;render()&lt;/code&gt;. Again, there&amp;rsquo;s a template &lt;code&gt;project_detail.html&lt;/code&gt;, which we have yet to create.&lt;/p&gt;
&lt;p&gt;Once your view functions are created, we need to hook them up to URLs. We&amp;rsquo;ll start by creating a file &lt;code&gt;projects/urls.py&lt;/code&gt; to hold the URL configuration for the app. This file should contain the following code:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.urls&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;views&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urlpatterns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;views&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;project_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;project_index&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;lt;int:pk&amp;gt;/&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;views&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;project_detail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;project_detail&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In &lt;strong&gt;line 5&lt;/strong&gt;, we hook up the root URL of our app to the &lt;code&gt;project_index&lt;/code&gt; view. It is slightly more complicated to hook up the &lt;code&gt;project_detail&lt;/code&gt; view. To do this, we want the URL to be &lt;code&gt;/1&lt;/code&gt;, or &lt;code&gt;/2&lt;/code&gt;, and so on, depending on the &lt;code&gt;pk&lt;/code&gt; of the project.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;pk&lt;/code&gt; value in the URL is the same &lt;code&gt;pk&lt;/code&gt; passed to the view function, so you need to dynamically generate these URLs depending on which project you want to view. To do this, we used the &lt;code&gt;&amp;lt;int:pk&amp;gt;&lt;/code&gt; notation. This just tells Django that the value passed in the URL is an integer, and its variable name is &lt;code&gt;pk&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With those now set up, we need to hook these URLs up to the project URLs. In &lt;code&gt;personal_portfolio/urls.py&lt;/code&gt;, add the following highlighted line of code:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.contrib&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;admin&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.urls&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;include&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;urlpatterns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;admin/&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;projects/&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;projects.urls&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This line of code includes all the URLs in the projects app but means they are accessed when prefixed by &lt;code&gt;projects/&lt;/code&gt;. There are now two full URLs that can be accessed with our project:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;localhost:8000/projects&lt;/code&gt;:&lt;/strong&gt; The project index page&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;localhost:8000/projects/3&lt;/code&gt;:&lt;/strong&gt; The detail view for the project with &lt;code&gt;pk=3&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These URLs still won&amp;rsquo;t work properly because we don&amp;rsquo;t have any HTML templates. But our views and logic are up and running so all that&amp;rsquo;s left to do is create those templates. If you want to check your code, take a look at the &lt;a href=&quot;https://github.com/realpython/materials/tree/d49c624500e4470107bb258642d2744eeeeefc5a/rp-portfolio&quot;&gt;source code&lt;/a&gt; for this section.&lt;/p&gt;
&lt;h3 id=&quot;projects-app-templates&quot;&gt;Projects App: Templates&lt;/h3&gt;
&lt;p&gt;Phew! You&amp;rsquo;re nearly there with this app. Our final step is to create two templates:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;project_index&lt;/code&gt; template&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;project_detail&lt;/code&gt; template &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As we&amp;rsquo;ve added Bootstrap styles to our application, we can use some pre-styled components to make the views look nice. Let&amp;rsquo;s start with the &lt;code&gt;project_index&lt;/code&gt; template.&lt;/p&gt;
&lt;p&gt;For the &lt;code&gt;project_index&lt;/code&gt; template, you&amp;rsquo;ll create a grid of &lt;a href=&quot;https://getbootstrap.com/docs/4.1/components/card/&quot;&gt;Bootstrap cards&lt;/a&gt;, with each card displaying details of the project. Of course, we don&amp;rsquo;t know how many projects there are going to be. In theory, there could be hundreds to display.&lt;/p&gt;
&lt;p&gt;We don&amp;rsquo;t want to have to create 100 different Bootstrap cards and hard-code in all the information to each project. Instead, we&amp;rsquo;re going to use a feature of the Django template engine: &lt;a href=&quot;https://realpython.com/python-for-loop/&quot;&gt;&lt;strong&gt;for loops&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Using this feature, you&amp;rsquo;ll be able to loop through all the projects and create a card for each one. The for loop syntax in the Django template engine is as follows:&lt;/p&gt;
&lt;div class=&quot;highlight htmldjango&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;projects&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;{# Do something... #}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endfor&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that you know how for loops work, you can add the following code to a file named &lt;code&gt;projects/templates/project_index.html&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight htmldjango&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;base.html&amp;quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;load&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;page_content&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;Projects&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;row&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;projects&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;col-md-4&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;card mb-2&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;card-img-top&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;project.image&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;card-body&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h5&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;card-title&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;project.title&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;card-text&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;project.description&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;project_detail&amp;#39;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;project.pk&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;                   &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;btn btn-primary&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;                    Read More
&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20 &lt;/span&gt;    &lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endfor&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22 &lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endblock&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There&amp;rsquo;s a lot of Bootstrap HTML here, which is not the focus of this tutorial. Feel free to copy and paste and take a look at the Bootstrap docs if you&amp;rsquo;re interested in learning more. Instead of focusing on the Bootstrap, there are a few things to highlight in this code block.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;line 1&lt;/strong&gt;, we extend &lt;code&gt;base.html&lt;/code&gt; as we did in the &lt;em&gt;Hello, World!&lt;/em&gt; app tutorial. I&amp;rsquo;ve added some more styling to this file to include a &lt;a href=&quot;https://getbootstrap.com/docs/4.1/components/navbar/&quot;&gt;navigation bar&lt;/a&gt; and so that all the content is contained in a &lt;a href=&quot;https://getbootstrap.com/docs/4.1/layout/overview/#containers&quot;&gt;Bootstrap container&lt;/a&gt;. The changes to &lt;code&gt;base.html&lt;/code&gt; can be seen in the &lt;a href=&quot;https://github.com/realpython/materials/blob/get-started-with-django-part-1/rp-portfolio/personal_portfolio/templates/base.html&quot;&gt;source code&lt;/a&gt; on GitHub.&lt;/p&gt;
&lt;p&gt;On &lt;strong&gt;line 2&lt;/strong&gt;, we include a &lt;code&gt;{% load static %}&lt;/code&gt; tag to include static files such as images. Remember back in the section on Django models, when you created the &lt;code&gt;Project&lt;/code&gt; model. One of its attributes was a filepath. That filepath is where we&amp;rsquo;re going to store the actual images for each project.&lt;/p&gt;
&lt;p&gt;Django automatically registers static files stored in a directory named &lt;code&gt;static/&lt;/code&gt; in each application. Our image file path names were of the structure: &lt;code&gt;img/&amp;lt;photo_name&amp;gt;.png&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When loading static files, Django looks in the &lt;code&gt;static/&lt;/code&gt; directory for files matching a given filepath within &lt;code&gt;static/&lt;/code&gt;. So, we need to create a directory named &lt;code&gt;static/&lt;/code&gt; with another directory named &lt;code&gt;img/&lt;/code&gt; inside. Inside &lt;code&gt;img/&lt;/code&gt;, you can copy over the images from the &lt;a href=&quot;https://github.com/realpython/materials/tree/7909f5a682a88d8488167bc6fe9b64a5b294f99a/rp-portfolio/projects/static/img&quot;&gt;source code&lt;/a&gt; on GitHub.&lt;/p&gt;
&lt;p&gt;On &lt;strong&gt;line 6&lt;/strong&gt;, we begin the for loop, looping over all projects passed in by the &lt;code&gt;context&lt;/code&gt; dictionary.&lt;/p&gt;
&lt;p&gt;Inside this for loop, we can access each individual project. To access the project&amp;rsquo;s attributes, you can use dot notation inside double curly brackets. For example, to access the project&amp;rsquo;s title, you use &lt;code&gt;{{ project.title }}&lt;/code&gt;. The same notation can be used to access any of the project&amp;rsquo;s attributes.&lt;/p&gt;
&lt;p&gt;On &lt;strong&gt;line 9&lt;/strong&gt;, we include our project image. Inside the &lt;code&gt;src&lt;/code&gt; attribute, we add the code &lt;code&gt;{% static project.image %}&lt;/code&gt;. This tells Django to look inside the static files to find a file matching &lt;code&gt;project.image&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The final point that we need to highlight is the link on &lt;strong&gt;line 13&lt;/strong&gt;. This is the link to our &lt;code&gt;project_detail&lt;/code&gt; page. Accessing URLs in Django is similar to accessing static files. The code for the URL has the following form:&lt;/p&gt;
&lt;div class=&quot;highlight htmldjango&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;lt;url path name&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;view_function_arguments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this case, we are accessing a URL path named &lt;code&gt;project_detail&lt;/code&gt;, which takes integer arguments corresponding to the &lt;code&gt;pk&lt;/code&gt; number of the project.&lt;/p&gt;
&lt;p&gt;With all that in place, if you start the Django server and visit &lt;code&gt;localhost:8000/projects&lt;/code&gt;, then you should see something like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-12-16_at_16.46.36.a71c744f096a.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Screenshot_2018-12-16_at_16.46.36.a71c744f096a.png&quot; width=&quot;2784&quot; height=&quot;1628&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-16_at_16.46.36.a71c744f096a.png&amp;amp;w=696&amp;amp;sig=712e1edbd4e78895079933d316f34aa93a6687ce 696w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-16_at_16.46.36.a71c744f096a.png&amp;amp;w=1392&amp;amp;sig=99292d6c026f3910d493f30d1dcf35a12c3d6276 1392w, https://files.realpython.com/media/Screenshot_2018-12-16_at_16.46.36.a71c744f096a.png 2784w&quot; sizes=&quot;75vw&quot; alt=&quot;project index view&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With the &lt;code&gt;project_index.html&lt;/code&gt; template in place, it&amp;rsquo;s time to create the &lt;code&gt;project_detail.html&lt;/code&gt; template. The code for this template is below:&lt;/p&gt;
&lt;div class=&quot;highlight htmldjango&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;base.html&amp;quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;load&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;page_content&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;project.title&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;row&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;col-md-8&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;project.image&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;100%&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;col-md-4&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;About the project:&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;project.description&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;br&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;Technology used:&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;project.technology&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endblock&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The code in this template has the same functionality as each project card in the &lt;code&gt;project_index.html&lt;/code&gt; template. The only difference is the introduction of some Bootstrap columns.&lt;/p&gt;
&lt;p&gt;If you visit &lt;code&gt;localhost:8000/projects/1&lt;/code&gt;, you should see the detail page for that first project you created:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-12-16_at_16.50.59.43297d79452f.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Screenshot_2018-12-16_at_16.50.59.43297d79452f.png&quot; width=&quot;2784&quot; height=&quot;1628&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-16_at_16.50.59.43297d79452f.png&amp;amp;w=696&amp;amp;sig=3218b4a2f2b423329bc9ccccfa2da8075ac41eb0 696w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-16_at_16.50.59.43297d79452f.png&amp;amp;w=1392&amp;amp;sig=01afcc50cae869f730605e67b1b951886afbe408 1392w, https://files.realpython.com/media/Screenshot_2018-12-16_at_16.50.59.43297d79452f.png 2784w&quot; sizes=&quot;75vw&quot; alt=&quot;project detail view&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this section, you learned how to use models, views, and templates to create a fully functioning app for your personal portfolio project. Check out the &lt;a href=&quot;https://github.com/realpython/materials/tree/7909f5a682a88d8488167bc6fe9b64a5b294f99a/rp-portfolio&quot;&gt;source code&lt;/a&gt; for this section on GitHub.&lt;/p&gt;
&lt;p&gt;In the next section, you&amp;rsquo;ll build a fully functioning blog for your site, and you&amp;rsquo;ll also learn about the Django admin page and forms.&lt;/p&gt;
&lt;h2 id=&quot;share-your-knowledge-with-a-blog&quot;&gt;Share Your Knowledge With a Blog&lt;/h2&gt;
&lt;p&gt;A blog is a great addition to any personal portfolio site. Whether you update it monthly or weekly, it&amp;rsquo;s a great place to share your knowledge as you learn. In this section, you&amp;rsquo;re going to build a fully functioning blog that will allow you to perform the following tasks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create, update, and delete blog posts&lt;/li&gt;
&lt;li&gt;Display posts to the user as either an index view or a detail view&lt;/li&gt;
&lt;li&gt;Assign categories to posts&lt;/li&gt;
&lt;li&gt;Allow users to comment on posts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You&amp;rsquo;ll also learn how to use the Django Admin interface, which is where you&amp;rsquo;ll create, update, and delete posts and categories as necessary.&lt;/p&gt;
&lt;p&gt;Before you get into building out the functionality of this part of your site, create a new Django app named &lt;code&gt;blog&lt;/code&gt;. Don&amp;rsquo;t delete &lt;code&gt;projects&lt;/code&gt;. You&amp;rsquo;ll want both apps in your Django project:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py startapp blog
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This may start to feel familiar to you, as its your third time doing this. Don&amp;rsquo;t forget to add &lt;code&gt;blog&lt;/code&gt; to your &lt;code&gt;INSTALLED_APPS&lt;/code&gt; in &lt;code&gt;personal_porfolio/settings.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;django.contrib.admin&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;django.contrib.auth&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;django.contrib.contenttypes&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;django.contrib.sessions&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;django.contrib.messages&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;django.contrib.staticfiles&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;projects&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;s2&quot;&gt;&amp;quot;blog&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Hold off on hooking up the URLs for now. As with the &lt;code&gt;projects&lt;/code&gt; app, you&amp;rsquo;ll start by adding your models.&lt;/p&gt;
&lt;h3 id=&quot;blog-app-models&quot;&gt;Blog App: Models&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;models.py&lt;/code&gt; file in this app is much more complicated than in the &lt;code&gt;projects&lt;/code&gt; app.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;re going to need three separate database tables for the blog: &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Post&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Category&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Comment&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These tables need to be related to one another. This is made easier because Django models come with fields specifically for this purpose.&lt;/p&gt;
&lt;p&gt;Below is the code for the &lt;code&gt;Category&lt;/code&gt; and &lt;code&gt;Post&lt;/code&gt; models:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;created_on&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTimeField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auto_now_add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;last_modified&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTimeField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auto_now&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;categories&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManyToManyField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Category&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;related_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;posts&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;Category&lt;/code&gt; model is very simple. All that&amp;rsquo;s needed is a single &lt;code&gt;CharField&lt;/code&gt; in which we store the name of the category.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;body&lt;/code&gt; fields on the &lt;code&gt;Post&lt;/code&gt; model are the same field types as you used in the &lt;code&gt;Project&lt;/code&gt; model. We only need a &lt;code&gt;CharField&lt;/code&gt; for the &lt;code&gt;title&lt;/code&gt; as we only want a short string for the post title. The body needs to be a long-form piece of text, so we use a &lt;code&gt;TextField&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The next two fields, &lt;code&gt;created_on&lt;/code&gt; and &lt;code&gt;last_modified&lt;/code&gt;, are Django &lt;code&gt;DateTimeFields&lt;/code&gt;. These store a &lt;a href=&quot;https://docs.python.org/3.7/library/datetime.html&quot;&gt;&lt;code&gt;datetime&lt;/code&gt;&lt;/a&gt; object containing the date and time when the post was created and modified respectively.&lt;/p&gt;
&lt;p&gt;On &lt;strong&gt;line 11&lt;/strong&gt;, the &lt;code&gt;DateTimeField&lt;/code&gt; takes an argument &lt;code&gt;auto_now_add=True&lt;/code&gt;. This assigns the current date and time to this field whenever an instance of this class is created.&lt;/p&gt;
&lt;p&gt;On &lt;strong&gt;line 12&lt;/strong&gt;, the &lt;code&gt;DateTimeField&lt;/code&gt; takes an argument &lt;code&gt;auto_now=True&lt;/code&gt;. This assigns the current date and time to this field whenever an instance of this class is saved. That means whenever you edit an instance of this class, the &lt;code&gt;date_modified&lt;/code&gt; is updated.&lt;/p&gt;
&lt;p&gt;The final field on the post model is the most interesting. We want to link our models for categories and posts in such a way that &lt;em&gt;many&lt;/em&gt; categories can be assigned to &lt;em&gt;many&lt;/em&gt; posts. Luckily, Django makes this easier for us by providing a &lt;code&gt;ManytoManyField&lt;/code&gt; field type. This field links the &lt;code&gt;Post&lt;/code&gt; and &lt;code&gt;Category&lt;/code&gt; models and allows us to create a relationship between the two tables.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ManyToManyField&lt;/code&gt; takes two arguments. The first is the model with which the relationship is, in this case its &lt;code&gt;Category&lt;/code&gt;. The second allows us to access the relationship from a &lt;code&gt;Category&lt;/code&gt; object, even though we haven&amp;rsquo;t added a field there. By adding a &lt;code&gt;related_name&lt;/code&gt; of &lt;code&gt;posts&lt;/code&gt;, we can access &lt;code&gt;category.posts&lt;/code&gt; to give us a list of posts with that category.&lt;/p&gt;
&lt;p&gt;The third and final model we need to add is &lt;code&gt;Comment&lt;/code&gt;. We&amp;rsquo;ll use another relationship field similar the &lt;code&gt;ManyToManyField&lt;/code&gt; that relates &lt;code&gt;Post&lt;/code&gt; and &lt;code&gt;Category&lt;/code&gt;. However, we only want the relationship to go one way: one post should have many comments.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll see how this works after we define the &lt;code&gt;Comment&lt;/code&gt; class:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;created_on&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTimeField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auto_now_add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ForeignKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Post&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;on_delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CASCADE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first three fields on this model should look familiar. There&amp;rsquo;s an &lt;code&gt;author&lt;/code&gt; field for users to add a name or alias, a &lt;code&gt;body&lt;/code&gt; field for the body of the comment, and a &lt;code&gt;created_on&lt;/code&gt; field that is identical to the &lt;code&gt;created_on&lt;/code&gt; field on the &lt;code&gt;Post&lt;/code&gt; model.&lt;/p&gt;
&lt;p&gt;On &lt;strong&gt;line 20&lt;/strong&gt;, we use another relational field, the &lt;code&gt;ForeignKey&lt;/code&gt; field. This is similar to the &lt;code&gt;ManyToManyField&lt;/code&gt; but instead defines a &lt;strong&gt;many to one&lt;/strong&gt; relationship. The reasoning behind this is that &lt;strong&gt;many&lt;/strong&gt; comments can be assigned to &lt;strong&gt;one&lt;/strong&gt; post. But you can&amp;rsquo;t have a comment that corresponds to many posts.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ForeignKey&lt;/code&gt; field takes two arguments. The first is the other model in the relationship, in this case, &lt;code&gt;Post&lt;/code&gt;. The second tells Django what to do when a post is deleted. If a post is deleted, then we don&amp;rsquo;t want the comments related to it hanging around. We, therefore, want to delete them as well, so we add the argument &lt;code&gt;on_delete=models.CASCADE&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once you&amp;rsquo;ve created the models, you can create the migration files with &lt;code&gt;makemigrations&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py makemigrations blog
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The final step is to migrate the tables. This time, don&amp;rsquo;t add the app-specific flag. Later on, you&amp;rsquo;ll need the &lt;code&gt;User&lt;/code&gt; model that Django creates for you:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py migrate
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that you&amp;rsquo;ve created the models, we can start to add some posts and categories. You won&amp;rsquo;t be doing this from the command line as you did with the projects, as typing out a whole blog post into the command line would be unpleasant to say the least! &lt;/p&gt;
&lt;p&gt;Instead, you&amp;rsquo;ll learn how to use the Django Admin, which will allow you to create instances of your model classes in a nice web interface.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t forget that you can check out the &lt;a href=&quot;https://github.com/realpython/materials/tree/6fdccb8ae85c5792e3639c406be09abc2d4803d8/rp-portfolio&quot;&gt;source code&lt;/a&gt; for this section on GitHub before moving onto the next section.&lt;/p&gt;
&lt;h3 id=&quot;blog-app-django-admin&quot;&gt;Blog App: Django Admin&lt;/h3&gt;
&lt;p&gt;The Django Admin is a fantastic tool and one of the great benefits of using Django. As you&amp;rsquo;re the only person who&amp;rsquo;s going to be writing blog posts and creating categories, there&amp;rsquo;s no need to create a user interface to do so.&lt;/p&gt;
&lt;p&gt;On the other hand, you don&amp;rsquo;t want to have to write blog posts in the command line. This is where the admin comes in. It allows you to create, update, and delete instances of your model classes and provides a nice interface for doing so.&lt;/p&gt;
&lt;p&gt;Before you can access the admin, you need to add yourself as a superuser. This is why, in the previous section, you applied migrations project-wide as opposed to just for the app. Django comes with built-in user models and a user management system that will allow you to login to the admin.&lt;/p&gt;
&lt;p&gt;To start off, you can add yourself as superuser using the following command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python manage.py createsuperuser
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You&amp;rsquo;ll then be prompted to enter a username followed by your email address and password. Once you&amp;rsquo;ve entered the required details, you&amp;rsquo;ll be notified that the superuser has been created. Don&amp;rsquo;t worry if you make a mistake since you can just start again:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;Username (leave blank to use &amp;#39;jasmine&amp;#39;): jfiner&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Email address: jfiner@example.com&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Password:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Password (again):&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Superuser created successfully.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Navigate to &lt;code&gt;localhost:8000/admin&lt;/code&gt; and log in with the credentials you just used to create a superuse. You&amp;rsquo;ll see a page similar to the one below:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-12-16_at_19.18.42.ee6fb5185d8a.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Screenshot_2018-12-16_at_19.18.42.ee6fb5185d8a.png&quot; width=&quot;2784&quot; height=&quot;1628&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-16_at_19.18.42.ee6fb5185d8a.png&amp;amp;w=696&amp;amp;sig=a1a3abf6fd548926dc4a1ccc84f8a9827192a3c6 696w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-16_at_19.18.42.ee6fb5185d8a.png&amp;amp;w=1392&amp;amp;sig=1efcdf95988c118a5118ccf17af84a488df7a910 1392w, https://files.realpython.com/media/Screenshot_2018-12-16_at_19.18.42.ee6fb5185d8a.png 2784w&quot; sizes=&quot;75vw&quot; alt=&quot;The Default Django Admin&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The User and Groups models should appear, but you&amp;rsquo;ll notice that there&amp;rsquo;s no reference to the models you&amp;rsquo;ve created yourself. That&amp;rsquo;s because you need to register them inside the admin.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;blog&lt;/code&gt; directory, open the file &lt;code&gt;admin.py&lt;/code&gt; and type the following lines of code:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.contrib&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;admin&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;blog.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Category&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PostAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ModelAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CategoryAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ModelAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PostAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CategoryAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On &lt;strong&gt;line 2&lt;/strong&gt;, you import the models you want to register on the admin page.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; We&amp;rsquo;re not adding the comments to the admin. That&amp;rsquo;s because it&amp;rsquo;s not usually necessary to edit or create comments yourself.&lt;/p&gt;
&lt;p&gt;If you wanted to add a feature where comments are moderated, then go ahead and add the Comments model too. The steps to do so are exactly the same!&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;On &lt;strong&gt;line 5&lt;/strong&gt; and &lt;strong&gt;line 9&lt;/strong&gt;, you define empty classes &lt;code&gt;PostAdmin&lt;/code&gt; and &lt;code&gt;CategoryAdmin&lt;/code&gt;. For the purposes of this tutorial, you don&amp;rsquo;t need to add any attributes or methods to these classes. They are used to customize what is shown on the admin pages. For this tutorial, the default configuration is enough.&lt;/p&gt;
&lt;p&gt;The last two lines are the most important. These register the models with the admin classes. If you now visit &lt;code&gt;localhost:8000/admin&lt;/code&gt;, then you should see that the &lt;code&gt;Post&lt;/code&gt; and &lt;code&gt;Category&lt;/code&gt; models are now visible:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-12-16_at_19.19.20.297b0ccfe2f3.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Screenshot_2018-12-16_at_19.19.20.297b0ccfe2f3.png&quot; width=&quot;2784&quot; height=&quot;1628&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-16_at_19.19.20.297b0ccfe2f3.png&amp;amp;w=696&amp;amp;sig=ea38f98ec131073e2a079638ce06962fa72364ee 696w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-16_at_19.19.20.297b0ccfe2f3.png&amp;amp;w=1392&amp;amp;sig=e89958435c80771497a510e5ebd2a34ead4c763a 1392w, https://files.realpython.com/media/Screenshot_2018-12-16_at_19.19.20.297b0ccfe2f3.png 2784w&quot; sizes=&quot;75vw&quot; alt=&quot;Django Admin with Posts and Categories&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you click into &lt;em&gt;Posts&lt;/em&gt; or &lt;em&gt;Categorys&lt;/em&gt;,  you should be able to add new instances of both models. I like to add the text of fake blog posts by using &lt;a href=&quot;https://www.lipsum.com/&quot;&gt;lorem ipsum&lt;/a&gt; dummy text.&lt;/p&gt;
&lt;p&gt;Create a couple of fake posts and assign them fake categories before moving onto the next section. That way, you&amp;rsquo;ll have posts you can view when we create our templates.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t forget to check out the &lt;a href=&quot;https://github.com/realpython/materials/tree/86c5009b84ef207c6dd1873ec9dc25afbe725347/rp-portfolio&quot;&gt;source code&lt;/a&gt; for this section before moving on to building out the views for our app.&lt;/p&gt;
&lt;h3 id=&quot;blog-app-views&quot;&gt;Blog App: Views&lt;/h3&gt;
&lt;p&gt;You&amp;rsquo;ll need to create three view functions in the &lt;code&gt;views.py&lt;/code&gt; file in the &lt;code&gt;blog&lt;/code&gt; directory:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;blog_index&lt;/code&gt;&lt;/strong&gt; will display a list of all your posts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;blog_detail&lt;/code&gt;&lt;/strong&gt; will display the full post as well as comments and a form to allow users to create new comments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;blog_category&lt;/code&gt;&lt;/strong&gt; will be similar to &lt;code&gt;blog_index&lt;/code&gt;, but the posts viewed will only be of a specific category chosen by the user.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The simplest view function to start with is &lt;code&gt;blog_index()&lt;/code&gt;. This will be very similar to the &lt;code&gt;project_index()&lt;/code&gt; view from your &lt;code&gt;project&lt;/code&gt; app. You&amp;rsquo;ll just query the &lt;code&gt;Post&lt;/code&gt; models and retrieve all its objects:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.shortcuts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;blog.models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Post&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;blog_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;-created_on&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;posts&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;blog_index.html&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On &lt;strong&gt;line 3&lt;/strong&gt;, you import the &lt;code&gt;Post&lt;/code&gt; model, and on &lt;strong&gt;line 6&lt;/strong&gt; inside the view function, you obtain a Queryset containing all the posts in the database. &lt;code&gt;order_by()&lt;/code&gt; orders the Queryset according to the argument given. The minus sign tells Django to start with the largest value rather than the smallest. We use this, as we want the posts to be ordered with the most recent post first.&lt;/p&gt;
&lt;p&gt;Finally, you define the &lt;code&gt;context&lt;/code&gt; dictionary and render the template. Don&amp;rsquo;t worry about creating it yet. You&amp;rsquo;ll get to creating those in the next section.&lt;/p&gt;
&lt;p&gt;Next, you can start to create the &lt;code&gt;blog_category()&lt;/code&gt; view. The view function will need to take a category name as an argument and query the &lt;code&gt;Post&lt;/code&gt; database for all posts that have been assigned the given category:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;blog_category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;categories__name__contains&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;category&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;17 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;s1&quot;&gt;&amp;#39;-created_on&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;18 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;19 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;category&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;posts&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;blog_category.html&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On &lt;strong&gt;line 14&lt;/strong&gt;, you&amp;rsquo;ve used a &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/topics/db/queries/#retrieving-specific-objects-with-filters&quot;&gt;Django Queryset filter&lt;/a&gt;. The argument of the filter tells Django what conditions need to be met for an object to be retrieved. In this case, we only want posts whose categories contain the category with the name corresponding to that given in the argument of the view function. Again, you&amp;rsquo;re using &lt;code&gt;order_by()&lt;/code&gt; to order posts starting with the most recent.&lt;/p&gt;
&lt;p&gt;We then add these posts and the category to the &lt;code&gt;context&lt;/code&gt; dictionary, and render our template.&lt;/p&gt;
&lt;p&gt;The last view function to add is &lt;code&gt;blog_detail()&lt;/code&gt;. This is more complicated as we are going to include a form. Before you add the form, just set up the view function to show a specific post with a comment associated with it. This function will be almost equivalent to the &lt;code&gt;project_detail()&lt;/code&gt; view function in the &lt;code&gt;projects&lt;/code&gt; app:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;blog_detail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;comments&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;25 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;post&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;26 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;comments&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;27 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;28 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;29 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;blog_detail.html&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The view function takes a &lt;code&gt;pk&lt;/code&gt; value as an argument and, on &lt;strong&gt;line 22&lt;/strong&gt;, retrieves the object with the given &lt;code&gt;pk&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On &lt;strong&gt;line 23&lt;/strong&gt;, we retrieve all the comments assigned to the given post using Django filters again.&lt;/p&gt;
&lt;p&gt;Lastly, add both &lt;code&gt;post&lt;/code&gt; and &lt;code&gt;comments&lt;/code&gt; to the &lt;code&gt;context&lt;/code&gt; dictionary and render the template.&lt;/p&gt;
&lt;p&gt;To add a form to the page, you&amp;rsquo;ll need to create another file in the &lt;code&gt;blog&lt;/code&gt; directory named &lt;code&gt;forms.py&lt;/code&gt;. Django forms are very similar to models. A form consists of a class where the class attributes are form fields. Django comes with some built-in form fields that you can use to quickly create the form you need.&lt;/p&gt;
&lt;p&gt;For this form, the only fields you&amp;rsquo;ll need are &lt;code&gt;author&lt;/code&gt;, which should be a &lt;code&gt;CharField&lt;/code&gt;, and &lt;code&gt;body&lt;/code&gt;, which can also be a &lt;code&gt;CharField&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If the &lt;code&gt;CharField&lt;/code&gt; of your form corresponds to a model &lt;code&gt;CharField&lt;/code&gt;, make sure both have the same &lt;code&gt;max_length&lt;/code&gt; value.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;blog/forms.py&lt;/code&gt; should contain the following code:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forms&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CommentForm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;forms&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forms&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;widget&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;forms&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextInput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attrs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&amp;quot;class&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;form-control&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&amp;quot;placeholder&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Your Name&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forms&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;widget&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;forms&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Textarea&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;attrs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&amp;quot;class&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;form-control&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&amp;quot;placeholder&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Leave a comment!&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You&amp;rsquo;ll also notice an argument &lt;code&gt;widget&lt;/code&gt; has been passed to both the fields. The &lt;code&gt;author&lt;/code&gt; field has the &lt;code&gt;forms.TextInput&lt;/code&gt; widget. This tells Django to load this field as an HTML text input element in the templates. The &lt;code&gt;body&lt;/code&gt; field uses a &lt;code&gt;forms.TextArea&lt;/code&gt; widget instead, so that the field is rendered as an HTML text area element.&lt;/p&gt;
&lt;p&gt;These widgets also take an argument &lt;code&gt;attrs&lt;/code&gt;, which is a dictionary and allows us to specify some CSS classes, which will help with formatting the template for this view later. It also allows us to add some placeholder text.&lt;/p&gt;
&lt;p&gt;When a form is posted, a &lt;code&gt;POST&lt;/code&gt; request is sent to the server. So, in the view function, we need to check if a &lt;code&gt;POST&lt;/code&gt; request has been received. We can then create a comment from the form fields. Django comes with a handy &lt;code&gt;is_valid()&lt;/code&gt; on its forms, so we can check that all the fields have been entered correctly.&lt;/p&gt;
&lt;p&gt;Once you&amp;rsquo;ve created the comment from the form, you&amp;rsquo;ll need to save it using &lt;code&gt;save()&lt;/code&gt; and then query the database for all the comments assigned to the given post. Your view function should contain the following code:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;blog_detail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;form&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CommentForm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;25 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;26 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;form&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CommentForm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;27 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_valid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;28 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;            &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;29 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;                &lt;span class=&quot;n&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cleaned_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;author&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;30 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;                &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cleaned_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;body&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;31 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;                &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;32 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;33 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;            &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;34 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;35 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;comments&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;36 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;37 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;post&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;38 &lt;/span&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;comments&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;39 &lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;s2&quot;&gt;&amp;quot;form&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CommentForm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt;40 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;41 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;blog_detail.html&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On &lt;strong&gt;line 25&lt;/strong&gt;, we create an instance of our form class. Don&amp;rsquo;t forget to import your form at the beginning of the file:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CommentForm&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We then go on to check if a &lt;code&gt;POST&lt;/code&gt; request has been received. If it has, then we create a new instance of our form, populated with the data entered into the form.&lt;/p&gt;
&lt;p&gt;The form is then validated using &lt;code&gt;is_valid()&lt;/code&gt;. If the form is valid, a new instance of &lt;code&gt;Comment&lt;/code&gt; is created. You can access the data from the form using &lt;code&gt;form.cleaned_data&lt;/code&gt;, which is a dictionary.&lt;/p&gt;
&lt;p&gt;They keys of the dictionary correspond to the form fields, so you can access the author using &lt;code&gt;form.cleaned_data[&#39;author&#39;]&lt;/code&gt;. Don&amp;rsquo;t forget to add the current post to the comment when you create it.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The life cycle of submitting a form can be a little complicated, so here&amp;rsquo;s an outline of how it works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When a user visits a page containing a form, they send a &lt;code&gt;GET&lt;/code&gt; request to the server. In this case, there&amp;rsquo;s no data entered in the form, so we just want to render the form and display it.&lt;/li&gt;
&lt;li&gt;When a user enters information and clicks the &lt;em&gt;Submit&lt;/em&gt; button, a &lt;code&gt;POST&lt;/code&gt; request, containing the data submitted with the form, is sent to the server. At this point, the data must be processed, and two things can happen:&lt;ul&gt;
&lt;li&gt;The form is valid, and the user is redirected to the next page.&lt;/li&gt;
&lt;li&gt;The form is invalid, and empty form is once again displayed. The user is back at step 1, and the process repeats.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;p&gt;The Django forms module will output some errors, which you can display to the user. This is beyond the scope of this tutorial, but you can read more about &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/topics/forms/#rendering-form-error-messages&quot;&gt;rendering form error messages&lt;/a&gt; in the Django documentation.&lt;/p&gt;
&lt;p&gt;On &lt;strong&gt;line 34&lt;/strong&gt;, save the comment and go on to add the &lt;code&gt;form&lt;/code&gt; to the &lt;code&gt;context&lt;/code&gt; dictionary so you can access the form in the HTML template.&lt;/p&gt;
&lt;p&gt;The final step before you get to create the templates and actually see this blog up and running is to hook up the URLs. You&amp;rsquo;ll need create another &lt;code&gt;urls.py&lt;/code&gt; file inside &lt;code&gt;blog/&lt;/code&gt; and add the URLs for the three views:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.urls&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;views&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;urlpatterns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;views&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blog_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;blog_index&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;lt;int:pk&amp;gt;/&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;views&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blog_detail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;blog_detail&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;lt;category&amp;gt;/&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;views&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blog_category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;blog_category&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once the blog-specific URLs are in place, you need to add them to the projects URL configuration using &lt;code&gt;include()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.contrib&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;admin&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.urls&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;include&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;urlpatterns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;admin/&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;projects/&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;projects.urls&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;blog/&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;blog.urls&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With this set up, all the blog URLs will be prefixed with &lt;code&gt;blog/&lt;/code&gt;, and you&amp;rsquo;ll have the following URL paths:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;localhost:8000/blog&lt;/code&gt;:&lt;/strong&gt; Blog index&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;localhost:8000/blog/1&lt;/code&gt;:&lt;/strong&gt; Blog detail view of blog with &lt;code&gt;pk=1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;localhost:8000/blog/python&lt;/code&gt;:&lt;/strong&gt; Blog index view of all posts with category &lt;code&gt;python&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These URLs won&amp;rsquo;t work just yet as you still need to create the templates.&lt;/p&gt;
&lt;p&gt;In this section, you created all the views for your blog application. You learned how to use filters when making queries and how to create Django forms. It won&amp;rsquo;t be long now until you can see your blog app in action!&lt;/p&gt;
&lt;p&gt;As always, don&amp;rsquo;t forget that you can check out the &lt;a href=&quot;https://github.com/realpython/materials/tree/8ae8693470e7126a233f5d49685ad65bce3cb537/rp-portfolio&quot;&gt;source code&lt;/a&gt; for this section on GitHub.&lt;/p&gt;
&lt;h3 id=&quot;blog-app-templates&quot;&gt;Blog App: Templates&lt;/h3&gt;
&lt;p&gt;The final piece of our blog app is the templates. By the end of this section, you&amp;rsquo;ll have created a fully functioning blog.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll notice there are some bootstrap elements included in the templates to make the interface prettier. These aren&amp;rsquo;t the focus of the tutorial so I&amp;rsquo;ve glossed over what they do but do check out the &lt;a href=&quot;https://getbootstrap.com/docs/4.1/getting-started/introduction/&quot;&gt;Bootstrap docs&lt;/a&gt; to find out more.&lt;/p&gt;
&lt;p&gt;The first template you&amp;rsquo;ll create is for the blog index in a new file &lt;code&gt;blog/templates/blog_index.html&lt;/code&gt;. This will be very similar to the projects index view.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll use a for loop to loop over all the posts. For each post, you&amp;rsquo;ll display the title and a snippet of the body. As always, you&amp;rsquo;ll extend the base template &lt;code&gt;personal_porfolio/templates/base.html&lt;/code&gt;, which contains our navigation bar and some extra formatting:&lt;/p&gt;
&lt;div class=&quot;highlight htmldjango&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;base.html&amp;quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;page_content&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;col-md-8 offset-md-2&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;Blog Index&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;    &lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;blog_detail&amp;#39;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post.pk&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post.title&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;small&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;        &lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post.created_on.date&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt; |&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;        Categories:&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;        &lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;category&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post.categories.all&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;blog_category&amp;#39;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;category.name&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;            &lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;category.name&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;        &lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endfor&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;small&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post.body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&amp;quot;:400&amp;quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt;...&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18 &lt;/span&gt;    &lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endfor&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20 &lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endblock&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On &lt;strong&gt;line 7&lt;/strong&gt;, we have the post title, which is a hyperlink. The link is a Django link where we are pointing to the URL named &lt;code&gt;blog_detail&lt;/code&gt;, which takes an integer as its argument and should correspond to the &lt;code&gt;pk&lt;/code&gt; value of the post.&lt;/p&gt;
&lt;p&gt;Underneath the title, we&amp;rsquo;ll display the &lt;code&gt;created_on&lt;/code&gt; attribute of the post as well as its categories. On &lt;strong&gt;line 11&lt;/strong&gt;, we use another for loop to loop over all the categories assigned to the post.&lt;/p&gt;
&lt;p&gt;On &lt;strong&gt;line 17&lt;/strong&gt;, we use a &lt;strong&gt;template filter&lt;/strong&gt; &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#slice&quot;&gt;&lt;code&gt;slice&lt;/code&gt;&lt;/a&gt; to cut off the post body at 400 characters so that the blog index is more readable.&lt;/p&gt;
&lt;p&gt;Once that&amp;rsquo;s in place, you should be able to access this page by visiting &lt;code&gt;localhost:8000/blog&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-12-17_at_23.50.45.fedcaa17e99d.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Screenshot_2018-12-17_at_23.50.45.fedcaa17e99d.png&quot; width=&quot;2784&quot; height=&quot;1628&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-17_at_23.50.45.fedcaa17e99d.png&amp;amp;w=696&amp;amp;sig=b6b77490140a81ab853e03e32c26e3288e5ab0c4 696w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-17_at_23.50.45.fedcaa17e99d.png&amp;amp;w=1392&amp;amp;sig=8f94d6fc2af62032514ef52372729769fd55f2c9 1392w, https://files.realpython.com/media/Screenshot_2018-12-17_at_23.50.45.fedcaa17e99d.png 2784w&quot; sizes=&quot;75vw&quot; alt=&quot;Blog Index View&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Next, create another HTML file &lt;code&gt;blog/templates/blog_category.html&lt;/code&gt; where your &lt;code&gt;blog_category&lt;/code&gt; template will live. This should be identical to &lt;code&gt;blog_index.html&lt;/code&gt;, except with the category name inside the &lt;code&gt;h1&lt;/code&gt; tag instead of &lt;code&gt;Blog Index&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight htmldjango&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;base.html&amp;quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;page_content&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;col-md-8 offset-md-2&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;category&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;blog_detail&amp;#39;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post.pk&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post.title&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;small&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post.created_on.date&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt; |&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;
            Categories:&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;
            &lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;category&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post.categories.all&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;blog_category&amp;#39;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;category.name&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;category.name&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;
            &lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endfor&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;small&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;post.body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&amp;quot;:400&amp;quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt;...&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endfor&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endblock&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Most of this template is identical to the previous template. The only difference is on &lt;strong&gt;line 4&lt;/strong&gt;, where we use another Django template filter &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#title&quot;&gt;&lt;code&gt;title&lt;/code&gt;&lt;/a&gt;. This applies titlecase to the string and makes words start with an uppercase character.&lt;/p&gt;
&lt;p&gt;With that template finished, you&amp;rsquo;ll be able to access your category view. If you defined a category named &lt;code&gt;python&lt;/code&gt;, you should be able to visit &lt;code&gt;localhost:8000/blog/python&lt;/code&gt; and see all the posts with that category:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-12-17_at_23.50.51.08dadaa185fc.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Screenshot_2018-12-17_at_23.50.51.08dadaa185fc.png&quot; width=&quot;2784&quot; height=&quot;1628&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-17_at_23.50.51.08dadaa185fc.png&amp;amp;w=696&amp;amp;sig=ba04bfb8c05fe5f7aed0aad6708a93b23286ccc7 696w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-17_at_23.50.51.08dadaa185fc.png&amp;amp;w=1392&amp;amp;sig=79cb0ff0b706773f0b4e2d542b30437ce91e2824 1392w, https://files.realpython.com/media/Screenshot_2018-12-17_at_23.50.51.08dadaa185fc.png 2784w&quot; sizes=&quot;75vw&quot; alt=&quot;Blog Category View&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The last template to create is the &lt;code&gt;post_detail&lt;/code&gt; template. In this template, you&amp;rsquo;ll display the title and full body of a post.&lt;/p&gt;
&lt;p&gt;Between the title and the body of the post, you&amp;rsquo;ll display the date the post was created and any categories. Underneath that, you&amp;rsquo;ll include a comments form so users can add a new comment. Under this, there will be a list of comments that have already been left:&lt;/p&gt;
&lt;div class=&quot;highlight html&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;{% extends &amp;quot;base.html&amp;quot; %}
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;{% block page_content %}
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;col-md-8 offset-md-2&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;{{ post.title }}&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;small&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;        {{ post.created_on.date }} |&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;        Categories:&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;        {% for category in post.categories.all %}
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;{% url &amp;#39;blog_category&amp;#39; category.name %}&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;            {{ category.name }}
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;        {% endfor %}
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;small&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;{{ post.body | linebreaks }}&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;Leave a comment:&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;/blog/{{ post.pk }}/&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;post&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &lt;/span&gt;        {% csrf_token %}
&lt;span class=&quot;lineno&quot;&gt;18 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;form-group&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19 &lt;/span&gt;            {{ form.author }}
&lt;span class=&quot;lineno&quot;&gt;20 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;form-group&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22 &lt;/span&gt;            {{ form.body }}
&lt;span class=&quot;lineno&quot;&gt;23 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;btn btn-primary&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;25 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;26 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;Comments:&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;27 &lt;/span&gt;    {% for comment in comments %}
&lt;span class=&quot;lineno&quot;&gt;28 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;29 &lt;/span&gt;        On {{comment.created_on.date }}&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;30 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;{{ comment.author }}&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; wrote:
&lt;span class=&quot;lineno&quot;&gt;31 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;32 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;{{ comment.body }}&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;33 &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;hr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;34 &lt;/span&gt;    {% endfor %}
&lt;span class=&quot;lineno&quot;&gt;35 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;36 &lt;/span&gt;{% endblock %}
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first few lines of the template in which we display the post title, date, and categories is the same logic as for the previous templates. This time, when rendering the post body, use a &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#linebreaks&quot;&gt;&lt;code&gt;linebreaks&lt;/code&gt;&lt;/a&gt; template filter. This tag registers line breaks as new paragraphs, so the body doesn&amp;rsquo;t appear as one long block of text.&lt;/p&gt;
&lt;p&gt;Underneath the post, on &lt;strong&gt;line 16&lt;/strong&gt;, you&amp;rsquo;ll display your form. The form action points to the URL path of the page to which you&amp;rsquo;re sending the &lt;code&gt;POST&lt;/code&gt; request to. In this case, it&amp;rsquo;s the same as the page that is currently being visited. You then add a &lt;code&gt;csrf_token&lt;/code&gt;, which provides security and renders the body and author fields of the form, followed by a submit button.&lt;/p&gt;
&lt;p&gt;To get the bootstrap styling on the author and body fields, you need to add the &lt;code&gt;form-control&lt;/code&gt; class to the text inputs.&lt;/p&gt;
&lt;p&gt;Because Django renders the inputs for you when you include &lt;code&gt;{{ form.body }}&lt;/code&gt; and &lt;code&gt;{{ form.author }}&lt;/code&gt;, you can&amp;rsquo;t add these classes in the template. That&amp;rsquo;s why you added the attributes to the form widgets in the previous section.&lt;/p&gt;
&lt;p&gt;Underneath the form, there&amp;rsquo;s another for loop that loops over all the comments on the given post. The comments, &lt;code&gt;body&lt;/code&gt;, &lt;code&gt;author&lt;/code&gt;, and &lt;code&gt;created_on&lt;/code&gt; attributes are all displayed.&lt;/p&gt;
&lt;p&gt;Once that template is in place, you should be able to visit &lt;code&gt;localhost:8000/blog/1&lt;/code&gt; and view your first post:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screenshot_2018-12-17_at_23.51.17.8849893e9b69.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Screenshot_2018-12-17_at_23.51.17.8849893e9b69.png&quot; width=&quot;2770&quot; height=&quot;2046&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-17_at_23.51.17.8849893e9b69.png&amp;amp;w=692&amp;amp;sig=84cd5d93b54a6af67a8e0fa6fe041ff71685e60c 692w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-12-17_at_23.51.17.8849893e9b69.png&amp;amp;w=1385&amp;amp;sig=5e31cb4041dccc36447872e5a5528f8afc90b276 1385w, https://files.realpython.com/media/Screenshot_2018-12-17_at_23.51.17.8849893e9b69.png 2770w&quot; sizes=&quot;75vw&quot; alt=&quot;Blog Detail View&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You should also be able to access the post detail pages by clicking on their title in the &lt;code&gt;blog_index&lt;/code&gt; view.&lt;/p&gt;
&lt;p&gt;The final finishing touch is to add a link to the &lt;code&gt;blog_index&lt;/code&gt; to the navigation bar in &lt;code&gt;base.html&lt;/code&gt;. This way, when you click on &lt;em&gt;Blog&lt;/em&gt; in the navigation bar, you&amp;rsquo;ll be able to visit the blog. Check out &lt;a href=&quot;https://github.com/realpython/materials/blob/4dd5d79634efbffeb8999052a9e94b3dba4b25ba/rp-portfolio/personal_portfolio/templates/base.html#L16&quot;&gt;the updates&lt;/a&gt; to &lt;code&gt;base.html&lt;/code&gt; in the source code to see how to add that link.&lt;/p&gt;
&lt;p&gt;With that now in place, your personal portfolio site is complete, and you&amp;rsquo;ve created your first Django site. The final version of the &lt;a href=&quot;https://github.com/realpython/materials/tree/4dd5d79634efbffeb8999052a9e94b3dba4b25ba/rp-portfolio&quot;&gt;source code&lt;/a&gt; containing all the features can be found on GitHub, so check it out! Click around the site a bit to see all the functionality and try leaving some comments on your posts!&lt;/p&gt;
&lt;p&gt;You may find a few things here and there that you think need polishing. Go ahead and tidy them up. The best way to learn more about this web framework is through practice, so try to extend this project and make it even better! If you&amp;rsquo;re not sure where to start, I&amp;rsquo;ve left a few ideas for you in the conclusion below!&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Congratulations, you&amp;rsquo;ve reached the end of the tutorial! We&amp;rsquo;ve covered a lot, so make sure to keep practicing and building. The more you build the easier it will become and the less you&amp;rsquo;ll have to refer back to this article or the &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/&quot;&gt;documentation&lt;/a&gt;. You&amp;rsquo;ll be building sophisticated web applications in no time.&lt;/p&gt;
&lt;p&gt;In this tutorial you&amp;rsquo;ve seen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to create Django &lt;strong&gt;projects&lt;/strong&gt; and &lt;strong&gt;apps&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;How to add web pages with &lt;strong&gt;views&lt;/strong&gt; and &lt;strong&gt;templates&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;How to get user input with &lt;strong&gt;forms&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;How to hook your views and templates up with &lt;strong&gt;URL&lt;/strong&gt; configurations&lt;/li&gt;
&lt;li&gt;How to add data to your site using relational databases with Django&amp;rsquo;s &lt;strong&gt;Object Relational Mapper&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;How to use the &lt;strong&gt;Django Admin&lt;/strong&gt; to manage your models&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition, you&amp;rsquo;ve learned about the MVT structure of Django web applications and why Django is such a good choice for web development.&lt;/p&gt;
&lt;p&gt;If you want to learn more about Django, do check out the &lt;a href=&quot;https://docs.djangoproject.com/en/2.1/&quot;&gt;documentation&lt;/a&gt; and make sure to stay tuned for Part 2 of this series!&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Working With JSON Data in Python</title>
      <id>https://realpython.com/courses/working-json-data-python/</id>
      <link href="https://realpython.com/courses/working-json-data-python/"/>
      <updated>2019-03-28T14:00:00+00:00</updated>
      <summary>Learn how to work with Python&#39;s built-in &quot;json&quot; module to serialize the data in your programs into JSON format. Then, you&#39;ll deserialize some JSON from an online API and convert it into Python objects.</summary>
      <content type="html">
        &lt;p&gt;JSON is a lightweight data-interchange format. It allows us to represent the objects in our Python programs as human-readable text that can be sent over the internet. Lots of APIs and databases use JSON for communication.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll learn how to work with Python&amp;rsquo;s built-in &lt;code&gt;json&lt;/code&gt; module to serialize the data in your programs into JSON format. Then, you&amp;rsquo;ll deserialize some JSON from an online API and convert it into Python objects.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>How to Stand Out in a Python Coding Interview</title>
      <id>https://realpython.com/python-coding-interview-tips/</id>
      <link href="https://realpython.com/python-coding-interview-tips/"/>
      <updated>2019-03-27T14:00:00+00:00</updated>
      <summary>In this step-by-step tutorial, you&#39;ll learn how to take your Python coding interview skills to the next level and use Python&#39;s built-in functions and modules to solve problems faster and more easily.</summary>
      <content type="html">
        &lt;p&gt;You&amp;rsquo;ve made it past the phone call with the recruiter, and now it&amp;rsquo;s time to show that you know how to solve problems with actual code. Whether it&amp;rsquo;s a HackerRank exercise, a take-home assignment, or an onsite whiteboard interview, this is your moment to prove your coding interview skills.&lt;/p&gt;
&lt;p&gt;But interviews aren&amp;rsquo;t just about solving problems: they&amp;rsquo;re also about showing that you can write clean production code. This means that you have a deep knowledge of Python&amp;rsquo;s built-in functionality and libraries. This knowledge shows companies that you can move quickly and won&amp;rsquo;t duplicate functionality that comes with the language just because you don&amp;rsquo;t know it exists.&lt;/p&gt;
&lt;p&gt;At &lt;em&gt;Real Python&lt;/em&gt;, we&amp;rsquo;ve put our heads together and discussed what tools we&amp;rsquo;re always impressed to see in coding interviews. This article will walk you through the best of that functionality, starting with Python built-ins, then Python&amp;rsquo;s native support for data structures, and finally Python&amp;rsquo;s powerful (and often underappreciated) standard library.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this article, you&amp;rsquo;ll learn how to:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;enumerate()&lt;/code&gt; to iterate over both indices and values&lt;/li&gt;
&lt;li&gt;Debug problematic code with &lt;code&gt;breakpoint()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Format strings effectively with f-strings&lt;/li&gt;
&lt;li&gt;Sort lists with custom arguments&lt;/li&gt;
&lt;li&gt;Use generators instead of list comprehensions to conserve memory&lt;/li&gt;
&lt;li&gt;Define default values when looking up dictionary keys&lt;/li&gt;
&lt;li&gt;Count hashable objects with the &lt;code&gt;collections.Counter&lt;/code&gt; class&lt;/li&gt;
&lt;li&gt;Use the standard library to get lists of permutations and combinations&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-mastery-course&quot; data-focus=&quot;false&quot;&gt;5 Thoughts On Python Mastery&lt;/a&gt;, a free course for Python developers that shows you the roadmap and the mindset you&#39;ll need to take your Python skills to the next level.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;select-the-right-built-in-function-for-the-job&quot;&gt;Select the Right Built-In Function for the Job&lt;/h2&gt;
&lt;p&gt;Python has a large standard library but only a small library of &lt;a href=&quot;https://docs.python.org/library/functions.html&quot;&gt;built-in functions&lt;/a&gt;, which are always available and don&amp;rsquo;t need to be imported. It&amp;rsquo;s worth going through each one, but until you get the chance to do so, here are a few built-in functions worth understanding how to use, and in the case of some of them, what alternatives to use instead.&lt;/p&gt;
&lt;h3 id=&quot;iterate-with-enumerate-instead-of-range&quot;&gt;Iterate With &lt;code&gt;enumerate()&lt;/code&gt; Instead of &lt;code&gt;range()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This scenario might come up more than any other in coding interviews: you have a list of elements, and you need to iterate over the list with access to both the indices and the values.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a classic coding interview question named FizzBuzz that can be solved by iterating over both indices and values. In FizzBuzz, you are given a list of integers. Your task is to do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Replace all integers that are evenly divisible by &lt;code&gt;3&lt;/code&gt; with &lt;code&gt;&quot;fizz&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Replace all integers divisible by &lt;code&gt;5&lt;/code&gt; with &lt;code&gt;&quot;buzz&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Replace all integers divisible by both &lt;code&gt;3&lt;/code&gt; and &lt;code&gt;5&lt;/code&gt; with &lt;code&gt;&quot;fizzbuzz&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Often, developers will solve this problem with &lt;code&gt;range()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;45&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;97&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;72&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;fizzbuzz&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;fizzbuzz&amp;#39;, 22, 14, &amp;#39;buzz&amp;#39;, 97, &amp;#39;fizz&amp;#39;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Range allows you to access the elements of &lt;code&gt;numbers&lt;/code&gt; by index and is a useful tool &lt;a href=&quot;https://realpython.com/python-range/&quot;&gt;for some situations&lt;/a&gt;. But in this case, where you want to get each element&amp;rsquo;s index and value at the same time, a more elegant solution uses &lt;code&gt;enumerate()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;45&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;97&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;72&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;fizzbuzz&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;fizzbuzz&amp;#39;, 22, 14, &amp;#39;buzz&amp;#39;, 97, &amp;#39;fizz&amp;#39;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For each element, &lt;code&gt;enumerate()&lt;/code&gt; returns a counter and the element value. The counter defaults to &lt;code&gt;0&lt;/code&gt;, which conveniently is also the element&amp;rsquo;s index. Don&amp;rsquo;t want to start your count at &lt;code&gt;0&lt;/code&gt;? Just use the optional &lt;code&gt;start&lt;/code&gt; parameter to set an offset:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;45&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;97&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;72&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;52&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;52 45&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;53 22&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;54 14&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;55 65&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;56 97&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;57 72&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By using the &lt;code&gt;start&lt;/code&gt; parameter, we access all of the same elements, starting with the first index, but now our count starts from the specified integer value.&lt;/p&gt;
&lt;h3 id=&quot;use-list-comprehensions-instead-of-map-and-filter&quot;&gt;Use List Comprehensions Instead of &lt;code&gt;map()&lt;/code&gt; and &lt;code&gt;filter()&lt;/code&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;I think dropping filter() and map() is pretty uncontroversial[.]&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;mdash; &lt;em&gt;Guido van Rossum, Python&amp;rsquo;s creator&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He may have been wrong about it being uncontroversial, but Guido had good reasons for wanting to remove &lt;code&gt;map()&lt;/code&gt; and &lt;code&gt;filter()&lt;/code&gt; from Python. One reason is that Python supports list comprehensions, which are often easier to read and support the same functionality as &lt;code&gt;map()&lt;/code&gt; and &lt;code&gt;filter()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s first take a look at how we&amp;rsquo;d structure a call to &lt;code&gt;map()&lt;/code&gt; and the equivalent list comprehension:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[16, 4, 1, 36, 81, 49]&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[16, 4, 1, 36, 81, 49]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Both approaches, using &lt;code&gt;map()&lt;/code&gt; and the list comprehension, return the same values, but the list comprehension is easier to read and understand.&lt;/p&gt;
&lt;p&gt;Now we can do the same thing for the &lt;code&gt;filter()&lt;/code&gt; and its equivalent list comprehension:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_odd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_odd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[1, 9, 7]&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_odd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[1, 9, 7]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Like we saw with &lt;code&gt;map()&lt;/code&gt;, the &lt;code&gt;filter()&lt;/code&gt; and list comprehension approaches return the same value, but the list comprehension is easier to follow.&lt;/p&gt;
&lt;p&gt;Developers who come from other languages may disagree that list comprehensions are easier to read than &lt;code&gt;map()&lt;/code&gt; and &lt;code&gt;filter()&lt;/code&gt;, but in my experience beginners are able to pick up list comprehensions much more intuitively.&lt;/p&gt;
&lt;p&gt;Either way, you&amp;rsquo;ll rarely go wrong using list comprehensions in a coding interview, as it will communicate that you know what&amp;rsquo;s most common in Python.&lt;/p&gt;
&lt;h3 id=&quot;debug-with-breakpoint-instead-of-print&quot;&gt;Debug With &lt;code&gt;breakpoint()&lt;/code&gt; Instead of &lt;code&gt;print()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;You may have debugged a small problem by adding &lt;code&gt;print()&lt;/code&gt; to your code and seeing what was printed out. This approach works well at first but quickly becomes cumbersome. Plus, in a coding interview setting, you hardly want &lt;code&gt;print()&lt;/code&gt; calls peppered throughout your code.&lt;/p&gt;
&lt;p&gt;Instead, you should be using a debugger. For non-trivial bugs, it&amp;rsquo;s almost always faster than using &lt;code&gt;print()&lt;/code&gt;, and given that debugging is a big part of writing software, it shows that you know how to use tools that will let you develop quickly on the job.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re using Python 3.7, you don&amp;rsquo;t need to import anything and can just call &lt;a href=&quot;https://realpython.com/python37-new-features/#the-breakpoint-built-in&quot;&gt;&lt;code&gt;breakpoint()&lt;/code&gt;&lt;/a&gt; at the location in your code where you&amp;rsquo;d like to drop into the debugger:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Some complicated code with bugs&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Calling &lt;code&gt;breakpoint()&lt;/code&gt; will put you into &lt;a href=&quot;https://realpython.com/python-debugging-pdb/&quot;&gt;&lt;code&gt;pdb&lt;/code&gt;&lt;/a&gt;, which is the default Python debugger. On Python 3.6 and older, you can do the same by importing &lt;code&gt;pdb&lt;/code&gt; explicitly:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pdb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pdb&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_trace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Like &lt;code&gt;breakpoint()&lt;/code&gt;, &lt;code&gt;pdb.set_trace()&lt;/code&gt; will put you into the &lt;code&gt;pdb&lt;/code&gt; debugger. It&amp;rsquo;s just not quite as clean and is a tad more to remember.&lt;/p&gt;
&lt;p&gt;There are other debuggers available that you may want to try, but &lt;code&gt;pdb&lt;/code&gt; is part of the standard library, so it&amp;rsquo;s always available. Whatever debugger you prefer, it&amp;rsquo;s worth trying them out to get used to the workflow before you&amp;rsquo;re in a coding interview setting.&lt;/p&gt;
&lt;h3 id=&quot;format-strings-with-f-strings&quot;&gt;Format strings With f-Strings&lt;/h3&gt;
&lt;p&gt;Python has a lot of different ways to handle string formatting, and it can be tricky to know what to use. In fact, we tackle formatting in depth in two separate articles: one about &lt;a href=&quot;https://realpython.com/python-string-formatting/&quot;&gt;string formatting in general&lt;/a&gt; and one specifically &lt;a href=&quot;https://realpython.com/python-f-strings/&quot;&gt;focused on f-strings&lt;/a&gt;. In a coding interview, where you&amp;rsquo;re (hopefully) using Python 3.6+, the suggested formatting approach is Python&amp;rsquo;s f-strings.&lt;/p&gt;
&lt;p&gt;f-strings support use of the &lt;a href=&quot;https://docs.python.org/3/library/string.html#format-specification-mini-language&quot;&gt;string formatting mini-language&lt;/a&gt;, as well as powerful string interpolation. These features allow you to add variables or even valid Python expressions and have them evaluated at runtime before being added to the string:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_name_and_decades&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;My name is &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; and I&amp;#39;m {age / 10:.5f} decades old.&amp;quot;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_name_and_decades&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Maria&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;My name is Maria and I&amp;#39;m 3.10000 decades old.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The f-string allows you to put &lt;code&gt;Maria&lt;/code&gt; into the string and add her age with the desired formatting in one succinct operation.&lt;/p&gt;
&lt;p&gt;The one risk to be aware of is that if you&amp;rsquo;re outputting user-generated values, then that can introduce security risks, in which case &lt;a href=&quot;https://realpython.com/python-string-formatting/#4-template-strings-standard-library&quot;&gt;Template Strings&lt;/a&gt; may be a safer option.&lt;/p&gt;
&lt;h3 id=&quot;sort-complex-lists-with-sorted&quot;&gt;Sort Complex Lists With &lt;code&gt;sorted()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Plenty of coding interview questions require some kind of sorting, and there are multiple valid ways you can sort items. Unless the interviewer wants you to implement your own sorting algorithm, it&amp;rsquo;s usually best to use &lt;code&gt;sorted()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ve probably seen the most simple uses of sorting, such as sorting lists of numbers or strings in ascending or descending order:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[1, 2, 3, 4, 5, 6, 7]&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cat&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;dog&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;cheetah&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;rhino&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;bear&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;rhino&amp;#39;, &amp;#39;dog&amp;#39;, &amp;#39;cheetah&amp;#39;, &amp;#39;cat&amp;#39;, &amp;#39;bear]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By default, &lt;code&gt;sorted()&lt;/code&gt; has sorted the input in ascending order, and the &lt;code&gt;reverse&lt;/code&gt; keyword argument causes it to sort in descending order.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s worth knowing about the optional keyword argument &lt;code&gt;key&lt;/code&gt; that lets you specify a function that will be called on every element prior to sorting. Adding a function allows custom sorting rules, which are especially helpful if you want to sort more complex data types:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;animals&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;type&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;penguin&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Stephanie&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;age&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;type&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;elephant&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Devon&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;age&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;type&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;puma&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Moe&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;age&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;animals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;age&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    {&amp;#39;type&amp;#39;: &amp;#39;elephant&amp;#39;, &amp;#39;name&amp;#39;: &amp;#39;Devon&amp;#39;, &amp;#39;age&amp;#39;: 3},&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    {&amp;#39;type&amp;#39;: &amp;#39;puma&amp;#39;, &amp;#39;name&amp;#39;: &amp;#39;Moe&amp;#39;, &amp;#39;age&amp;#39;: 5},&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    {&amp;#39;type&amp;#39;: &amp;#39;penguin&amp;#39;, &amp;#39;name&amp;#39;: &amp;#39;Stephanie, &amp;#39;age&amp;#39;: 8},&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By passing in a &lt;a href=&quot;https://docs.python.org/3/tutorial/controlflow.html#lambda-expressions&quot;&gt;lambda function&lt;/a&gt; that returns each element&amp;rsquo;s age, you can easily sort a list of dictionaries by a single value of each of those dictionaries. In this case, the dictionary is now sorted in ascending order by age.&lt;/p&gt;
&lt;h2 id=&quot;leverage-data-structures-effectively&quot;&gt;Leverage Data Structures Effectively&lt;/h2&gt;
&lt;p&gt;Algorithms get a lot of attention in coding interviews, but data structures are arguably even more important. In a coding interviewing context, picking the right data structure can have a major impact on performance.&lt;/p&gt;
&lt;p&gt;Beyond theoretical data structures, Python has powerful and convenient functionality built into its standard data structure implementations. These data structures are incredibly useful in coding interviews because they give you lots of functionality by default and let you focus your time on other parts of the problem.&lt;/p&gt;
&lt;h3 id=&quot;store-unique-values-with-sets&quot;&gt;Store Unique Values With Sets&lt;/h3&gt;
&lt;p&gt;You&amp;rsquo;ll often need to remove duplicate elements from an existing dataset. New developers will sometimes do so with lists when they should be using sets, which enforce the uniqueness of all elements.&lt;/p&gt;
&lt;p&gt;Pretend you have a function named &lt;code&gt;get_random_word()&lt;/code&gt;. It will always return a random selection from a small set of words:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;random&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all_words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;all the words in the world&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_random_word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You&amp;rsquo;re supposed to call &lt;code&gt;get_random_word()&lt;/code&gt; repeatedly to get 1000 random words and then return a data structure containing every unique word. Here are two common, suboptimal approaches and one good approach.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bad Approach&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;get_unique_words()&lt;/code&gt; stores values in a list then converts the list into a set:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_unique_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_random_word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_unique_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;world&amp;#39;, &amp;#39;all&amp;#39;, &amp;#39;the&amp;#39;, &amp;#39;words&amp;#39;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This approach isn&amp;rsquo;t terrible, but it unnecessarily creates a list and then converts it to a set. Interviewers almost always notice (and ask about) this type of design choice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Worse Approach&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To avoid converting from a list to a set, you now store values in a list without using any other data structures. You then test for uniqueness by comparing new values with all elements currently in the list:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_unique_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_random_word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_unique_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;world&amp;#39;, &amp;#39;all&amp;#39;, &amp;#39;the&amp;#39;, &amp;#39;words&amp;#39;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is worse than the first approach, because you have to compare every new word against every word already in the list. That means that as the number of words grows, the number of lookups grows quadratically. In other words, the time complexity grows on the order of O(N²).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Good Approach&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now, you skip using lists altogether and instead use a set from the start:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_unique_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_random_word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_unique_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;world&amp;#39;, &amp;#39;all&amp;#39;, &amp;#39;the&amp;#39;, &amp;#39;words&amp;#39;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This may not look much different than the other approaches except for making use of a set from the beginning. If you consider what&amp;rsquo;s happening within &lt;code&gt;.add()&lt;/code&gt;, it even sounds like the second approach: get the word, check if it&amp;rsquo;s already in the set, and if not, add it to the data structure.&lt;/p&gt;
&lt;p&gt;So why is using a set different from the second approach?&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s different because sets store elements in a manner that allows near-constant-time checks whether a value is in the set or not, unlike lists, which require linear-time lookups. The difference in lookup time means that the &lt;a href=&quot;https://stackoverflow.com/a/487278/2141768&quot;&gt;time complexity&lt;/a&gt; for adding to a set grows at a rate of O(N), which is much better than the O(N²) from the second approach in most cases.&lt;/p&gt;
&lt;h3 id=&quot;save-memory-with-generators&quot;&gt;Save Memory With Generators&lt;/h3&gt;
&lt;p&gt;List comprehensions are convenient tools but can sometimes lead to unnecessary memory usage.&lt;/p&gt;
&lt;p&gt;Imagine you&amp;rsquo;ve been asked to find the sum of the first 1000 perfect squares, starting with 1. You know about list comprehensions, so you quickly code up a working solution:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;333833500&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Your solution makes a list of every perfect square between 1 and 1,000,000 and sums the values. Your code returns the right answer, but then your interviewer starts increasing the number of perfect squares you need to sum.&lt;/p&gt;
&lt;p&gt;At first, your function keeps popping out the right answer, but soon it starts slowing down until eventually the process seems to hang for an eternity. This is the last thing you want happening in a coding interview.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s going on here?&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s making a list of every perfect square you&amp;rsquo;ve requested and summing them all. A list with 1000 perfect squares may not be large in computer-terms, but 100 million or 1 billion is quite a bit of information and can easily overwhelm your computer&amp;rsquo;s available memory resources. That&amp;rsquo;s what&amp;rsquo;s happening here.&lt;/p&gt;
&lt;p&gt;Thankfully, there&amp;rsquo;s a quick way to solve the memory problem. You just replace the brackets with parentheses:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;333833500&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Swapping out the brackets changes your list comprehension into a &lt;a href=&quot;https://realpython.com/introduction-to-python-generators/&quot;&gt;generator expression&lt;/a&gt;. Generator expressions are perfect for when you know you want to retrieve data from a sequence, but you don&amp;rsquo;t need to access all of it at the same time.&lt;/p&gt;
&lt;p&gt;Instead of creating a list, the generator expression returns a &lt;code&gt;generator&lt;/code&gt; object. That object knows where it is in the current state (for example, &lt;code&gt;i = 49&lt;/code&gt;) and only calculates the next value when it&amp;rsquo;s asked for.&lt;/p&gt;
&lt;p&gt;So when &lt;code&gt;sum&lt;/code&gt; iterates over the generator object by calling &lt;code&gt;.__next__()&lt;/code&gt; repeatedly, the generator checks what &lt;code&gt;i&lt;/code&gt; equals, calculates &lt;code&gt;i * i&lt;/code&gt;, increments &lt;code&gt;i&lt;/code&gt; internally, and returns the proper value to &lt;code&gt;sum&lt;/code&gt;. The design allows generators to be used on massive sequences of data, because only one element exists in memory at a time.&lt;/p&gt;
&lt;h3 id=&quot;define-default-values-in-dictionaries-with-get-and-setdefault&quot;&gt;Define Default Values in Dictionaries With &lt;code&gt;.get()&lt;/code&gt; and &lt;code&gt;.setdefault()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;One of the most common programming tasks involves adding, modifying, or retrieving an item that may or may not be in a dictionary. Python dictionaries have elegant functionality to make these tasks clean and easy, but developers often check explicitly for values when it isn&amp;rsquo;t necessary.&lt;/p&gt;
&lt;p&gt;Imagine you have a dictionary named &lt;code&gt;cowboy&lt;/code&gt;, and you want to get that cowboy&amp;rsquo;s name. One approach is to explicitly check for the key with a conditional:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cowboy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;age&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;horse&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;mustang&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;hat_size&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;large&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cowboy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cowboy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;The Man with No Name&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;The Man with No Name&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This approach first checks if the &lt;code&gt;name&lt;/code&gt; key exists in the dictionary, and if so, it returns the corresponding value. Otherwise, it returns a default value.&lt;/p&gt;
&lt;p&gt;While explicitly checking for keys does work, it can easily be replaced with one line if you use &lt;code&gt;.get()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cowboy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;The Man with No Name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;get()&lt;/code&gt; performs the same operations that were performed in the first approach, but now they&amp;rsquo;re handled automatically. If the key exists, then the proper value will be returned. Otherwise, the default value will get returned.&lt;/p&gt;
&lt;p&gt;But what if you want to update the dictionary with a default value while still accessing the &lt;code&gt;name&lt;/code&gt; key? &lt;code&gt;.get()&lt;/code&gt; doesn&amp;rsquo;t really help you here, so you&amp;rsquo;re left with explicitly checking for the value again:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cowboy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;cowboy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;The Man with No Name&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cowboy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Checking for the value and setting a default is a valid approach and is easy to read, but again Python offers a more elegant method with &lt;code&gt;.setdefault()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cowboy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setdefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;The Man with No Name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.setdefault()&lt;/code&gt; accomplishes exactly the same thing as the snippet above. It checks if &lt;code&gt;name&lt;/code&gt; exists in &lt;code&gt;cowboy&lt;/code&gt;, and if so it returns that value. Otherwise, it sets &lt;code&gt;cowboy[&#39;name&#39;]&lt;/code&gt; to &lt;code&gt;The Man with No Name&lt;/code&gt; and returns the new value.&lt;/p&gt;
&lt;h2 id=&quot;take-advantage-of-pythons-standard-library&quot;&gt;Take Advantage of Python&amp;rsquo;s Standard Library&lt;/h2&gt;
&lt;p&gt;By default, Python comes with a lot of functionality that&amp;rsquo;s just an &lt;code&gt;import&lt;/code&gt; statement away. It&amp;rsquo;s powerful on its own, but knowing how to leverage the standard library can supercharge your coding interview skills.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s hard to pick the most useful pieces from all of the available modules, so this section will focus on just a small subset of its utility functions. Hopefully, these will prove useful to you in coding interviews and also whet your appetite to learn more about the advanced functionality of these and other modules.&lt;/p&gt;
&lt;h3 id=&quot;handle-missing-dictionary-keys-with-collectionsdefaultdict&quot;&gt;Handle Missing Dictionary Keys With &lt;code&gt;collections.defaultdict()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;.get()&lt;/code&gt; and &lt;code&gt;.setdefault()&lt;/code&gt; work well when you&amp;rsquo;re setting a default for a single key, but it&amp;rsquo;s common to want a default value for all possible unset keys, especially when programming in a coding interview context.&lt;/p&gt;
&lt;p&gt;Pretend you have a group of students, and you need to keep track of their grades on homework assignments. The input value is a list of tuples with the format &lt;code&gt;(student_name, grade)&lt;/code&gt;, but you want to easily look up all the grades for a single student without iterating over the list.&lt;/p&gt;
&lt;p&gt;One way to store the grade data uses a dictionary that maps student names to lists of grades:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;student_grades&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grades&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;elliot&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;91&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;neelam&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;98&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;bianca&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;81&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;elliot&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;88&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grade&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grades&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;student_grades&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;student_grades&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;student_grades&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;student_grades&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;elliot&amp;#39;: [91, 88], &amp;#39;neelam&amp;#39;: [98], &amp;#39;bianca&amp;#39;: [81]}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this approach, you iterate over the students and check if their names are already properties in the dictionary. If not, you add them to the dictionary with an empty list as the default value. You then append their actual grades to that student&amp;rsquo;s list of grades.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s an even cleaner approach that uses a &lt;code&gt;defaultdict&lt;/code&gt;,  which extends standard &lt;code&gt;dict&lt;/code&gt; functionality to allow you to set a default value that will be operated upon if the key doesn&amp;rsquo;t exist:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;collections&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultdict&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;student_grades&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultdict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grade&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grades&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;student_grades&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this case, you&amp;rsquo;re creating a &lt;code&gt;defaultdict&lt;/code&gt; that uses the &lt;code&gt;list()&lt;/code&gt; constructor with no arguments as a default factory method. &lt;code&gt;list()&lt;/code&gt; with no arguments returns an empty list, so &lt;code&gt;defaultdict&lt;/code&gt; calls &lt;code&gt;list()&lt;/code&gt; if the name doesn&amp;rsquo;t exist and then allows the grade to be appended. If you want to get fancy, you could also use a lambda function as your factory value to return an arbitrary constant.&lt;/p&gt;
&lt;p&gt;Leveraging a &lt;code&gt;defaultdict&lt;/code&gt; can lead to cleaner application code because you don&amp;rsquo;t have to worry about default values at the key level. Instead, you can handle them once at the &lt;code&gt;defaultdict&lt;/code&gt; level and afterwards act as if the key is always present.&lt;/p&gt;
&lt;h3 id=&quot;count-hashable-objects-with-collectionscounter&quot;&gt;Count Hashable Objects With &lt;code&gt;collections.Counter&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;You have a long string of words with no punctuation or capital letters and you want to count how many times each word appears.&lt;/p&gt;
&lt;p&gt;You could use a dictionary or &lt;code&gt;defaultdict&lt;/code&gt; and increment the counts, but &lt;code&gt;collections.Counter&lt;/code&gt; provides a cleaner and more convenient way to do exactly that. Counter is a subclass of &lt;code&gt;dict&lt;/code&gt; that uses &lt;code&gt;0&lt;/code&gt; as the default value for any missing element and makes it easier to count occurrences of objects:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;collections&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Counter&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;if there was there was but if &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;there was not there was not&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counts&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Counter({&amp;#39;if&amp;#39;: 2, &amp;#39;there&amp;#39;: 4, &amp;#39;was&amp;#39;: 4, &amp;#39;not&amp;#39;: 2, &amp;#39;but&amp;#39;: 1})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you pass in the list of words to &lt;code&gt;Counter&lt;/code&gt;, it stores each word along with a count of how many times that word occurred in the list.&lt;/p&gt;
&lt;p&gt;Are you curious what the two most common words were? Just use &lt;code&gt;.most_common()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;most_common&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[(&amp;#39;there&amp;#39;, 4), (&amp;#39;was&amp;#39;, 4)]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.most_common()&lt;/code&gt; is a convenience method and simply returns the &lt;code&gt;n&lt;/code&gt; most frequent inputs by count.&lt;/p&gt;
&lt;h3 id=&quot;access-common-string-groups-with-string-constants&quot;&gt;Access Common String Groups With &lt;code&gt;string&lt;/code&gt; Constants&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s trivia time! Is &lt;code&gt;&#39;A&#39; &amp;gt; &#39;a&#39;&lt;/code&gt; true or false?&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s false, because the ASCII code for &lt;code&gt;A&lt;/code&gt; is 65, but &lt;code&gt;a&lt;/code&gt; is 97, and 65 is not greater than 97.&lt;/p&gt;
&lt;p&gt;Why does the answer matter? Because if you want to check if a character is part of the English alphabet, one popular way is to see if it&amp;rsquo;s between &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;z&lt;/code&gt; (65 and 122 on the ASCII chart).&lt;/p&gt;
&lt;p&gt;Checking the ASCII code works but is clumsy and easy to mess up in coding interviews, especially if you can&amp;rsquo;t remember whether lowercase or uppercase ASCII characters come first. It&amp;rsquo;s much easier to use the constants defined as part of the &lt;a href=&quot;https://docs.python.org/3/library/string.html&quot;&gt;&lt;code&gt;string&lt;/code&gt; module&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can see one in use in &lt;code&gt;is_upper()&lt;/code&gt;, which returns whether all characters in a string are uppercase letters:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_upper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;letter&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;letter&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ascii_uppercase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_upper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Thanks Geir&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;False&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_upper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;LOL&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;is_upper()&lt;/code&gt; iterates over the letters in &lt;code&gt;word&lt;/code&gt;, and checks if the letters are part of &lt;code&gt;string.ascii_uppercase&lt;/code&gt;. If you print out &lt;code&gt;string.ascii_uppercase&lt;/code&gt; you&amp;rsquo;ll see that it&amp;rsquo;s just a lowly string. The value is set to the literal &lt;code&gt;&#39;ABCDEFGHIJKLMNOPQRSTUVWXYZ&#39;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;All &lt;code&gt;string&lt;/code&gt; constants are just strings of frequently referenced string values. They include the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;string.ascii_letters&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string.ascii_uppercase&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string.ascii_lowercase&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string.digits&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string.hexdigits&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string.octdigits&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string.punctuation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string.printable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string.whitespace&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are easier to use and, even more importantly, easier to read.&lt;/p&gt;
&lt;h3 id=&quot;generate-permutations-and-combinations-with-itertools&quot;&gt;Generate Permutations and Combinations With &lt;code&gt;itertools&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Interviewers love to give real life scenarios to make coding interviews seem less intimidating, so here&amp;rsquo;s a contrived example: you go to an amusement park and decide to figure out every possible pair of friends that could sit together on a roller coaster.&lt;/p&gt;
&lt;p&gt;Unless generating these pairs is the primary purpose of the interview question, it&amp;rsquo;s likely that generating all the possible pairs is just a tedious step on the way towards a working algorithm. You could calculate them yourself with nested for-loops, or you could use the powerful &lt;a href=&quot;https://realpython.com/python-itertools/&quot;&gt;&lt;code&gt;itertools&lt;/code&gt; library&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;itertools&lt;/code&gt; has multiple tools for generating iterable sequences of input data, but right now we&amp;rsquo;ll just focus on two common functions: &lt;code&gt;itertools.permutations()&lt;/code&gt; and &lt;code&gt;itertools.combinations()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;itertools.permutations()&lt;/code&gt; builds a list of all permutations, meaning it&amp;rsquo;s a list of every possible grouping of input values with a length matching the &lt;code&gt;count&lt;/code&gt; parameter. The &lt;code&gt;r&lt;/code&gt; keyword argument lets us specify how many values go in each grouping:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;itertools&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;friends&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Monique&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Ashish&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Devon&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Bernie&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itertools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;permutations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;friends&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[(&amp;#39;Monique&amp;#39;, &amp;#39;Ashish&amp;#39;), (&amp;#39;Monique&amp;#39;, &amp;#39;Devon&amp;#39;), (&amp;#39;Monique&amp;#39;, &amp;#39;Bernie&amp;#39;),&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(&amp;#39;Ashish&amp;#39;, &amp;#39;Monique&amp;#39;), (&amp;#39;Ashish&amp;#39;, &amp;#39;Devon&amp;#39;), (&amp;#39;Ashish&amp;#39;, &amp;#39;Bernie&amp;#39;),&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(&amp;#39;Devon&amp;#39;, &amp;#39;Monique&amp;#39;), (&amp;#39;Devon&amp;#39;, &amp;#39;Ashish&amp;#39;), (&amp;#39;Devon&amp;#39;, &amp;#39;Bernie&amp;#39;),&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(&amp;#39;Bernie&amp;#39;, &amp;#39;Monique&amp;#39;), (&amp;#39;Bernie&amp;#39;, &amp;#39;Ashish&amp;#39;), (&amp;#39;Bernie&amp;#39;, &amp;#39;Devon&amp;#39;)]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With permutations, the order of the elements matters, so &lt;code&gt;(&#39;sam&#39;, &#39;devon&#39;)&lt;/code&gt; represents a different pairing than &lt;code&gt;(&#39;devon&#39;, &#39;sam&#39;)&lt;/code&gt;, meaning that they would both be included in the list.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;itertools.combinations()&lt;/code&gt; builds combinations. These are also the possible groupings of the input values, but now the order of the values doesn&amp;rsquo;t matter. Because &lt;code&gt;(&#39;sam&#39;, &#39;devon&#39;)&lt;/code&gt; and &lt;code&gt;(&#39;devon&#39;, &#39;sam&#39;)&lt;/code&gt; represent the same pair, only one of them would be included in the output list:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itertools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;combinations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;friends&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[(&amp;#39;Monique&amp;#39;, &amp;#39;Ashish&amp;#39;), (&amp;#39;Monique&amp;#39;, &amp;#39;Devon&amp;#39;), (&amp;#39;Monique&amp;#39;, &amp;#39;Bernie&amp;#39;),&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(&amp;#39;Ashish&amp;#39;, &amp;#39;Devon&amp;#39;), (&amp;#39;Ashish&amp;#39;, &amp;#39;Bernie&amp;#39;), (&amp;#39;Devon&amp;#39;, &amp;#39;Bernie&amp;#39;)]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since the order of the values matters with combinations, there are fewer combinations than permutations for the same input list. Again, because we set &lt;code&gt;r&lt;/code&gt; to 2, each grouping has two names in it.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.combinations()&lt;/code&gt; and &lt;code&gt;.permutations()&lt;/code&gt; are just small examples of a powerful library, but even these two functions can be quite useful when you&amp;rsquo;re trying to solve an algorithm problem quickly.&lt;/p&gt;
&lt;h2 id=&quot;conclusion-coding-interview-superpowers&quot;&gt;Conclusion: Coding Interview Superpowers&lt;/h2&gt;
&lt;p&gt;You can now feel comfortable using some of Python&amp;rsquo;s less common, but more powerful, standard features in your next coding interview. There&amp;rsquo;s a lot to learn about the language as a whole but this article should have given you a starting point to go deeper while letting you use Python more effectively when you interview.&lt;/p&gt;
&lt;p&gt;In this article, you&amp;rsquo;ve learned different types of standard tools to supercharge your coding interview skills:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/library/functions.html#built-in-functions&quot;&gt;Powerful built-in functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Data structures built to handle common scenarios with barely any code&lt;/li&gt;
&lt;li&gt;Standard library packages that have feature-rich solutions for specific problems, letting you write better code faster&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Interviewing may not be the best approximation of real software development, but it&amp;rsquo;s worth knowing how to succeed in any programming environment, even interviews. Thankfully, learning how to use Python during coding interviews can help you understand the language more deeply, which will pay dividends during day-to-day development.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Django Migrations 101</title>
      <id>https://realpython.com/courses/django-migrations-101/</id>
      <link href="https://realpython.com/courses/django-migrations-101/"/>
      <updated>2019-03-26T14:00:00+00:00</updated>
      <summary>With this course you’ll get comfortable with Django migrations and learn how to create database tables without writing any SQL, how to automatically modify your database after you changed your models, and how to revert changes made to your database.</summary>
      <content type="html">
        &lt;p&gt;Since version 1.7, Django has come with built-in support for database migrations. In Django, database migrations usually go hand in hand with models: whenever you code up a new model, you also generate a migration to create the necessary table in the database. However, migrations can do much more.&lt;/p&gt;
&lt;p&gt;With this course you’ll get comfortable with Django migrations and learn how to create database tables without writing any SQL, how to automatically modify your database after you changed your models, and how to revert changes made to your database.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Documenting Python Projects With Sphinx and Read The Docs</title>
      <id>https://realpython.com/courses/documenting-python-projects-sphinx-read-the-docs/</id>
      <link href="https://realpython.com/courses/documenting-python-projects-sphinx-read-the-docs/"/>
      <updated>2019-03-26T13:30:00+00:00</updated>
      <summary>In this video series we&#39;ll cover creating Python documentation from scratch using Sphinx, as well as getting your code repository hooked up to Read The Docs, to automatically build and publish your code documentation.</summary>
      <content type="html">
        &lt;p&gt;In this video series we&amp;rsquo;ll cover creating Python documentation from scratch using Sphinx, as well as getting your code repository hooked up to Read The Docs, to automatically build and publish your code documentation.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This course uses Python 2.7 in its coding examples.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;resources&quot;&gt;Resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/myusuf3/doctut&quot;&gt;&lt;code&gt;doctut&lt;/code&gt; Example Project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://doctut.readthedocs.io/en/latest/&quot;&gt;&lt;code&gt;doctut&lt;/code&gt; Example Docs on Read The Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>An Intro to Threading in Python</title>
      <id>https://realpython.com/intro-to-python-threading/</id>
      <link href="https://realpython.com/intro-to-python-threading/"/>
      <updated>2019-03-25T14:00:00+00:00</updated>
      <summary>In this intermediate-level tutorial, you&#39;ll learn how to use threading in your Python programs. You&#39;ll see how to create threads, how to coordinate and synchronize them, and how to handle common problems that arise in threading.</summary>
      <content type="html">
        &lt;p&gt;Python threading allows you to have different parts of your program run concurrently and can simplify your design. If you&amp;rsquo;ve got some experience in Python and want to speed up your program using threads, then this tutorial is for you!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this article, you&amp;rsquo;ll learn:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What threads are&lt;/li&gt;
&lt;li&gt;How to create threads and wait for them to finish&lt;/li&gt;
&lt;li&gt;How to use a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;How to avoid race conditions&lt;/li&gt;
&lt;li&gt;How to use the common tools that Python &lt;code&gt;threading&lt;/code&gt; provides&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This article assumes you&amp;rsquo;ve got the Python basics down pat and that you&amp;rsquo;re using at least version 3.6 to run the examples. If you need a refresher, you can start with the &lt;a href=&quot;https://realpython.com/learning-paths/&quot;&gt;Python Learning Paths&lt;/a&gt; and get up to speed.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re not sure if you want to use Python &lt;code&gt;threading&lt;/code&gt;, &lt;code&gt;asyncio&lt;/code&gt;, or &lt;code&gt;multiprocessing&lt;/code&gt;, then you can check out &lt;a href=&quot;https://realpython.com/python-concurrency/&quot;&gt;Speed Up Your Python Program With Concurrency&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All of the sources used in this tutorial are available to you in the &lt;a href=&quot;https://github.com/realpython/materials/tree/master/intro-to-threading&quot;&gt;&lt;em&gt;Real Python&lt;/em&gt; GitHub repo&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-mastery-course&quot; data-focus=&quot;false&quot;&gt;5 Thoughts On Python Mastery&lt;/a&gt;, a free course for Python developers that shows you the roadmap and the mindset you&#39;ll need to take your Python skills to the next level.&lt;/p&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;&lt;i class=&quot;fa fa-graduation-cap&quot; aria-hidden=&quot;true&quot;&gt;&lt;/i&gt; Take the Quiz:&lt;/strong&gt; Test your knowledge with our interactive “Python Threading” quiz. Upon completion you will receive a score so you can track your learning progress over time:&lt;/p&gt;&lt;p class=&quot;text-center my-2&quot;&gt;&lt;a class=&quot;btn btn-primary&quot; href=&quot;/quizzes/python-threading/&quot; target=&quot;_blank&quot;&gt;Take the Quiz »&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&quot;what-is-a-thread&quot;&gt;What Is a Thread?&lt;/h2&gt;
&lt;p&gt;A thread is a separate flow of execution. This means that your program will have two things happening at once. But for most Python 3 implementations the different threads do not actually execute at the same time: they merely appear to.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s tempting to think of threading as having two (or more) different processors running on your program, each one doing an independent task at the same time. That&amp;rsquo;s almost right. The threads may be running on different processors, but they will only be running one at a time. &lt;/p&gt;
&lt;p&gt;Getting multiple tasks running simultaneously requires a non-standard implementation of Python, writing some of your code in a different language, or using &lt;code&gt;multiprocessing&lt;/code&gt; which comes with some extra overhead.&lt;/p&gt;
&lt;p&gt;Because of the way CPython implementation of Python works, threading may not speed up all tasks.  This is due to interactions with the &lt;a href=&quot;https://realpython.com/python-gil/&quot;&gt;GIL&lt;/a&gt; that essentially limit one Python thread to run at a time. &lt;/p&gt;
&lt;p&gt;Tasks that spend much of their time waiting for external events are generally good candidates for threading.  Problems that require heavy CPU computation and spend little time waiting for external events might not run faster at all.  &lt;/p&gt;
&lt;p&gt;This is true for code written in Python and running on the standard CPython implementation.  If your threads are written in C they have the ability to release the GIL and run concurrently.  If you are running on a different Python implementation, check with the documentation too see how it handles threads.  &lt;/p&gt;
&lt;p&gt;If you are running a standard Python implementation, writing in only Python, and have a CPU-bound problem, you should check out the &lt;code&gt;multiprocessing&lt;/code&gt; module instead.&lt;/p&gt;
&lt;p&gt;Architecting your program to use threading can also provide gains in design clarity. Most of the examples you&amp;rsquo;ll learn about in this tutorial are not necessarily going to run faster because they use threads. Using threading in them helps to make the design cleaner and easier to reason about.&lt;/p&gt;
&lt;p&gt;So, let&amp;rsquo;s stop talking about threading and start using it!&lt;/p&gt;
&lt;h2 id=&quot;starting-a-thread&quot;&gt;Starting a Thread&lt;/h2&gt;
&lt;p&gt;Now that you&amp;rsquo;ve got an idea of what a thread is, let&amp;rsquo;s learn how to make one. The Python standard library provides &lt;a href=&quot;https://docs.python.org/3/library/threading.html&quot;&gt;&lt;code&gt;threading&lt;/code&gt;&lt;/a&gt;, which contains most of the primitives you&amp;rsquo;ll see in this article. &lt;code&gt;Thread&lt;/code&gt;, in this module, nicely encapsulates threads, providing a clean interface to work with them.&lt;/p&gt;
&lt;p&gt;To start a separate thread, you create a &lt;code&gt;Thread&lt;/code&gt; instance and then tell it to &lt;code&gt;.start()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;logging&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;threading&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;thread_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: starting&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: finishing&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(asctime)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(message)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basicConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;                        &lt;span class=&quot;n&quot;&gt;datefmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main    : before creating thread&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thread_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main    : before running thread&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main    : wait for the thread to finish&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20 &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# x.join()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main    : all done&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you look around the logging statements, you can see that the &lt;code&gt;main&lt;/code&gt; section is creating and starting the thread:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thread_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you create a &lt;code&gt;Thread&lt;/code&gt;, you pass it a function and a list containing the arguments to that function. In this case, you&amp;rsquo;re telling the &lt;code&gt;Thread&lt;/code&gt; to run &lt;code&gt;thread_function()&lt;/code&gt; and to pass it &lt;code&gt;1&lt;/code&gt; as an argument.&lt;/p&gt;
&lt;p&gt;For this article, you&amp;rsquo;ll use sequential integers as names for your threads. There is &lt;code&gt;threading.get_ident()&lt;/code&gt;, which returns a unique name for each thread, but these are usually neither short nor easily readable.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;thread_function()&lt;/code&gt; itself doesn&amp;rsquo;t do much. It simply logs some messages with a &lt;code&gt;time.sleep()&lt;/code&gt; in between them.&lt;/p&gt;
&lt;p&gt;When you run this program as it is (with line twenty commented out), the output will look like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./single_thread.py
&lt;span class=&quot;go&quot;&gt;Main    : before creating thread&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : before running thread&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: starting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : wait for the thread to finish&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : all done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: finishing&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You&amp;rsquo;ll notice that the &lt;code&gt;Thread&lt;/code&gt; finished after the &lt;code&gt;Main&lt;/code&gt; section of your code did. You&amp;rsquo;ll come back to why that is and talk about the mysterious line twenty in the next section.&lt;/p&gt;
&lt;h3 id=&quot;daemon-threads&quot;&gt;Daemon Threads&lt;/h3&gt;
&lt;p&gt;In computer science, a &lt;a href=&quot;https://en.wikipedia.org/wiki/Daemon_(computing)&quot;&gt;&lt;code&gt;daemon&lt;/code&gt;&lt;/a&gt; is a process that runs in the background.&lt;/p&gt;
&lt;p&gt;Python &lt;code&gt;threading&lt;/code&gt; has a more specific meaning for &lt;code&gt;daemon&lt;/code&gt;. A &lt;code&gt;daemon&lt;/code&gt; thread will shut down immediately when the program exits. One way to think about these definitions is to consider the &lt;code&gt;daemon&lt;/code&gt; thread a thread that runs in the background without worrying about shutting it down.&lt;/p&gt;
&lt;p&gt;If a program is running &lt;code&gt;Threads&lt;/code&gt; that are not &lt;code&gt;daemons&lt;/code&gt;, then the program will wait for those threads to complete before it terminates. &lt;code&gt;Threads&lt;/code&gt; that &lt;em&gt;are&lt;/em&gt; daemons, however, are just killed wherever they are when the program is exiting.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look a little more closely at the output of your program above. The last two lines are the interesting bit. When you run the program, you&amp;rsquo;ll notice that there is a pause (of about 2 seconds) after &lt;code&gt;__main__&lt;/code&gt; has printed its &lt;code&gt;all done&lt;/code&gt; message and before the thread is finished.&lt;/p&gt;
&lt;p&gt;This pause is Python waiting for the non-daemonic thread to complete. When your Python program ends, part of the shutdown process is to clean up the threading routine.&lt;/p&gt;
&lt;p&gt;If you look at the &lt;a href=&quot;https://github.com/python/cpython/blob/df5cdc11123a35065bbf1636251447d0bfe789a5/Lib/threading.py#L1263&quot;&gt;source for Python &lt;code&gt;threading&lt;/code&gt;&lt;/a&gt;, you&amp;rsquo;ll see that &lt;code&gt;threading._shutdown()&lt;/code&gt; walks through all of the running threads and calls &lt;code&gt;.join()&lt;/code&gt; on every one that does not have the &lt;code&gt;daemon&lt;/code&gt; flag set.&lt;/p&gt;
&lt;p&gt;So your program waits to exit because the thread itself is waiting in a sleep. As soon as it has completed and printed the message, &lt;code&gt;.join()&lt;/code&gt; will return and the program can exit.&lt;/p&gt;
&lt;p&gt;Frequently, this behavior is what you want, but there are other options available to us. Let&amp;rsquo;s first repeat the program with a &lt;code&gt;daemon&lt;/code&gt; thread. You do that by changing how you construct the &lt;code&gt;Thread&lt;/code&gt;, adding the &lt;code&gt;daemon=True&lt;/code&gt; flag:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thread_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;daemon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you run the program now, you should see this output:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./daemon_thread.py
&lt;span class=&quot;go&quot;&gt;Main    : before creating thread&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : before running thread&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: starting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : wait for the thread to finish&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : all done&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The difference here is that the final line of the output is missing. &lt;code&gt;thread_function()&lt;/code&gt; did not get a chance to complete. It was a &lt;code&gt;daemon&lt;/code&gt; thread, so when &lt;code&gt;__main__&lt;/code&gt; reached the end of its code and the program wanted to finish, the daemon was killed.&lt;/p&gt;
&lt;h3 id=&quot;join-a-thread&quot;&gt;&lt;code&gt;join()&lt;/code&gt; a Thread&lt;/h3&gt;
&lt;p&gt;Daemon threads are handy, but what about when you want to wait for a thread to stop? What about when you want to do that and not exit your program? Now let&amp;rsquo;s go back to your original program and look at that commented out line twenty:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# x.join()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To tell one thread to wait for another thread to finish, you call &lt;code&gt;.join()&lt;/code&gt;. If you uncomment that line, the main thread will pause and wait for the thread &lt;code&gt;x&lt;/code&gt; to complete running.&lt;/p&gt;
&lt;p&gt;Did you test this on the code with the daemon thread or the regular thread? It turns out that it doesn&amp;rsquo;t matter. If you &lt;code&gt;.join()&lt;/code&gt; a thread, that statement will wait until either kind of thread is finished.&lt;/p&gt;
&lt;h2 id=&quot;working-with-many-threads&quot;&gt;Working With Many Threads&lt;/h2&gt;
&lt;p&gt;The example code so far has only been working with two threads: the main thread and one you started with the &lt;code&gt;threading.Thread&lt;/code&gt; object. &lt;/p&gt;
&lt;p&gt;Frequently, you&amp;rsquo;ll want to start a number of threads and have them do interesting work. Let&amp;rsquo;s start by looking at the harder way of doing that, and then you&amp;rsquo;ll move on to an easier method.&lt;/p&gt;
&lt;p&gt;The harder way of starting multiple threads is the one you already know:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;logging&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;threading&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;thread_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: starting&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: finishing&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(asctime)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(message)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basicConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;datefmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;threads&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main    : create and start thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thread_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main    : before joining thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main    : thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; done&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code uses the same mechanism you saw above to start a thread, create a &lt;code&gt;Thread&lt;/code&gt; object, and then call &lt;code&gt;.start()&lt;/code&gt;. The program keeps a list of &lt;code&gt;Thread&lt;/code&gt; objects so that it can then wait for them later using &lt;code&gt;.join()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Running this code multiple times will likely produce some interesting results. Here&amp;rsquo;s an example output from my machine:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./multiple_threads.py
&lt;span class=&quot;go&quot;&gt;Main    : create and start thread 0.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: starting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : create and start thread 1.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: starting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : create and start thread 2.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 2: starting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : before joining thread 0.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 2: finishing&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: finishing&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: finishing&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : thread 0 done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : before joining thread 1.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : thread 1 done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : before joining thread 2.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : thread 2 done&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you walk through the output carefully, you&amp;rsquo;ll see all three threads getting started in the order you might expect, but in this case they finish in the opposite order! Multiple runs will produce different orderings. Look for the &lt;code&gt;Thread x: finishing&lt;/code&gt; message to tell you when each thread is done.&lt;/p&gt;
&lt;p&gt;The order in which threads are run is determined by the operating system and can be quite hard to predict. It may (and likely will) vary from run to run, so you need to be aware of that when you design algorithms that use threading.&lt;/p&gt;
&lt;p&gt;Fortunately, Python gives you several primitives that you&amp;rsquo;ll look at later to help coordinate threads and get them running together. Before that, let&amp;rsquo;s look at how to make managing a group of threads a bit easier.&lt;/p&gt;
&lt;h2 id=&quot;using-a-threadpoolexecutor&quot;&gt;Using a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s an easier way to start up a group of threads than the one you saw above. It&amp;rsquo;s called a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;, and it&amp;rsquo;s part of the standard library in &lt;a href=&quot;https://docs.python.org/3/library/concurrent.futures.html&quot;&gt;&lt;code&gt;concurrent.futures&lt;/code&gt;&lt;/a&gt; (as of Python 3.2).&lt;/p&gt;
&lt;p&gt;The easiest way to create it is as a context manager, using the &lt;code&gt;with&lt;/code&gt; statement to manage the creation and destruction of the pool.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the &lt;code&gt;__main__&lt;/code&gt; from the last example rewritten to use a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;concurrent.futures&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# [rest of code]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(asctime)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(message)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basicConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;datefmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;concurrent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;futures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_workers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thread_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The code creates a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; as a context manager, telling it how many worker threads it wants in the pool. It then uses &lt;code&gt;.map()&lt;/code&gt; to step through an iterable of things, in your case &lt;code&gt;range(3)&lt;/code&gt;, passing each one to a thread in the pool.&lt;/p&gt;
&lt;p&gt;The end of the &lt;code&gt;with&lt;/code&gt; block causes the &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; to do a &lt;code&gt;.join()&lt;/code&gt; on each of the threads in the pool. It is &lt;em&gt;strongly&lt;/em&gt; recommended that you use &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; as a context manager when you can so that you never forget to &lt;code&gt;.join()&lt;/code&gt; the threads.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Using a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; can cause some confusing errors. &lt;/p&gt;
&lt;p&gt;For example, if you call a function that takes no parameters, but you pass it parameters in &lt;code&gt;.map()&lt;/code&gt;, the thread will throw an exception.&lt;/p&gt;
&lt;p&gt;Unfortunately, &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; will hide that exception, and (in the case above) the program terminates with no output. This can be quite confusing to debug at first.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Running your corrected example code will produce output that looks like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./executor.py
&lt;span class=&quot;go&quot;&gt;Thread 0: starting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: starting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 2: starting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: finishing&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: finishing&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 2: finishing&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Again, notice how &lt;code&gt;Thread 1&lt;/code&gt; finished before &lt;code&gt;Thread 0&lt;/code&gt;. The scheduling of threads is done by the operating system and does not follow a plan that&amp;rsquo;s easy to figure out.&lt;/p&gt;
&lt;h2 id=&quot;race-conditions&quot;&gt;Race Conditions&lt;/h2&gt;
&lt;p&gt;Before you move on to some of the other features tucked away in Python &lt;code&gt;threading&lt;/code&gt;, let&amp;rsquo;s talk a bit about one of the more difficult issues you&amp;rsquo;ll run into when writing threaded programs: &lt;a href=&quot;https://en.wikipedia.org/wiki/Race_condition&quot;&gt;race conditions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once you&amp;rsquo;ve seen what a race condition is and looked at one happening, you&amp;rsquo;ll move on to some of the primitives provided by the standard library to prevent race conditions from happening.&lt;/p&gt;
&lt;p&gt;Race conditions can occur when two or more threads access a shared piece of data or resource. In this example, you&amp;rsquo;re going to create a large race condition that happens every time, but be aware that most race conditions are not this obvious. Frequently, they only occur rarely, and they can produce confusing results. As you can imagine, this makes them quite difficult to debug.&lt;/p&gt;
&lt;p&gt;Fortunately, this race condition will happen every time, and you&amp;rsquo;ll walk through it in detail to explain what is happening.&lt;/p&gt;
&lt;p&gt;For this example, you&amp;rsquo;re going to write a class that updates a database. Okay, you&amp;rsquo;re not really going to have a database: you&amp;rsquo;re just going to fake it, because that&amp;rsquo;s not the point of this article.&lt;/p&gt;
&lt;p&gt;Your &lt;code&gt;FakeDatabase&lt;/code&gt; will have &lt;code&gt;.__init__()&lt;/code&gt; and &lt;code&gt;.update()&lt;/code&gt; methods:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FakeDatabase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: starting update&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;local_copy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;local_copy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;local_copy&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: finishing update&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;FakeDatabase&lt;/code&gt; is keeping track of a single number: &lt;code&gt;.value&lt;/code&gt;. This is going to be the shared data on which you&amp;rsquo;ll see the race condition.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.__init__()&lt;/code&gt; simply initializes &lt;code&gt;.value&lt;/code&gt; to zero. So far, so good.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.update()&lt;/code&gt; looks a little strange. It&amp;rsquo;s simulating reading a value from a database, doing some computation on it, and then writing a new value back to the database.&lt;/p&gt;
&lt;p&gt;In this case, reading from the database just means copying &lt;code&gt;.value&lt;/code&gt; to a local variable. The computation is just to add one to the value and then &lt;code&gt;.sleep()&lt;/code&gt; for a little bit. Finally, it writes the value back by copying the local value back to &lt;code&gt;.value&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how you&amp;rsquo;ll use this &lt;code&gt;FakeDatabase&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(asctime)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(message)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basicConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;datefmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;database&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FakeDatabase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Testing update. Starting value is &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;concurrent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;futures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_workers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Testing update. Ending value is &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The program creates a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; with two threads and then calls &lt;code&gt;.submit()&lt;/code&gt; on each of them, telling them to run &lt;code&gt;database.update()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.submit()&lt;/code&gt; has a signature that allows both positional and named arguments to be passed to the function running in the thread:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the usage above, &lt;code&gt;index&lt;/code&gt; is passed as the first and only positional argument to &lt;code&gt;database.update()&lt;/code&gt;. You&amp;rsquo;ll see later in this article where you can pass multiple arguments in a similar manner.&lt;/p&gt;
&lt;p&gt;Since each thread runs &lt;code&gt;.update()&lt;/code&gt;, and &lt;code&gt;.update()&lt;/code&gt; adds one to &lt;code&gt;.value&lt;/code&gt;, you might expect &lt;code&gt;database.value&lt;/code&gt; to be &lt;code&gt;2&lt;/code&gt; when it&amp;rsquo;s printed out at the end. But you wouldn&amp;rsquo;t be looking at this example if that was the case. If you run the above code, the output looks like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./racecond.py
&lt;span class=&quot;go&quot;&gt;Testing unlocked update. Starting value is 0.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: starting update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: starting update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: finishing update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: finishing update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Testing unlocked update. Ending value is 1.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You might have expected that to happen, but let&amp;rsquo;s look at the details of what&amp;rsquo;s really going on here, as that will make the solution to this problem easier to understand.&lt;/p&gt;
&lt;h3 id=&quot;one-thread&quot;&gt;One Thread&lt;/h3&gt;
&lt;p&gt;Before you dive into this issue with two threads, let&amp;rsquo;s step back and talk a bit about some details of how threads work.&lt;/p&gt;
&lt;p&gt;You won&amp;rsquo;t be diving into all of the details here, as that&amp;rsquo;s not important at this level. We&amp;rsquo;ll also be simplifying a few things in a way that won&amp;rsquo;t be technically accurate but will give you the right idea of what is happening.&lt;/p&gt;
&lt;p&gt;When you tell your &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; to run each thread, you tell it which function to run and what parameters to pass to it: &lt;code&gt;executor.submit(database.update, index)&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;The result of this is that each of the threads in the pool will call &lt;code&gt;database.update(index)&lt;/code&gt;. Note that &lt;code&gt;database&lt;/code&gt; is a reference to the one &lt;code&gt;FakeDatabase&lt;/code&gt; object created in &lt;code&gt;__main__&lt;/code&gt;. Calling &lt;code&gt;.update()&lt;/code&gt; on that object calls an &lt;a href=&quot;https://realpython.com/instance-class-and-static-methods-demystified/&quot;&gt;instance method&lt;/a&gt; on that object.&lt;/p&gt;
&lt;p&gt;Each thread is going to have a reference to the same &lt;code&gt;FakeDatabase&lt;/code&gt; object, &lt;code&gt;database&lt;/code&gt;. Each thread will also have a unique value, &lt;code&gt;index&lt;/code&gt;, to make the logging statements a bit easier to read:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/intro-threading-shared-database.267a5d8c6aa1.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-66&quot; src=&quot;https://files.realpython.com/media/intro-threading-shared-database.267a5d8c6aa1.png&quot; width=&quot;1623&quot; height=&quot;663&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-shared-database.267a5d8c6aa1.png&amp;amp;w=405&amp;amp;sig=a2cf6c8e812b6c948114c50b5402d51fb25ed155 405w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-shared-database.267a5d8c6aa1.png&amp;amp;w=811&amp;amp;sig=3708e9d6821e459c00fe576ffaa01cd2cab14b70 811w, https://files.realpython.com/media/intro-threading-shared-database.267a5d8c6aa1.png 1623w&quot; sizes=&quot;75vw&quot; alt=&quot;Thread 1 and Thread 2 use the same shared database.&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When the thread starts running &lt;code&gt;.update()&lt;/code&gt;, it has its own version of all of the data &lt;strong&gt;local&lt;/strong&gt; to the function. In the case of &lt;code&gt;.update()&lt;/code&gt;, this is &lt;code&gt;local_copy&lt;/code&gt;. This is definitely a good thing. Otherwise, two threads running the same function would always confuse each other. It means that all variables that are scoped (or local) to a function are &lt;strong&gt;thread-safe&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Now you can start walking through what happens if you run the program above with a single thread and a single call to &lt;code&gt;.update()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The image below steps through the execution of &lt;code&gt;.update()&lt;/code&gt; if only a single thread is run. The statement is shown on the left followed by a diagram showing the values in the thread&amp;rsquo;s &lt;code&gt;local_value&lt;/code&gt; and the shared &lt;code&gt;database.value&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/intro-threading-single-thread.85204fa11210.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-66&quot; src=&quot;https://files.realpython.com/media/intro-threading-single-thread.85204fa11210.png&quot; width=&quot;1383&quot; height=&quot;2901&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-single-thread.85204fa11210.png&amp;amp;w=345&amp;amp;sig=3964a752874e9128c41941a52f9c7342e2f1e12a 345w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-single-thread.85204fa11210.png&amp;amp;w=691&amp;amp;sig=552252777e57ea21cc6aaa6ed39263aa1e60c1da 691w, https://files.realpython.com/media/intro-threading-single-thread.85204fa11210.png 1383w&quot; sizes=&quot;75vw&quot; alt=&quot;Single thread modifying a shared database&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The diagram is laid out so that time increases as you move from top to bottom. It begins when &lt;code&gt;Thread 1&lt;/code&gt; is created and ends when it is terminated.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;Thread 1&lt;/code&gt; starts, &lt;code&gt;FakeDatabase.value&lt;/code&gt; is zero. The first line of code in the method, &lt;code&gt;local_copy = self.value&lt;/code&gt;, copies the value zero to the local variable. Next it increments the value of &lt;code&gt;local_copy&lt;/code&gt; with the &lt;code&gt;local_copy += 1&lt;/code&gt; statement. You can see &lt;code&gt;.value&lt;/code&gt; in &lt;code&gt;Thread 1&lt;/code&gt; getting set to one.&lt;/p&gt;
&lt;p&gt;Next &lt;code&gt;time.sleep()&lt;/code&gt; is called, which makes the current thread pause and allows other threads to run. Since there is only one thread in this example, this has no effect.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;Thread 1&lt;/code&gt; wakes up and continues, it copies the new value from &lt;code&gt;local_copy&lt;/code&gt; to &lt;code&gt;FakeDatabase.value&lt;/code&gt;, and then the thread is complete. You can see that &lt;code&gt;database.value&lt;/code&gt; is set to one.&lt;/p&gt;
&lt;p&gt;So far, so good. You ran &lt;code&gt;.update()&lt;/code&gt; once and &lt;code&gt;FakeDatabase.value&lt;/code&gt; was incremented to one.&lt;/p&gt;
&lt;h3 id=&quot;two-threads&quot;&gt;Two Threads&lt;/h3&gt;
&lt;p&gt;Getting back to the race condition, the two threads will be running concurrently but not at the same time. They will each have their own version of &lt;code&gt;local_copy&lt;/code&gt; and will each point to the same &lt;code&gt;database&lt;/code&gt;. It is this shared &lt;code&gt;database&lt;/code&gt; object that is going to cause the problems.&lt;/p&gt;
&lt;p&gt;The program starts with &lt;code&gt;Thread 1&lt;/code&gt; running &lt;code&gt;.update()&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/intro-threading-two-threads-part1.c1c0e65a8481.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-66&quot; src=&quot;https://files.realpython.com/media/intro-threading-two-threads-part1.c1c0e65a8481.png&quot; width=&quot;1953&quot; height=&quot;1641&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-two-threads-part1.c1c0e65a8481.png&amp;amp;w=488&amp;amp;sig=5a03e51ef3b111ab7525d287bfc56571927da0db 488w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-two-threads-part1.c1c0e65a8481.png&amp;amp;w=976&amp;amp;sig=fe92c3576eefae044f17a55010816e0d1c9ec240 976w, https://files.realpython.com/media/intro-threading-two-threads-part1.c1c0e65a8481.png 1953w&quot; sizes=&quot;75vw&quot; alt=&quot;Thread 1 gets a copy of shared data and increments it.&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;Thread 1&lt;/code&gt; calls &lt;code&gt;time.sleep()&lt;/code&gt;, it allows the other thread to start running. This is where things get interesting.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Thread 2&lt;/code&gt; starts up and does the same operations. It&amp;rsquo;s also copying &lt;code&gt;database.value&lt;/code&gt; into its private &lt;code&gt;local_copy&lt;/code&gt;, and this shared &lt;code&gt;database.value&lt;/code&gt; has not yet been updated:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/intro-threading-two-threads-part2.df42d4fbfe21.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-66&quot; src=&quot;https://files.realpython.com/media/intro-threading-two-threads-part2.df42d4fbfe21.png&quot; width=&quot;2013&quot; height=&quot;1641&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-two-threads-part2.df42d4fbfe21.png&amp;amp;w=503&amp;amp;sig=469e3f134c8c24d34a4923a3afa7c4261b199935 503w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-two-threads-part2.df42d4fbfe21.png&amp;amp;w=1006&amp;amp;sig=59d3261bd00bf21276180b03684a5b36cc949c01 1006w, https://files.realpython.com/media/intro-threading-two-threads-part2.df42d4fbfe21.png 2013w&quot; sizes=&quot;75vw&quot; alt=&quot;Thread 2 gets a copy of shared data and increments it.&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;Thread 2&lt;/code&gt; finally goes to sleep, the shared &lt;code&gt;database.value&lt;/code&gt; is still unmodified at zero, and both private versions of &lt;code&gt;local_copy&lt;/code&gt; have the value one.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Thread 1&lt;/code&gt; now wakes up and saves its version of &lt;code&gt;local_copy&lt;/code&gt; and then terminates, giving &lt;code&gt;Thread 2&lt;/code&gt; a final chance to run. &lt;code&gt;Thread 2&lt;/code&gt; has no idea that &lt;code&gt;Thread 1&lt;/code&gt; ran and updated &lt;code&gt;database.value&lt;/code&gt; while it was sleeping. It stores &lt;em&gt;its&lt;/em&gt; version of &lt;code&gt;local_copy&lt;/code&gt; into &lt;code&gt;database.value&lt;/code&gt;, also setting it to one:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/intro-threading-two-threads-part3.18576920f88f.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-66&quot; src=&quot;https://files.realpython.com/media/intro-threading-two-threads-part3.18576920f88f.png&quot; width=&quot;2013&quot; height=&quot;1551&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-two-threads-part3.18576920f88f.png&amp;amp;w=503&amp;amp;sig=3b11ef66873019a9edf48a7ff7445bc60d6daada 503w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-two-threads-part3.18576920f88f.png&amp;amp;w=1006&amp;amp;sig=bb0878750a37d23cfca07f30e95f289b1ee82770 1006w, https://files.realpython.com/media/intro-threading-two-threads-part3.18576920f88f.png 2013w&quot; sizes=&quot;75vw&quot; alt=&quot;Both threads write 1 to shared database.&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The two threads have interleaving access to a single shared object, overwriting each other&amp;rsquo;s results. Similar race conditions can arise when one thread frees memory or closes a file handle before the other thread is finished accessing it.&lt;/p&gt;
&lt;h3 id=&quot;why-this-isnt-a-silly-example&quot;&gt;Why This Isn&amp;rsquo;t a Silly Example&lt;/h3&gt;
&lt;p&gt;The example above is contrived to make sure that the race condition happens every time you run your program. Because the operating system can swap out a thread at any time, it is possible to interrupt a statement like &lt;code&gt;x = x + 1&lt;/code&gt; after it has read the value of &lt;code&gt;x&lt;/code&gt; but before it has written back the incremented value.&lt;/p&gt;
&lt;p&gt;The details of how this happens are quite interesting, but not needed for the rest of this article, so feel free to skip over this hidden section.&lt;/p&gt;
&lt;div class=&quot;card mb-3&quot; id=&quot;collapse_carda9325a&quot;&gt;
&lt;div class=&quot;card-header border-0&quot;&gt;&lt;p class=&quot;m-0&quot;&gt;&lt;button class=&quot;btn&quot; data-toggle=&quot;collapse&quot; data-target=&quot;#collapsea9325a&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapsea9325a&quot;&gt;How Does This Really Work&lt;/button&gt; &lt;button class=&quot;btn btn-link float-right&quot; data-toggle=&quot;collapse&quot; data-target=&quot;#collapsea9325a&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapsea9325a&quot;&gt;Show/Hide&lt;/button&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;collapsea9325a&quot; class=&quot;collapse&quot; data-parent=&quot;#collapse_carda9325a&quot;&gt;&lt;div class=&quot;card-body&quot; markdown=&quot;1&quot;&gt;

&lt;p&gt;The code above isn&amp;rsquo;t quite as out there as you might originally have thought. It was designed to force a race condition every time you run it, but that makes it much easier to solve than most race conditions.&lt;/p&gt;
&lt;p&gt;There are two things to keep in mind when thinking about race conditions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Even an operation like &lt;code&gt;x += 1&lt;/code&gt; takes the processor many steps. Each of these steps is a separate instruction to the processor.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The operating system can swap which thread is running &lt;em&gt;at any time&lt;/em&gt;. A thread can be swapped out after any of these small instructions. This means that a thread can be put to sleep to let another thread run in the &lt;em&gt;middle&lt;/em&gt; of a Python statement.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&amp;rsquo;s look at this in detail. The REPL below shows a function that takes a parameter and increments it:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;inc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;dis&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  2           0 LOAD_FAST                0 (x)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;              2 LOAD_CONST               1 (1)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;              4 INPLACE_ADD&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;              6 STORE_FAST               0 (x)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;              8 LOAD_CONST               0 (None)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;             10 RETURN_VALUE&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The REPL example uses &lt;a href=&quot;https://docs.python.org/3/library/dis.html&quot;&gt;&lt;code&gt;dis&lt;/code&gt;&lt;/a&gt; from the Python standard library to show the smaller steps that the processor does to implement your function. It does a &lt;code&gt;LOAD_FAST&lt;/code&gt; of the data value &lt;code&gt;x&lt;/code&gt;, it does a &lt;code&gt;LOAD_CONST 1&lt;/code&gt;, and then it uses the &lt;code&gt;INPLACE_ADD&lt;/code&gt; to add those values together.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re stopping here for a specific reason. This is the point in &lt;code&gt;.update()&lt;/code&gt; above where &lt;code&gt;time.sleep()&lt;/code&gt; forced the threads to switch. It is entirely possible that, every once in while, the operating system would switch threads at that exact point even without &lt;code&gt;sleep()&lt;/code&gt;, but the call to &lt;code&gt;sleep()&lt;/code&gt; makes it happen every time.&lt;/p&gt;
&lt;p&gt;As you learned above, the operating system can swap threads at any time. You&amp;rsquo;ve walked down this listing to the statement marked &lt;code&gt;4&lt;/code&gt;. If the operating system swaps out this thread and runs a different thread that also modifies &lt;code&gt;x&lt;/code&gt;, then when this thread resumes, it will overwrite &lt;code&gt;x&lt;/code&gt; with an incorrect value.&lt;/p&gt;
&lt;p&gt;Technically, this example won&amp;rsquo;t have a race condition because &lt;code&gt;x&lt;/code&gt; is local to &lt;code&gt;inc()&lt;/code&gt;. It does illustrate how a thread can be interrupted during a single Python operation, however. The same LOAD, MODIFY, STORE set of operations also happens on global and shared values. You can explore with the &lt;code&gt;dis&lt;/code&gt; module and prove that yourself.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s rare to get a race condition like this to occur, but remember that an infrequent event taken over millions of iterations becomes likely to happen. The rarity of these race conditions makes them much, much harder to debug than regular bugs.&lt;/p&gt;
&lt;p&gt;Now back to your regularly scheduled tutorial!&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;p&gt;Now that you&amp;rsquo;ve seen a race condition in action, let&amp;rsquo;s find out how to solve them!&lt;/p&gt;
&lt;h2 id=&quot;basic-synchronization-using-lock&quot;&gt;Basic Synchronization Using &lt;code&gt;Lock&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;There are a number of ways to avoid or solve race conditions. You won&amp;rsquo;t look at all of them here, but there are a couple that are used frequently. Let&amp;rsquo;s start with &lt;code&gt;Lock&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To solve your race condition above, you need to find a way to allow only one thread at a time into the read-modify-write section of your code. The most common way to do this is called &lt;code&gt;Lock&lt;/code&gt; in Python. In some other languages this same idea is called  a &lt;code&gt;mutex&lt;/code&gt;. Mutex comes from MUTual EXclusion, which is exactly what a &lt;code&gt;Lock&lt;/code&gt; does.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;Lock&lt;/code&gt; is an object that acts like a hall pass. Only one thread at a time can have the &lt;code&gt;Lock&lt;/code&gt;. Any other thread that wants the &lt;code&gt;Lock&lt;/code&gt; must wait until the owner of the &lt;code&gt;Lock&lt;/code&gt; gives it up.&lt;/p&gt;
&lt;p&gt;The basic functions to do this are &lt;code&gt;.acquire()&lt;/code&gt; and &lt;code&gt;.release()&lt;/code&gt;. A thread will call &lt;code&gt;my_lock.acquire()&lt;/code&gt; to get the lock. If the lock is already held, the calling thread will wait until it is released. There&amp;rsquo;s an important point here. If one thread gets the lock but never gives it back, your program will be stuck. You&amp;rsquo;ll read more about this later.&lt;/p&gt;
&lt;p&gt;Fortunately, Python&amp;rsquo;s &lt;code&gt;Lock&lt;/code&gt; will also operate as a context manager, so you can use it in a &lt;code&gt;with&lt;/code&gt; statement, and it gets released automatically when the &lt;code&gt;with&lt;/code&gt; block exits for any reason.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look at the &lt;code&gt;FakeDatabase&lt;/code&gt; with a &lt;code&gt;Lock&lt;/code&gt; added to it. The calling function stays the same:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FakeDatabase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;locked_update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: starting update&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; about to lock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; has lock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;local_copy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;local_copy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;local_copy&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; about to release lock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; after release&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: finishing update&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Other than adding a bunch of debug logging so you can see the locking more clearly, the big change here is to add a member called &lt;code&gt;._lock&lt;/code&gt;, which is a &lt;code&gt;threading.Lock()&lt;/code&gt; object. This &lt;code&gt;._lock&lt;/code&gt; is initialized in the unlocked state and locked and released by the &lt;code&gt;with&lt;/code&gt; statement.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s worth noting here that the thread running this function will hold on to that &lt;code&gt;Lock&lt;/code&gt; until it is completely finished updating the database. In this case, that means it will hold the &lt;code&gt;Lock&lt;/code&gt; while it copies, updates, sleeps, and then writes the value back to the database.&lt;/p&gt;
&lt;p&gt;If you run this version with logging set to warning level, you&amp;rsquo;ll see this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./fixrace.py
&lt;span class=&quot;go&quot;&gt;Testing locked update. Starting value is 0.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: starting update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: starting update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: finishing update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: finishing update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Testing locked update. Ending value is 2.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Look at that. Your program finally works!&lt;/p&gt;
&lt;p&gt;You can turn on full logging by setting the level to &lt;code&gt;DEBUG&lt;/code&gt; by adding this statement after you configure the logging output in &lt;code&gt;__main__&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Running this program with &lt;code&gt;DEBUG&lt;/code&gt; logging turned on looks like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./fixrace.py
&lt;span class=&quot;go&quot;&gt;Testing locked update. Starting value is 0.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: starting update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0 about to lock&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0 has lock&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: starting update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1 about to lock&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0 about to release lock&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0 after release&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: finishing update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1 has lock&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1 about to release lock&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1 after release&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: finishing update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Testing locked update. Ending value is 2.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this output you can see &lt;code&gt;Thread 0&lt;/code&gt; acquires the lock and is still holding it when it goes to sleep. &lt;code&gt;Thread 1&lt;/code&gt; then starts and attempts to acquire the same lock. Because &lt;code&gt;Thread 0&lt;/code&gt; is still holding it, &lt;code&gt;Thread 1&lt;/code&gt; has to wait. This is the mutual exclusion that a &lt;code&gt;Lock&lt;/code&gt; provides.&lt;/p&gt;
&lt;p&gt;Many of the examples in the rest of this article will have &lt;code&gt;WARNING&lt;/code&gt; and &lt;code&gt;DEBUG&lt;/code&gt; level logging. We&amp;rsquo;ll generally only show the &lt;code&gt;WARNING&lt;/code&gt; level output, as the &lt;code&gt;DEBUG&lt;/code&gt; logs can be quite lengthy. Try out the programs with the logging turned up and see what they do.&lt;/p&gt;
&lt;h2 id=&quot;deadlock&quot;&gt;Deadlock&lt;/h2&gt;
&lt;p&gt;Before you move on, you should look at a common problem when using &lt;code&gt;Locks&lt;/code&gt;. As you saw, if the &lt;code&gt;Lock&lt;/code&gt; has already been acquired, a second call to &lt;code&gt;.acquire()&lt;/code&gt; will wait until the thread that is holding the &lt;code&gt;Lock&lt;/code&gt; calls &lt;code&gt;.release()&lt;/code&gt;. What do you think happens when you run this code:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;threading&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;before first acquire&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;before second acquire&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;acquired lock twice&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When the program calls &lt;code&gt;l.acquire()&lt;/code&gt; the second time, it hangs waiting for the &lt;code&gt;Lock&lt;/code&gt; to be released. In this example, you can fix the deadlock by removing the second call, but deadlocks usually happen from one of two subtle things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;An implementation bug where a &lt;code&gt;Lock&lt;/code&gt; is not released properly&lt;/li&gt;
&lt;li&gt;A design issue where a utility function needs to be called by functions that might or might not already have the &lt;code&gt;Lock&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first situation happens sometimes, but using a &lt;code&gt;Lock&lt;/code&gt; as a context manager greatly reduces how often. It is recommended to write code whenever possible to make use of context managers, as they help to avoid situations where an exception skips you over the &lt;code&gt;.release()&lt;/code&gt; call.&lt;/p&gt;
&lt;p&gt;The design issue can be a bit trickier in some languages. Thankfully, Python threading has a second object, called &lt;code&gt;RLock&lt;/code&gt;, that is designed for just this situation. It allows a thread to &lt;code&gt;.acquire()&lt;/code&gt; an &lt;code&gt;RLock&lt;/code&gt; multiple times before it calls &lt;code&gt;.release()&lt;/code&gt;. That thread is still required to call &lt;code&gt;.release()&lt;/code&gt; the same number of times it called &lt;code&gt;.acquire()&lt;/code&gt;, but it should be doing that anyway.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Lock&lt;/code&gt; and &lt;code&gt;RLock&lt;/code&gt; are two of the basic tools used in threaded programming to prevent race conditions. There are a few other that work in different ways. Before you look at them, let&amp;rsquo;s shift to a slightly different problem domain.&lt;/p&gt;
&lt;h2 id=&quot;producer-consumer-threading&quot;&gt;Producer-Consumer Threading&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem&quot;&gt;Producer-Consumer Problem&lt;/a&gt; is a standard computer science problem used to look at threading or process synchronization issues. You&amp;rsquo;re going to look at a variant of it to get some ideas of what primitives the Python &lt;code&gt;threading&lt;/code&gt; module provides.&lt;/p&gt;
&lt;p&gt;For this example, you&amp;rsquo;re going to imagine a program that needs to read messages from a network and write them to disk. The program does not request a message when it wants. It must be listening and accept messages as they come in. The messages will not come in at a regular pace, but will be coming in bursts. This part of the program is called the producer.&lt;/p&gt;
&lt;p&gt;On the other side, once you have a message, you need to write it to a database. The database access is slow, but fast enough to keep up to the average pace of messages. It is &lt;em&gt;not&lt;/em&gt; fast enough to keep up when a burst of messages comes in. This part is the consumer.&lt;/p&gt;
&lt;p&gt;In between the producer and the consumer, you will create a &lt;code&gt;Pipeline&lt;/code&gt; that will be the part that changes as you learn about different synchronization objects.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the basic layout. Let&amp;rsquo;s look at a solution using &lt;code&gt;Lock&lt;/code&gt;. It doesn&amp;rsquo;t work perfectly, but it uses tools you already know, so it&amp;rsquo;s a good place to start.&lt;/p&gt;
&lt;h3 id=&quot;producer-consumer-using-lock&quot;&gt;Producer-Consumer Using &lt;code&gt;Lock&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Since this is an article about Python &lt;code&gt;threading&lt;/code&gt;, and since you just read about the &lt;code&gt;Lock&lt;/code&gt; primitive, let&amp;rsquo;s try to solve this problem with two threads using a &lt;code&gt;Lock&lt;/code&gt; or two.&lt;/p&gt;
&lt;p&gt;The general design is that there is a &lt;code&gt;producer&lt;/code&gt; thread that reads from the fake network and puts the message into a &lt;code&gt;Pipeline&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SENTINEL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Pretend we&amp;#39;re getting a message from the network.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;101&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer got message: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Send a sentinel message to tell consumer we&amp;#39;re done&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SENTINEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To generate a fake message, the &lt;code&gt;producer&lt;/code&gt; gets a random number between one and one hundred. It calls &lt;code&gt;.set_message()&lt;/code&gt; on the &lt;code&gt;pipeline&lt;/code&gt; to send it to the &lt;code&gt;consumer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;producer&lt;/code&gt; also  uses a &lt;code&gt;SENTINEL&lt;/code&gt; value to signal the consumer to stop after it has sent ten values. This is a little awkward, but don&amp;rsquo;t worry, you&amp;rsquo;ll see ways to get rid of this &lt;code&gt;SENTINEL&lt;/code&gt; value after you work through this example.&lt;/p&gt;
&lt;p&gt;On the other side of the &lt;code&gt;pipeline&lt;/code&gt; is the consumer:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Pretend we&amp;#39;re saving a number in the database.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SENTINEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Consumer&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SENTINEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Consumer storing message: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;consumer&lt;/code&gt; reads a message from the &lt;code&gt;pipeline&lt;/code&gt; and writes it to a fake database, which in this case is just printing it to the display. If it gets the &lt;code&gt;SENTINEL&lt;/code&gt; value, it returns from the function, which will terminate the thread.&lt;/p&gt;
&lt;p&gt;Before you look at the really interesting part, the &lt;code&gt;Pipeline&lt;/code&gt;, here&amp;rsquo;s the &lt;code&gt;__main__&lt;/code&gt; section, which spawns these threads:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(asctime)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(message)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basicConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;datefmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# logging.getLogger().setLevel(logging.DEBUG)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;concurrent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;futures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_workers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This should look fairly familiar as it&amp;rsquo;s close to the &lt;code&gt;__main__&lt;/code&gt; code in the previous examples.&lt;/p&gt;
&lt;p&gt;Remember that you can turn on &lt;code&gt;DEBUG&lt;/code&gt; logging to see all of the logging messages by uncommenting  this line:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# logging.getLogger().setLevel(logging.DEBUG)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It can be worthwhile to walk through the &lt;code&gt;DEBUG&lt;/code&gt; logging messages to see exactly where each thread acquires and releases the locks.&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s take a look at the &lt;code&gt;Pipeline&lt;/code&gt; that passes messages from the &lt;code&gt;producer&lt;/code&gt; to the &lt;code&gt;consumer&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    Class to allow a single element pipeline between producer and consumer.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer_lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer_lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:about to acquire getlock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:have getlock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:about to release setlock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;release&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:setlock released&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;set_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:about to acquire setlock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:have setlock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:about to release getlock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;release&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:getlock released&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Woah! That&amp;rsquo;s a lot of code. A pretty high percentage of that is just logging statements to make it easier to see what&amp;rsquo;s happening when you run it. Here&amp;rsquo;s the same code with all of the logging statements removed:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    Class to allow a single element pipeline between producer and consumer.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer_lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer_lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;release&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;set_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;release&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That seems a bit more manageable. The &lt;code&gt;Pipeline&lt;/code&gt; in this version of your code has three members:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;.message&lt;/code&gt;&lt;/strong&gt; stores the message to pass.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;.producer_lock&lt;/code&gt;&lt;/strong&gt; is a &lt;code&gt;threading.Lock&lt;/code&gt; object that restricts access to the message by the &lt;code&gt;producer&lt;/code&gt; thread.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;.consumer_lock&lt;/code&gt;&lt;/strong&gt; is also a &lt;code&gt;threading.Lock&lt;/code&gt; that restricts access to the message by the &lt;code&gt;consumer&lt;/code&gt; thread.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;__init__()&lt;/code&gt; initializes these three members and then calls &lt;code&gt;.acquire()&lt;/code&gt; on the &lt;code&gt;.consumer_lock&lt;/code&gt;. This is the state you want to start in. The &lt;code&gt;producer&lt;/code&gt; is allowed to add a new message, but the &lt;code&gt;consumer&lt;/code&gt; needs to wait until a message is present.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.get_message()&lt;/code&gt; and &lt;code&gt;.set_messages()&lt;/code&gt; are nearly opposites. &lt;code&gt;.get_message()&lt;/code&gt; calls &lt;code&gt;.acquire()&lt;/code&gt; on the &lt;code&gt;consumer_lock&lt;/code&gt;. This is the call that will make the &lt;code&gt;consumer&lt;/code&gt; wait until a message is ready.&lt;/p&gt;
&lt;p&gt;Once the &lt;code&gt;consumer&lt;/code&gt; has acquired the &lt;code&gt;.consumer_lock&lt;/code&gt;, it copies out the value in &lt;code&gt;.message&lt;/code&gt; and then calls &lt;code&gt;.release()&lt;/code&gt; on the &lt;code&gt;.producer_lock&lt;/code&gt;. Releasing this lock is what allows the &lt;code&gt;producer&lt;/code&gt; to insert the next message into the &lt;code&gt;pipeline&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Before you go on to &lt;code&gt;.set_message()&lt;/code&gt;, there&amp;rsquo;s something subtle going on in &lt;code&gt;.get_message()&lt;/code&gt; that&amp;rsquo;s pretty easy to miss. It might seem tempting to get rid of &lt;code&gt;message&lt;/code&gt; and just have the function end with &lt;code&gt;return self.message&lt;/code&gt;. See if you can figure out why you don&amp;rsquo;t want to do that before moving on.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the answer. As soon as the &lt;code&gt;consumer&lt;/code&gt; calls &lt;code&gt;.producer_lock.release()&lt;/code&gt;, it can be swapped out, and the &lt;code&gt;producer&lt;/code&gt; can start running. That could happen before &lt;code&gt;.release()&lt;/code&gt; returns! This means that there is a slight possibility that when the function returns &lt;code&gt;self.message&lt;/code&gt;, that could actually be the &lt;em&gt;next&lt;/em&gt; message generated, so you would lose the first message. This is another example of a race condition.&lt;/p&gt;
&lt;p&gt;Moving on to &lt;code&gt;.set_message()&lt;/code&gt;, you can see the opposite side of the transaction. The &lt;code&gt;producer&lt;/code&gt; will call this with a message. It will acquire the &lt;code&gt;.producer_lock&lt;/code&gt;, set the &lt;code&gt;.message&lt;/code&gt;, and the call &lt;code&gt;.release()&lt;/code&gt; on then &lt;code&gt;consumer_lock&lt;/code&gt;, which will allow the &lt;code&gt;consumer&lt;/code&gt; to read that value.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s run the code that has logging set to &lt;code&gt;WARNING&lt;/code&gt; and see what it looks like:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./prodcom_lock.py
&lt;span class=&quot;go&quot;&gt;Producer got data 43&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 45&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 43&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 86&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 45&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 40&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 86&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 62&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 40&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 15&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 62&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 16&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 15&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 61&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 16&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 73&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 61&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 22&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 73&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 22&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At first, you might find it odd that the producer gets two messages before the consumer even runs. If you look back at the &lt;code&gt;producer&lt;/code&gt; and &lt;code&gt;.set_message()&lt;/code&gt;, you will notice that the only place it will wait for a &lt;code&gt;Lock&lt;/code&gt; is when it attempts to put the message into the pipeline. This is done after the &lt;code&gt;producer&lt;/code&gt; gets the message and logs that it has it.&lt;/p&gt;
&lt;p&gt;When the &lt;code&gt;producer&lt;/code&gt; attempts to send this second message, it will call &lt;code&gt;.set_message()&lt;/code&gt; the second time and it will block.&lt;/p&gt;
&lt;p&gt;The operating system can swap threads at any time, but it generally lets each thread have a reasonable amount of time to run before swapping it out. That&amp;rsquo;s why the &lt;code&gt;producer&lt;/code&gt; usually runs until it blocks in the second call to &lt;code&gt;.set_message()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once a thread is blocked, however, the operating system will always swap it out and find a different thread to run. In this case, the only other thread with anything to do is the &lt;code&gt;consumer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;consumer&lt;/code&gt; calls &lt;code&gt;.get_message()&lt;/code&gt;, which reads the message and calls &lt;code&gt;.release()&lt;/code&gt; on the &lt;code&gt;.producer_lock&lt;/code&gt;, thus allowing the &lt;code&gt;producer&lt;/code&gt; to run again the next time threads are swapped.&lt;/p&gt;
&lt;p&gt;Notice that the first message was &lt;code&gt;43&lt;/code&gt;, and that is exactly what the &lt;code&gt;consumer&lt;/code&gt; read, even though the &lt;code&gt;producer&lt;/code&gt; had already generated the &lt;code&gt;45&lt;/code&gt; message.&lt;/p&gt;
&lt;p&gt;While it works for this limited test, it is not a great solution to the producer-consumer problem in general because it only allows a single value in the pipeline at a time. When the &lt;code&gt;producer&lt;/code&gt; gets a burst of messages, it will have nowhere to put them.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s move on to a better way to solve this problem, using a &lt;code&gt;Queue&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;producer-consumer-using-queue&quot;&gt;Producer-Consumer Using &lt;code&gt;Queue&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;If you want to be able to handle more than one value in the pipeline at a time, you&amp;rsquo;ll need a data structure for the pipeline that allows the number to grow and shrink as data backs up from the &lt;code&gt;producer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Python&amp;rsquo;s standard library has a &lt;code&gt;queue&lt;/code&gt; module which, in turn, has a &lt;code&gt;Queue&lt;/code&gt; class. Let&amp;rsquo;s change the &lt;code&gt;Pipeline&lt;/code&gt; to use a &lt;code&gt;Queue&lt;/code&gt; instead of just a variable protected by a &lt;code&gt;Lock&lt;/code&gt;. You&amp;rsquo;ll also use a different way to stop the worker threads by using a different primitive from Python &lt;code&gt;threading&lt;/code&gt;, an &lt;code&gt;Event&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start with the &lt;code&gt;Event&lt;/code&gt;. The &lt;code&gt;threading.Event&lt;/code&gt; object allows one thread to signal an &lt;code&gt;event&lt;/code&gt; while many other threads can be waiting for that &lt;code&gt;event&lt;/code&gt; to happen. The key usage in this code is that the threads that are waiting for the event do not necessarily need to stop what they are doing, they can just check the status of the &lt;code&gt;Event&lt;/code&gt; every once in a while.&lt;/p&gt;
&lt;p&gt;The triggering of the event can be many things. In this example, the main thread will simply sleep for a while and then &lt;code&gt;.set()&lt;/code&gt; it:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(asctime)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(message)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basicConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;                        &lt;span class=&quot;n&quot;&gt;datefmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# logging.getLogger().setLevel(logging.DEBUG)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;concurrent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;futures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_workers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main: about to set event&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The only changes here are the creation of the &lt;code&gt;event&lt;/code&gt; object on line 6, passing the &lt;code&gt;event&lt;/code&gt; as a parameter on lines 8 and 9, and the final section on lines 11 to 13, which sleep for a second, log a message, and then call &lt;code&gt;.set()&lt;/code&gt; on the event.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;producer&lt;/code&gt; also did not have to change too much:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Pretend we&amp;#39;re getting a number from the network.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;101&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer got message: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer received EXIT event. Exiting&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It now will loop until it sees that the event was set on line 3. It also no longer puts the &lt;code&gt;SENTINEL&lt;/code&gt; value into the &lt;code&gt;pipeline&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;consumer&lt;/code&gt; had to change a little more:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Pretend we&amp;#39;re saving a number in the database.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Consumer&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;            &lt;span class=&quot;s2&quot;&gt;&amp;quot;Consumer storing message: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;  (queue size=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;qsize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Consumer received EXIT event. Exiting&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While you got to take out the code related to the &lt;code&gt;SENTINEL&lt;/code&gt; value, you did have to do a slightly more complicated &lt;code&gt;while&lt;/code&gt; condition. Not only does it loop until the &lt;code&gt;event&lt;/code&gt; is set, but it also needs to keep looping until the &lt;code&gt;pipeline&lt;/code&gt; has been emptied.&lt;/p&gt;
&lt;p&gt;Making sure the queue is empty before the consumer finishes prevents another fun issue. If the &lt;code&gt;consumer&lt;/code&gt; does exit while the &lt;code&gt;pipeline&lt;/code&gt; has messages in it, there are two bad things that can happen. The first is that you lose those final messages, but the more serious one is that the &lt;code&gt;producer&lt;/code&gt; can get caught on the &lt;code&gt;producer_lock&lt;/code&gt; and never return.&lt;/p&gt;
&lt;p&gt;This happens if the &lt;code&gt;event&lt;/code&gt; gets triggered after the &lt;code&gt;producer&lt;/code&gt; has checked the &lt;code&gt;.is_set()&lt;/code&gt; condition but before it calls &lt;code&gt;pipeline.set_message()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If that happens, it&amp;rsquo;s possible for the producer to wake up and exit with the &lt;code&gt;.producer_lock&lt;/code&gt; still being held. The &lt;code&gt;producer&lt;/code&gt; will then try to &lt;code&gt;.acquire()&lt;/code&gt; the &lt;code&gt;.producer_lock&lt;/code&gt;, but the &lt;code&gt;consumer&lt;/code&gt; has exited and will never &lt;code&gt;.release()&lt;/code&gt; it.&lt;/p&gt;
&lt;p&gt;The rest of the &lt;code&gt;consumer&lt;/code&gt; should look familiar.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Pipeline&lt;/code&gt; has changed dramatically, however:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxsize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:about to get from queue&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:got &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; from queue&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;set_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:about to add &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; to queue&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:added &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; to queue&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can see that &lt;code&gt;Pipeline&lt;/code&gt; is a subclass of &lt;code&gt;queue.Queue&lt;/code&gt;. &lt;code&gt;Queue&lt;/code&gt; has an optional parameter when initializing to specify a maximum size of the queue.&lt;/p&gt;
&lt;p&gt;If you give a positive number for &lt;code&gt;maxsize&lt;/code&gt;, it will limit the queue to that number of elements, causing &lt;code&gt;.put()&lt;/code&gt; to block until there are fewer than &lt;code&gt;maxsize&lt;/code&gt; elements. If you don&amp;rsquo;t specify &lt;code&gt;maxsize&lt;/code&gt;, then the queue will grow to the limits of your computer&amp;rsquo;s memory.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.get_message()&lt;/code&gt; and &lt;code&gt;.set_message()&lt;/code&gt; got much smaller. They basically wrap &lt;code&gt;.get()&lt;/code&gt; and &lt;code&gt;.put()&lt;/code&gt; on the &lt;code&gt;Queue&lt;/code&gt;. You might be wondering where all of the locking code that prevents the threads from causing race conditions went.&lt;/p&gt;
&lt;p&gt;The core devs who wrote the standard library knew that a &lt;code&gt;Queue&lt;/code&gt; is frequently used in multi-threading environments and incorporated all of that locking code inside the &lt;code&gt;Queue&lt;/code&gt; itself. &lt;code&gt;Queue&lt;/code&gt; is thread-safe.&lt;/p&gt;
&lt;p&gt;Running this program looks like the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./prodcom_queue.py
&lt;span class=&quot;go&quot;&gt;Producer got message: 32&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 51&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 25&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 94&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 29&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 32 (queue size=3)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 96&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 51 (queue size=3)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 6&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 25 (queue size=3)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 31&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;[many lines deleted]&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Producer got message: 80&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 94 (queue size=6)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 33&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 20 (queue size=6)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 48&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 31 (queue size=6)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 52&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 98 (queue size=6)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main: about to set event&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 13&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 59 (queue size=6)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer received EXIT event. Exiting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 75 (queue size=6)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 97 (queue size=5)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 80 (queue size=4)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 33 (queue size=3)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 48 (queue size=2)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 52 (queue size=1)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 13 (queue size=0)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer received EXIT event. Exiting&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you read through the output in my example, you can see some interesting things happening. Right at the top, you can see the &lt;code&gt;producer&lt;/code&gt; got to create five messages and place four of them on the queue. It got swapped out by the operating system before it could place the fifth one.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;consumer&lt;/code&gt; then ran and pulled off the first message. It printed out that message as well as how deep the queue was at that point:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;Consumer storing message: 32 (queue size=3)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is how you know that the fifth message hasn&amp;rsquo;t made it into the &lt;code&gt;pipeline&lt;/code&gt; yet. The queue is down to size three after a single message was removed. You also know that the &lt;code&gt;queue&lt;/code&gt; can hold ten messages, so the &lt;code&gt;producer&lt;/code&gt; thread didn&amp;rsquo;t get blocked by the &lt;code&gt;queue&lt;/code&gt;. It was swapped out by the OS.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Your output will be different. Your output will change from run to run. That&amp;rsquo;s the fun part of working with threads!&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;As the program starts to wrap up, can you see the main thread generating the &lt;code&gt;event&lt;/code&gt; which causes the &lt;code&gt;producer&lt;/code&gt; to exit immediately. The &lt;code&gt;consumer&lt;/code&gt; still has a bunch of work do to, so it keeps running until it has cleaned out the &lt;code&gt;pipeline&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Try playing with different queue sizes and calls to &lt;code&gt;time.sleep()&lt;/code&gt; in the &lt;code&gt;producer&lt;/code&gt; or the &lt;code&gt;consumer&lt;/code&gt; to simulate longer network or disk access times respectively. Even slight changes to these elements of the program will make large differences in your results.&lt;/p&gt;
&lt;p&gt;This is a much better solution to the producer-consumer problem, but you can simplify it even more. The &lt;code&gt;Pipeline&lt;/code&gt; really isn&amp;rsquo;t needed for this problem. Once you take away the logging, it just becomes a &lt;code&gt;queue.Queue&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what the final code looks like using &lt;code&gt;queue.Queue&lt;/code&gt; directly:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;concurrent.futures&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;logging&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;queue&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;random&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;threading&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Pretend we&amp;#39;re getting a number from the network.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;101&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer got message: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer received event. Exiting&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Pretend we&amp;#39;re saving a number in the database.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&amp;quot;Consumer storing message: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; (size=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;qsize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Consumer received event. Exiting&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(asctime)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(message)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basicConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;datefmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxsize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;concurrent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;futures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_workers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main: about to set event&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That&amp;rsquo;s easier to read and shows how using Python&amp;rsquo;s built-in primitives can simplify a complex problem.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Lock&lt;/code&gt; and &lt;code&gt;Queue&lt;/code&gt; are handy classes to solve concurrency issues, but there are others provided by the standard library. Before you wrap up this tutorial, let&amp;rsquo;s do a quick survey of some of them.&lt;/p&gt;
&lt;h2 id=&quot;threading-objects&quot;&gt;Threading Objects&lt;/h2&gt;
&lt;p&gt;There are a few more primitives offered by the Python &lt;code&gt;threading&lt;/code&gt; module. While you didn&amp;rsquo;t need these for the examples above, they can come in handy in different use cases, so it&amp;rsquo;s good to be familiar with them.&lt;/p&gt;
&lt;h3 id=&quot;semaphore&quot;&gt;Semaphore&lt;/h3&gt;
&lt;p&gt;The first Python &lt;code&gt;threading&lt;/code&gt; object to look at is &lt;code&gt;threading.Semaphore&lt;/code&gt;. A &lt;code&gt;Semaphore&lt;/code&gt; is a counter with a few special properties. The first one is that the counting is atomic. This means that there is a guarantee that the operating system will not swap out the thread in the middle of incrementing or decrementing the counter.&lt;/p&gt;
&lt;p&gt;The internal counter is incremented when you call &lt;code&gt;.release()&lt;/code&gt; and decremented when you call &lt;code&gt;.acquire()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The next special property is that if a thread calls &lt;code&gt;.acquire()&lt;/code&gt; when the counter is zero, that thread will block until a different thread calls &lt;code&gt;.release()&lt;/code&gt; and increments the counter to one.&lt;/p&gt;
&lt;p&gt;Semaphores are frequently used to protect a resource that has a limited capacity. An example would be if you have a pool of connections and want to limit the size of that pool to a specific number.&lt;/p&gt;
&lt;h3 id=&quot;timer&quot;&gt;Timer&lt;/h3&gt;
&lt;p&gt;A &lt;code&gt;threading.Timer&lt;/code&gt; is a way to schedule a function to be called after a certain amount of time has passed. You create a &lt;code&gt;Timer&lt;/code&gt; by passing in a number of seconds to wait and a function to call:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;30.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You start the &lt;code&gt;Timer&lt;/code&gt; by calling &lt;code&gt;.start()&lt;/code&gt;. The function will be called on a new thread at some point after the specified time, but be aware that there is no promise that it will be called exactly at the time you want.&lt;/p&gt;
&lt;p&gt;If you want to stop a &lt;code&gt;Timer&lt;/code&gt; that you&amp;rsquo;ve already started, you can cancel it by calling &lt;code&gt;.cancel()&lt;/code&gt;. Calling &lt;code&gt;.cancel()&lt;/code&gt; after the &lt;code&gt;Timer&lt;/code&gt; has triggered does nothing and does not produce an exception.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;Timer&lt;/code&gt; can be used to prompt a user for action after a specific amount of time. If the user does the action before the &lt;code&gt;Timer&lt;/code&gt; expires, &lt;code&gt;.cancel()&lt;/code&gt; can be called.&lt;/p&gt;
&lt;h3 id=&quot;barrier&quot;&gt;Barrier&lt;/h3&gt;
&lt;p&gt;A &lt;code&gt;threading.Barrier&lt;/code&gt; can be used to keep a fixed number of threads in sync. When creating a &lt;code&gt;Barrier&lt;/code&gt;, the caller must specify how many threads will be synchronizing on it. Each thread calls &lt;code&gt;.wait()&lt;/code&gt; on the &lt;code&gt;Barrier&lt;/code&gt;. They all will remain blocked until the specified number of threads are waiting, and then the are all released at the same time.&lt;/p&gt;
&lt;p&gt;Remember that threads are scheduled by the operating system so, even though all of the threads are released simultaneously, they will be scheduled to run one at a time.&lt;/p&gt;
&lt;p&gt;One use for a &lt;code&gt;Barrier&lt;/code&gt; is to allow a pool of threads to initialize themselves. Having the threads wait on a &lt;code&gt;Barrier&lt;/code&gt; after they are initialized will ensure that none of the threads start running before all of the threads are finished with their initialization.&lt;/p&gt;
&lt;h2 id=&quot;conclusion-threading-in-python&quot;&gt;Conclusion: Threading in Python&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ve now seen much of what Python &lt;code&gt;threading&lt;/code&gt; has to offer and some examples of how to build threaded programs and the problems they solve. You&amp;rsquo;ve also seen a few instances of the problems that arise when writing and debugging threaded programs.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;d like to explore other options for concurrency in Python, check out &lt;a href=&quot;https://realpython.com/python-concurrency/&quot;&gt;Speed Up Your Python Program With Concurrency&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested in doing a deep dive on the &lt;code&gt;asyncio&lt;/code&gt; module, go read &lt;a href=&quot;https://realpython.com/async-io-python/&quot;&gt;Async IO in Python: A Complete Walkthrough&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Whatever you do, you now have the information and confidence you need to write programs using Python threading!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Special thanks to reader JL Diaz for helping to clean up the introduction.&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;&lt;i class=&quot;fa fa-graduation-cap&quot; aria-hidden=&quot;true&quot;&gt;&lt;/i&gt; Take the Quiz:&lt;/strong&gt; Test your knowledge with our interactive “Python Threading” quiz. Upon completion you will receive a score so you can track your learning progress over time:&lt;/p&gt;&lt;p class=&quot;text-center my-2&quot;&gt;&lt;a class=&quot;btn btn-primary&quot; href=&quot;/quizzes/python-threading/&quot; target=&quot;_blank&quot;&gt;Take the Quiz »&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Intro to Object-Oriented Programming (OOP) in Python</title>
      <id>https://realpython.com/courses/intro-object-oriented-programming-oop-python/</id>
      <link href="https://realpython.com/courses/intro-object-oriented-programming-oop-python/"/>
      <updated>2019-03-21T14:00:00+00:00</updated>
      <summary>In this video series, you&#39;ll learn the fundamentals of object-oriented programming (OOP) in Python and how to work with classes, objects, and constructors.</summary>
      <content type="html">
        &lt;p&gt;Object-oriented programming is one of the biggest and most important subjects in all of programming. This series will provide you with a basic conceptual understanding of Object-Oriented Programming so you can take your Python programming skills to the next level.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll learn how to define custom types using classes, and how to instantiate those classes into python objects that can be used throughout your program.&lt;/p&gt;
&lt;p&gt;Finally, you&amp;rsquo;ll learn how classes can subclass one another with  brief introduction to inheritance, which allows you to write maintainable and less redundant Python code.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>13 Project Ideas for Intermediate Python Developers</title>
      <id>https://realpython.com/intermediate-python-project-ideas/</id>
      <link href="https://realpython.com/intermediate-python-project-ideas/"/>
      <updated>2019-03-20T14:00:00+00:00</updated>
      <summary>In this article, you&#39;ll learn how you can get started on 13 Python project ideas that are just right for intermediate Python developers. They&#39;ll challenge you enough to help you become a better Pythonista but will still be doable!</summary>
      <content type="html">
        &lt;p&gt;Learning the basics of Python is a wonderful experience. But the euphoria of just learning can be replaced by the hunger for hands-on projects. It&amp;rsquo;s normal to want to build projects, hence the need for project ideas.&lt;/p&gt;
&lt;p&gt;The problem though is that some projects are either too simple for an intermediate Python developer or too hard. This article will suggest projects you can work on as an intermediate Python developer. These project ideas will provide the appropriate level of challenge for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this article, you&amp;rsquo;ll learn:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The importance of building projects&lt;/li&gt;
&lt;li&gt;The major platforms you can build projects for&lt;/li&gt;
&lt;li&gt;Thirteen project ideas you can work on&lt;/li&gt;
&lt;li&gt;Some tips for working on projects&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-mastery-course&quot; data-focus=&quot;false&quot;&gt;5 Thoughts On Python Mastery&lt;/a&gt;, a free course for Python developers that shows you the roadmap and the mindset you&#39;ll need to take your Python skills to the next level.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-importance-of-building-projects&quot;&gt;The Importance of Building Projects&lt;/h2&gt;
&lt;p&gt;Working on projects is vital to pushing your career as a Python developer forward. They make you apply the skills and knowledge you&amp;rsquo;re acquiring.&lt;/p&gt;
&lt;p&gt;Projects can help you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Build confidence&lt;/strong&gt;: You will believe more in your ability to create software regardless the level of complexity.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Explore other technologies&lt;/strong&gt;: You will learn about other technologies needed in building a complete product such as databases, servers, and other languages.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Understand programming concepts better&lt;/strong&gt;: You will learn to write code better and understand concepts such as design patterns and object-oriented programming.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Experience a complete software development life cycle&lt;/strong&gt;: You will learn how to plan before writing code, manage the coding process and update software.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is a lot to gain from building projects as a Python developer.&lt;/p&gt;
&lt;h2 id=&quot;choosing-a-project-platform&quot;&gt;Choosing a Project Platform&lt;/h2&gt;
&lt;p&gt;You need to build your software to run on a platform so that people who lack certain technical knowledge can use your software. The web, desktop, and command-line are the three major platforms you&amp;rsquo;ll want to build your projects for.&lt;/p&gt;
&lt;h3 id=&quot;web&quot;&gt;Web&lt;/h3&gt;
&lt;p&gt;Web applications are applications that run on the web, they can be accessed on any device without being downloaded, provided there is access to the internet. If you want your projects to be accessible by everyone with internet access, it needs to be a web application. &lt;/p&gt;
&lt;p&gt;A web application has a back end and front end. The back end is the part where the business logic is: your back-end code will manipulate and store data. The front end is the interface of the application: your front-end code will determine the look of a web application.&lt;/p&gt;
&lt;p&gt;As an intermediate Python developer, your major focus will be the back-end code. However, the front-end code is important too, so you will need some knowledge of HTML, CSS, and maybe JavaScript to create a simple-looking interface. Just the basics will be enough.&lt;/p&gt;
&lt;p&gt;Another option is to use Python for both the front end and back end. Thanks to the &lt;a href=&quot;https://anvil.works/learn&quot;&gt;&lt;code&gt;anvil&lt;/code&gt;&lt;/a&gt; library, which eliminates the need for HTML, CSS, and JavaScript, you can focus on Python code alone.&lt;/p&gt;
&lt;p&gt;You can build web applications with Python through web frameworks such as &lt;a href=&quot;https://www.djangoproject.com/&quot;&gt;&lt;code&gt;django&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;http://flask.pocoo.org/&quot;&gt;&lt;code&gt;flask&lt;/code&gt;&lt;/a&gt;. The list of frameworks for building web applications using Python is long. There are plenty to choose from, but &lt;code&gt;django&lt;/code&gt; and &lt;code&gt;flask&lt;/code&gt; remain the most popular web frameworks.&lt;/p&gt;
&lt;h3 id=&quot;desktop-gui&quot;&gt;Desktop GUI&lt;/h3&gt;
&lt;p&gt;Every time you perform a task on your PC, be it a desktop or laptop, it is through an application. As an intermediate Python developer, you can make your own desktop applications. &lt;/p&gt;
&lt;p&gt;You do not have to learn any front-end technology to create your own Graphical User Interface (GUI) applications, as you saw with web applications. You can build all the parts using Python.&lt;/p&gt;
&lt;p&gt;There are frameworks for building your desktop applications. &lt;a href=&quot;https://pysimplegui.readthedocs.io/tutorial/&quot;&gt;&lt;code&gt;PySimpleGUI&lt;/code&gt;&lt;/a&gt; is one of them, and it&amp;rsquo;s pretty user-friendly for an intermediate Python developer. &lt;/p&gt;
&lt;p&gt;An advanced GUI framework like &lt;a href=&quot;https://pypi.org/project/PyQt5/&quot;&gt;&lt;code&gt;PyQt5&lt;/code&gt;&lt;/a&gt; is quite powerful, but it may have a steep learning curve.&lt;/p&gt;
&lt;p&gt;The software you create for the Desktop GUI is able to work on any of the Windows, Linux, or Mac operating systems. All you have to do after creating the project is compile it to an executable for your operating system of choice.&lt;/p&gt;
&lt;h3 id=&quot;command-line&quot;&gt;Command-Line&lt;/h3&gt;
&lt;p&gt;Command-line applications are those applications that work in a console window. This is the command prompt on Windows and the Terminal on Linux and Mac. &lt;/p&gt;
&lt;p&gt;You&amp;rsquo;d click to use a web or GUI application, but you&amp;rsquo;d type in commands for command-line applications. Users of command-line applications need to have some technical knowledge since they&amp;rsquo;ll need to use commands.&lt;/p&gt;
&lt;p&gt;Command-line applications may not be as beautiful or easy to use as web or GUI applications, but that doesn&amp;rsquo;t make them less powerful than web or GUI applications.&lt;/p&gt;
&lt;p&gt;You can improve the look of your command-line applications by applying colors to the text. There are libraries you can use for coloring, such as &lt;a href=&quot;https://pypi.org/project/colorama/&quot;&gt;&lt;code&gt;colorama&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://pypi.org/project/colored/&quot;&gt;&lt;code&gt;colored&lt;/code&gt;&lt;/a&gt;. You can spice things up and use some color.&lt;/p&gt;
&lt;p&gt;You can use frameworks such as &lt;a href=&quot;https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/&quot;&gt;&lt;code&gt;docopt&lt;/code&gt;, &lt;code&gt;argparse&lt;/code&gt;, and &lt;code&gt;click&lt;/code&gt;&lt;/a&gt; to build your applications.&lt;/p&gt;
&lt;h2 id=&quot;web-project-ideas&quot;&gt;Web Project Ideas&lt;/h2&gt;
&lt;p&gt;In this section, you&amp;rsquo;ll see project ideas for the web. These project ideas can be classified as utility and education tools. &lt;/p&gt;
&lt;p&gt;Here are the project ideas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Content Aggregator&lt;/li&gt;
&lt;li&gt;Regex Query Tool&lt;/li&gt;
&lt;li&gt;URL Shortener&lt;/li&gt;
&lt;li&gt;Post-It Note&lt;/li&gt;
&lt;li&gt;Quiz Application&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;content-aggregator&quot;&gt;Content Aggregator&lt;/h3&gt;
&lt;p&gt;Content is king. It exists everywhere on the web, from blogs to social media platforms. To keep up, you need to search for new information on the internet constantly. One way to stay updated is to check all the sites manually to see what the new posts are. But this is time consuming and quite tiring.&lt;/p&gt;
&lt;p&gt;This is where the content aggregator comes in: A content aggregator fetches information from various places online and gathers all of that information in one place. Therefore, you don&amp;rsquo;t have to visit multiple sites to get the latest info: one website is enough.&lt;/p&gt;
&lt;p&gt;With the content aggregator, all of the latest information can be gotten from one site that aggregates all the content. People can see the posts that interest them and can decide to find out more about them without traipsing all over the internet. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Content Aggregators&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Content Aggregator idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://alltop.com/&quot;&gt;AllTop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.hvper.com/&quot;&gt;Hvper&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project idea is to aggregate content. First, you need to know what sites you&amp;rsquo;ll want the Content Aggregator to get content from. Then, you can use libraries such as &lt;a href=&quot;https://realpython.com/python-requests/&quot;&gt;&lt;code&gt;requests&lt;/code&gt;&lt;/a&gt; for sending HTTP requests and &lt;a href=&quot;https://www.crummy.com/software/BeautifulSoup/bs4/doc/&quot;&gt;&lt;code&gt;BeautifulSoup&lt;/code&gt;&lt;/a&gt; to parse and scrape the necessary content from the sites.&lt;/p&gt;
&lt;p&gt;Your application can implement its content aggregation as a background process. Libraries such as &lt;code&gt;celery&lt;/code&gt; or &lt;code&gt;apscheduler&lt;/code&gt; can help with that. You can try out &lt;code&gt;apscheduler&lt;/code&gt;. It&amp;rsquo;s great for small background processes.&lt;/p&gt;
&lt;p&gt;After scraping content from various sites, you&amp;rsquo;ll need to save it somewhere. So, you&amp;rsquo;ll use a database to save the scraped content.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For a tougher challenge, you can add more websites. This will help you &lt;a href=&quot;https://realpython.com/python-web-scraping-practical-introduction/&quot;&gt;learn how to study and extract information from websites&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can also have users subscribe to certain sites that you aggregate. Then, at the end of the day, the content aggregator will send the articles for that day to the email address of the user.&lt;/p&gt;
&lt;h3 id=&quot;regex-query-tool&quot;&gt;Regex Query Tool&lt;/h3&gt;
&lt;p&gt;You and I deal with text daily. This article, which is also text, has a structure. This makes it easier for you to understand. Sometimes, you need to find certain information in text, and using the regular search tool in text editors can be ineffective. &lt;/p&gt;
&lt;p&gt;This is where the Regex Query Tool comes in. A regex is a set of strings, so the regex query tool will check for the validity of the queries. When the regex matches patterns in the text, it tells the user and highlights the matched patterns. So, your Regex Query Tool will check the validity of the regex strings passed in by the user.&lt;/p&gt;
&lt;p&gt;With the Regex Query Tool, users can quickly check the validity of their regex strings on the web. This makes it easier for them, instead of having to check the strings with a text editor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Regex Query Tools&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Regex Query Tool idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.freeformatter.com/regex-tester.html&quot;&gt;FreeFormatter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.regextester.com/&quot;&gt;RegexTester&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this type of project is to tell the user the validity of the inputted query strings. You can make it give a positive or negative response such as &lt;code&gt;Query String Is Valid&lt;/code&gt; and &lt;code&gt;Query String Is Invalid&lt;/code&gt;, implementing the positive response in green and the negative in red.&lt;/p&gt;
&lt;p&gt;You don&amp;rsquo;t have to implement the query tool from scratch. You can use Python&amp;rsquo;s standard &lt;code&gt;re&lt;/code&gt; library, which you can use to run the query strings on the inputted text. The &lt;code&gt;re&lt;/code&gt; library will return &lt;code&gt;None&lt;/code&gt; when the query string matches nothing, and it&amp;rsquo;ll return the matched strings when positive.&lt;/p&gt;
&lt;p&gt;Some users may not understand regex fully, so you can make a page to explain how regex works. You can make documentation that is interesting enough to keep the users excited about learning and understanding regex.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Making a project that just returns the validity of the regex is fine. But you can also add a replacement feature. This means the application will check for the validity of the regex and also allow users to replace the matched strings with something else. So the tool is no longer a find tool but also a replace tool.&lt;/p&gt;
&lt;h3 id=&quot;url-shortener&quot;&gt;URL Shortener&lt;/h3&gt;
&lt;p&gt;URLs can be extremely long and not user-friendly. When people share links or even try to remember a URL, it&amp;rsquo;s difficult because most URLs are filled with more difficult characters and don&amp;rsquo;t form meaningful words.&lt;/p&gt;
&lt;p&gt;This is where the URL Shortener comes in. A URL Shortener reduces the characters or letters in a URL, making them easier to read and remember. A URL like &lt;code&gt;xyz.com/wwryb78&amp;amp;svnhkn%sghq?sfiyh&lt;/code&gt; can be shortened to &lt;code&gt;xyz.com/piojwr&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With the URL Shortener, URLs become a joy to work with.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of URL Shorteners&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the URL Shortener idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://bitly.com/&quot;&gt;Bitly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.msht.us/&quot;&gt;MeShort&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project idea is to shorten URLs. The main task the application will accomplish is to shorten URLs and then redirect users to the original URL when the shortened URL is visited.&lt;/p&gt;
&lt;p&gt;In the application, the users will input the original URL, and they will get the new, shortened URL as the result. To do this, you can use a combination of the &lt;code&gt;random&lt;/code&gt; and &lt;code&gt;string&lt;/code&gt; modules to generate the characters for the shortened URL.&lt;/p&gt;
&lt;p&gt;Since users will visit the shortened URL days, months, or even years after, you&amp;rsquo;ll need to save the original and shortened URLs in a database. When a request comes in, the application checks if the URL exists and redirects to the original, or else it redirects to a 404 page.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Generating a shortened URL with random characters makes for a better URL than the long, random ones. But, you can make the result better for the users. You can add a feature to customize URLs, so the users can customize the generated URLs themselves. &lt;/p&gt;
&lt;p&gt;Without a doubt, a custom &lt;code&gt;xyz.com/mysite&lt;/code&gt; URL is better than a randomly generated &lt;code&gt;xyz.com/piojwr&lt;/code&gt; URL.&lt;/p&gt;
&lt;h3 id=&quot;post-it-note&quot;&gt;Post-It Note&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s human to have many thoughts and ideas in a day, but it&amp;rsquo;s also human to forget. One way to work around forgetting things is to jot them down before they disappear into thin air. While some of forgotten thoughts and ideas may be trivial, some can be quite powerful.&lt;/p&gt;
&lt;p&gt;This is where a Post-It note comes in: A Post-It note is a small paper with low-tack adhesive at the back, making it attachable to surfaces such as documents, walls. Post-It notes make it easier to jot things down. The Post-It note project idea is something similar. It allows users to jot things down, making them accessible anywhere, since it&amp;rsquo;s a web application.&lt;/p&gt;
&lt;p&gt;With the Post-It note, people can now jot things down anywhere, without the fear of forgetting things or misplacing the notes&amp;mdash;which is a possibility with physical notes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Post-It Notes&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Post-It Note idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pinup.com/&quot;&gt;Pinup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://note.ly/&quot;&gt;Note.ly&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project is to allow users to jot down thoughts. This means that each user will have their own notes, so the application will need to have an account creation feature. This ensures that the notes of each user remain private to them.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;django&lt;/code&gt; comes with a user authentication system, so it may be a good choice. You can use other frameworks like &lt;code&gt;bottle&lt;/code&gt; or &lt;code&gt;flask&lt;/code&gt;, but you&amp;rsquo;ll have to implement the user authentication system on your own.&lt;/p&gt;
&lt;p&gt;Since users may need to separate their notes under different sections, implementing a feature to allow users to categorize their notes will make the application more useful. &lt;/p&gt;
&lt;p&gt;As an example, you may need to have notes on algorithms and data structures, so you&amp;rsquo;ll need to be able to separate the notes in those categories.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll need to store the information and notes of each user, so a database becomes an essential part of this project. The &lt;code&gt;MySQLdb&lt;/code&gt; module can be used if you want to use a MySQL database or the &lt;code&gt;psycopg2&lt;/code&gt; module for a PostgreSQL database. There are other modules you can use, but it all depends on the database you choose to use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Since it&amp;rsquo;s human for users to forget their ideas, it&amp;rsquo;s also human for them to forget that they even made a note somewhere. You can add a feature to remind users of their notes. This feature will allow users to set a time for the reminder, so the application will send the reminder to the users when it&amp;rsquo;s time, by email.&lt;/p&gt;
&lt;h3 id=&quot;quiz-application&quot;&gt;Quiz Application&lt;/h3&gt;
&lt;p&gt;Knowledge is power. There are so many things in the world to learn, and quizzes help in testing the understanding of those concepts. You, as an intermediate Python developer, do not have to understand everything about the language. Taking tests is one way to find out things you don&amp;rsquo;t fully understand.&lt;/p&gt;
&lt;p&gt;This is where the Quiz Application comes in. The Quiz Application will present questions to the users and expect the right answers to those questions. Think of the Quiz Application as a kind of questionnaire. &lt;/p&gt;
&lt;p&gt;With the Quiz Application, special users you can call administrators will be allowed to create tests, so regular users can answer the questions and test their understanding of the topics in the quiz. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Quiz Applications&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Quiz Application idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://myquiz.org/&quot;&gt;myQuiz&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kahoot.com/&quot;&gt;Kahoot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project is to set quizzes and have people answer them. Therefore, users should be able to set questions, and other users should be able to answer those questions. The application will then display the final score and the right answers.&lt;/p&gt;
&lt;p&gt;If you want users to be able to have a record of their scores, you may have to implement an account creation feature. &lt;/p&gt;
&lt;p&gt;Users creating the tests should be able to create tests with the questions and answers by simply uploading a text file. The text file will have a format that you can decide, so the application can convert from a file to a quiz.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll need to implement a database for this project. The database will store the questions, possible answers, correct answers, and the scores for each user.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For more of a challenge, you can allow users to add timers to the quizzes. This way, the creators of a quiz can determine how many seconds or minutes a user should spend on each question in the quiz. &lt;/p&gt;
&lt;p&gt;It would be great to also have a quiz-sharing feature, so users can share interesting quizzes with their friends on other platforms.&lt;/p&gt;
&lt;h2 id=&quot;gui-project-ideas&quot;&gt;GUI Project Ideas&lt;/h2&gt;
&lt;p&gt;In this section, you&amp;rsquo;ll see project ideas for Graphical User Interfaces. These project ideas can be classified as entertainment, finance, and utility tools. &lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s are the project ideas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MP3 Player&lt;/li&gt;
&lt;li&gt;Alarm Tool&lt;/li&gt;
&lt;li&gt;File Manager&lt;/li&gt;
&lt;li&gt;Expense Tracker&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;mp3-player&quot;&gt;MP3 Player&lt;/h3&gt;
&lt;p&gt;Audio is as important as text today if not more important. Since audio files are digital files, you&amp;rsquo;ll need a tool that can play them. Without a player, you&amp;rsquo;ll never be able to listen to the contents of an audio file.&lt;/p&gt;
&lt;p&gt;This is where the MP3 Player comes in. The MP3 Player is a device for playing MP3s and other digital audio files. This MP3 Player GUI project idea attempts to emulate the physical MP3 Player. You can build software that allows you play an MP3 files on your desktop or laptop computer.&lt;/p&gt;
&lt;p&gt;When you are done building the MP3 Player project, users can play their MP3 files and other digital audio files without having to purchase a physical MP3 Player. They&amp;rsquo;ll be able to play the MP3 files using their computers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of MP3 Players&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the MP3 Player idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://getmusicbee.com/&quot;&gt;MusicBee&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.foobar2000.org/&quot;&gt;Foobar2000&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project is to allow users to play MP3 and digital audio files. To be engaging for users, the application has to have a simple but beautiful user interface.&lt;/p&gt;
&lt;p&gt;You can have an interface for listing the available MP3 files. You can also give users the option to list other digital audio files that are not MP3.&lt;/p&gt;
&lt;p&gt;The users will also expect the MP3 Player to have an interface that shows information on the file that is playing. Some of the information you can include are the name of the file, its length, the amount played, and the amount not played, in minutes and seconds.&lt;/p&gt;
&lt;p&gt;Python has libraries that can play audio files, such as &lt;a href=&quot;https://pypi.org/project/Pygame/&quot;&gt;&lt;code&gt;pygame&lt;/code&gt;&lt;/a&gt;, which allows you to work with multimedia files in few lines of code. You can also check out &lt;a href=&quot;https://pypi.org/project/PyMedia/&quot;&gt;&lt;code&gt;pymedia&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://pypi.org/project/simpleaudio/&quot;&gt;&lt;code&gt;simpleaudio&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These libraries can handle a lot of digital audio files. They can handle other file types, not just the MP3 files.&lt;/p&gt;
&lt;p&gt;You can also implement a feature that allows users to create a playlist. To do this, you&amp;rsquo;ll need a database to store information on the created playlists. Python&amp;rsquo;s &lt;code&gt;sqlite3&lt;/code&gt; module allows you to use the SQLite database.&lt;/p&gt;
&lt;p&gt;The SQLite database is a better option in this case, because it is file based and easier to set up than other SQL databases. While SQLite is file based, &lt;a href=&quot;https://stackoverflow.com/questions/19946298/what-is-the-advantage-of-using-sqlite-rather-than-file&quot;&gt;it is better for saving data than a regular file&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For a more exciting challenge, you can add a feature to allow the MP3 player to repeat currently playing files or even shuffle the list of files to be played.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s also possible to implement a feature that allows users to increase and decrease the playing speed of the audio file. Users will find this interesting, as they&amp;rsquo;ll be able to play files at a slower or faster pace than usual.&lt;/p&gt;
&lt;h3 id=&quot;alarm-tool&quot;&gt;Alarm Tool&lt;/h3&gt;
&lt;p&gt;As they say, &amp;ldquo;Time and tide wait for no man.&amp;rdquo; But with a lot of things going on in our lives, it&amp;rsquo;s difficult to not lose track of time. To be able to keep track of time, a reminder is needed.&lt;/p&gt;
&lt;p&gt;This is where the Alarm Tool comes in. An alarm is a device that gives an audio or visual signal about a certain condition. This Alarm Tool project idea is an attempt to build an alarm as software. The Alarm Tool gives an audio signal when a certain condition is met. The set time is the certain condition in this case.&lt;/p&gt;
&lt;p&gt;With the Alarm Tool, users can set alarms to remind them of things at certain times of the day. The Alarm Tool project will work from the user&amp;rsquo;s laptop or desktop device, so they do not have to purchase a physical timer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Alarm Tools&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Alarm Tool idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://freealarmclocksoftware.com/&quot;&gt;FreeAlarmClock&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.apimac.com/mac/timer/&quot;&gt;TimerForMac&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project is to activate audio signals at certain times of the day. So, timing and the audio signal to be played are the most important parts of the Alarm Tool.&lt;/p&gt;
&lt;p&gt;The Alarm Tool should allow users to create, edit, and delete alarms. It should also have an interface that lists all the alarms, provided they have not being deleted by the user. So, it should list the active and inactive alarms.&lt;/p&gt;
&lt;p&gt;Since it is an alarm, the application has to play tones at the set time. There are libraries for playing audio, like the &lt;a href=&quot;https://pypi.org/project/Pygame/&quot;&gt;&lt;code&gt;pygame&lt;/code&gt;&lt;/a&gt; library.&lt;/p&gt;
&lt;p&gt;In your code logic, the application has to keep checking for set alarm times. When the time is reached, it triggers a function to play the alarm tone.&lt;/p&gt;
&lt;p&gt;Since the application will check for set alarm times, it means the application has to save the alarms in a database. The database should store things like the alarm date, time, and tone location.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As an extra feature, you can allow users to set recurring alarms. They&amp;rsquo;ll be able to set alarms that will ring at a certain time on certain days of the week, every week. As an example, an alarm can be set at 2:00 PM every Monday.&lt;/p&gt;
&lt;p&gt;You can also add a snooze feature, so your users can snooze alarms instead of only dismissing them.&lt;/p&gt;
&lt;h3 id=&quot;file-manager&quot;&gt;File Manager&lt;/h3&gt;
&lt;p&gt;The number of files on the personal computer of an average PC user is pretty high. If all of those files were placed in a single directory, it would be difficult to navigate and find files or directories. So, there is a need to arrange the files and manage them properly.&lt;/p&gt;
&lt;p&gt;This is where a file manager comes in. A file manager allows users to manage files and directories through a user interface. While files can be managed through the command-line, not all users know how to do that.&lt;/p&gt;
&lt;p&gt;With a file manager, users can arrange, access, and administer their files and directories properly without knowing how to use the command line. Some of the tasks a file manager allows users to perform includes copying, moving, and renaming files or directories.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of File Manager Tools&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the File Manager idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://freecommander.com/en/summary/&quot;&gt;FreeCommander&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://explorerplusplus.com/&quot;&gt;Explorer++&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of the file manager project is to give users an interface to manage their files. Users want a file manager that has a file management tool that looks good and is easy to use. &lt;/p&gt;
&lt;p&gt;You can use the &lt;code&gt;PySimpleGUI&lt;/code&gt; library to create unique user interfaces with a powerful widget, without having to deal with a lot of complexity.&lt;/p&gt;
&lt;p&gt;Your users should be able to perform simple tasks like creating new directories or empty text files. They should also be able to copy and move files or directories. &lt;/p&gt;
&lt;p&gt;The &lt;code&gt;sys&lt;/code&gt;, &lt;code&gt;os&lt;/code&gt;, and &lt;code&gt;shutil&lt;/code&gt; libraries will be quite useful for this project, as they can be used to execute actions on the files in the background, while the user clicks away.&lt;/p&gt;
&lt;p&gt;The grid and list views are popular views today, so you can implement both in the application. This gives the user the option to choose which view option is suitable for them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To make the file manager a bit more advanced, you can implement a search feature. So users can search for files and directories without having to find them manually.&lt;/p&gt;
&lt;p&gt;You can also implement a sort feature. This will allow users to sort files according to different orders, such as time, alphabetical order, or size.&lt;/p&gt;
&lt;h3 id=&quot;expense-tracker&quot;&gt;Expense Tracker&lt;/h3&gt;
&lt;p&gt;We have daily expenses, from groceries to clothing to bills. There are so many expenses that it&amp;rsquo;s normal to lose track of them and keep spending till we&amp;rsquo;re almost out of cash. A tracker can help people watch their expenses.&lt;/p&gt;
&lt;p&gt;This is where the expense tracker comes in. An expense tracker is a software tool that allows users to keep track of their expenses. It can also analyze the expenses, depending on how advanced it is, but let&amp;rsquo;s keep it simple for now.&lt;/p&gt;
&lt;p&gt;With the expense tracker, users can set a budget and track their spending so as to make better financial decisions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Expense Trackers&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Expense Tracker idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://buddi.digitalcave.ca/&quot;&gt;Buddi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gnucash.org/&quot;&gt;GnuCash&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project is to keep track of the user&amp;rsquo;s expenses. Some statistical analysis has to be done to be able to give users correct information on their expenses and help them spend better.&lt;/p&gt;
&lt;p&gt;While tracking the expenses is the key thing, a good interface is also important. With &lt;code&gt;PySimpleGUI&lt;/code&gt;, you can create a unique interface to improve the experience of the users.&lt;/p&gt;
&lt;p&gt;PyData libraries such as &lt;code&gt;pandas&lt;/code&gt; and &lt;code&gt;matplotlib&lt;/code&gt; can be helpful for building the expense tracker. &lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://pandas.pydata.org/pandas-docs/stable/&quot;&gt;&lt;code&gt;pandas&lt;/code&gt;&lt;/a&gt; library can be used for the data analysis, and the &lt;a href=&quot;https://matplotlib.org/&quot;&gt;&lt;code&gt;matplotlib&lt;/code&gt;&lt;/a&gt; library can be used for plotting graphs. Graphs will give the users a visual representation of their expenses, and a visual representation is usually easier to understand.&lt;/p&gt;
&lt;p&gt;The application will receive data from the users. The data here is the inputted expenses. So, you&amp;rsquo;ll have to store the expenses in a database. The SQLite database is a good database choice for this project since it can be set up quickly. You can use &lt;code&gt;sqlite3&lt;/code&gt; module for the SQLite database.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For your users to benefit from this project, they&amp;rsquo;ll have to input their expenses regularly, which might slip their mind. 
It could be useful for you to implement a reminder feature. So the application will send a notification at certain times of the day or the week, reminding them to make use of the expense tracker.&lt;/p&gt;
&lt;h2 id=&quot;command-line-project-ideas&quot;&gt;Command-Line Project Ideas&lt;/h2&gt;
&lt;p&gt;In this section, you&amp;rsquo;ll see project ideas for the command-line. The project ideas discussed can be classified as utility tools. &lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s are the project ideas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Contact Book&lt;/li&gt;
&lt;li&gt;File Connectivity Checker&lt;/li&gt;
&lt;li&gt;Bulk File Rename Tool&lt;/li&gt;
&lt;li&gt;Directory Tree Generator&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;contact-book&quot;&gt;Contact Book&lt;/h3&gt;
&lt;p&gt;We come across lots of people daily. We make acquaintances and friends. We get their contacts to keep in touch later on. Sadly, keeping the received contact details can be hard. One way to do this is to write the contact details down. But this is not secure as the physical book can easily be lost.&lt;/p&gt;
&lt;p&gt;This is where the Contact Book project comes in. A contact book is a tool for saving a contact&amp;rsquo;s details, such as name, address, phone number, and email address. With this contact book project, you can build a software tool that people can use to save and find contact details.&lt;/p&gt;
&lt;p&gt;With the contact book project idea, users can save their contacts with less risk of losing the saved contact details. It&amp;rsquo;ll always be accessible from their computer, through the command-line.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Contact Book Tools&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There are Contact Book applications, but it&amp;rsquo;s rare to find command-line Contact Book products, as most are web, mobile, or GUI applications.&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Contact Book idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.simplemobiletools.contacts&amp;amp;hl=en_US&quot;&gt;Simple Contacts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://connect.pobuca.com/&quot;&gt;Pobuca Connect&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project is to save contact details. It&amp;rsquo;s important that you set up the commands users can use to enter the contact details. You can use the &lt;code&gt;argparse&lt;/code&gt; or &lt;code&gt;click&lt;/code&gt; command-line frameworks. They abstract a lot of complex stuff, so you only have to focus on the logic to be run when executing commands.&lt;/p&gt;
&lt;p&gt;Some features you should implement include the commands to delete contacts, update contact information, and list saved contacts. You can also allow users to list contacts using different parameters, such as alphabetical order or contact creation date.&lt;/p&gt;
&lt;p&gt;Since it&amp;rsquo;s a command-line project, the SQLite database will be fine for saving contacts. SQLite is user-friendly to set up. You may save the contact details in a file, but a file will not offer the benefits you can gain from using SQLite, such as performance and security.&lt;/p&gt;
&lt;p&gt;To use the SQLite database in this project, the Python &lt;code&gt;sqlite3&lt;/code&gt; module will be very useful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Remember how the database is stored on the user&amp;rsquo;s computer? What if something happens, like the user losing their files? It means they&amp;rsquo;ll also lose the contact details.&lt;/p&gt;
&lt;p&gt;You can challenge yourself further and backup the database to an online storage platform. To do this, you can upload the database files to the cloud at certain intervals.&lt;/p&gt;
&lt;p&gt;You can also add a command that allows users to backup the database themselves. This way, the user can still have access to the contacts if the database file is lost. &lt;/p&gt;
&lt;p&gt;You should note that you may need some form of identification, so the contact book can tell which database file belongs to which user. Implementing a user authentication feature is one way to go about it.&lt;/p&gt;
&lt;h3 id=&quot;site-connectivity-checker&quot;&gt;Site Connectivity Checker&lt;/h3&gt;
&lt;p&gt;When you visit a URL, you expect to get the requested pages on your browser. But this is not always the case. Sometimes, sites can be down, so you won&amp;rsquo;t get the desired results. Instead, you&amp;rsquo;ll be presented with error messages. You can keep trying a site that is down, till it comes up and you get the information you need.&lt;/p&gt;
&lt;p&gt;This is where the Site Connectivity Checker project comes in. The Site Connectivity Checker visits a URL and returns the status of the URL: it is either live or not. The Site Connectivity Checker will visit the URL at intervals, returning the results of each visit.&lt;/p&gt;
&lt;p&gt;Instead of manually visiting a URL, a Site Connectivity Checker can do all of that manual work for you. This way, you&amp;rsquo;ll only get the results of the check without having to spend time on the browser, waiting for the site to go live.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Site Connectivity Checkers&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Site Connectivity Checker idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Ping_(networking_utility)&quot;&gt;Ping&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.site24x7.com/check-website-availability.html&quot;&gt;Site24x7&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project is to check the status of sites. So, you need to write code for checking the status of a website.&lt;/p&gt;
&lt;p&gt;You can choose to use either &lt;a href=&quot;https://en.wikipedia.org/wiki/Transmission_Control_Protocol&quot;&gt;TCP&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol&quot;&gt;ICMP&lt;/a&gt; for your connections. The &lt;a href=&quot;https://docs.python.org/3/library/socket.html&quot;&gt;&lt;code&gt;socket&lt;/code&gt;&lt;/a&gt; module is one to check out. You can also read &lt;a href=&quot;https://realpython.com/python-sockets/&quot;&gt;Socket Programming in Python (Guide)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Through your chosen framework, be it the &lt;code&gt;docopt&lt;/code&gt;, &lt;code&gt;click&lt;/code&gt;, or &lt;code&gt;argparse&lt;/code&gt; framework, you can add commands to allow users to add and remove sites from the list of sites to be checked.&lt;/p&gt;
&lt;p&gt;The users should also be able to start the tool, stop it, and determine the intervals.&lt;/p&gt;
&lt;p&gt;Since you&amp;rsquo;ll have to save the list of files to be checked, you can either save it in a file (just a list of sites) or use a SQLite database through the &lt;code&gt;sqlite3&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The application can check for the connectivity status of sites and display the results to the command-line. But this will require the user to keep checking the command-line. &lt;/p&gt;
&lt;p&gt;You can increase the challenge and implement a notification feature. The notification feature can be a sound played in the background to alert the user when a site&amp;rsquo;s status changes. You&amp;rsquo;ll need a database to store the previous status of a site. That&amp;rsquo;s the only way the tool can tell when the status changes.&lt;/p&gt;
&lt;h3 id=&quot;bulk-file-rename-tool&quot;&gt;Bulk File Rename Tool&lt;/h3&gt;
&lt;p&gt;Sometimes, you need to name all the files in a directory according to certain conventions. For example, you can name all the files in a directory with &lt;code&gt;File0001.jpg&lt;/code&gt;, where the numbers increase based on the number of files in the directory. Doing this manually can be stressful and repetitive.&lt;/p&gt;
&lt;p&gt;The Bulk File Rename Tool allows users to rename a large number of files, without having to manually rename files.&lt;/p&gt;
&lt;p&gt;This saves users a lot of time. It spares them the trouble of having to do boring repetitive work and make mistakes. With the Bulk File Rename Tool, users can rename files in a couple of seconds without any mistakes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Bulk File Rename Tools&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Bulk File Rename idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Ren_(command)&quot;&gt;Ren&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.tecmint.com/rename-multiple-files-in-linux/&quot;&gt;Rename&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project idea is to rename files. So, the application needs to find a way to manipulate the target files. The &lt;code&gt;os&lt;/code&gt;, &lt;code&gt;sys&lt;/code&gt;, and &lt;code&gt;shutil&lt;/code&gt; libraries will be useful for a large part of this project.&lt;/p&gt;
&lt;p&gt;Your users will be able to rename all the files in the directory, using naming conventions. Therefore, they should be able to pass in the naming convention of choice. The &lt;code&gt;regex&lt;/code&gt; module will help match the required naming patterns, if you understand how regex works.&lt;/p&gt;
&lt;p&gt;A user may want to pass in a naming convention such as &lt;code&gt;myfiles&lt;/code&gt; as part of the commands and expect that the tool renames all the files like &lt;code&gt;myfilesXYZ&lt;/code&gt;, where &lt;code&gt;XYZ&lt;/code&gt; is a number. They should also be able to choose the directory where the files to be renamed are.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The major challenge in this project is to rename all the files in a directory. But users may only need to name a certain number of files. To test your skills, you can implement a feature to allow users to choose the number of files to be renamed, instead of all the files. &lt;/p&gt;
&lt;p&gt;Note that renaming only a certain number of files will require the tool to sort the files based on alphabetical order, time of file creation, or file size, depending on the user&amp;rsquo;s requirements. &lt;/p&gt;
&lt;h3 id=&quot;directory-tree-generator&quot;&gt;Directory Tree Generator&lt;/h3&gt;
&lt;p&gt;Directories are like family trees: each directory has a particular relationship with other directories. No directories ever stays on its own, except an empty root directory. &lt;/p&gt;
&lt;p&gt;When you&amp;rsquo;re working with files and directories, it is difficult to see the relationship between directories, as you can only see what exists in the current directory. You&amp;rsquo;re either using a file manager or working from the command-line. &lt;/p&gt;
&lt;p&gt;With a Directory Tree Generator, you can see the relationship between files and directories like a tree or a map. &lt;/p&gt;
&lt;p&gt;This makes it easier to understand the positioning of files and directories. A directory tree map is important when you&amp;rsquo;re explaining certain concepts, and a Directory Tree Generator makes it easier to get a visual representation of the file and directory relationships.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Directory Tree Generators&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Directory Tree Generator idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Tree_(command)&quot;&gt;Tree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mauriziodimatteo/dirtreex&quot;&gt;Dirtreex&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of the Directory Tree Generator is to visualize the relationships between files and directories. The &lt;code&gt;os&lt;/code&gt; library can be very useful in listing the files and directories in a chosen directory.&lt;/p&gt;
&lt;p&gt;Using a framework such as &lt;code&gt;docopt&lt;/code&gt; or &lt;code&gt;argparse&lt;/code&gt; helps abstract a lot of stuff, allowing you to focus on writing code for the application&amp;rsquo;s logic.&lt;/p&gt;
&lt;p&gt;In the application&amp;rsquo;s logic, you can decide how you want to represent files or directories. Using different colors is a brilliant way to go about it. You can use the &lt;code&gt;colored&lt;/code&gt; library to print the files and directories in different colors. &lt;/p&gt;
&lt;p&gt;You can also decide how deep you&amp;rsquo;d like the Directory Tree Generator to go. For example, if a directory has children directories twelve levels deep, you may decide to go only as deep as the fifth level.&lt;/p&gt;
&lt;p&gt;If you wish, you can also let the user decide how deep they want the Directory Tree Generator to go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Since the results of the generated directory tree will be on the command-line, you can go one step further. You can have the generator create images of the directory tree, so it&amp;rsquo;ll basically turn the text into an image. &lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll find the &lt;a href=&quot;https://pillow.readthedocs.io/en/stable/&quot;&gt;&lt;code&gt;pillow&lt;/code&gt;&lt;/a&gt; library useful for doing this.&lt;/p&gt;
&lt;h2 id=&quot;tips-for-working-on-projects&quot;&gt;Tips for Working on Projects&lt;/h2&gt;
&lt;p&gt;Working on projects can be difficult. That&amp;rsquo;s one reason why motivation and interest in a project will make it a less daunting task.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested in a project, you&amp;rsquo;ll be able to put in the time to research as well as find libraries and tools that will help you with the project.&lt;/p&gt;
&lt;p&gt;Here are some tips:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Find a source of motivation&lt;/li&gt;
&lt;li&gt;Break the project into subtasks&lt;/li&gt;
&lt;li&gt;Do research on the subtasks&lt;/li&gt;
&lt;li&gt;Build each subtasks, one step at a time&lt;/li&gt;
&lt;li&gt;Reach out for help if you&amp;rsquo;re stuck&lt;/li&gt;
&lt;li&gt;Put the subtasks together&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this article, you&amp;rsquo;ve seen a couple of Python project ideas you may find interesting. &lt;/p&gt;
&lt;p&gt;The project ideas cover a range of platforms. You saw project ideas for the Web, GUI, and Command-line platforms. &lt;/p&gt;
&lt;p&gt;You can choose to build a project for different platforms. Using the URL Shortener as an example, you may choose to build one for the Web, GUI, or the Command-line.&lt;/p&gt;
&lt;p&gt;Since you&amp;rsquo;re an intermediate Python developer, these projects can be quite challenging but interesting.&lt;/p&gt;
&lt;p&gt;The best way to make a project happen is to just get started. In no time, you&amp;rsquo;ll be finished and discover how much you&amp;rsquo;ve benefited from working on a project!&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Python Decorators 101</title>
      <id>https://realpython.com/courses/python-decorators-101/</id>
      <link href="https://realpython.com/courses/python-decorators-101/"/>
      <updated>2019-03-19T14:00:00+00:00</updated>
      <summary>In this course on Python decorators, you&#39;ll learn what they are and how to create and use them. Decorators provide a simple syntax for calling higher-order functions in Python. By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.</summary>
      <content type="html">
        &lt;p&gt;In this course on Python decorators, you&amp;rsquo;ll learn what they are and how to create and use them. Decorators provide a simple syntax for calling higher-order functions in Python. By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>How to Build a Python GUI Application With wxPython</title>
      <id>https://realpython.com/python-gui-with-wxpython/</id>
      <link href="https://realpython.com/python-gui-with-wxpython/"/>
      <updated>2019-03-18T14:00:00+00:00</updated>
      <summary>In this step-by-step tutorial, you&#39;ll learn how to create a cross-platform graphical user interface (GUI) using Python and the wxPython toolkit. A graphical user interface is an application that has buttons, windows, and lots of other widgets that the user can use to interact with your application.</summary>
      <content type="html">
        &lt;p&gt;There are many graphical user interface (GUI) toolkits that you can use with the Python programming language. The big three are Tkinter, wxPython, and PyQt. Each of these toolkits will work with Windows, macOS, and Linux, with PyQt having the additional capability of working on mobile. &lt;/p&gt;
&lt;p&gt;A graphical user interface is an application that has buttons, windows, and lots of other widgets that the user can use to interact with your application. A good example would be a web browser. It has buttons, tabs, and a main window where all the content loads.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this article, you&amp;rsquo;ll learn how to build a graphical user interface with Python using the wxPython GUI toolkit.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are the topics covered:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Getting Started with wxPython&lt;/li&gt;
&lt;li&gt;Definition of a GUI&lt;/li&gt;
&lt;li&gt;Creating a Skeleton Application&lt;/li&gt;
&lt;li&gt;Creating a Working Application&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s start learning!&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-tricks-sample&quot; data-focus=&quot;false&quot;&gt;Click here to get access to a chapter from Python Tricks: The Book&lt;/a&gt; that shows you Python&#39;s best practices with simple examples you can apply instantly to write more beautiful + Pythonic code.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;getting-started-with-wxpython&quot;&gt;Getting Started With wxPython&lt;/h2&gt;
&lt;p&gt;The wxPython GUI toolkit is a Python wrapper around a C++ library called &lt;a href=&quot;https://www.wxwidgets.org/&quot;&gt;wxWidgets&lt;/a&gt;. The initial release of wxPython was in 1998, so wxPython has been around quite a long time. wxPython&amp;rsquo;s primary difference from other toolkits, such as &lt;strong&gt;PyQt&lt;/strong&gt; or &lt;strong&gt;Tkinter&lt;/strong&gt;, is that wxPython uses the actual widgets on the native platform whenever possible. This makes wxPython applications look native to the operating system that it is running on.&lt;/p&gt;
&lt;p&gt;PyQt and Tkinter both draw their widgets themselves, which is why they don&amp;rsquo;t always match the native widgets, although PyQt is very close.&lt;/p&gt;
&lt;p&gt;This is not to say that wxPython does not support custom widgets. In fact, the wxPython toolkit has many custom widgets included with it, along with dozens upon dozens of core widgets. The wxPython &lt;a href=&quot;https://wxpython.org/pages/downloads/&quot;&gt;downloads&lt;/a&gt; page has a section called &lt;strong&gt;Extra Files&lt;/strong&gt; that is worth checking out. &lt;/p&gt;
&lt;p&gt;Here, there is a download of the wxPython Demo package. This is a nice little application that demonstrates the vast majority of the widgets that are included with wxPython. The demo allows a developer to view the code in one tab and run it in a second tab. You can even edit and re-run the code in the demo to see how your changes affect the application.&lt;/p&gt;
&lt;h3 id=&quot;installing-wxpython&quot;&gt;Installing wxPython&lt;/h3&gt;
&lt;p&gt;You will be using the latest wxPython for this article, which is &lt;strong&gt;wxPython 4&lt;/strong&gt;, also known as the Phoenix release. The wxPython 3 and wxPython 2 versions are built only for &lt;strong&gt;Python 2&lt;/strong&gt;. When Robin Dunn, the primary maintainer of wxPython, created the wxPython 4 release, he deprecated a lot of aliases and cleaned up a lot of code to make wxPython more Pythonic and easier to maintain. &lt;/p&gt;
&lt;p&gt;You will want to consult the following links if you are migrating from an older version of wxPython to wxPython 4 (Phoenix):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://wxpython.org/Phoenix/docs/html/classic_vs_phoenix.html&quot;&gt;Classic vs Phoenix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;wxPython Project Phoenix &lt;a href=&quot;https://wxpython.org/Phoenix/docs/html/MigrationGuide.html&quot;&gt;Migration Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The wxPython 4 package is compatible with both Python 2.7 and Python 3.&lt;/p&gt;
&lt;p&gt;You can now use &lt;code&gt;pip&lt;/code&gt; to install wxPython 4, which was not possible in the legacy versions of wxPython. You can do the following to install it on your machine:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install wxpython
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; On Mac OS X you will need a compiler installed such as &lt;strong&gt;XCode&lt;/strong&gt; for the install to complete successfully. Linux may also require you to install some dependencies before the &lt;code&gt;pip&lt;/code&gt; installer will work correctly. &lt;/p&gt;
&lt;p&gt;For example, I needed to install &lt;strong&gt;freeglut3-dev&lt;/strong&gt;, &lt;strong&gt;libgstreamer-plugins-base0.10-dev&lt;/strong&gt;, and &lt;strong&gt;libwebkitgtk-3.0-dev&lt;/strong&gt; on Xubuntu to get it to install. &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Fortunately, the error messages that &lt;code&gt;pip&lt;/code&gt; displays are helpful in figuring out what is missing, and you can use the &lt;a href=&quot;https://github.com/wxWidgets/Phoenix/blob/master/README.rst#prerequisites&quot;&gt;prerequisites&lt;/a&gt; section on the wxPython Github page to help you find the information you need if you want to install wxPython on Linux.&lt;/p&gt;
&lt;p&gt;There are some Python wheels available for the most popular Linux versions that you can find in the &lt;a href=&quot;https://extras.wxpython.org/wxPython4/extras/linux&quot;&gt;Extras Linux&lt;/a&gt; section with both GTK2 and GTK3 versions. To install one of these wheels, you would use the following command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install -U -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-18.04/ wxPython
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Be sure you have modified the command above to match your version of Linux.&lt;/p&gt;
&lt;h3 id=&quot;definition-of-a-gui&quot;&gt;Definition of a GUI&lt;/h3&gt;
&lt;p&gt;As was mentioned in the introduction, a graphical user interface (GUI) is an interface that is drawn on the screen for the user to interact with.&lt;/p&gt;
&lt;p&gt;User interfaces have some common components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Main window&lt;/li&gt;
&lt;li&gt;Menu&lt;/li&gt;
&lt;li&gt;Toolbar&lt;/li&gt;
&lt;li&gt;Buttons&lt;/li&gt;
&lt;li&gt;Text Entry&lt;/li&gt;
&lt;li&gt;Labels&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these items are known generically as &lt;strong&gt;widgets&lt;/strong&gt;. There are many other common widgets and many custom widgets that wxPython supports. A developer will take the widgets and arrange them logically on a window for the user to interact with.&lt;/p&gt;
&lt;h3 id=&quot;event-loops&quot;&gt;Event Loops&lt;/h3&gt;
&lt;p&gt;A graphical user interface works by waiting for the user to do something. The something is called an &lt;strong&gt;event&lt;/strong&gt;. Events happen when the user types something while your application is in focus or when the user uses their mouse to press a button or other widget. &lt;/p&gt;
&lt;p&gt;Underneath the covers, the GUI toolkit is running an infinite loop that is called an &lt;strong&gt;event loop&lt;/strong&gt;. The event loop just waits for events to occur and then acts on those events according to what the developer has coded the application to do. When the application doesn&amp;rsquo;t catch an event, it effectively ignores that it even happened.&lt;/p&gt;
&lt;p&gt;When you are programming a graphical user interface, you will want to keep in mind that you will need to hook up each of the widgets to &lt;strong&gt;event handlers&lt;/strong&gt; so that your application will do something. &lt;/p&gt;
&lt;p&gt;There is a special consideration that you need to keep in mind when working with event loops: they can be blocked. When you block an event loop, the GUI will become unresponsive and appear to freeze to the user. &lt;/p&gt;
&lt;p&gt;Any process that you launch in a GUI that will take longer than a quarter second should probably be launched as a separate thread or process. This will prevent your GUI from freezing and give the user a better user experience. &lt;/p&gt;
&lt;p&gt;The wxPython framework has special thread-safe methods that you can use to communicate back to your application to let it know that the thread is finished or to give it an update.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s create a skeleton application to demonstrate how events work.&lt;/p&gt;
&lt;h2 id=&quot;creating-a-skeleton-application&quot;&gt;Creating a Skeleton Application&lt;/h2&gt;
&lt;p&gt;An application skeleton in a GUI context is a user interface with widgets that don&amp;rsquo;t have any event handlers. These are useful for prototyping. You basically just create the GUI and present it to your stakeholders for sign-off before spending a lot of time on the backend logic.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start by creating a &lt;code&gt;Hello World&lt;/code&gt; application with wxPython:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;wx&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Hello World&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainLoop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Mac users may get the following message: &lt;em&gt;This program needs access to the screen. Please run with a Framework build of python, and only when you are logged in on the main display of your Mac.&lt;/em&gt; If you see this message and you are not running in a virtualenv, then you need to run your application with &lt;strong&gt;pythonw&lt;/strong&gt; instead of &lt;strong&gt;python&lt;/strong&gt;. If you are running wxPython from within a virtualenv, then see the &lt;a href=&quot;https://wiki.wxpython.org/wxPythonVirtualenvOnMac&quot;&gt;wxPython wiki&lt;/a&gt; for the solution.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;In this example, you have two parts: &lt;code&gt;wx.App&lt;/code&gt; and the &lt;code&gt;wx.Frame&lt;/code&gt;. The &lt;code&gt;wx.App&lt;/code&gt; is wxPython&amp;rsquo;s application object and is required for running your GUI. The &lt;code&gt;wx.App&lt;/code&gt; starts something called a &lt;code&gt;.MainLoop()&lt;/code&gt;. This is the event loop that you learned about in the previous section. &lt;/p&gt;
&lt;p&gt;The other piece of the puzzle is &lt;code&gt;wx.Frame&lt;/code&gt;, which will create a window for the user to interact with. In this case, you told wxPython that the frame has no parent and that its title is &lt;code&gt;Hello World&lt;/code&gt;. Here is what it looks like when you run the code:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/hello1.c3e5d5e1cdb9.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/hello1.c3e5d5e1cdb9.png&quot; width=&quot;400&quot; height=&quot;250&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/hello1.c3e5d5e1cdb9.png&amp;amp;w=100&amp;amp;sig=5914916bf0badc17d5f8d3f6e00e32cb19ad0cd1 100w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/hello1.c3e5d5e1cdb9.png&amp;amp;w=200&amp;amp;sig=bbfd92446bb1916ee56ee4db5d60e4a2eebddbef 200w, https://files.realpython.com/media/hello1.c3e5d5e1cdb9.png 400w&quot; sizes=&quot;75vw&quot; alt=&quot;Hello World in wxPython&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The application will look different when you run it on Mac or Windows.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;By default, a &lt;code&gt;wx.Frame&lt;/code&gt; will include minimize, maximize, and exit buttons along the top. You won&amp;rsquo;t normally create an application in this manner though. Most wxPython code will require you to subclass the &lt;code&gt;wx.Frame&lt;/code&gt; and other widgets so that you can get the full power of the toolkit. &lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take a moment and rewrite your code as a class:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;wx&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Hello World&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainLoop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can use this code as a template for your application. However, this application doesn&amp;rsquo;t do very much, so let&amp;rsquo;s take a moment to learn a little about some of the other widgets you could add.&lt;/p&gt;
&lt;h3 id=&quot;widgets&quot;&gt;Widgets&lt;/h3&gt;
&lt;p&gt;The wxPython toolkit has more than one hundred widgets to choose from. This allows you to create rich applications, but it can also be daunting trying to figure out which widget to use. This is why the &lt;strong&gt;wxPython Demo&lt;/strong&gt; is helpful, as it has a search filter that you can use to help you find the widgets that might apply to your project.&lt;/p&gt;
&lt;p&gt;Most GUI applications allow the user to enter some text and press a button. Let&amp;rsquo;s go ahead and add those widgets:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;wx&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Hello World&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_ctrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextCtrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;my_btn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Press Me&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;55&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainLoop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you run this code, your application should look like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/hello2.4b3bac9c90eb.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/hello2.4b3bac9c90eb.png&quot; width=&quot;400&quot; height=&quot;250&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/hello2.4b3bac9c90eb.png&amp;amp;w=100&amp;amp;sig=c943d9b392d5c56daa09c61b979236aec515bcbe 100w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/hello2.4b3bac9c90eb.png&amp;amp;w=200&amp;amp;sig=5b0b2d5b47ffb7b1c762295e9dcf158f0194216c 200w, https://files.realpython.com/media/hello2.4b3bac9c90eb.png 400w&quot; sizes=&quot;75vw&quot; alt=&quot;Hello World in wxPython with widgets&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The first widget you need to add is something called &lt;code&gt;wx.Panel&lt;/code&gt;. This widget is not required, but recommended. On Windows, you are actually required to use a Panel so that the background color of the frame is the right shade of gray. Tab traversal is disabled without a Panel on Windows.&lt;/p&gt;
&lt;p&gt;When you add the panel widget to a frame and the panel is the sole child of the frame, it will automatically expand to fill the frame with itself.&lt;/p&gt;
&lt;p&gt;The next step is to add a &lt;code&gt;wx.TextCtrl&lt;/code&gt; to the panel. The first argument for almost all widgets is which parent the widget should go onto. In this case, you want the text control and the button to be on top of the panel, so it is the parent you specify. &lt;/p&gt;
&lt;p&gt;You also need to tell wxPython where to place the widget, which you can do by passing in a position via the &lt;code&gt;pos&lt;/code&gt; parameter. In wxPython, the origin location is (0,0) which is the upper left corner of the parent. So for the text control, you tell wxPython that you want to position its top left corner 5 pixels from the left (x) and 5 pixels from the top (y).&lt;/p&gt;
&lt;p&gt;Then you add your button to the panel and give it a label. To prevent the widgets from overlapping, you need to set the y-coordinate to 55 for the button&amp;rsquo;s position.&lt;/p&gt;
&lt;h3 id=&quot;absolute-positioning&quot;&gt;Absolute Positioning&lt;/h3&gt;
&lt;p&gt;When you provide exact coordinates for your widget&amp;rsquo;s position, the technique that you used is called &lt;strong&gt;absolute positioning&lt;/strong&gt;. Most GUI toolkits provide this capability, but it&amp;rsquo;s not actually recommended.&lt;/p&gt;
&lt;p&gt;As your application becomes more complex, it becomes difficult to keep track of all the widget locations and if you have to move the widgets around. Resetting all those positions becomes a nightmare.&lt;/p&gt;
&lt;p&gt;Fortunately all modern GUI toolkits provide a solution for this, which is what you will learn about next.&lt;/p&gt;
&lt;h3 id=&quot;sizers-dynamic-sizing&quot;&gt;Sizers (Dynamic Sizing)&lt;/h3&gt;
&lt;p&gt;The wxPython toolkit includes &lt;strong&gt;sizers&lt;/strong&gt;, which are used for creating dynamic layouts. They manage the placement of your widgets for you and will adjust them when you resize the application window. Other GUI toolkits will refer to sizers as layouts, which is what PyQt does. &lt;/p&gt;
&lt;p&gt;Here are the primary types of sizers that you will see used most often:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;wx.BoxSizer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wx.GridSizer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wx.FlexGridSizer&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s add a &lt;code&gt;wx.BoxSizer&lt;/code&gt; to your example and see if we can make it work a bit more nicely:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;wx&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Hello World&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;my_sizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BoxSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VERTICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_ctrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextCtrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;my_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_ctrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EXPAND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;my_btn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Press Me&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;my_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CENTER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_sizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainLoop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here you create an instance of a &lt;code&gt;wx.BoxSizer&lt;/code&gt; and pass it &lt;code&gt;wx.VERTICAL&lt;/code&gt;, which is the orientation that widgets are added to the sizer.&lt;/p&gt;
&lt;p&gt;In this case, the widgets will be added vertically, which means they will be added one at a time from top to bottom. You may also set a BoxSizer&amp;rsquo;s orientation to &lt;code&gt;wx.HORIZONTAL&lt;/code&gt;. When you do that, the widgets would be added from left to right.&lt;/p&gt;
&lt;p&gt;To add a widget to a sizer, you will use &lt;code&gt;.Add()&lt;/code&gt;. It accepts up to five arguments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;window&lt;/code&gt; (the widget)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;proportion&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;flag&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;border&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;userData&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;window&lt;/code&gt; argument is the widget to be added while &lt;code&gt;proportion&lt;/code&gt; sets how much space relative to other widgets in the sizer this particular widget should take. By default, it is zero, which tells wxPython to leave the widget at its default proportion.&lt;/p&gt;
&lt;p&gt;The third argument is &lt;code&gt;flag&lt;/code&gt;. You can actually pass in multiple flags if you wish as long as you separate them with a pipe character: &lt;code&gt;|&lt;/code&gt;. The wxPython toolkit uses &lt;code&gt;|&lt;/code&gt; to add flags using a series of bitwise ORs.&lt;/p&gt;
&lt;p&gt;In this example, you add the text control with the &lt;code&gt;wx.ALL&lt;/code&gt; and &lt;code&gt;wx.EXPAND&lt;/code&gt; flags. The &lt;code&gt;wx.ALL&lt;/code&gt; flag tells wxPython that you want to add a border on all sides of the widget while &lt;code&gt;wx.EXPAND&lt;/code&gt; makes the widgets expand as much as they can within the sizer.&lt;/p&gt;
&lt;p&gt;Finally, you have the &lt;code&gt;border&lt;/code&gt; parameter, which tells wxPython how many pixels of border you want around the widget. The &lt;code&gt;userData&lt;/code&gt; parameter is only used when you want to do something complex with your sizing of the widget and is actually quite rare to see in practice.&lt;/p&gt;
&lt;p&gt;Adding the button to the sizer follows the exact same steps. However, to make things a bit more interesting, I went ahead and switched out the &lt;code&gt;wx.EXPAND&lt;/code&gt; flag for &lt;code&gt;wx.CENTER&lt;/code&gt; so that the button would be centered on-screen.&lt;/p&gt;
&lt;p&gt;When you run this version of the code, your application should look like the following:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/sizers.9f6c203121e6.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/sizers.9f6c203121e6.png&quot; width=&quot;400&quot; height=&quot;250&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/sizers.9f6c203121e6.png&amp;amp;w=100&amp;amp;sig=ef9024be1b46ebd2e2636d963ed13733df364e6d 100w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/sizers.9f6c203121e6.png&amp;amp;w=200&amp;amp;sig=c246e06c9963cf0541f874c5df1d67f0aa497f53 200w, https://files.realpython.com/media/sizers.9f6c203121e6.png 400w&quot; sizes=&quot;75vw&quot; alt=&quot;Hello World in wxPython with Sizers&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;d like to learn more about sizers, the &lt;a href=&quot;https://wxpython.org/Phoenix/docs/html/sizers_overview.html&quot;&gt;wxPython documentation&lt;/a&gt; has a nice page on the topic.&lt;/p&gt;
&lt;h3 id=&quot;adding-an-event&quot;&gt;Adding an Event&lt;/h3&gt;
&lt;p&gt;While your application looks more interesting visually, it still doesn&amp;rsquo;t really do anything. For example, if you press the button, nothing really happens. &lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s give the button a job:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;wx&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Hello World&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;my_sizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BoxSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VERTICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_ctrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextCtrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;my_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_ctrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EXPAND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;my_btn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Press Me&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;my_btn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EVT_BUTTON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;on_press&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;my_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CENTER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_sizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_press&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;You didn&amp;#39;t enter anything!&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;You typed: &amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{value}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainLoop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The widgets in wxPython allow you to attach event bindings to them so that they can respond to certain types of events.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The code block above uses f-strings. You can read all about them in &lt;a href=&quot;https://realpython.com/python-f-strings/&quot;&gt;Python 3&amp;rsquo;s f-Strings: An Improved String Formatting Syntax (Guide)&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You want the button to do something when the user presses it. You can accomplish this by calling the button&amp;rsquo;s &lt;code&gt;.Bind()&lt;/code&gt; method. &lt;code&gt;.Bind()&lt;/code&gt; takes the event you want to bind to, the handler to call when the event happens, an optional source, and a couple of optional ids. &lt;/p&gt;
&lt;p&gt;In this example, you bind your button object to the &lt;code&gt;wx.EVT_BUTTON&lt;/code&gt; event and tell it to call &lt;code&gt;on_press()&lt;/code&gt; when that event gets fired. &lt;/p&gt;
&lt;p&gt;An event gets &amp;ldquo;fired&amp;rdquo; when the user does the event you have bound to. In this case, the event that you set up is the button press event, &lt;code&gt;wx.EVT_BUTTON&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;&lt;code&gt;.on_press()&lt;/code&gt; accepts a second argument that you can call &lt;code&gt;event&lt;/code&gt;. This is by convention. You could call it something else if you wanted to. However, the event parameter here refers to the fact that when this method is called, its second argument should be an event object of some sort. &lt;/p&gt;
&lt;p&gt;Within &lt;code&gt;.on_press()&lt;/code&gt;, you can get the text control&amp;rsquo;s contents by calling its &lt;code&gt;GetValue()&lt;/code&gt; method. You then print a string to stdout depending on what the contents of the text control is.&lt;/p&gt;
&lt;p&gt;Now that you have the basics out of the way, let&amp;rsquo;s learn how to create an application that does something useful!&lt;/p&gt;
&lt;h2 id=&quot;creating-a-working-application&quot;&gt;Creating a Working Application&lt;/h2&gt;
&lt;p&gt;The first step when creating something new is to figure out what you want to create. In this case, I have taken the liberty of making that decision for you. You will learn how to create a MP3 tag editor! The next step when creating something new is to find out what packages can help you accomplish your task. &lt;/p&gt;
&lt;p&gt;If you do a Google search for &lt;code&gt;Python mp3 tagging&lt;/code&gt;, you will find you have several options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mp3-tagger&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eyeD3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mutagen&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I tried out a couple of these and decided that &lt;strong&gt;eyeD3&lt;/strong&gt; had a nice API that you could use without getting bogged down with the MP3&amp;rsquo;s ID3 specification. You can install &lt;strong&gt;eyeD3&lt;/strong&gt; using &lt;code&gt;pip&lt;/code&gt;, like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install eyed3
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When installing this package on macOS, you may need to install &lt;code&gt;libmagic&lt;/code&gt; using &lt;code&gt;brew&lt;/code&gt;. Windows and Linux users shouldn&amp;rsquo;t have any issues installing eyeD3.&lt;/p&gt;
&lt;h3 id=&quot;designing-the-user-interface&quot;&gt;Designing the User Interface&lt;/h3&gt;
&lt;p&gt;When it comes to designing an interface, it&amp;rsquo;s always nice to just kind of sketch out how you think the user interface should look. &lt;/p&gt;
&lt;p&gt;You will need to be able to do the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open up one or more MP3 files&lt;/li&gt;
&lt;li&gt;Display the current MP3 tags&lt;/li&gt;
&lt;li&gt;Edit an MP3 tag&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most user interfaces use a menu or a button for opening files or folders. You can go with a &lt;em&gt;File&lt;/em&gt; menu for this. Since you will probably want to see tags for multiple MP3 files, you will need to find a widget that can do this in a nice manner.&lt;/p&gt;
&lt;p&gt;Something that is tabular with columns and rows would be ideal because then you can have labeled columns for the MP3 tags. The wxPython toolkit has a few widgets that would work for this, with the top two being the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;wx.grid.Grid&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wx.ListCtrl&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You should use &lt;code&gt;wx.ListCtrl&lt;/code&gt; in this case as the &lt;code&gt;Grid&lt;/code&gt; widget is overkill, and frankly it is also quite a bit more complex. Finally, you need a button to use to edit a selected MP3&amp;rsquo;s tag. &lt;/p&gt;
&lt;p&gt;Now that you know what you want, you can draw it up:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/mockup.b145e58828b0.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/mockup.b145e58828b0.png&quot; width=&quot;450&quot; height=&quot;400&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/mockup.b145e58828b0.png&amp;amp;w=112&amp;amp;sig=fa9a713da01a3ebc15fc341b764af9c3c6ea353d 112w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/mockup.b145e58828b0.png&amp;amp;w=225&amp;amp;sig=6bfcb6378599846b10ba467612edad6505a41afb 225w, https://files.realpython.com/media/mockup.b145e58828b0.png 450w&quot; sizes=&quot;75vw&quot; alt=&quot;MP3 Editor in wxPython&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The illustration above gives us an idea of how the application should look. Now that you know what you want to do, it&amp;rsquo;s time to code!&lt;/p&gt;
&lt;h3 id=&quot;creating-the-user-interface&quot;&gt;Creating the User Interface&lt;/h3&gt;
&lt;p&gt;There are many different approaches when it comes to writing a new application. For example, do you need to follow the &lt;a href=&quot;https://en.wikipedia.org/wiki/Model-view-controller&quot;&gt;Model-View-Controller&lt;/a&gt; design pattern? How do you split up the classes? One class per file? There are many such questions, and as you get more experienced with GUI design, you&amp;rsquo;ll know how you want to answer them. &lt;/p&gt;
&lt;p&gt;In your case, you really only need two classes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;wx.Panel&lt;/code&gt; class&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;wx.Frame&lt;/code&gt; class&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You could argue for creating a controller type module as well, but for something like this, you really do not need it. A case could also be made for putting each class into its own module, but to keep it compact, you will create a single Python file for all of your code. &lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start with imports and the panel class:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;eyed3&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;glob&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;wx&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Mp3Panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;main_sizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BoxSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VERTICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row_obj_dict&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ListCtrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; 
            &lt;span class=&quot;n&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LC_REPORT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BORDER_SUNKEN&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Artist&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;140&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Album&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;140&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Title&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;main_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EXPAND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;edit_button&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Edit&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;edit_button&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EVT_BUTTON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;on_edit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;main_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edit_button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CENTER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main_sizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_edit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in on_edit&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_mp3_listing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;folder_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, you import the &lt;code&gt;eyed3&lt;/code&gt; package, Python&amp;rsquo;s &lt;code&gt;glob&lt;/code&gt; package, and the &lt;code&gt;wx&lt;/code&gt; package for your user interface. Next, you subclass &lt;code&gt;wx.Panel&lt;/code&gt; and create your user interface. You need a dictionary for storing data about your MP3s, which you can name &lt;code&gt;row_obj_dict&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;Then you create a &lt;code&gt;wx.ListCtrl&lt;/code&gt; and set it to report mode (&lt;code&gt;wx.LC_REPORT&lt;/code&gt;) with a sunken border (&lt;code&gt;wx.BORDER_SUNKEN&lt;/code&gt;). The list control can take on a few other forms depending on the style flag that you pass in, but the report flag is the most popular.&lt;/p&gt;
&lt;p&gt;To make the &lt;code&gt;ListCtrl&lt;/code&gt; have the correct headers, you will need to call &lt;code&gt;.InsertColumn()&lt;/code&gt; for each column header. You then supply the index of the column, its label, and how wide in pixels the column should be.&lt;/p&gt;
&lt;p&gt;The last step is to add your &lt;code&gt;Edit&lt;/code&gt; button, an event handler, and a method. You can create the binding to the event and leave the method that it calls empty for now.&lt;/p&gt;
&lt;p&gt;Now you should write the code for the frame:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Mp3Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                         &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Mp3 Tag Editor&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mp3Panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mp3Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainLoop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This class is much simpler than the first one in that all you need to do is set the title of the frame and instantiate the panel class, &lt;code&gt;Mp3Panel&lt;/code&gt;. When you are all done, your user interface should look like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/tag_editor.01b0ac1ff939.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/tag_editor.01b0ac1ff939.png&quot; width=&quot;400&quot; height=&quot;250&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/tag_editor.01b0ac1ff939.png&amp;amp;w=100&amp;amp;sig=ec89c862dfd2d32db070070feef77de9543b6651 100w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/tag_editor.01b0ac1ff939.png&amp;amp;w=200&amp;amp;sig=b9be5e771a35e8d0b3de01b06b00b7f855b97229 200w, https://files.realpython.com/media/tag_editor.01b0ac1ff939.png 400w&quot; sizes=&quot;75vw&quot; alt=&quot;wxPython MP3 Tag Editor&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The user interface looks almost right, but you don&amp;rsquo;t have a &lt;em&gt;File&lt;/em&gt; menu. This makes it impossible to add MP3s to the application and edit their tags!&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s fix that now.&lt;/p&gt;
&lt;h3 id=&quot;make-a-functioning-application&quot;&gt;Make a Functioning Application&lt;/h3&gt;
&lt;p&gt;The first step in making your application work is to update the application so that it has a &lt;em&gt;File&lt;/em&gt; menu because then you can add MP3 files to your creation. Menus are almost always added to the &lt;code&gt;wx.Frame&lt;/code&gt; class, so that is the class you need to modify.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Some applications have moved away from having menus in their applications. One of the first to do so was Microsoft Office when they added the Ribbon Bar. The wxPython toolkit has a custom widget that you can use to create ribbons in &lt;code&gt;wx.lib.agw.ribbon&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The other type of application that has dropped menus of late are web browsers, such as Google Chrome and Mozilla Firefox. They just use toolbars nowadays.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Let&amp;rsquo;s learn how to add a menu bar to our application:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Mp3Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                          &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Mp3 Tag Editor&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mp3Panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_menu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_menu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;menu_bar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MenuBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;file_menu&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Menu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;open_folder_menu_item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file_menu&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID_ANY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Open Folder&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
            &lt;span class=&quot;s1&quot;&gt;&amp;#39;Open a folder with MP3s&amp;#39;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;menu_bar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_menu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;amp;File&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EVT_MENU&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
            &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;on_open_folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open_folder_menu_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetMenuBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;menu_bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_open_folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Choose a directory:&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirDialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                           &lt;span class=&quot;n&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DD_DEFAULT_STYLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ShowModal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID_OK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_mp3_listing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, you add a call to &lt;code&gt;.create_menu()&lt;/code&gt; within the class&amp;rsquo;s constructor. Then in &lt;code&gt;.create_menu()&lt;/code&gt; itself, you will create a &lt;code&gt;wx.MenuBar&lt;/code&gt; instance and a &lt;code&gt;wx.Menu&lt;/code&gt; instance. &lt;/p&gt;
&lt;p&gt;To add a menu item to a menu, you call the menu instance&amp;rsquo;s &lt;code&gt;.Append()&lt;/code&gt; and pass it the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A unique identifier&lt;/li&gt;
&lt;li&gt;The label for the new menu item&lt;/li&gt;
&lt;li&gt;A help string&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next, you need to add the menu to the menubar, so you will need to call the menubar&amp;rsquo;s &lt;code&gt;.Append()&lt;/code&gt;. It takes the menu instance and the label for menu. This label is a bit odd in that you called it &lt;code&gt;&amp;amp;File&lt;/code&gt; instead of &lt;code&gt;File&lt;/code&gt;. The ampersand tells wxPython to create a keyboard shortcut of &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-alt&quot;&gt;Alt&lt;/kbd&gt;&lt;span&gt;+&lt;/span&gt;&lt;kbd class=&quot;key-f&quot;&gt;F&lt;/kbd&gt;&lt;/span&gt; to open the &lt;code&gt;File&lt;/code&gt; menu using just your keyboard.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you would like to add keyboard shortcuts to your application, then you will want to use an instance of &lt;code&gt;wx.AcceleratorTable&lt;/code&gt; to create them. You can read more about Accerator Tables in the wxPython &lt;a href=&quot;https://wxpython.org/Phoenix/docs/html/wx.AcceleratorTable.html&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;To create an event binding, you will need to call &lt;code&gt;self.Bind()&lt;/code&gt;, which binds the frame to &lt;code&gt;wx.EVT_MENU&lt;/code&gt;. When you use &lt;code&gt;self.Bind()&lt;/code&gt; for a menu event, you need to not only tell wxPython which &lt;code&gt;handler&lt;/code&gt; to use, but also which &lt;code&gt;source&lt;/code&gt; to bind the handler to. &lt;/p&gt;
&lt;p&gt;Finally, you must call the frame&amp;rsquo;s &lt;code&gt;.SetMenuBar()&lt;/code&gt; and pass it the menubar instance for it to be shown to the user.&lt;/p&gt;
&lt;p&gt;Now that you have the menu added to your frame, let&amp;rsquo;s go over the menu item&amp;rsquo;s event handler, which is reproduced again below:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_open_folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Choose a directory:&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirDialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DD_DEFAULT_STYLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ShowModal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID_OK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_mp3_listing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since you want the user to choose a folder that contains MP3s, you will want to use wxPython&amp;rsquo;s &lt;code&gt;wx.DirDialog&lt;/code&gt;. The &lt;code&gt;wx.DirDialog&lt;/code&gt; allows the user to only open directories.&lt;/p&gt;
&lt;p&gt;You can set the dialog&amp;rsquo;s title and various style flags. To show the dialog, you will need to call &lt;code&gt;.ShowModal()&lt;/code&gt;. This will cause the dialog to show modally, which means that the user won&amp;rsquo;t be able to interact with your main application while the dialog is shown.&lt;/p&gt;
&lt;p&gt;If the user presses the dialog&amp;rsquo;s &lt;em&gt;OK&lt;/em&gt; button, you can get the user&amp;rsquo;s path choice via the dialog&amp;rsquo;s &lt;code&gt;.GetPath()&lt;/code&gt;. You will want to pass that path to your panel class, which you can do here by calling the panel&amp;rsquo;s &lt;code&gt;.update_mp3_listing()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally you need to close the dialog. To close a dialog, the recommended method is to call its &lt;code&gt;.Destroy()&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;Dialogs do have a &lt;code&gt;.Close()&lt;/code&gt; method, but that basically just hides the dialog, and it will not destroy itself when you close your application, which can lead to weird issues such as your application now shutting down properly. It&amp;rsquo;s simpler to call &lt;code&gt;.Destroy()&lt;/code&gt; on the dialog to prevent this issue.&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s update your &lt;code&gt;Mp3Panel&lt;/code&gt; class. You can start by updating &lt;code&gt;.update_mp3_listing()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_mp3_listing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_folder_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder_path&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClearAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Artist&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;140&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Album&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;140&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Title&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Year&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;mp3s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;folder_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;/*.mp3&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mp3_objects&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mp3s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mp3_object&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eyed3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
            &lt;span class=&quot;n&quot;&gt;mp3_object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
            &lt;span class=&quot;n&quot;&gt;mp3_object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
            &lt;span class=&quot;n&quot;&gt;mp3_object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mp3_objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3_object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row_obj_dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mp3_object&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here you set the current directory to the specified folder and then you clear the list control. This keeps the list control fresh and only showing the MP3s that you are currently working on. That also means that you need to re-insert all the columns again.&lt;/p&gt;
&lt;p&gt;Next, you&amp;rsquo;ll want to take the folder that was passed in and use Python&amp;rsquo;s &lt;code&gt;glob&lt;/code&gt; &lt;a href=&quot;https://docs.python.org/3/library/glob.html&quot;&gt;module&lt;/a&gt; to search for MP3 files.&lt;/p&gt;
&lt;p&gt;Then you can loop over the MP3s and turn them into &lt;code&gt;eyed3&lt;/code&gt; objects. You can do this by calling the &lt;code&gt;.load()&lt;/code&gt; of &lt;code&gt;eyed3&lt;/code&gt;. Assuming that the MP3s have the appropriate tags already, you can then add the artist, album, and title of the MP3 to the list control. &lt;/p&gt;
&lt;p&gt;Interestingly, the method of adding a new row to a list control object is by calling &lt;code&gt;.InsertItem()&lt;/code&gt; for the first column and &lt;code&gt;SetItem()&lt;/code&gt; for all the subsequent columns. &lt;/p&gt;
&lt;p&gt;The last step is to save off your MP3 object to your Python dictionary, &lt;code&gt;row_obj_dict&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now you need to update the &lt;code&gt;.on_edit()&lt;/code&gt; event handler so that you can edit an MP3&amp;rsquo;s tags:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_edit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;selection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetFocusedItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row_obj_dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EditDialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ShowModal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_mp3_listing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_folder_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first thing you need to do is get the user&amp;rsquo;s selection by calling the list control&amp;rsquo;s &lt;code&gt;.GetFocusedItem()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the user has not selected anything in the list control, it will return &lt;code&gt;-1&lt;/code&gt;. Assuming that the user did select something, you will want to extract the MP3 object from your dictionary and open a MP3 tag editor dialog. This will be a custom dialog that you will use to edit the artist, album, and title tags of the MP3 file. &lt;/p&gt;
&lt;p&gt;As usual, show the dialog modally. When the dialog closes, the last two lines in &lt;code&gt;.on_edit()&lt;/code&gt; will execute. These two lines will update the list control so it displays the current MP3 tag information that the user just edited and destroy the dialog.&lt;/p&gt;
&lt;h4 id=&quot;creating-an-editing-dialog&quot;&gt;Creating an Editing Dialog&lt;/h4&gt;
&lt;p&gt;The final piece of the puzzle is creating an MP3 tag editing dialog. For brevity, we will skip sketching out this interface as it is a series of rows that contains labels and text controls. The text controls should have the existing tag information pre-populated within them. You can create a label for the text controls by creating instances of &lt;code&gt;wx.StaticText&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When you need to create a custom dialog, the &lt;code&gt;wx.Dialog&lt;/code&gt; class is your friend. You can use that to design the editor:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EditDialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Editing &amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{mp3.tag.title}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;quot;&amp;#39;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main_sizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BoxSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VERTICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;artist&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextCtrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_widgets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Artist&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextCtrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_widgets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Album&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextCtrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_widgets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Title&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;btn_sizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BoxSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;save_btn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Save&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;save_btn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EVT_BUTTON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;on_save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;btn_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save_btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;btn_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID_CANCEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;btn_sizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CENTER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main_sizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here you want to start off by sub-classing &lt;code&gt;wx.Dialog&lt;/code&gt; and giving it a custom title based on the title of the MP3 that you are editing.&lt;/p&gt;
&lt;p&gt;Next you can create the sizer you want to use and the widgets. To make things easier, you can create a helper method called &lt;code&gt;.add_widgets()&lt;/code&gt; for adding the &lt;code&gt;wx.StaticText&lt;/code&gt; widgets as rows with the text control instances. The only other widget here is the &lt;em&gt;Save&lt;/em&gt; button. &lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s write the &lt;code&gt;add_widgets&lt;/code&gt; method next:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_widgets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_ctrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;row_sizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BoxSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HORIZONTAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StaticText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;label_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                              &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;row_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;row_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_ctrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EXPAND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row_sizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EXPAND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;add_widgets()&lt;/code&gt; takes the label&amp;rsquo;s text and the text control instance. It then creates a horizontally oriented &lt;code&gt;BoxSizer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next you will create an instance of &lt;code&gt;wx.StaticText&lt;/code&gt; using the passed-in text for its label parameter. You will also set its size to be &lt;code&gt;50&lt;/code&gt; pixels wide and the default height is set with a &lt;code&gt;-1&lt;/code&gt;. Since you want the label before the text control, you will add the StaticText widget to your BoxSizer first and then add the text control .&lt;/p&gt;
&lt;p&gt;Finally, you want to add the horizontal sizer to the top level vertical sizer. By nesting the sizers in each other, you can design complex applications.&lt;/p&gt;
&lt;p&gt;Now you will need to create the &lt;code&gt;on_save()&lt;/code&gt; event handler so that you can save your changes:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;artist&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here you set the tags to the contents of the text controls and then call the &lt;code&gt;eyed3&lt;/code&gt; object&amp;rsquo;s &lt;code&gt;.save()&lt;/code&gt;. Finally, you call the &lt;code&gt;.Close()&lt;/code&gt; of the dialog. The reason you call .&lt;code&gt;Close()&lt;/code&gt; here instead of &lt;code&gt;.Destroy()&lt;/code&gt; is that you already call &lt;code&gt;.Destroy()&lt;/code&gt; in the &lt;code&gt;.on_edit()&lt;/code&gt; of your panel subclass. &lt;/p&gt;
&lt;p&gt;Now your application is complete!&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You learned a lot about wxPython in this article. You became familiar with the basics of creating GUI applications using wxPython. &lt;/p&gt;
&lt;p&gt;You now know more about the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to work with some of wxPython&amp;rsquo;s widgets&lt;/li&gt;
&lt;li&gt;How events work in wxPython&lt;/li&gt;
&lt;li&gt;How absolute positioning compares with sizers&lt;/li&gt;
&lt;li&gt;How to create a skeleton application&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally you learned how to create a working application, an &lt;a href=&quot;https://github.com/realpython/materials/tree/master/build-a-gui-with-wxpython&quot;&gt;MP3 tag editor&lt;/a&gt;. You can use what you learned in this article to continue to enhance this application or perhaps create an amazing application on your own.&lt;/p&gt;
&lt;p&gt;The wxPython GUI toolkit is robust and full of interesting widgets that you can use to build cross-platform applications. You are limited by only your imagination.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading&lt;/h2&gt;
&lt;p&gt;If you would like to learn more about wxPython, you can check out some of the following links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https://wxpython.org/&quot;&gt;Official wxPython website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Zetcode&amp;rsquo;s &lt;a href=&quot;http://zetcode.com/wxpython/&quot;&gt;wxPython tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.blog.pythonlibrary.org/category/wxpython/&quot;&gt;Mouse Vs Python Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more information on what else you can do with Python, you might want to check out &lt;a href=&quot;https://realpython.com/what-can-i-do-with-python/&quot;&gt;What Can I Do with Python?&lt;/a&gt; If you&amp;rsquo;d like to learn more about Python&amp;rsquo;s &lt;code&gt;super()&lt;/code&gt;, then &lt;a href=&quot;https://realpython.com/python-super/&quot;&gt;Supercharge Your Classes With Python super()&lt;/a&gt; may be just right for you.&lt;/p&gt;
&lt;p&gt;You can also &lt;a href=&quot;https://github.com/realpython/materials/tree/master/build-a-gui-with-wxpython&quot;&gt;download the code&lt;/a&gt; for the MP3 tag editor application that you created in this article if you want to study it more in depth.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Understanding the Python Mock Object Library</title>
      <id>https://realpython.com/python-mock-library/</id>
      <link href="https://realpython.com/python-mock-library/"/>
      <updated>2019-03-13T14:00:00+00:00</updated>
      <summary>In this tutorial, you&#39;ll learn how to use the Python mock object library, unittest.mock, to create and use mock objects to improve your tests. Obstacles like complex logic and unpredictable dependencies make writing valuable tests difficult, but unittest.mock can help you overcome these obstacles.</summary>
      <content type="html">
        &lt;p&gt;When you&amp;rsquo;re writing robust code, tests are essential for verifying that your application logic is correct, reliable, and efficient. However, the value of your tests depends on how well they demonstrate these criteria. Obstacles such as complex logic and unpredictable dependencies make writing valuable tests difficult. The Python mock object library, &lt;code&gt;unittest.mock&lt;/code&gt;, can help you overcome these obstacles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;By the end of this article, you&amp;rsquo;ll be able to:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create Python mock objects using &lt;code&gt;Mock&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Assert you&amp;rsquo;re using objects as you intended&lt;/li&gt;
&lt;li&gt;Inspect usage data stored on your Python mocks&lt;/li&gt;
&lt;li&gt;Configure certain aspects of your Python mock objects&lt;/li&gt;
&lt;li&gt;Substitute your mocks for real objects using &lt;code&gt;patch()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Avoid common problems inherent in Python mocking&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You&amp;rsquo;ll begin by seeing what mocking is and how it will improve your tests.&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-mastery-course&quot; data-focus=&quot;false&quot;&gt;5 Thoughts On Python Mastery&lt;/a&gt;, a free course for Python developers that shows you the roadmap and the mindset you&#39;ll need to take your Python skills to the next level.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;what-is-mocking&quot;&gt;What Is Mocking?&lt;/h2&gt;
&lt;p&gt;A &lt;a href=&quot;https://en.wikipedia.org/wiki/Mock_object&quot;&gt;mock object&lt;/a&gt; substitutes and imitates a real object within a &lt;a href=&quot;https://realpython.com/python-testing/&quot;&gt;testing environment&lt;/a&gt;. It is a versatile and powerful tool for &lt;a href=&quot;https://realpython.com/python-cli-testing/#mocks&quot;&gt;improving the quality of your tests&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One reason to use Python mock objects is to control your code&amp;rsquo;s behavior during testing.&lt;/p&gt;
&lt;p&gt;For example, if your code makes &lt;a href=&quot;https://realpython.com/python-requests/&quot;&gt;HTTP requests&lt;/a&gt; to external services, then your tests execute predictably only so far as the services are behaving as you expected. Sometimes, a temporary change in the behavior of these external services can cause intermittent failures within your test suite.&lt;/p&gt;
&lt;p&gt;Because of this, it would be better for you to test your code in a controlled environment. &lt;a href=&quot;https://realpython.com/testing-third-party-apis-with-mocks/&quot;&gt;Replacing the actual request with a mock object&lt;/a&gt; would allow you to simulate external service outages and successful responses in a predictable way.&lt;/p&gt;
&lt;p&gt;Sometimes, it is difficult to test certain areas of your codebase. Such areas include &lt;code&gt;except&lt;/code&gt; blocks and &lt;code&gt;if&lt;/code&gt; statements that are hard to satisfy. Using Python mock objects can help you control the execution path of your code to reach these areas and improve your &lt;a href=&quot;https://en.wikipedia.org/wiki/Code_coverage&quot;&gt;code coverage&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another reason to use mock objects is to better understand how you&amp;rsquo;re using their real counterparts in your code. A Python mock object contains data about its usage that you can inspect such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you called a method&lt;/li&gt;
&lt;li&gt;How you called the method&lt;/li&gt;
&lt;li&gt;How often you called the method&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Understanding what a mock object does is the first step to learning how to use one. &lt;/p&gt;
&lt;p&gt;Now, you&amp;rsquo;ll see how to use Python mock objects.&lt;/p&gt;
&lt;h2 id=&quot;the-python-mock-library&quot;&gt;The Python Mock Library&lt;/h2&gt;
&lt;p&gt;The Python &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html&quot;&gt;mock object library&lt;/a&gt; is &lt;code&gt;unittest.mock&lt;/code&gt;. It provides an easy way to introduce mocks into your tests.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The standard library includes &lt;code&gt;unittest.mock&lt;/code&gt; in Python 3.3 and later. If you&amp;rsquo;re using an older version of Python, you&amp;rsquo;ll need to install the official backport of the library. To do so, install &lt;code&gt;mock&lt;/code&gt; from &lt;a href=&quot;https://pypi.org/project/mock/&quot;&gt;PyPI&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install mock
&lt;/pre&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;p&gt;&lt;code&gt;unittest.mock&lt;/code&gt; provides a class called &lt;code&gt;Mock&lt;/code&gt; which you will use to imitate real objects in your codebase. &lt;code&gt;Mock&lt;/code&gt; offers incredible flexibility and insightful data. This, along with its subclasses, will meet most Python mocking needs that you will face in your tests.&lt;/p&gt;
&lt;p&gt;The library also provides a function, called &lt;code&gt;patch()&lt;/code&gt;, which replaces the real objects in your code with &lt;code&gt;Mock&lt;/code&gt; instances. You can use &lt;code&gt;patch()&lt;/code&gt; as either a decorator or a context manager, giving you control over the scope in which the object will be mocked. Once the designated scope exits, &lt;code&gt;patch()&lt;/code&gt; will clean up your code by replacing the mocked objects with their original counterparts.&lt;/p&gt;
&lt;p&gt;Finally, &lt;code&gt;unittest.mock&lt;/code&gt; provides solutions for some of the issues inherent in mocking objects.&lt;/p&gt;
&lt;p&gt;Now, you have a better understanding of what mocking is and the library you&amp;rsquo;ll be using to do it. Let&amp;rsquo;s dive in and explore what features and functionalities &lt;code&gt;unittest.mock&lt;/code&gt; offers.&lt;/p&gt;
&lt;h2 id=&quot;the-mock-object&quot;&gt;The &lt;code&gt;Mock&lt;/code&gt; Object&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;unittest.mock&lt;/code&gt; offers a base class for mocking objects called &lt;code&gt;Mock&lt;/code&gt;. The use cases for &lt;code&gt;Mock&lt;/code&gt; are practically limitless because &lt;code&gt;Mock&lt;/code&gt; is so flexible.&lt;/p&gt;
&lt;p&gt;Begin by instantiating a new &lt;code&gt;Mock&lt;/code&gt; instance:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock id=&amp;#39;4561344720&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, you are able to substitute an object in your code with your new &lt;code&gt;Mock&lt;/code&gt;. You can do this by passing it as an argument to a function or by redefining another object:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Pass mock as an argument to do_something()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;do_something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Patch the json library&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you substitute an object in your code, the &lt;code&gt;Mock&lt;/code&gt; must look like the real object it is replacing. Otherwise, your code will not be able to use the &lt;code&gt;Mock&lt;/code&gt; in place of the original object.&lt;/p&gt;
&lt;p&gt;For example, if you are mocking the &lt;code&gt;json&lt;/code&gt; library and your program calls &lt;code&gt;dumps()&lt;/code&gt;, then your Python mock object must also contain &lt;code&gt;dumps()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, you&amp;rsquo;ll see how &lt;code&gt;Mock&lt;/code&gt; deals with this challenge.&lt;/p&gt;
&lt;h3 id=&quot;lazy-attributes-and-methods&quot;&gt;Lazy Attributes and Methods&lt;/h3&gt;
&lt;p&gt;A &lt;code&gt;Mock&lt;/code&gt; must simulate any object that it replaces. To achieve such flexibility, it &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#quick-guide&quot;&gt;creates its attributes when you access them&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;some_attribute&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.some_attribute&amp;#39; id=&amp;#39;4394778696&amp;#39;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;do_something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.do_something()&amp;#39; id=&amp;#39;4394778920&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since &lt;code&gt;Mock&lt;/code&gt; can create arbitrary attributes on the fly, it is suitable to replace any object.&lt;/p&gt;
&lt;p&gt;Using an example from earlier, if you&amp;rsquo;re mocking the &lt;code&gt;json&lt;/code&gt; library and you call &lt;code&gt;dumps()&lt;/code&gt;, the Python mock object will create the method so that its interface can match the library&amp;rsquo;s interface:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dumps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.dumps()&amp;#39; id=&amp;#39;4392249776&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice two key characteristics of this mocked version of &lt;code&gt;dumps()&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Unlike the &lt;a href=&quot;https://realpython.com/python-json/#serializing-json&quot;&gt;real &lt;code&gt;dumps()&lt;/code&gt;&lt;/a&gt;, this mocked method requires no arguments. In fact, it will accept any arguments that you pass to it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The return value of &lt;code&gt;dumps()&lt;/code&gt; is also a &lt;code&gt;Mock&lt;/code&gt;. The capability of &lt;code&gt;Mock&lt;/code&gt; to recursively define other mocks allows for you to use mocks in complex situations:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;k&amp;quot;: &amp;quot;v&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;k&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.loads().get()&amp;#39; id=&amp;#39;4379599424&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because the return value of each mocked method is also a &lt;code&gt;Mock&lt;/code&gt;, you can use your mocks in a multitude of ways.&lt;/p&gt;
&lt;p&gt;Mocks are flexible, but they&amp;rsquo;re also informative. Next, you&amp;rsquo;ll learn how you can use mocks to understand your code better.&lt;/p&gt;
&lt;h3 id=&quot;assertions-and-inspection&quot;&gt;Assertions and Inspection&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Mock&lt;/code&gt; instances store data on how you used them. For instance, you can see if you called a method, how you called the method, and so on. There are two main ways to use this information.&lt;/p&gt;
&lt;p&gt;First, you can assert that your program used an object as you expected:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Create a mock object&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.loads()&amp;#39; id=&amp;#39;4550144184&amp;#39;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# You know that you called loads() so you can&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# make assertions to test that expectation&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_once_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.loads()&amp;#39; id=&amp;#39;4550144184&amp;#39;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# If an assertion fails, the mock will raise an AssertionError&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;795&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;assert_called_once&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;AssertionError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;AssertionError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;Expected &amp;#39;loads&amp;#39; to have been called once. Called 2 times.&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_once_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;824&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;assert_called_once_with&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;AssertionError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;AssertionError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;Expected &amp;#39;loads&amp;#39; to be called once. Called 2 times.&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_not_called&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;777&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;assert_not_called&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;AssertionError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;AssertionError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;Expected &amp;#39;loads&amp;#39; to not have been called. Called 2 times.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.assert_called()&lt;/code&gt; ensures you called the mocked method while &lt;code&gt;.assert_called_once()&lt;/code&gt; checks that you called the method exactly one time.&lt;/p&gt;
&lt;p&gt;Both assertion functions have variants that let you inspect the arguments passed to the mocked method:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.assert_called_with(*args, **kwargs)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.assert_called_once_with(*args, **kwargs)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To pass these assertions, you must call the mocked method with the same arguments that you pass to the actual method:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;814&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;assert_called_with&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;AssertionError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_error_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;cause&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;AssertionError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;Expected call: loads(&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Actual call: loads(s=&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;json.loads.assert_called_with(&#39;{&quot;key&quot;: &quot;value&quot;}&#39;)&lt;/code&gt; raised an &lt;code&gt;AssertionError&lt;/code&gt; because it expected you to call &lt;a href=&quot;https://realpython.com/python-json/#deserializing-json&quot;&gt;&lt;code&gt;loads()&lt;/code&gt;&lt;/a&gt; with a positional argument, but you actually called it with a keyword argument. &lt;code&gt;json.loads.assert_called_with(s=&#39;{&quot;key&quot;: &quot;value&quot;}&#39;)&lt;/code&gt; gets this assertion correct.&lt;/p&gt;
&lt;p&gt;Second, you can view special attributes to understand how your application used an object:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Create a mock object&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.loads()&amp;#39; id=&amp;#39;4391026640&amp;#39;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Number of times you called loads():&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call_count&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# The last loads() call:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call_args&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;call(&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# List of loads() calls:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call_args_list&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[call(&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;)]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# List of calls to json&amp;#39;s methods (recursively):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method_calls&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[call.loads(&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;)]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can write tests using these attributes to make sure that your objects behave as you intended.&lt;/p&gt;
&lt;p&gt;Now, you can create mocks and inspect their usage data. Next, you&amp;rsquo;ll see how to customize mocked methods so that they become more useful in your testing environment.&lt;/p&gt;
&lt;h3 id=&quot;managing-a-mocks-return-value&quot;&gt;Managing a Mock&amp;rsquo;s Return Value&lt;/h3&gt;
&lt;p&gt;One reason to use mocks is to control your code&amp;rsquo;s behavior during tests. One way to do this is to specify a function&amp;rsquo;s &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.return_value&quot;&gt;return value&lt;/a&gt;. Let&amp;rsquo;s use an example to see how this works.&lt;/p&gt;
&lt;p&gt;First, create a file called &lt;code&gt;my_calendar.py&lt;/code&gt;. Add &lt;code&gt;is_weekday()&lt;/code&gt;, a function that uses Python&amp;rsquo;s &lt;a href=&quot;https://docs.python.org/2/library/datetime.html&quot;&gt;&lt;code&gt;datetime&lt;/code&gt;&lt;/a&gt; library to determine whether or not today is a week day. Finally, write a test that asserts that the function works as expected:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Python&amp;#39;s datetime library treats Monday as 0 and Sunday as 6&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Test if today is a weekday&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since you&amp;rsquo;re testing if today is a weekday, the result depends on the day you run your test:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python my_calendar.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If this command produces no output, the assertion was successful. Unfortunately, if you run the command on a weekend, you&amp;rsquo;ll get an &lt;code&gt;AssertionError&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python my_calendar.py
&lt;span class=&quot;go&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  File &amp;quot;test.py&amp;quot;, line 9, in &amp;lt;module&amp;gt;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    assert is_weekday()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;AssertionError&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When writing tests, it is important to ensure that the results are predictable. You can use &lt;code&gt;Mock&lt;/code&gt; to eliminate uncertainty from your code during testing. In this case, you can mock &lt;code&gt;datetime&lt;/code&gt; and set the &lt;code&gt;.return_value&lt;/code&gt; for &lt;code&gt;.today()&lt;/code&gt; to a day that you choose:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Save a couple of test days&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;tuesday&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2019&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;month&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;saturday&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2019&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;month&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Mock datetime to control today&amp;#39;s date&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Python&amp;#39;s datetime library treats Monday as 0 and Sunday as 6&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Mock .today() to return Tuesday&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tuesday&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Test Tuesday is a weekday&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Mock .today() to return Saturday&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;saturday&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Test Saturday is not a weekday&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the example, &lt;code&gt;.today()&lt;/code&gt; is a mocked method. You&amp;rsquo;ve removed the inconsistency by assigning a specific day to the mock&amp;rsquo;s &lt;code&gt;.return_value&lt;/code&gt;. That way, when you call &lt;code&gt;.today()&lt;/code&gt;, it returns the &lt;code&gt;datetime&lt;/code&gt; that you specified.&lt;/p&gt;
&lt;p&gt;In the first test, you ensure &lt;code&gt;tuesday&lt;/code&gt; is a weekday. In the second test, you verify that &lt;code&gt;saturday&lt;/code&gt; is not a weekday. Now, it doesn&amp;rsquo;t matter what day you run your tests on because you&amp;rsquo;ve mocked &lt;code&gt;datetime&lt;/code&gt; and have control over the object&amp;rsquo;s behavior.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Further Reading:&lt;/strong&gt; Though mocking &lt;code&gt;datetime&lt;/code&gt; like this is a good practice example for using &lt;code&gt;Mock&lt;/code&gt;, a fantastic library already exists for mocking &lt;code&gt;datetime&lt;/code&gt; called &lt;a href=&quot;https://github.com/spulec/freezegun&quot;&gt;&lt;code&gt;freezegun&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;When building your tests, you will likely come across cases where mocking a function&amp;rsquo;s return value will not be enough. This is because functions are often more complicated than a simple one-way flow of logic.&lt;/p&gt;
&lt;p&gt;Sometimes, you&amp;rsquo;ll want to make functions return different values when you call them more than once or even raise exceptions. You can do this using &lt;code&gt;.side_effect&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;managing-a-mocks-side-effects&quot;&gt;Managing a Mock&amp;rsquo;s Side Effects&lt;/h3&gt;
&lt;p&gt;You can control your code&amp;rsquo;s behavior by specifying a mocked function&amp;rsquo;s &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.side_effect&quot;&gt;side effects&lt;/a&gt;. A &lt;code&gt;.side_effect&lt;/code&gt; defines what happens when you call the mocked function.&lt;/p&gt;
&lt;p&gt;To test how this works, add a new function to &lt;code&gt;my_calendar.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://localhost/api/holidays&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;get_holidays()&lt;/code&gt; makes a request to the &lt;code&gt;localhost&lt;/code&gt; server for a set of holidays. If the server responds successfully, &lt;code&gt;get_holidays()&lt;/code&gt; will return a dictionary. Otherwise, the method will return &lt;code&gt;None&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can test how &lt;code&gt;get_holidays()&lt;/code&gt; will respond to a connection timeout by setting &lt;code&gt;requests.get.side_effect&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For this example, you&amp;rsquo;ll only see the relevant code from &lt;code&gt;my_calendar.py&lt;/code&gt;. You&amp;rsquo;ll build a test case using Python&amp;rsquo;s &lt;a href=&quot;https://docs.python.org/3/library/unittest.html&quot;&gt;&lt;code&gt;unittest&lt;/code&gt;&lt;/a&gt; library:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Mock requests to control its behavior&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://localhost/api/holidays&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestCalendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_get_holidays_timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Test a connection timeout&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;side_effect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertRaises&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You use &lt;code&gt;.assertRaises()&lt;/code&gt; to verify that &lt;code&gt;get_holidays()&lt;/code&gt; raises an exception given the new side effect of &lt;code&gt;get()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Run this test to see the result of your test:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python my_calendar.py
&lt;span class=&quot;go&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;-------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Ran 1 test in 0.000s&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;OK&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you want to be a little more dynamic, you can set &lt;code&gt;.side_effect&lt;/code&gt; to a function that &lt;code&gt;Mock&lt;/code&gt; will invoke when you call your mocked method. The mock shares the arguments and return value of the &lt;code&gt;.side_effect&lt;/code&gt; function:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Mock requests to control its behavior&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://localhost/api/holidays&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestCalendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;log_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Log a fake request for test output purposes&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Making a request to &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{url}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Request received!&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# Create a new Mock to imitate a Response&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s1&quot;&gt;&amp;#39;12/25&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Christmas&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s1&quot;&gt;&amp;#39;7/4&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Independence Day&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_get_holidays_logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Test a successful, logged request&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;side_effect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log_request&lt;/span&gt;
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;12/25&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Christmas&amp;#39;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;First, you created &lt;code&gt;.log_request()&lt;/code&gt;, which takes a URL, logs some output using &lt;code&gt;print()&lt;/code&gt;, then returns a &lt;code&gt;Mock&lt;/code&gt; response. Next, you set the &lt;code&gt;.side_effect&lt;/code&gt; of &lt;code&gt;get()&lt;/code&gt; to &lt;code&gt;.log_request()&lt;/code&gt;, which you&amp;rsquo;ll use when you call &lt;code&gt;get_holidays()&lt;/code&gt;. When you run your test, you&amp;rsquo;ll see that &lt;code&gt;get()&lt;/code&gt; forwards its arguments to &lt;code&gt;.log_request()&lt;/code&gt; then accepts the return value and returns it as well:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python my_calendar.py
&lt;span class=&quot;go&quot;&gt;Making a request to http://localhost/api/holidays.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Request received!&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;-------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Ran 1 test in 0.000s&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;OK&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Great! The &lt;code&gt;print()&lt;/code&gt; statements logged the correct values. Also, &lt;code&gt;get_holidays()&lt;/code&gt; returned the holidays dictionary.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.side_effect&lt;/code&gt; can also be an iterable. The iterable must consist of return values, exceptions, or a mixture of both. The iterable will produce its next value every time you call your mocked method. For example, you can test that a retry after a &lt;code&gt;Timeout&lt;/code&gt; returns a successful response:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Mock requests to control its behavior&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://localhost/api/holidays&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestCalendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_get_holidays_retry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Create a new Mock to imitate a Response&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s1&quot;&gt;&amp;#39;12/25&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Christmas&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s1&quot;&gt;&amp;#39;7/4&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Independence Day&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Set the side effect of .get()&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;side_effect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# Test that the first request raises a Timeout&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertRaises&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Now retry, expecting a successful response&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;12/25&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Christmas&amp;#39;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Finally, assert .get() was called twice&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first time you call &lt;code&gt;get_holidays()&lt;/code&gt;, &lt;code&gt;get()&lt;/code&gt; raises a &lt;code&gt;Timeout&lt;/code&gt;. The second time, the method returns a valid holidays dictionary. These side effects match the order they appear in the list passed to &lt;code&gt;.side_effect&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can set &lt;code&gt;.return_value&lt;/code&gt; and &lt;code&gt;.side_effect&lt;/code&gt; on a &lt;code&gt;Mock&lt;/code&gt; directly. However, because a Python mock object needs to be flexible in creating its attributes, there is a better way to configure these and other settings.&lt;/p&gt;
&lt;h3 id=&quot;configuring-your-mock&quot;&gt;Configuring Your Mock&lt;/h3&gt;
&lt;p&gt;You can configure a &lt;code&gt;Mock&lt;/code&gt; to set up some of the object&amp;rsquo;s behaviors. Some configurable members include &lt;code&gt;.side_effect&lt;/code&gt;, &lt;code&gt;.return_value&lt;/code&gt;, and &lt;code&gt;.name&lt;/code&gt;. You configure a &lt;code&gt;Mock&lt;/code&gt; when you &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock&quot;&gt;create&lt;/a&gt; one or when you use &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.configure_mock&quot;&gt;&lt;code&gt;.configure_mock()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can configure a &lt;code&gt;Mock&lt;/code&gt; by specifying certain attributes when you initialize an object:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;side_effect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;ne&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;939&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;__call__&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_mock_self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_mock_call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;995&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;_mock_call&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effect&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;Exception&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Real Python Mock&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;Real Python Mock&amp;#39; id=&amp;#39;4434041432&amp;#39;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return_value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While &lt;code&gt;.side_effect&lt;/code&gt; and &lt;code&gt;.return_value&lt;/code&gt; can be set on the &lt;code&gt;Mock&lt;/code&gt; instance, itself, other attributes like &lt;code&gt;.name&lt;/code&gt; can only be set through &lt;code&gt;.__init__()&lt;/code&gt; or &lt;code&gt;.configure_mock()&lt;/code&gt;. If you try to set the &lt;code&gt;.name&lt;/code&gt; of the &lt;code&gt;Mock&lt;/code&gt; on the instance, you will get a different result:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Real Python Mock&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;Real Python Mock.name&amp;#39; id=&amp;#39;4434041544&amp;#39;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Real Python Mock&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;Real Python Mock&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.name&lt;/code&gt; is a common attribute for objects to use. So, &lt;code&gt;Mock&lt;/code&gt; doesn&amp;rsquo;t let you set that value on the instance in the same way you can with &lt;code&gt;.return_value&lt;/code&gt; or &lt;code&gt;.side_effect&lt;/code&gt;. If you access &lt;code&gt;mock.name&lt;/code&gt; you will create a &lt;code&gt;.name&lt;/code&gt; attribute instead of configuring your mock.&lt;/p&gt;
&lt;p&gt;You can configure an existing &lt;code&gt;Mock&lt;/code&gt; using &lt;code&gt;.configure_mock()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configure_mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return_value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By unpacking a dictionary into either &lt;code&gt;.configure_mock()&lt;/code&gt; or &lt;code&gt;Mock.__init__()&lt;/code&gt;, you can even configure your Python mock object&amp;rsquo;s attributes. Using &lt;code&gt;Mock&lt;/code&gt; configurations, you could simplify a previous example:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Verbose, old Mock&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;12/25&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Christmas&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;7/4&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Independence Day&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Shiny, new .configure_mock()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;holidays&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;12/25&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Christmas&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;7/4&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Independence Day&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;json.return_value&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, you can create and configure Python mock objects. You can also use mocks to control the behavior of your application. So far, you&amp;rsquo;ve used mocks as arguments to functions or patching objects in the same module as your tests.&lt;/p&gt;
&lt;p&gt;Next, you&amp;rsquo;ll learn how to substitute your mocks for real objects in other modules.&lt;/p&gt;
&lt;h2 id=&quot;patch&quot;&gt;&lt;code&gt;patch()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;unittest.mock&lt;/code&gt; provides a powerful mechanism for mocking objects, called &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#patch&quot;&gt;&lt;code&gt;patch()&lt;/code&gt;&lt;/a&gt;, which looks up an object in a given module and replaces that object with a &lt;code&gt;Mock&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Usually, you use &lt;code&gt;patch()&lt;/code&gt; as a decorator or a context manager to provide a scope in which you will mock the target object.&lt;/p&gt;
&lt;h3 id=&quot;patch-as-a-decorator&quot;&gt;&lt;code&gt;patch()&lt;/code&gt; as a Decorator&lt;/h3&gt;
&lt;p&gt;If you want to mock an object for the duration of your entire test function, you can use &lt;code&gt;patch()&lt;/code&gt; as a function &lt;a href=&quot;https://realpython.com/primer-on-python-decorators/&quot;&gt;decorator&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To see how this works, reorganize your &lt;code&gt;my_calendar.py&lt;/code&gt; file by putting the logic and tests into separate files:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Python&amp;#39;s datetime library treats Monday as 0 and Sunday as 6&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://localhost/api/holidays&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These functions are now in their own file, separate from their tests. Next, you&amp;rsquo;ll re-create your tests in a file called &lt;code&gt;tests.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Up to this point, you&amp;rsquo;ve monkey patched objects in the file in which they exist. &lt;a href=&quot;https://en.wikipedia.org/wiki/Monkey_patch&quot;&gt;Monkey patching&lt;/a&gt; is the replacement of one object with another at runtime. Now, you&amp;rsquo;ll use &lt;code&gt;patch()&lt;/code&gt; to replace your objects in &lt;code&gt;my_calendar.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestCalendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;nd&quot;&gt;@patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;my_calendar.requests&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_get_holidays_timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock_requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;mock_requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;side_effect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertRaises&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;mock_requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Originally, you created a &lt;code&gt;Mock&lt;/code&gt; and patched &lt;code&gt;requests&lt;/code&gt; in the local scope. Now, you need to access the &lt;code&gt;requests&lt;/code&gt; library in &lt;code&gt;my_calendar.py&lt;/code&gt; from &lt;code&gt;tests.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For this case, you used &lt;code&gt;patch()&lt;/code&gt; as a decorator and passed the target object&amp;rsquo;s path. The target path was &lt;code&gt;&#39;my_calendar.requests&#39;&lt;/code&gt; which consists of the module name and the object.&lt;/p&gt;
&lt;p&gt;You also defined a new parameter for the test function. &lt;code&gt;patch()&lt;/code&gt; uses this parameter to pass the mocked object into your test. From there, you can modify the mock or make assertions as necessary.&lt;/p&gt;
&lt;p&gt;You can execute this test module to ensure it&amp;rsquo;s working as expected:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python tests.py
&lt;span class=&quot;go&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;-------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Ran 1 test in 0.001s&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;OK&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Technical Detail:&lt;/strong&gt; &lt;code&gt;patch()&lt;/code&gt; returns an instance of &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#unittest.mock.MagicMock&quot;&gt;&lt;code&gt;MagicMock&lt;/code&gt;&lt;/a&gt;, which is a &lt;code&gt;Mock&lt;/code&gt; subclass. &lt;code&gt;MagicMock&lt;/code&gt; is useful because it implements most &lt;a href=&quot;https://dbader.org/blog/python-dunder-methods&quot;&gt;magic methods&lt;/a&gt; for you, such as &lt;code&gt;.__len__()&lt;/code&gt;, &lt;code&gt;.__str__()&lt;/code&gt;, and &lt;code&gt;.__iter__()&lt;/code&gt;, with reasonable defaults.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Using &lt;code&gt;patch()&lt;/code&gt; as a decorator worked well in this example. In some cases, it is more readable, more effective, or easier to use &lt;code&gt;patch()&lt;/code&gt; as a context manager.&lt;/p&gt;
&lt;h3 id=&quot;patch-as-a-context-manager&quot;&gt;&lt;code&gt;patch()&lt;/code&gt; as a Context Manager&lt;/h3&gt;
&lt;p&gt;Sometimes, you&amp;rsquo;ll want to use &lt;code&gt;patch()&lt;/code&gt; as a &lt;a href=&quot;https://dbader.org/blog/python-context-managers-and-with-statement&quot;&gt;context manager&lt;/a&gt; rather than a decorator. Some reasons why you might prefer a context manager include the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You only want to mock an object for a part of the test scope.&lt;/li&gt;
&lt;li&gt;You are already using too many decorators or parameters, which hurts your test&amp;rsquo;s readability.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To use &lt;code&gt;patch()&lt;/code&gt; as a context manager, you use Python&amp;rsquo;s &lt;code&gt;with&lt;/code&gt; statement:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestCalendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_get_holidays_timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;my_calendar.requests&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock_requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;mock_requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;side_effect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertRaises&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;mock_requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When the test exits the &lt;code&gt;with&lt;/code&gt; statement, &lt;code&gt;patch()&lt;/code&gt; replaces the mocked object with the original.&lt;/p&gt;
&lt;p&gt;Until now, you&amp;rsquo;ve mocked complete objects, but sometimes you&amp;rsquo;ll only want to mock a part of an object.&lt;/p&gt;
&lt;h3 id=&quot;patching-an-objects-attributes&quot;&gt;Patching an Object&amp;rsquo;s Attributes&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s say you only want to mock one method of an object instead of the entire object. You can do so by using &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#patch-object&quot;&gt;&lt;code&gt;patch.object()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, &lt;code&gt;.test_get_holidays_timeout()&lt;/code&gt; really only needs to mock &lt;code&gt;requests.get()&lt;/code&gt; and set its &lt;code&gt;.side_effect&lt;/code&gt; to &lt;code&gt;Timeout&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestCalendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;nd&quot;&gt;@patch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;get&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;side_effect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_get_holidays_timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock_requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertRaises&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example, you&amp;rsquo;ve mocked only &lt;code&gt;get()&lt;/code&gt; rather than all of &lt;code&gt;requests&lt;/code&gt;. Every other attribute remains the same.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;object()&lt;/code&gt; takes the same configuration parameters that &lt;code&gt;patch()&lt;/code&gt; does. But instead of passing the target&amp;rsquo;s path, you provide the target object, itself, as the first parameter. The second parameter is the attribute of the target object that you are trying to mock. You can also use &lt;code&gt;object()&lt;/code&gt; as a context manager like &lt;code&gt;patch()&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Further Reading:&lt;/strong&gt; Besides objects and attributes, you can also &lt;code&gt;patch()&lt;/code&gt; dictionaries with &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch.dict&quot;&gt;&lt;code&gt;patch.dict()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Learning how to use &lt;code&gt;patch()&lt;/code&gt; is critical to mocking objects in other modules. However, sometimes it&amp;rsquo;s not obvious what the target object&amp;rsquo;s path is.&lt;/p&gt;
&lt;h3 id=&quot;where-to-patch&quot;&gt;Where to Patch&lt;/h3&gt;
&lt;p&gt;Knowing where to tell &lt;code&gt;patch()&lt;/code&gt; to look for the object you want mocked is important because if you choose the wrong target location, the result of &lt;code&gt;patch()&lt;/code&gt; could be something you didn&amp;rsquo;t expect.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s say you are mocking &lt;code&gt;is_weekday()&lt;/code&gt; in &lt;code&gt;my_calendar.py&lt;/code&gt; using &lt;code&gt;patch()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;my_calendar.is_weekday&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;my_calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;MagicMock name=&amp;#39;is_weekday()&amp;#39; id=&amp;#39;4336501256&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;First, you import &lt;code&gt;my_calendar.py&lt;/code&gt;. Then you patch &lt;code&gt;is_weekday()&lt;/code&gt;, replacing it with a &lt;code&gt;Mock&lt;/code&gt;. Great! This is working as expected.&lt;/p&gt;
&lt;p&gt;Now, let&amp;rsquo;s change this example slightly and import the function directly:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;my_calendar.is_weekday&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Depending on what day you are reading this tutorial, your console output may read &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt;. The important thing is that the output is not a &lt;code&gt;Mock&lt;/code&gt; like before.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Notice that even though the target location you passed to &lt;code&gt;patch()&lt;/code&gt; did not change, the result of calling &lt;code&gt;is_weekday()&lt;/code&gt; is different. The difference is due to the change in how you imported the function.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;from my_calendar import is_weekday&lt;/code&gt; binds the real function to the local scope. So, even though you &lt;code&gt;patch()&lt;/code&gt; the function later, you ignore the mock because you already have a local reference to the un-mocked function.&lt;/p&gt;
&lt;p&gt;A &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#where-to-patch&quot;&gt;good rule of thumb&lt;/a&gt; is to &lt;code&gt;patch()&lt;/code&gt; the object where it is looked up.&lt;/p&gt;
&lt;p&gt;In the first example, mocking &lt;code&gt;&#39;my_calendar.is_weekday()&#39;&lt;/code&gt; works because you look up the function in the &lt;code&gt;my_calendar&lt;/code&gt; module. In the second example, you have a local reference to &lt;code&gt;is_weekday()&lt;/code&gt;. Since you use the function found in the local scope, you should mock the local function:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__.is_weekday&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;MagicMock name=&amp;#39;is_weekday()&amp;#39; id=&amp;#39;4502362992&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, you have a firm grasp on the power of &lt;code&gt;patch()&lt;/code&gt;. You&amp;rsquo;ve seen how to &lt;code&gt;patch()&lt;/code&gt; objects and attributes as well as where to patch them.&lt;/p&gt;
&lt;p&gt;Next, you&amp;rsquo;ll see some common problems inherent in object mocking and the solutions that &lt;code&gt;unittest.mock&lt;/code&gt; provides.&lt;/p&gt;
&lt;h2 id=&quot;common-mocking-problems&quot;&gt;Common Mocking Problems&lt;/h2&gt;
&lt;p&gt;Mocking objects can introduce several problems into your tests. Some problems are inherent in mocking while others are specific to &lt;code&gt;unittest.mock&lt;/code&gt;. Keep in mind that there are other issues with mocking that are not mentioned in this tutorial.&lt;/p&gt;
&lt;p&gt;The ones covered here are similar to each other in that the problem they cause is fundamentally the same. In each case, the test assertions are irrelevant. Though the intention of each mock is valid, the mocks themselves are not.&lt;/p&gt;
&lt;h3 id=&quot;changes-to-object-interfaces-and-misspellings&quot;&gt;Changes to Object Interfaces and Misspellings&lt;/h3&gt;
&lt;p&gt;Classes and function definitions change all the time. When the interface of an object changes, any tests relying on a &lt;code&gt;Mock&lt;/code&gt; of that object may become irrelevant.&lt;/p&gt;
&lt;p&gt;For example, you rename a method but forget that a test mocks that method and invokes &lt;code&gt;.assert_not_called()&lt;/code&gt;. After the change, &lt;code&gt;.assert_not_called()&lt;/code&gt; is still &lt;code&gt;True&lt;/code&gt;. The assertion is not useful, though, because the method no longer exists.&lt;/p&gt;
&lt;p&gt;Irrelevant tests may not sound critical, but if they are your only tests and you assume that they work properly, the situation could be disastrous for your application.&lt;/p&gt;
&lt;p&gt;A problem specific to &lt;code&gt;Mock&lt;/code&gt; is that a misspelling can break a test. Recall that a &lt;code&gt;Mock&lt;/code&gt; creates its interface when you access its members. So, you will inadvertently create a new attribute if you misspell its name.&lt;/p&gt;
&lt;p&gt;If you call &lt;code&gt;.asert_called()&lt;/code&gt; instead of &lt;code&gt;.assert_called()&lt;/code&gt;, your test will not raise an &lt;code&gt;AssertionError&lt;/code&gt;. This is because you&amp;rsquo;ve created a new method on the Python mock object named &lt;code&gt;.asert_called()&lt;/code&gt; instead of evaluating an actual assertion.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Technical Detail:&lt;/strong&gt; Interestingly, &lt;code&gt;assret&lt;/code&gt; is a special misspelling of &lt;code&gt;assert&lt;/code&gt;. If you try to access an attribute that starts with &lt;code&gt;assret&lt;/code&gt; (or &lt;code&gt;assert&lt;/code&gt;), &lt;code&gt;Mock&lt;/code&gt; will automatically raise an &lt;code&gt;AttributeError&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;These problems occur when you mock objects within your own codebase. A different problem arises when you mock objects interacting with external codebases.&lt;/p&gt;
&lt;h3 id=&quot;changes-to-external-dependencies&quot;&gt;Changes to External Dependencies&lt;/h3&gt;
&lt;p&gt;Imagine again that your code makes a request to an external API. In this case, the external dependency is the API which is susceptible to change without your consent.&lt;/p&gt;
&lt;p&gt;On one hand, unit tests test isolated components of code. So, mocking the code that makes the request helps you to test your isolated components under controlled conditions. However, it also presents a potential problem.&lt;/p&gt;
&lt;p&gt;If an external dependency changes its interface, your Python mock objects will become invalid. If this happens (and the interface change is a breaking one), your tests will pass because your mock objects have masked the change, but your production code will fail.&lt;/p&gt;
&lt;p&gt;Unfortunately, this is not a problem that &lt;code&gt;unittest.mock&lt;/code&gt; provides a solution for. You must exercise judgment when mocking external dependencies.&lt;/p&gt;
&lt;p&gt;All three of these issues can cause test irrelevancy and potentially costly issues because they threaten the integrity of your mocks. &lt;code&gt;unittest.mock&lt;/code&gt; gives you some tools for dealing with these problems.&lt;/p&gt;
&lt;h2 id=&quot;avoiding-common-problems-using-specifications&quot;&gt;Avoiding Common Problems Using Specifications&lt;/h2&gt;
&lt;p&gt;As mentioned before, if you change a class or function definition or you misspell a Python mock object&amp;rsquo;s attribute, you can cause problems with your tests.&lt;/p&gt;
&lt;p&gt;These problems occur because &lt;code&gt;Mock&lt;/code&gt; creates attributes and methods when you access them. The answer to these issues is to prevent &lt;code&gt;Mock&lt;/code&gt; from creating attributes that don&amp;rsquo;t conform to the object you&amp;rsquo;re trying to mock.&lt;/p&gt;
&lt;p&gt;When configuring a &lt;code&gt;Mock&lt;/code&gt;, you can pass an object specification to the &lt;code&gt;spec&lt;/code&gt; parameter. The &lt;code&gt;spec&lt;/code&gt; parameter accepts a list of names or another object and defines the mock&amp;rsquo;s interface. If you attempt to access an attribute that does not belong to the specification, &lt;code&gt;Mock&lt;/code&gt; will raise an &lt;code&gt;AttributeError&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;is_weekday&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;get_holidays&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.is_weekday()&amp;#39; id=&amp;#39;4569015856&amp;#39;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;582&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;__getattr__&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;AttributeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Mock object has no attribute &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%r&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;AttributeError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;Mock object has no attribute &amp;#39;create_event&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, you&amp;rsquo;ve specified that &lt;code&gt;calendar&lt;/code&gt; has methods called &lt;code&gt;.is_weekday()&lt;/code&gt; and &lt;code&gt;.get_holidays()&lt;/code&gt;. When you access &lt;code&gt;.is_weekday()&lt;/code&gt;, it returns a &lt;code&gt;Mock&lt;/code&gt;. When you access &lt;code&gt;.create_event()&lt;/code&gt;, a method that does not match the specification, &lt;code&gt;Mock&lt;/code&gt; raises an &lt;code&gt;AttributeError&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Specifications work the same way if you configure the &lt;code&gt;Mock&lt;/code&gt; with an object:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.is_weekday()&amp;#39; id=&amp;#39;4569435216&amp;#39;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;582&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;__getattr__&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;AttributeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Mock object has no attribute &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%r&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;AttributeError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;Mock object has no attribute &amp;#39;create_event&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.is_weekday()&lt;/code&gt; is available to &lt;code&gt;calendar&lt;/code&gt; because you configured &lt;code&gt;calendar&lt;/code&gt; to match the &lt;code&gt;my_calendar&lt;/code&gt; module&amp;rsquo;s interface.&lt;/p&gt;
&lt;p&gt;Furthermore, &lt;code&gt;unittest.mock&lt;/code&gt; provides convenient methods of automatically specifying a &lt;code&gt;Mock&lt;/code&gt; instance&amp;rsquo;s interface.&lt;/p&gt;
&lt;p&gt;One way to implement automatic specifications is &lt;code&gt;create_autospec&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_autospec&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_autospec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;MagicMock name=&amp;#39;mock.is_weekday()&amp;#39; id=&amp;#39;4579049424&amp;#39;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;582&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;__getattr__&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;AttributeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Mock object has no attribute &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%r&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;AttributeError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;Mock object has no attribute &amp;#39;create_event&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Like before, &lt;code&gt;calendar&lt;/code&gt; is a &lt;code&gt;Mock&lt;/code&gt; instance whose interface matches &lt;code&gt;my_calendar&lt;/code&gt;. If you&amp;rsquo;re using &lt;code&gt;patch()&lt;/code&gt;, you can send an argument to the &lt;code&gt;autospec&lt;/code&gt; parameter to achieve the same result:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__.my_calendar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;autospec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;MagicMock name=&amp;#39;my_calendar.is_weekday()&amp;#39; id=&amp;#39;4579094312&amp;#39;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;582&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;__getattr__&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;AttributeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Mock object has no attribute &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%r&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;AttributeError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;Mock object has no attribute &amp;#39;create_event&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ve learned so much about mocking objects using &lt;code&gt;unittest.mock&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;Now, you&amp;rsquo;re able to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;Mock&lt;/code&gt; to imitate objects in your tests&lt;/li&gt;
&lt;li&gt;Check usage data to understand how you use your objects&lt;/li&gt;
&lt;li&gt;Customize your mock objects&amp;rsquo; return values and side effects&lt;/li&gt;
&lt;li&gt;&lt;code&gt;patch()&lt;/code&gt; objects throughout your codebase&lt;/li&gt;
&lt;li&gt;See and avoid problems with using Python mock objects&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You have built a foundation of understanding that will help you build better tests. You can use mocks to gain insights into your code that you would not have been able to get otherwise.&lt;/p&gt;
&lt;p&gt;I leave you with one final disclaimer. &lt;em&gt;Beware of overusing mock objects!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s easy to take advantage of the power of Python mock objects and mock so much that you actually decrease the value of your tests.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested in learning more about &lt;code&gt;unittest.mock&lt;/code&gt;, I encourage you to read its excellent &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Managing Multiple Python Versions With pyenv</title>
      <id>https://realpython.com/intro-to-pyenv/</id>
      <link href="https://realpython.com/intro-to-pyenv/"/>
      <updated>2019-03-11T14:00:00+00:00</updated>
      <summary>In this step-by-step tutorial, you&#39;ll learn how to install multiple Python versions and switch between them with ease, including project-specific virtual environments, even if you don&#39;t have sudo access with pyenv.</summary>
      <content type="html">
        &lt;p&gt;Have you ever wanted to contribute to a project that supports multiple versions of Python but aren&amp;rsquo;t sure how you would easily &lt;a href=&quot;https://realpython.com/python-testing/&quot;&gt;test&lt;/a&gt; all the versions? Are you ever curious about the latest and greatest versions of Python? Maybe you&amp;rsquo;d like to try out these new features, but you don&amp;rsquo;t want to worry about messing up your development environment. Luckily, managing multiple version of Python doesn&amp;rsquo;t have to be confusing if you use &lt;code&gt;pyenv&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;This article will provide you with a great overview of how to maximize your time spent working on projects and minimize the time spent in headaches trying to use the right version of Python.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this article, you&amp;rsquo;ll learn how to:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install multiple versions of Python&lt;/li&gt;
&lt;li&gt;Install the latest development version of Python&lt;/li&gt;
&lt;li&gt;Switch between the installed versions&lt;/li&gt;
&lt;li&gt;Use virtual environments with &lt;code&gt;pyenv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Activate different Python versions and virtual environments automatically&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-tricks-sample&quot; data-focus=&quot;false&quot;&gt;Click here to get access to a chapter from Python Tricks: The Book&lt;/a&gt; that shows you Python&#39;s best practices with simple examples you can apply instantly to write more beautiful + Pythonic code.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;why-use-pyenv&quot;&gt;Why Use &lt;code&gt;pyenv&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;pyenv&lt;/code&gt; is a wonderful tool for managing multiple Python versions. Even if you already have Python installed on your system, it is worth having &lt;code&gt;pyenv&lt;/code&gt; installed so that you can easily try out new language features or help contribute to a project that is on a different version of Python.&lt;/p&gt;
&lt;h3 id=&quot;why-not-use-system-python&quot;&gt;Why Not Use System Python?&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;System Python&amp;rdquo; is the Python that comes installed on your operating system. If you&amp;rsquo;re on Mac or Linux, then by default, when you type &lt;code&gt;python&lt;/code&gt; in your terminal, you get a nice Python REPL. &lt;/p&gt;
&lt;p&gt;So, why not use it? One way to look at it is that this Python really &lt;em&gt;belongs&lt;/em&gt; to the operating system. After all, it came installed with the operating system. That&amp;rsquo;s even reflected when you run &lt;code&gt;which&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; which python
&lt;span class=&quot;go&quot;&gt;/usr/bin/python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;code&gt;python&lt;/code&gt; is available to all users as evidenced by its location &lt;code&gt;/usr/bin/python&lt;/code&gt;. Chances are, this isn&amp;rsquo;t the version of Python you want either:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -V
&lt;span class=&quot;go&quot;&gt;Pyhton 2.7.12&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To install a package into your system Python, you have to run &lt;code&gt;sudo pip install&lt;/code&gt;. That&amp;rsquo;s because you&amp;rsquo;re installing the Python package globally, which is a real problem if another user comes along and wants to install a slightly older version of the package.&lt;/p&gt;
&lt;p&gt;Problems with multiple versions of the same package tend to creep up on you and bite you when you least expect it. One common way this problem presents itself is a popular and stable package suddenly misbehaving on your system. After hours of troubleshooting and Googling, you may find that you&amp;rsquo;ve installed the wrong version of a dependency, and it&amp;rsquo;s ruining your day.&lt;/p&gt;
&lt;p&gt;Even if your Python version is installed in &lt;code&gt;/usr/local/bin/python3&lt;/code&gt;, you&amp;rsquo;re still not safe. You will run into the same permissions and flexibility problems described above.&lt;/p&gt;
&lt;p&gt;In addition, you don&amp;rsquo;t really have much control over what version of Python comes installed on your OS. If you want to use the latest features in Python, and you&amp;rsquo;re on Ubuntu for example, you might just be out of luck. The default versions might be too old, which means you&amp;rsquo;ll just have to wait for a new OS to come out.&lt;/p&gt;
&lt;p&gt;Finally, some operating systems actually use the packaged Python for operation. Take &lt;code&gt;yum&lt;/code&gt; for example, which makes heavy use of Python to do its job. If you install a new version of Python and aren&amp;rsquo;t careful to install it into your user space, you could seriously damage your ability to use your OS.&lt;/p&gt;
&lt;h3 id=&quot;what-about-a-package-manager&quot;&gt;What About a Package Manager?&lt;/h3&gt;
&lt;p&gt;The next logical place to look is package managers. Programs such as &lt;code&gt;apt&lt;/code&gt;, &lt;code&gt;yum&lt;/code&gt;, &lt;code&gt;brew&lt;/code&gt;, or &lt;code&gt;port&lt;/code&gt; are typical next options. After all, this is how you install most packages to your system. Unfortunately, you&amp;rsquo;ll find some of the same problems using a package manager.&lt;/p&gt;
&lt;p&gt;By default, package managers tend to install their packages into the global system space instead of the user space. Again, these system level packages pollute your development environment and make it hard to share a workspace with others.&lt;/p&gt;
&lt;p&gt;Once again, you still don&amp;rsquo;t have control over what version of Python you can install. It&amp;rsquo;s true some repositories give you a greater selection, but by default, you&amp;rsquo;re looking at whatever version of Python your particular vendor is up to on any given day.&lt;/p&gt;
&lt;p&gt;Even if you do install Python from a package manager, consider what would happen if you&amp;rsquo;re writing a package and want to support and test on Python 3.4 - 3.7.&lt;/p&gt;
&lt;p&gt;What would happen on your system when you type &lt;code&gt;python3&lt;/code&gt;? How would you switch quickly between the different versions? You can certainly do it, but it is tedious and prone to error. Nevermind the fact that if you want PyPy, Jython, or Miniconda, then you&amp;rsquo;re probably just out of luck with your package manager.&lt;/p&gt;
&lt;p&gt;With these constraints in mind, let&amp;rsquo;s recap the criteria that would let you install and manage Python versions easily and flexibly:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install Python in your user space&lt;/li&gt;
&lt;li&gt;Install multiple versions of Python&lt;/li&gt;
&lt;li&gt;Specify the exact Python version you want&lt;/li&gt;
&lt;li&gt;Switch between the installed versions&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;pyenv&lt;/code&gt; lets you do all of these things and more.&lt;/p&gt;
&lt;h2 id=&quot;installing-pyenv&quot;&gt;Installing &lt;code&gt;pyenv&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Before you install &lt;code&gt;pyenv&lt;/code&gt; itself, you&amp;rsquo;re going to need some OS-specific dependencies. These dependencies are mostly development utilities written in C and are required because &lt;code&gt;pyenv&lt;/code&gt; installs Python by building from source. For a more detailed breakdown and explanation of the build dependencies, you can check out the &lt;a href=&quot;https://devguide.python.org/setup/#build-dependencies&quot;&gt;official docs.&lt;/a&gt; In this tutorial, you&amp;rsquo;ll see the most common ways to install these dependencies.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;pyenv&lt;/code&gt; did not originally support Windows. However, there appears to be some basic support with the &lt;a href=&quot;https://github.com/pyenv-win/pyenv-win&quot;&gt;pyenv-win&lt;/a&gt; project that recently became active. If you use Windows, feel free to check it out.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;build-dependencies&quot;&gt;Build Dependencies&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;pyenv&lt;/code&gt; builds Python from source, which means you&amp;rsquo;ll need build dependencies to actually use &lt;code&gt;pyenv&lt;/code&gt;. The build dependencies vary by platform. If you are on &lt;strong&gt;Ubuntu/Debian&lt;/strong&gt; and want to install the build dependencies, you could use the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; sudo apt-get install -y make build-essential libssl-dev zlib1g-dev &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev python-openssl
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This uses &lt;a href=&quot;https://wiki.debian.org/Apt&quot;&gt;Apt&lt;/a&gt; to install all the build dependencies. Let this run, and you&amp;rsquo;ll be ready to go for Debian systems.&lt;/p&gt;
&lt;p&gt;If you use &lt;strong&gt;Fedora/CentOS/RHEL&lt;/strong&gt;, you could use &lt;code&gt;yum&lt;/code&gt; to install your build dependencies:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; sudo yum install zlib-devel bzip2 bzip2-devel readline-devel sqlite &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
sqlite-devel openssl-devel xz xz-devel libffi-devel
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command will install all the build dependencies for Python using &lt;code&gt;yum&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;macOS&lt;/strong&gt; users can use the following command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; brew install readline xz
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command relies on &lt;a href=&quot;https://brew.sh/&quot;&gt;Homebrew&lt;/a&gt; and installs the few dependencies for macOS users. &lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re instead using &lt;strong&gt;openSUSE&lt;/strong&gt; then you would run the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; zypper in zlib-devel bzip2 libbz2-devel libffi-devel &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
libopenssl-devel readline-devel sqlite3 sqlite3-devel xz xz-devel
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once again, this command installs all the Python build dependencies for your system. &lt;/p&gt;
&lt;p&gt;Finally, for &lt;strong&gt;Alpine&lt;/strong&gt; users, you can use this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; apk add libffi-dev ncurses-dev openssl-dev readline-dev &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
tk-dev xz-dev zlib-dev
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command uses &lt;a href=&quot;https://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management&quot;&gt;&lt;code&gt;apk&lt;/code&gt;&lt;/a&gt; as the package manager and will install all build dependencies for Python on Alpine.&lt;/p&gt;
&lt;h3 id=&quot;using-the-pyenv-installer&quot;&gt;Using the pyenv-installer&lt;/h3&gt;
&lt;p&gt;After you&amp;rsquo;ve installed the build dependencies, you&amp;rsquo;re ready to install &lt;code&gt;pyenv&lt;/code&gt; itself. I recommend using the &lt;a href=&quot;https://github.com/pyenv/pyenv-installer&quot;&gt;pyenv-installer project&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; curl https://pyenv.run &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; bash
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will install &lt;code&gt;pyenv&lt;/code&gt; along with a few plugins that are useful:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;pyenv&lt;/code&gt;&lt;/strong&gt;: The actual &lt;code&gt;pyenv&lt;/code&gt; application&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;pyenv-virtualenv&lt;/code&gt;&lt;/strong&gt;: Plugin for &lt;code&gt;pyenv&lt;/code&gt; and virtual environments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;pyenv-update&lt;/code&gt;&lt;/strong&gt;: Plugin for updating &lt;code&gt;pyenv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;pyenv-doctor&lt;/code&gt;&lt;/strong&gt;: Plugin to verify that &lt;code&gt;pyenv&lt;/code&gt; and build dependencies are installed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;pyenv-which-ext&lt;/code&gt;&lt;/strong&gt;: Plugin to automatically lookup system commands&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The above command is the same as downloading the &lt;a href=&quot;https://github.com/pyenv/pyenv-installer/blob/master/bin/pyenv-installer&quot;&gt;pyenv-installer script&lt;/a&gt; and running it locally. So if you&amp;rsquo;d like to see exactly what you&amp;rsquo;re running, you can view the file yourself. Alternatively, if you really don&amp;rsquo;t want to run a script, you can checkout the &lt;a href=&quot;https://github.com/pyenv/pyenv#basic-github-checkout&quot;&gt;manual installation instructions.&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;At the end of the run, you should see something like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;WARNING: seems you still have not added &amp;#39;pyenv&amp;#39; to the load path.&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt; Load pyenv automatically by adding
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt; the following to ~/.bashrc:

&lt;span class=&quot;go&quot;&gt;export PATH=&amp;quot;$HOME/.pyenv/bin:$PATH&amp;quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;eval &amp;quot;$(pyenv init -)&amp;quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;eval &amp;quot;$(pyenv virtualenv-init -)&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The output will be based on your shell. But you should follow the instructions to add &lt;code&gt;pyenv&lt;/code&gt; to your path and to initialize &lt;code&gt;pyenv&lt;/code&gt;/&lt;code&gt;pyenv-virtualenv&lt;/code&gt; auto completion. Once you&amp;rsquo;ve done this, you need to reload your shell:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SHELL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Or just restart your terminal&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That&amp;rsquo;s it. You now have &lt;code&gt;pyenv&lt;/code&gt; and four useful plugins installed.&lt;/p&gt;
&lt;h2 id=&quot;using-pyenv-to-install-python&quot;&gt;Using &lt;code&gt;pyenv&lt;/code&gt; to Install Python&lt;/h2&gt;
&lt;p&gt;Now that you have &lt;code&gt;pyenv&lt;/code&gt; installed, installing Python is the next step. You have many versions of Python to choose from. If you wanted to see all the available CPython 3.6 through 3.8, you can do this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv install --list &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; grep &lt;span class=&quot;s2&quot;&gt;&amp;quot; 3\.[678]&amp;quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.4&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.5&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.6&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.7&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.7.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.7-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.7.1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.7.2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above shows all the Python versions that &lt;code&gt;pyenv&lt;/code&gt; knows about that match the regular expression. In this case, that is all available CPython versions 3.6 through 3.8. Likewise, if you wanted to see all the Jython versions, you could do this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv install --list &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; grep &lt;span class=&quot;s2&quot;&gt;&amp;quot;jython&amp;quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-2.5.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-2.5-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-2.5.1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-2.5.2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-2.5.3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-2.5.4-rc1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-2.7.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-2.7.1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Again, you can see all the Jython versions that &lt;code&gt;pyenv&lt;/code&gt; has to offer. If you want to see all the versions, you can do the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv install --list
&lt;span class=&quot;go&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt; There are a lot
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once you find the version you want, you can install it with a single command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv install -v &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.7.2
&lt;span class=&quot;go&quot;&gt;/tmp/python-build.20190208022403.30568 ~&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Downloading Python-3.7.2.tar.xz...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;-&amp;gt; https://www.python.org/ftp/python/3.7.2/Python-3.7.2.tar.xz&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Installing Python-3.7.2...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;/tmp/python-build.20190208022403.30568/Python-3.7.2 /tmp/python-build.20190208022403.30568 ~&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[...]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Installing collected packages: setuptools, pip&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Successfully installed pip-18.1 setuptools-40.6.2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Installed Python-3.7.2 to /home/realpython/.pyenv/versions/3.7.2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Having Problems?&lt;/strong&gt; The &lt;a href=&quot;https://github.com/pyenv/pyenv&quot;&gt;&lt;code&gt;pyenv&lt;/code&gt; documentation&lt;/a&gt; has great installation notes as well as a useful &lt;a href=&quot;https://github.com/pyenv/pyenv/wiki&quot;&gt;FAQ&lt;/a&gt; along with &lt;a href=&quot;https://github.com/pyenv/pyenv/wiki/Common-build-problems&quot;&gt;common build problems&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This will take a while because &lt;code&gt;pyenv&lt;/code&gt; is building Python from source, but once it&amp;rsquo;s done, you&amp;rsquo;ll have Python 3.7.2 available on your local machine. If you don&amp;rsquo;t want to see all the output, just remove the &lt;code&gt;-v&lt;/code&gt; flag. Even development versions of CPython can be installed:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv install &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.8-dev
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; If you&amp;rsquo;ve been using &lt;code&gt;pyenv&lt;/code&gt; for a while and don&amp;rsquo;t see the version you&amp;rsquo;re looking for, you may need to run &lt;code&gt;pyenv update&lt;/code&gt; to update the tool and make sure you have access to the latest versions.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;For the rest of the tutorial, the examples assume you&amp;rsquo;ve installed &lt;code&gt;3.6.8&lt;/code&gt; and &lt;code&gt;2.7.15&lt;/code&gt;, but you&amp;rsquo;re free to substitute these values for the Python versions you actually installed. Also note that the system Python version in the examples is &lt;code&gt;2.7.12&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;installation-location&quot;&gt;Installation Location&lt;/h3&gt;
&lt;p&gt;As mentioned before, &lt;code&gt;pyenv&lt;/code&gt; works by building Python from source. Each version that you have installed is located nicely in your &lt;code&gt;pyenv&lt;/code&gt; root directory:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ls ~/.pyenv/versions/
&lt;span class=&quot;go&quot;&gt;2.7.15  3.6.8  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;All of your versions will be located here. This is handy because removing these versions is trivial:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; rm -rf ~/.pyenv/versions/2.7.15
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Of course &lt;code&gt;pyenv&lt;/code&gt; also provides a command to uninstall a particular Python version:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv uninstall &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;.7.15
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&quot;using-your-new-python&quot;&gt;Using Your New Python&lt;/h3&gt;
&lt;p&gt;Now that you&amp;rsquo;ve installed a couple of different Python versions, let&amp;rsquo;s see some basics on how to use them. First, check what versions of Python you have available:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;* system (set by /home/realpython/.pyenv/version)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  2.7.15&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;*&lt;/code&gt; indicates that the &lt;code&gt;system&lt;/code&gt; Python version is active currently. You&amp;rsquo;ll also notice that this is set by a file in your root &lt;code&gt;pyenv&lt;/code&gt; directory. This means that, by default, you are still using your system Python:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -V
&lt;span class=&quot;go&quot;&gt;Python 2.7.12&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you try to confirm this using &lt;code&gt;which&lt;/code&gt;, you&amp;rsquo;ll see this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; which python
&lt;span class=&quot;go&quot;&gt;/home/realpython/.pyenv/shims/python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This might be surprising, but this is how &lt;code&gt;pyenv&lt;/code&gt; works. &lt;code&gt;pyenv&lt;/code&gt; inserts itself into your &lt;code&gt;PATH&lt;/code&gt; and from your OS&amp;rsquo;s perspective &lt;em&gt;is&lt;/em&gt; the executable that is getting called. If you want to see the actual path, you can run the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv which python
&lt;span class=&quot;go&quot;&gt;/usr/bin/python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If, for example, you wanted to use version 2.7.15, then you can use the &lt;code&gt;global&lt;/code&gt; command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv global &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;.7.15
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -V
&lt;span class=&quot;go&quot;&gt;Python 2.7.15&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;  system&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;* 2.7.15 (set by /home/realpython/.pyenv/version)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; A great way to get peace of mind that the version of Python you just installed is working properly is to run the built-in test suite:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv global &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.8-dev
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -m &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will kick off lots of internal Python tests that will verify your installation. You can just kick back and watch the tests pass.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;If you ever want to go back to the system version of Python as the default, you can run this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv global system
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -V
&lt;span class=&quot;go&quot;&gt;Python 2.7.12&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can now switch between different versions of Python with ease. This is just the beginning. If you have many versions that you want to switch between, typing these commands consistently is tedious. This section goes over the basics, but a better workflow is described in &lt;a href=&quot;#working-with-multiple-environments&quot;&gt;working with multiple environments.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;exploring-pyenv-commands&quot;&gt;Exploring &lt;code&gt;pyenv&lt;/code&gt; Commands&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;pyenv&lt;/code&gt; offers many commands. You can see a complete list of all available commands with this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv commands
&lt;span class=&quot;go&quot;&gt;activate&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;commands&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;completions&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;deactivate&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;virtualenvs&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;whence&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;which&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This outputs all command names. Each command has a &lt;code&gt;--help&lt;/code&gt; flag that will give you more detailed information. For example, if you wanted to see more information on the &lt;code&gt;shims&lt;/code&gt; command you could run the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv shims --help
&lt;span class=&quot;go&quot;&gt;Usage: pyenv shims [--short]&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;List existing pyenv shims&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The help message describes what the command is used for and any options you can use in conjunction with the command. In the following sections, you&amp;rsquo;ll find a quick, high-level overview of the most used commands.&lt;/p&gt;
&lt;h3 id=&quot;install&quot;&gt;&lt;code&gt;install&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;You&amp;rsquo;ve already seen the &lt;code&gt;install&lt;/code&gt; command above. This command can be used to install a specific version of Python. For example, if you wanted to install &lt;code&gt;3.6.8&lt;/code&gt; you would use this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv install &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.6.8
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The output shows us &lt;code&gt;pyenv&lt;/code&gt; downloading and installing Python. Some of the common flags you may want to use are the following:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;
&lt;table class=&quot;table table-hover&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-l/--list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lists all available Python versions for installation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-g/--debug&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Builds a debug version of Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-v/--verbose&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verbose mode: print compilation status to stdout&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h3 id=&quot;versions&quot;&gt;&lt;code&gt;versions&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;versions&lt;/code&gt; command displays all currently installed Python versions:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;* system (set by /home/realpython/.pyenv/version)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  2.7.15&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This output shows not only that &lt;code&gt;2.7.15&lt;/code&gt;, &lt;code&gt;3.6.8&lt;/code&gt;, &lt;code&gt;3.8-dev&lt;/code&gt;, and your &lt;code&gt;system&lt;/code&gt; Python are installed, but also shows you that the &lt;code&gt;system&lt;/code&gt; Python is active. If you only care about the current active version, you can use the following command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv version
&lt;span class=&quot;go&quot;&gt;system (set by /home/realpython/.pyenv/version)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command is similar to &lt;code&gt;versions&lt;/code&gt; but only shows you the current active Python version.&lt;/p&gt;
&lt;h3 id=&quot;which&quot;&gt;&lt;code&gt;which&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;which&lt;/code&gt; command is helpful for determining the full path to a system executable. Because &lt;code&gt;pyenv&lt;/code&gt; works by using shims, this command allows you to see the full path to the executable &lt;code&gt;pyenv&lt;/code&gt; is running. For example, if you wanted to see where &lt;code&gt;pip&lt;/code&gt; is installed, you could run this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv which pip
&lt;span class=&quot;go&quot;&gt;/home/realpython/.pyenv/versions/3.6.8/bin/pip&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The output displays the full system path for &lt;code&gt;pip&lt;/code&gt;. This can be helpful when you&amp;rsquo;ve installed command-line applications.&lt;/p&gt;
&lt;h3 id=&quot;global&quot;&gt;&lt;code&gt;global&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;global&lt;/code&gt; command sets the global Python version. This can be overridden with other commands, but is useful for ensuring you use a particular Python version by default. If you wanted to use &lt;code&gt;3.6.8&lt;/code&gt; by default, then you could run this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv global &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.6.8
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command sets the &lt;code&gt;~/.pyenv/version&lt;/code&gt; to &lt;code&gt;3.6.8&lt;/code&gt;. For more information, see the section on &lt;a href=&quot;#specifying-your-python-version&quot;&gt;specifying your Python version&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;local&quot;&gt;&lt;code&gt;local&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;local&lt;/code&gt; command is often used to set an application-specific Python version. You could use it to set the version to &lt;code&gt;2.7.15&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;.7.15
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command creates a &lt;code&gt;.python-version&lt;/code&gt; file in your current directory. If you have &lt;code&gt;pyenv&lt;/code&gt; active in your environment, this file will automatically activate this version for you.&lt;/p&gt;
&lt;h3 id=&quot;shell&quot;&gt;&lt;code&gt;shell&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;shell&lt;/code&gt; command is used to set a shell-specific Python version. For example, if you wanted to test out the &lt;code&gt;3.8-dev&lt;/code&gt; version of Python, you can do this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv shell &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.8-dev
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command activates the version specified by setting the &lt;code&gt;PYENV_VERSION&lt;/code&gt; environment variable. This command overwrites any applications or global settings you may have. If you want to deactivate the version, you can use the &lt;code&gt;--unset&lt;/code&gt; flag.&lt;/p&gt;
&lt;h2 id=&quot;specifying-your-python-version&quot;&gt;Specifying Your Python Version&lt;/h2&gt;
&lt;p&gt;One of the more confusing parts of &lt;code&gt;pyenv&lt;/code&gt; is how exactly the &lt;code&gt;python&lt;/code&gt; command gets resolved and what commands can be used to modify it. As mentioned in the commands, there are 3 ways to modify which version of &lt;code&gt;python&lt;/code&gt; you&amp;rsquo;re using. So how do all these commands interact with one another? The resolution order looks a little something like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/pyenv-pyramid.d2f35a19ded9.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/pyenv-pyramid.d2f35a19ded9.png&quot; width=&quot;751&quot; height=&quot;786&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pyenv-pyramid.d2f35a19ded9.png&amp;amp;w=187&amp;amp;sig=1b68b85f9a3123f477e960b1623452e1b2b92659 187w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pyenv-pyramid.d2f35a19ded9.png&amp;amp;w=375&amp;amp;sig=1d79c37a5123a7ea60c63989e2785e06aa29faa3 375w, https://files.realpython.com/media/pyenv-pyramid.d2f35a19ded9.png 751w&quot; sizes=&quot;75vw&quot; alt=&quot;Pyenv pyramid for order of resolution&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This pyramid is meant to be read from top to bottom. The first of these options that &lt;code&gt;pyenv&lt;/code&gt; can find is the option it will use. Let&amp;rsquo;s see a quick example:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;* system (set by /home/realpython/.pyenv/version)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  2.7.15&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, your &lt;code&gt;system&lt;/code&gt; Python is being used as denoted by the &lt;code&gt;*&lt;/code&gt;. To exercise the next most global setting, you use &lt;code&gt;global&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv global &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.6.8
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;  system&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  2.7.15&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;* 3.6.8 (set by /home/realpython/.pyenv/version)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can see that now &lt;code&gt;pyenv&lt;/code&gt; wants to use &lt;code&gt;3.6.8&lt;/code&gt; as our Python version. It even indicates the location of the file it found. That file does indeed exist, and you can list its contents:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; cat ~/.pyenv/version
&lt;span class=&quot;go&quot;&gt;3.6.8&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, let&amp;rsquo;s create a &lt;code&gt;.python-version&lt;/code&gt; file with &lt;code&gt;local&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;.7.15
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;  system&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;* 2.7.15 (set by /home/realpython/.python-version)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ls -a
&lt;span class=&quot;go&quot;&gt;.  ..  .python-version&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; cat .python-version
&lt;span class=&quot;go&quot;&gt;2.7.15&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here again, &lt;code&gt;pyenv&lt;/code&gt; indicates how it would resolve our &lt;code&gt;python&lt;/code&gt; command. This time it comes from &lt;code&gt;~/.python-version&lt;/code&gt;. Note that the searching for &lt;code&gt;.python-version&lt;/code&gt; is recursive:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mkdir subdirectory
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; subdirectory
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ls -la &lt;span class=&quot;c1&quot;&gt;# Notice no .python-version file&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;. ..&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;  system&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;* 2.7.15 (set by /home/realpython/.python-version)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Even though there isn&amp;rsquo;t a &lt;code&gt;.python-version&lt;/code&gt; in &lt;code&gt;subdirectory&lt;/code&gt;, the version is still set to &lt;code&gt;2.7.15&lt;/code&gt; because &lt;code&gt;.python-version&lt;/code&gt; exists in a parent directory.&lt;/p&gt;
&lt;p&gt;Finally, you can set the Python version with &lt;code&gt;shell&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv shell &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.8-dev
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;  system&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  2.7.15&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;* 3.8-dev (set by PYENV_VERSION environment variable)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;All this did is set the &lt;code&gt;$PYENV_VERSION&lt;/code&gt; environment variable:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PYENV_VERSION&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you&amp;rsquo;re feeling overwhelmed by the options, the section on &lt;a href=&quot;#working-with-multiple-environments&quot;&gt;working with multiple environments&lt;/a&gt; goes over an opinionated process for managing these files, mostly using &lt;code&gt;local&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;virtual-environments-and-pyenv&quot;&gt;Virtual Environments and &lt;code&gt;pyenv&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Virtual environments are a big part of managing Python installations and applications. If you haven&amp;rsquo;t heard of virtual environments before, you can check out &lt;a href=&quot;https://realpython.com/python-virtual-environments-a-primer/&quot;&gt;Python Virtual Environments: A Primer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Virtual environments and &lt;code&gt;pyenv&lt;/code&gt; are a match made in heaven. &lt;code&gt;pyenv&lt;/code&gt; has a wonderful plugin called &lt;a href=&quot;https://github.com/pyenv/pyenv-virtualenv&quot;&gt;&lt;code&gt;pyenv-virtualenv&lt;/code&gt;&lt;/a&gt; that makes working with multiple Python version &lt;em&gt;and&lt;/em&gt; multiple virtual environments a breeze. If you&amp;rsquo;re wondering what the difference is between &lt;code&gt;pyenv&lt;/code&gt;, &lt;code&gt;pyenv-virtualenv&lt;/code&gt;, and tools like &lt;code&gt;virtualenv&lt;/code&gt; or &lt;code&gt;venv&lt;/code&gt;, then don&amp;rsquo;t worry. You&amp;rsquo;re not alone.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what you need to know:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;pyenv&lt;/strong&gt; manages multiple versions of Python itself.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;virtualenv/venv&lt;/strong&gt; manages virtual environments for a specific Python version.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pyenv-virtualenv&lt;/strong&gt; manages virtual environments for across varying versions of Python.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;rsquo;re a die-hard &lt;code&gt;virtualenv&lt;/code&gt; or &lt;code&gt;venv&lt;/code&gt; user, don&amp;rsquo;t worry: &lt;code&gt;pyenv&lt;/code&gt; plays nicely with either. In fact, you can keep the same workflow you&amp;rsquo;ve had if you&amp;rsquo;d prefer, though I think &lt;code&gt;pyenv-virtualenv&lt;/code&gt; makes for a nicer experience when you&amp;rsquo;re switching between multiple environments that require different Python versions.&lt;/p&gt;
&lt;p&gt;The good news is that since you used the &lt;code&gt;pyenv-installer&lt;/code&gt; script to install &lt;code&gt;pyenv&lt;/code&gt;, you already have &lt;code&gt;pyenv-virtualenv&lt;/code&gt; installed and ready to go.&lt;/p&gt;
&lt;h3 id=&quot;creating-virtual-environments&quot;&gt;Creating Virtual Environments&lt;/h3&gt;
&lt;p&gt;Creating a virtual environment is a single command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv virtualenv &amp;lt;python_version&amp;gt; &amp;lt;environment_name&amp;gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Technically, the &lt;code&gt;&amp;lt;python_version&amp;gt;&lt;/code&gt; is optional, but you should consider always specifying it so that you&amp;rsquo;re certain of what Python version you&amp;rsquo;re using.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;environment_name&amp;gt;&lt;/code&gt; is just a name for you to help keep your environments separate. A good practice is to name your environments the same name as your project. For example, if you were working on &lt;code&gt;myproject&lt;/code&gt; and wanted to develop against Python 3.6.8, you would run this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv virtualenv &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.6.8 myproject
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The output includes messages that show a couple of extra Python packages getting installed, namely &lt;code&gt;wheel&lt;/code&gt;, &lt;code&gt;pip&lt;/code&gt;, and &lt;code&gt;setuptools&lt;/code&gt;. This is strictly for convenience and just sets up a more full featured environment for each of your virtual environments.&lt;/p&gt;
&lt;h3 id=&quot;activating-your-versions&quot;&gt;Activating Your Versions&lt;/h3&gt;
&lt;p&gt;Now that you&amp;rsquo;ve created your virtual environment, using it is the next step. Normally, you should activate your environments by running the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; myproject
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You&amp;rsquo;ve seen the &lt;code&gt;pyenv local&lt;/code&gt; command before, but this time, instead of specifying a Python version, you specify an environment. This creates a &lt;code&gt;.python-version&lt;/code&gt; file in your current working directory and because you ran &lt;code&gt;eval &quot;$(pyenv virtualenv-init -)&quot;&lt;/code&gt; in your environment, the environment will automatically be activated.&lt;/p&gt;
&lt;p&gt;You can verify this by running the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv which python
&lt;span class=&quot;go&quot;&gt;/home/realpython/.pyenv/versions/myproject/bin/python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can see a new version has been created called &lt;code&gt;myproject&lt;/code&gt; and the &lt;code&gt;python&lt;/code&gt; executable is pointing to that version. If you look at any executable this environment provides, you will see the same thing. Take, for example, &lt;code&gt;pip&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv which pip
&lt;span class=&quot;go&quot;&gt;/home/realpython/.pyenv/versions/myproject/bin/pip&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you did not configure &lt;code&gt;eval &quot;$(pyenv virtualenv-init -)&quot;&lt;/code&gt; to run in your shell, you can manually activate/deactivate your Python versions with this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv activate &amp;lt;environment_name&amp;gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv deactivate
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above is what &lt;code&gt;pyenv-virtualenv&lt;/code&gt; is doing when it enters or exits a directory with a &lt;code&gt;.python-version&lt;/code&gt; file in it.&lt;/p&gt;
&lt;h2 id=&quot;working-with-multiple-environments&quot;&gt;Working With Multiple Environments&lt;/h2&gt;
&lt;p&gt;Putting everything you&amp;rsquo;ve learned together, you can work effectively with multiple environments. Let&amp;rsquo;s assume you have the following versions installed:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;* system (set by /home/realpython/.pyenv/version)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  2.7.15&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now you want to work on two different, aptly named, projects:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;project1&lt;/strong&gt; supports Python 2.7  and 3.6.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;project2&lt;/strong&gt; supports Python 3.6 and experiments with 3.8-dev.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can see that, by default, you are using the system Python, which is indicated by the &lt;code&gt;*&lt;/code&gt; in the &lt;code&gt;pyenv versions&lt;/code&gt; output. First, create a virtual environment for the first project:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; project1/
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv which python
&lt;span class=&quot;go&quot;&gt;/usr/bin/python&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv virtualenv &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.6.8 project1
&lt;span class=&quot;go&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; project1
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -V
&lt;span class=&quot;go&quot;&gt;/home/realpython/.pyenv/versions/project1/bin/python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, notice that when you &lt;code&gt;cd&lt;/code&gt; out of the directory, you default back to the system Python:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv which python
&lt;span class=&quot;go&quot;&gt;/usr/bin/python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can follow the above steps and create a virtual environment for project2:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; project2/
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv which python
&lt;span class=&quot;go&quot;&gt;/usr/bin/python&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv virtualenv &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.8-dev project2
&lt;span class=&quot;go&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.8-dev
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv which python
&lt;span class=&quot;go&quot;&gt;/home/realpython/.pyenv/versions/3.8-dev/bin/python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These are one time steps for your projects. Now, as you &lt;code&gt;cd&lt;/code&gt; between the projects, your environments will automatically activate:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; project2/
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -V
&lt;span class=&quot;go&quot;&gt;Python 3.8.0a0&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ../project1
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -V
&lt;span class=&quot;go&quot;&gt;Python 3.6.8&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;No more remembering to activate environments: you can switch between all your projects, and &lt;code&gt;pyenv&lt;/code&gt; will take care of automatically activating the correct Python versions &lt;em&gt;and&lt;/em&gt; the correct virtual environments.&lt;/p&gt;
&lt;h2 id=&quot;activating-multiple-versions-simultaneously&quot;&gt;Activating Multiple Versions Simultaneously&lt;/h2&gt;
&lt;p&gt;As described in the example above, &lt;code&gt;project2&lt;/code&gt; uses experimental features in 3.8. Suppose you wanted to ensure that your code still works on Python 3.6. If you try running &lt;code&gt;python3.6&lt;/code&gt;, you&amp;rsquo;ll get this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; project2/
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python3.6 -V
&lt;span class=&quot;go&quot;&gt;pyenv: python3.6: command not found&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;The `python3.6&amp;#39; command exists in these Python versions:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8/envs/project1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  project1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;pyenv&lt;/code&gt; informs you that, while Python 3.6 is not available in the current active environment, it is available in other environments. &lt;code&gt;pyenv&lt;/code&gt; gives you a way to activate multiple environments at once using a familiar command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; project2 &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.6.8
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This indicates to &lt;code&gt;pyenv&lt;/code&gt; that you would like to use the virtual environment &lt;code&gt;project2&lt;/code&gt; as the first option. So if a command, for example &lt;code&gt;python&lt;/code&gt;, can be resolved in both environments, it will pick &lt;code&gt;project2&lt;/code&gt; before &lt;code&gt;3.6.8&lt;/code&gt;. Let&amp;rsquo;s see what happens if you run this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python3.6 -V
&lt;span class=&quot;go&quot;&gt;Python 3.6.8&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;code&gt;pyenv&lt;/code&gt; attempts to find the &lt;code&gt;python3.6&lt;/code&gt; command, and because it finds it in an environment that is active, it allows the command to execute. This is extremely useful for tools like &lt;a href=&quot;https://tox.readthedocs.io/en/latest/&quot;&gt;tox&lt;/a&gt; that require multiple versions of Python to be available on your &lt;code&gt;PATH&lt;/code&gt; in order to execute.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; If you&amp;rsquo;re using tox and &lt;code&gt;pyenv&lt;/code&gt;, you should checkout the &lt;a href=&quot;https://pypi.org/project/tox-pyenv/&quot;&gt;tox-pyenv&lt;/a&gt; package.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Suppose that in the above example, you&amp;rsquo;ve found a compatibility problem with your library and would like to do some local testing. The testing requires that you install all the dependencies. You should follow the steps to create a new environment:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv virtualenv &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.6.8 project2-tmp
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; project2-tmp
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once you&amp;rsquo;re satisfied with your local testing, you can easily switch back to your default environment:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; project2 &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.6.8
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You can now more easily contribute to a project that wants to support multiple environments. You can also more easily test out the latest and greatest Python versions without having to worry about messing up your development machine, all with a wonderful tool: &lt;code&gt;pyenv&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ve seen how &lt;code&gt;pyenv&lt;/code&gt; can help you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install multiple versions of Python&lt;/li&gt;
&lt;li&gt;Switch between the installed versions&lt;/li&gt;
&lt;li&gt;Use virtual environments with &lt;code&gt;pyenv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Activate different Python versions and virtual environments automatically&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you still have questions, feel free to reach out either in the comments section or on Twitter. Additionally, the &lt;a href=&quot;https://github.com/pyenv/pyenv&quot;&gt;pyenv documentation&lt;/a&gt; is a great resource.&lt;/p&gt;
&lt;h2 id=&quot;bonus-displaying-your-environment-name-in-your-command-prompt&quot;&gt;Bonus: Displaying Your Environment Name in Your Command Prompt&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re like me and constantly switching between various virtual environments and Python versions, it&amp;rsquo;s easy to get confused about which version is currently active. I use &lt;a href=&quot;https://github.com/robbyrussell/oh-my-zsh&quot;&gt;oh-my-zsh&lt;/a&gt; and the &lt;a href=&quot;https://github.com/agnoster/agnoster-zsh-theme&quot;&gt;agnoster theme&lt;/a&gt;, which by default makes my prompt look like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/agnoster-no-pyenv-prompt.01f0966b78d7.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/agnoster-no-pyenv-prompt.01f0966b78d7.png&quot; width=&quot;1087&quot; height=&quot;393&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/agnoster-no-pyenv-prompt.01f0966b78d7.png&amp;amp;w=271&amp;amp;sig=af4cd711e388ddcb715370bfabe325212a90cc90 271w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/agnoster-no-pyenv-prompt.01f0966b78d7.png&amp;amp;w=543&amp;amp;sig=1bbe7d3f47f4a1c8bf8be5936c916262b816cfa0 543w, https://files.realpython.com/media/agnoster-no-pyenv-prompt.01f0966b78d7.png 1087w&quot; sizes=&quot;75vw&quot; alt=&quot;Agnoster theme with no pyenv prompt&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At a glance, I don&amp;rsquo;t know which Python version is active. To figure it out, I would have to run &lt;code&gt;python -V&lt;/code&gt; or &lt;code&gt;pyenv version&lt;/code&gt;. To help reduce my time spent on figuring out my active Python environment, I add the &lt;code&gt;pyenv&lt;/code&gt; virtual environment I&amp;rsquo;m using to my prompt:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/ljones-agnoster-pyenv-prompt.0b24a4a8f077.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/ljones-agnoster-pyenv-prompt.0b24a4a8f077.png&quot; width=&quot;1089&quot; height=&quot;378&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/ljones-agnoster-pyenv-prompt.0b24a4a8f077.png&amp;amp;w=272&amp;amp;sig=a1b3eeb3dddcf15e0e0bad3519d63508e5c8f6ed 272w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/ljones-agnoster-pyenv-prompt.0b24a4a8f077.png&amp;amp;w=544&amp;amp;sig=50c98a638b1aeeb42a54d37d828beb718981c7ac 544w, https://files.realpython.com/media/ljones-agnoster-pyenv-prompt.0b24a4a8f077.png 1089w&quot; sizes=&quot;75vw&quot; alt=&quot;Environment name being shown in ZSH command prompt&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My Python version in this case is &lt;code&gt;project1-venv&lt;/code&gt; and is displayed immediately at the beginning of the prompt. This allows me to quickly see what version of Python I&amp;rsquo;m using right away. If you&amp;rsquo;d like to use this too, you can use my &lt;a href=&quot;https://gist.github.com/loganasherjones/bd9b7614f80b96cf700fd60e9e256f41&quot;&gt;agnoster-pyenv theme&lt;/a&gt;.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Writing Comments in Python</title>
      <id>https://realpython.com/courses/writing-comments-python/</id>
      <link href="https://realpython.com/courses/writing-comments-python/"/>
      <updated>2019-03-08T00:02:05+00:00</updated>
      <summary>Learn how to write Python comments that are clean, concise, and useful. Quickly get up to speed on what the best practices are, which types of comments it&#39;s best to avoid, and how you can practice writing cleaner comments.</summary>
      <content type="html">
        &lt;p&gt;Learn how to write Python comments that are clean, concise, and useful. Quickly get up to speed on what the best practices are, which types of comments it&amp;rsquo;s best to avoid, and how you can practice writing cleaner comments.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  

</feed>
