<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jean-Michel Plourde</title>
    <description>The latest articles on DEV Community by Jean-Michel Plourde (@jmplourde).</description>
    <link>https://dev.to/jmplourde</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F95305%2F2e4dd400-5453-42fe-8081-c9ccfe4c7ad3.jpeg</url>
      <title>DEV Community: Jean-Michel Plourde</title>
      <link>https://dev.to/jmplourde</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jmplourde"/>
    <language>en</language>
    <item>
      <title>Running Python code as a migration operation in Django</title>
      <dc:creator>Jean-Michel Plourde</dc:creator>
      <pubDate>Thu, 24 Aug 2023 15:18:44 +0000</pubDate>
      <link>https://dev.to/jmplourde/running-python-code-as-a-migration-operation-in-django-5gbj</link>
      <guid>https://dev.to/jmplourde/running-python-code-as-a-migration-operation-in-django-5gbj</guid>
      <description>&lt;p&gt;&lt;em&gt;This blog post was originally published on:&lt;/em&gt; &lt;a href="https://jmplourde.com/til-django-run-python-migration-operation/" rel="noopener noreferrer"&gt;https://jmplourde.com/til-django-run-python-migration-operation/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I started learning Django, I was confused with the difference between &lt;code&gt;makemigrations&lt;/code&gt; and &lt;code&gt;migrate&lt;/code&gt; commands. I got it after a while, but I never truly had to work with complex migrations or write custome ones. I recently had to write a custom migration to change a &lt;code&gt;ForeignKey&lt;/code&gt; field to a &lt;code&gt;OneToOneField&lt;/code&gt; and I used the &lt;a href="https://docs.djangoproject.com/en/4.2/ref/migration-operations/#runsql" rel="noopener noreferrer"&gt;RunSQL&lt;/a&gt; special operation which allows to run raw sql queries. It allowed me to better understand migrations and gave me the confidence to push my changes in production.&lt;/p&gt;

&lt;p&gt;Now I wanted to refactor an unnecessary many-to-many model into a one-to-one field in another model. The models looked like the following code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;account_models&lt;/span&gt;


&lt;span class="c1"&gt;# the model I want to get rid of
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomeAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;account_models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;verbose_name_plural&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;home addresses&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Home&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OneToOneField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  &lt;span class="c1"&gt;# the field I want to replace the model with
&lt;/span&gt;        &lt;span class="n"&gt;account_models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PROTECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The challenge was to copy the &lt;code&gt;Address&lt;/code&gt; refered in each &lt;code&gt;HomeAddress&lt;/code&gt; row to the refered &lt;code&gt;Home&lt;/code&gt;. I wasn't sure how I would solve this and dreaded the raw SQL query, not wanting to mess up the production data. I asked ChatGPT for some inspiration and it suggested using the &lt;a href="https://docs.djangoproject.com/en/4.2/ref/migration-operations/#runpython" rel="noopener noreferrer"&gt;RunPython&lt;/a&gt; special operation. You first declare a function that takes two arguments; the first being the name of the app containing the historical models that matches the operation and the second is an instance of &lt;a href="https://docs.djangoproject.com/en/4.2/ref/schema-editor/#django.db.backends.base.schema.BaseDatabaseSchemaEditor" rel="noopener noreferrer"&gt;SchemaEditor&lt;/a&gt; the class that turns operations into SQL. The function code describes the changes that will be applied.&lt;/p&gt;

&lt;p&gt;A second function is declared that accepts the same two arguments and its code should undo what has been done by the first function. This makes the migration reversible otherwise the changes are permanent. The two functions are passed to the &lt;code&gt;RunPython&lt;/code&gt; function called inside the &lt;code&gt;operations&lt;/code&gt; list of the &lt;code&gt;Migration&lt;/code&gt; class. For database backends that do not support &lt;a href="https://en.wikipedia.org/wiki/Data_definition_language" rel="noopener noreferrer"&gt;Data Definition Language (DDL)&lt;/a&gt; transactions (&lt;code&gt;CREATE&lt;/code&gt;, &lt;code&gt;ALTER&lt;/code&gt;, etc.), &lt;code&gt;RunPython&lt;/code&gt; will run its content inside a transaction.&lt;/p&gt;

&lt;p&gt;For databases that do support DDL like PostgreSQL, no other transactions are added besides the one generated by a migration. A migration with both schema changes and &lt;code&gt;RunPython&lt;/code&gt; operation will raise an exception stating the changes cannot be applied because it has pending trigger events.&lt;/p&gt;

&lt;p&gt;What I ended up doing is generating a migration to add the &lt;code&gt;Home.address&lt;/code&gt; field, then generated an empty migration and wrote the following code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;account_models&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;replace_home_with_address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schema_editor&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;home_address_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my_app&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;HomeAddress&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;home_address&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;home_address_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;home_address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;
        &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;account_models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;address_line_1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;123 Fake Street&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;address_line_2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Building 1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Test City&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;zip_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;12345&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;country_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;home_address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;subdivision_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;home_address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subdivision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;home_address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;
        &lt;span class="n"&gt;home_address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


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

    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my_app&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0002_auto_20230821_1308&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RunPython&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;replace_home_with_address&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So here, I loop over the &lt;code&gt;HomeAddress&lt;/code&gt; rows and for each one of them, I put the &lt;code&gt;HomeAddress.address&lt;/code&gt; reference into &lt;code&gt;HomeAddress.home.address&lt;/code&gt; and save. Note that for readability purpose, I didn't include the query prefetching code.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you have any comments, suggestions or ideas please share them with me in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>django</category>
      <category>database</category>
      <category>backend</category>
    </item>
    <item>
      <title>Testing with Django REST Framework</title>
      <dc:creator>Jean-Michel Plourde</dc:creator>
      <pubDate>Tue, 19 Jul 2022 12:05:33 +0000</pubDate>
      <link>https://dev.to/jmplourde/testing-with-django-rest-framework-295a</link>
      <guid>https://dev.to/jmplourde/testing-with-django-rest-framework-295a</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;It’s not because things are difficult that we dare not venture. It’s because we dare not venture that they are difficult.&lt;/em&gt; - Seneca&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A section of Django REST framework (DRF) &lt;a href="https://www.django-rest-framework.org/api-guide/testing" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; is dedicated to testing your API views. It has a lot of information about the different tools the library is providing and code examples on how to use them. I was asked how to test an API call by mimicking a request, so here is how I usually do it in my projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  The serializer
&lt;/h2&gt;

&lt;p&gt;First, we have a basic model for a home which fields describe its address. &lt;code&gt;BaseModel&lt;/code&gt; is an abstract class that adds a &lt;code&gt;UUID&lt;/code&gt;, a &lt;code&gt;created_at&lt;/code&gt; and a &lt;code&gt;modified_at&lt;/code&gt; fields to my objects:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SupportedCountries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;CAN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CANADA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;US&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UNITED_STATES&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="n"&gt;CHOICES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CAN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Canada&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;US&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;United States&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Home&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;address_line_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;address_line_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blank&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Address line 2 (optional)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;state_province&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;State/Province&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;zip_code_postal_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Zip code/Postal code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SupportedCountries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CHOICES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SupportedCountries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;US&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__str__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The resulting serializer is thus very simple with no inner serializer. We declare all the fields from the model plus the &lt;code&gt;id&lt;/code&gt; field from the &lt;code&gt;BaseModel&lt;/code&gt; abstract class:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomeSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelSerializer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Home&lt;/span&gt;
        &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;address_line_1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;address_line_2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;state_province&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;zip_code_postal_code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;country&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  The test class
&lt;/h2&gt;

&lt;p&gt;To generate objects based on my models for my tests, I use &lt;a href="https://factoryboy.readthedocs.io/en/stable/orms.html" rel="noopener noreferrer"&gt;factory boy&lt;/a&gt; and I populate the fields with fake data using &lt;a href="https://faker.readthedocs.io/en/master/" rel="noopener noreferrer"&gt;faker&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;factory.django&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DjangoModelFactory&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomeFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DjangoModelFactory&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Home&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;address_line_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;street_address&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;address_line_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;building_number&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;state_province&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;zip_code_postal_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postcode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SupportedCountries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;US&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Home&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I usually use &lt;a href="https://docs.djangoproject.com/en/4.0/topics/testing/overview/" rel="noopener noreferrer"&gt;Django TestCase&lt;/a&gt; class for testing purposes, which is a subclass of Python unittest.TestCase. It has a client to authenticate a user so you could test mixins and permission behaviors. DRF has a class called &lt;a href="https://www.django-rest-framework.org/api-guide/testing/#api-test-cases" rel="noopener noreferrer"&gt;APITestCase&lt;/a&gt; that extends Django &lt;code&gt;TestCase&lt;/code&gt; with the same functions but using &lt;a href="https://www.django-rest-framework.org/api-guide/testing/#apiclient" rel="noopener noreferrer"&gt;APIClient&lt;/a&gt; which allows one to authenticate as an API user.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestHomeAPIView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;APITestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api:home_view&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# use the view url
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HomeFactory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UserFactory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;force_authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;expected_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;address_line_1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address_line_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;address_line_2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address_line_2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;state_province&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state_province&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;zip_code_postal_code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zip_code_postal_code&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;country&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertListEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expected_content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's dive into that test code and explain what it does. First we have the &lt;code&gt;setUp&lt;/code&gt; function which is common to TestCase and define code that will be run before every function prefixed with &lt;code&gt;test_&lt;/code&gt; in the class. I define the url for my view and a &lt;code&gt;home&lt;/code&gt; object that will be recreated each time, making my test deterministic. I then create a user with a factory that gives the permissions I need for testing the view. The last line is really important, even more so for an API view with &lt;a href="https://www.django-rest-framework.org/api-guide/authentication/" rel="noopener noreferrer"&gt;authentication&lt;/a&gt; and &lt;a href="https://www.django-rest-framework.org/api-guide/permissions/" rel="noopener noreferrer"&gt;permission&lt;/a&gt; mechanisms. By default, the &lt;code&gt;DEFAULT_PERMISSION_CLASSES&lt;/code&gt; array contains &lt;code&gt;rest_framework.permissions.IsAuthenticated&lt;/code&gt; so all my views require to be logged in as a user. We authenticate the user with the &lt;code&gt;APIClient&lt;/code&gt; so we can make calls to the view and the permissions can be evaluated.&lt;/p&gt;

&lt;p&gt;I then declare &lt;code&gt;test_get&lt;/code&gt; to make a simple &lt;code&gt;GET&lt;/code&gt; test of my view. I first capture the call response into a variable of the same name. At first, I assumed &lt;code&gt;response.content&lt;/code&gt; would be available, but because of template rendering mechanism internal to Django, it is not. We need to manually declare the rendering. Then I assert that my response was successful and generated an &lt;a href="https://http.cat/200" rel="noopener noreferrer"&gt;HTTP 200&lt;/a&gt; that means everything went fine. The next line declares an &lt;code&gt;expected_content&lt;/code&gt; variable which is dict containing all the fields of my &lt;code&gt;home&lt;/code&gt; object. Lastly, I parse a JSON object to a Python object using &lt;code&gt;json.loads&lt;/code&gt;, which I compare to my expected content to make sure what I received is correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Django REST Framework has a great documentation with a thorough section on tests. We went through some bits of code for the model, the serializer and finally the test. This test was written à la Django using factory boy and Faker, and leveraging DRF &lt;code&gt;APIClient&lt;/code&gt; contained in &lt;code&gt;APITestCase&lt;/code&gt; testing class.&lt;/p&gt;

&lt;p&gt;We went from start to finish of my testing process for an API view. There might be some portions that could be optimized, but it proved to be reliable for me.&lt;/p&gt;

&lt;p&gt;Please share with me any tips or amazing bits of code that make your testing experience with Django REST Framework easier. If you spot an error, do not hesitate to leave a comment.&lt;/p&gt;

</description>
      <category>python</category>
      <category>django</category>
      <category>testing</category>
      <category>opensource</category>
    </item>
    <item>
      <title>git-worktree: Working on multiple branches at the same time</title>
      <dc:creator>Jean-Michel Plourde</dc:creator>
      <pubDate>Sun, 13 Mar 2022 15:50:15 +0000</pubDate>
      <link>https://dev.to/jmplourde/git-worktree-working-on-multiple-branches-at-the-same-time-e30</link>
      <guid>https://dev.to/jmplourde/git-worktree-working-on-multiple-branches-at-the-same-time-e30</guid>
      <description>&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/@switch_dtp_fotografie?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Lucas van Oort&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/branches?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The struggle
&lt;/h2&gt;

&lt;p&gt;I can't count how many times I'm in the middle of something then suddenly I need to attend to a situation in the same project. I stash my current work, checkout the required branch and do some work. Then I come back to my original task and now I am confuse why everything break. Oh yeah, I forgot to unstash my changes.&lt;/p&gt;

&lt;p&gt;An alternative could be to commit my current work, but I am very strict on what I commit and the messages I craft. When I come back to these commits, I want them to make sense and group related work.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work
&lt;/h2&gt;

&lt;p&gt;When using &lt;code&gt;git clone&lt;/code&gt; or &lt;code&gt;git init&lt;/code&gt; with &lt;a href="https://stackoverflow.com/questions/5540883/whats-the-practical-difference-between-a-bare-and-non-bare-repository" rel="noopener noreferrer"&gt;non-bare repos&lt;/a&gt;, there is a main worktree created in the folder with the &lt;code&gt;main&lt;/code&gt; branch checked out. Everything that is not &lt;code&gt;.git&lt;/code&gt; inside the current folder is the main working tree. I have heard developers call that the working space or the current workspace but the correct term is a working tree. &lt;/p&gt;

&lt;p&gt;Enters &lt;a href="https://git-scm.com/docs/git-worktree" rel="noopener noreferrer"&gt;&lt;code&gt;git-worktree&lt;/code&gt;&lt;/a&gt;: this command lets you create additional working trees called &lt;code&gt;linked worktree&lt;/code&gt;. You specify a folder and &lt;code&gt;git-worktree&lt;/code&gt; will create a linked worktree inside of it so you can checkout any existing or new branch inside of it. All you have to do then is &lt;code&gt;cd&lt;/code&gt; into that folder and you are now working on another branch, on a working tree parallel to the main one.&lt;/p&gt;

&lt;p&gt;No more breaking things or bamboozzling!&lt;/p&gt;

&lt;h2&gt;
  
  
  How I use it
&lt;/h2&gt;

&lt;p&gt;In a project folder, I create a &lt;code&gt;worktrees&lt;/code&gt; sub directory. Inside that &lt;code&gt;worktrees&lt;/code&gt; directory, I create a subfolder for each worktree I want to add.&lt;/p&gt;

&lt;p&gt;This makes the root of the project the &lt;code&gt;main&lt;/code&gt; or default worktree. To switch from a worktree to another, it is only a matter of navigating folders in &lt;code&gt;worktrees&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note how&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# I am in `myproject/worktrees/task-123`&lt;/span&gt;
&lt;span class="c"&gt;# Sudden need to work on another task?&lt;/span&gt;
git worktree  add ../task-xyz task-xyz
&lt;span class="nb"&gt;pushd&lt;/span&gt; ../task-xyz
&lt;span class="c"&gt;# do work&lt;/span&gt;
&lt;span class="nb"&gt;popd&lt;/span&gt;
&lt;span class="c"&gt;# and we are back&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzzi69mgxq7g84mg0q6t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzzi69mgxq7g84mg0q6t.png" alt="Screenshot of my terminal after creating a git worktree and using ls command to see the content" width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Some use cases for the command
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You have tests that take a while to run but you can't switch to a branch because that would change files and introduce unwanted behaviors.&lt;/li&gt;
&lt;li&gt;You are working on a feature and need to hop on another  branch to fix some code on a submitted merge request but there is uncommitted work.&lt;/li&gt;
&lt;li&gt;You want to try something quick while working on code you don't want to lose.&lt;/li&gt;
&lt;li&gt;In 2025, I discovered that worktrees are perfect to work on a branche while your coding agent(s) are working on other branches simultaneously.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;Here are some useful &lt;code&gt;git-worktree&lt;/code&gt; commands from the &lt;a href="https://www.npmjs.com/package/tldr" rel="noopener noreferrer"&gt;npm tldr&lt;/a&gt; page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Finuhkkk92qocb24grrcm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Finuhkkk92qocb24grrcm.png" alt="Terminal screenshot of the result of the npm tldr command for git-worktree" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next step
&lt;/h2&gt;

&lt;p&gt;I would like to fully use &lt;code&gt;git-worktree&lt;/code&gt; with Docker. I will explore possibilities on having different containers running in parallel from different worktrees without having to manually change the &lt;code&gt;docker-compose.yml&lt;/code&gt; ports. Hit me up if you have a found a solution to this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;git-worktree&lt;/code&gt; allows us to work on multiple branches in parallel without forcing us to commit or stash between checkouts. I am more than happy to ditch &lt;code&gt;git stash&lt;/code&gt; and to never look back.&lt;/p&gt;

&lt;p&gt;I am always eager to learn from others so let me know how you work with &lt;code&gt;git-worktree&lt;/code&gt; or in which scenarios it is helpful to you. Maybe you have an even better solution?&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>git</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Automating complex terminal flows with tmuxinator</title>
      <dc:creator>Jean-Michel Plourde</dc:creator>
      <pubDate>Sun, 30 Aug 2020 14:03:46 +0000</pubDate>
      <link>https://dev.to/jmplourde/automating-multiple-terminals-with-tmux-and-tmuxinator-47n2</link>
      <guid>https://dev.to/jmplourde/automating-multiple-terminals-with-tmux-and-tmuxinator-47n2</guid>
      <description>&lt;p&gt;&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@_imkiran?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Sai Kiran Anagani&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/automation?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;I was working on a big e-commerce project that required to have 5 terminals open, doing a certain amount of commands and let those terminal sit idle. Spinning up the back and front end, the database and the local environments required to remember a considerable amount of commands in a precise order and takes between 2 and 3 minutes to complete. Seems not like a lot but if I do it once per day, five days a week for 50 weeks: that's ~8 hours of manual work. I thought about using some bash scripting and that's when I discovered tmux and tmuxinator.&lt;/p&gt;

&lt;h2&gt;
  
  
  tmux
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/tmux/tmux/wiki" rel="noopener noreferrer"&gt;tmux&lt;/a&gt; stands for Terminal Multiplexer. It lets you switch between several programs inside one terminal, detach and reattach them to a different terminal at will. It will work with multiple programs and shells in one terminal, like a windows manager. tmux protects your remote connections if installed on a remote server. It's ideal for SSH, PuTTy and other connections. Programs can be remotely accessed by many local computers.&lt;/p&gt;

&lt;p&gt;Let's install it first.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Ubuntu and Debian based distros&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;tmux

&lt;span class="c"&gt;# CentOS and Fedora&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;tmux

&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;tmux


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  tmuxinator
&lt;/h2&gt;

&lt;p&gt;tmux is a great and powerful tool that can save a lot of trouble, but everything is still manual. Fortunately, tmux is wonderfully automatable and that's where tmuxinator shines.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tmuxinator/tmuxinator" rel="noopener noreferrer"&gt;tmuxinator&lt;/a&gt; is a tool to automate the creation of sessions with tmux. We create a new project, which opens up a YAML file. In this file, we specify how many windows and panes we want and their layouts. There is many hooks to run commands at certain moments of the tmux run: when a project start, when it stops, etc.&lt;/p&gt;

&lt;p&gt;tmux executes any command as if you typed it. It waits for a command to finish before launching the next. This is perfect for waiting on an SSH connection to run your next commands.&lt;/p&gt;

&lt;p&gt;Let's install it:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Ubuntu and Debian based distros&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;tmuxinator

&lt;span class="c"&gt;# Fedora&lt;/span&gt;
gem &lt;span class="nb"&gt;install &lt;/span&gt;tmuxinator

&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;tmuxinator


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I highly recommand setting up an aliases for tmuxinator so you don't have to write the whole name for every command.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# .bashrc/.zshrc/.*rc&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tmuxinator"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To create a new tmuxinator project we use the new command&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

tmuxinator new MyProject


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Every tmuxinator command can be abbreviated to a single letter. With our alias the command is simply&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

mux n MyProject


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This creates a yaml file where we put all our flow to be automated. I recommend doing the manual setting up of your project in real time while you edit the yaml file to make things easier and to not forget a step. &lt;/p&gt;

&lt;p&gt;Let's have a look at the actual project I'm using for my local dev env:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myproject&lt;/span&gt;
&lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~/&lt;/span&gt;

&lt;span class="na"&gt;startup_window&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vagrant and docker-git&lt;/span&gt;

&lt;span class="na"&gt;startup_pane&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

&lt;span class="c1"&gt;# The first level is the list of windows. &lt;/span&gt;
&lt;span class="na"&gt;windows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Second level is each window name. Each element use a -&lt;/span&gt;

  &lt;span class="c1"&gt;# Commands can be specified inline &lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;ide&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/usr/local/pycharm-2020.1.3/bin/pycharm.sh&lt;/span&gt; 

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;watchers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# 3rd level is the window options&lt;/span&gt;
      &lt;span class="c1"&gt;# layout option is how the panes appear in a windows&lt;/span&gt;
      &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;even-horizontal&lt;/span&gt;                             

      &lt;span class="c1"&gt;# List of panes inside a window. Each is named              &lt;/span&gt;
      &lt;span class="na"&gt;panes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;                  
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;vendor watcher&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# 4th level is a list of commands to execute&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;j myproject-app&lt;/span&gt;         
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;clear&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm run watch:vendor&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;customer watcher&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;j myproject-app&lt;/span&gt;         &lt;span class="c1"&gt;# zsh jump plugin&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;clear&lt;/span&gt;             
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm run watch:customer&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;vagrant and docker-git&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;even-vertical&lt;/span&gt;
      &lt;span class="na"&gt;panes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;vagrant&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;j MyProjectFiles&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;clear&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;workon MyProject&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;vagrant up&lt;/span&gt;
          &lt;span class="c1"&gt;# this command takes time and tmuxinator &lt;/span&gt;
          &lt;span class="c1"&gt;# waits before executing the next one&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;vagrant ssh&lt;/span&gt;      
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;source /usr/local/virtualenvs/myproject374/bin/activate&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd /srv/myproject-api&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export $(cat .env | xargs)&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sudo service supervisor restart&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;clear&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;docker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;j myproject&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;clear&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker stop myproject_mysql&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker rm myproject_mysql&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker run --name myproject_mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=true -p 3306:3306 -d mysql:5.6.35&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;clear&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker ps&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We then start this automation with the command&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

tmuxinator start MyProject

&lt;span class="c"&gt;# or &lt;/span&gt;
mux s MyProject


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can edit the file with&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

tmuxinator edit MyProject

&lt;span class="c"&gt;# or &lt;/span&gt;
mux e MyProject


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When starting the project, everything kicks in at the same time in a matter of seconds. The only thing that takes time is waiting for the VMs to be created, but since everything runs in parallel, we can already start to code in the IDE. We went from 2-3 minutes to 10 seconds, that's a huge gain!&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's dive into the details of tmux
&lt;/h2&gt;

&lt;h3&gt;
  
  
  tmux Server and client
&lt;/h3&gt;

&lt;p&gt;tmux keeps all its states in one single main process called a tmux server. It runs in the backgroup, manages all the programs and keep track of their outputs. It launches automagically when a tmux command is used and stops by itself when there is no program left to run. &lt;/p&gt;

&lt;p&gt;When a user starts a tmux client, it takes over his terminal and attach to the server so they can talk via a socket file in &lt;code&gt;/tmp&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pane
&lt;/h3&gt;

&lt;p&gt;Inside tmux, every terminal belongs to a pane, that is a rectangular area much like a tile in a windows manager. You can navigate between panes with &lt;code&gt;ctrl+b ←/↑/↓/→&lt;/code&gt;, split panes vertically with &lt;code&gt;ctrl+b %&lt;/code&gt; and horizontally with &lt;code&gt;ctrl+b "&lt;/code&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Window
&lt;/h3&gt;

&lt;p&gt;Every pane is contained inside a window, which is the whole area of the terminal. There can be many window per session. These windows can be reordered and have a name. You can switch from a window to another either with its number &lt;code&gt;ctrl+b &amp;lt;0-9&amp;gt;&lt;/code&gt; or &lt;code&gt;ctrl+b n&lt;/code&gt; and &lt;code&gt;ctrl+b p&lt;/code&gt; for next and previous. Windows have a layout which is how each panes appear inside a window. In a layout, panes are seperated by a line called a pane border. You can either use one of the 4 preset layouts or specify your own.&lt;/p&gt;

&lt;h3&gt;
  
  
  Status bar
&lt;/h3&gt;

&lt;p&gt;At the bottom of the terminal, there is a status bar. It indicates which session, pane and window is currently active.&lt;/p&gt;

&lt;h3&gt;
  
  
  Session
&lt;/h3&gt;

&lt;p&gt;One or many windows are in a session. Each session window has a numbered index and a name, at the bottom in the status bar. A window can be part, or attached, to many sessions. A session can be attached to one or more clients and has a unique name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Commands
&lt;/h3&gt;

&lt;p&gt;Inside tmuxinator, we can execute commands such as switching window or pane, close session or open new one. To get into command mode, use &lt;code&gt;ctrl+b&lt;/code&gt; then the command. When entering a semicolon, you can enter commands much like the vi/vim command mode.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus: Here is a &lt;a href="https://tmuxcheatsheet.com/" rel="noopener noreferrer"&gt;tmux cheatsheet&lt;/a&gt; that is super helpful to get into tmux and help tmuxinator scripting
&lt;/h3&gt;

</description>
      <category>productivity</category>
      <category>linux</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Which Linux distribution is your favorite and why?</title>
      <dc:creator>Jean-Michel Plourde</dc:creator>
      <pubDate>Tue, 06 Aug 2019 19:19:15 +0000</pubDate>
      <link>https://dev.to/jmplourde/which-linux-distribution-is-your-favorite-and-why-5gd8</link>
      <guid>https://dev.to/jmplourde/which-linux-distribution-is-your-favorite-and-why-5gd8</guid>
      <description>&lt;p&gt;Two years ago, I installed Ubuntu on my school/dev laptop to force myself to learn computer fundamentals and powerful tools. So far it proved a really good move. Recently, I discovered &lt;a href="https://distrowatch.com/" rel="noopener noreferrer"&gt;distro watch&lt;/a&gt; and it inspired me this question: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which Linux distro is your favorite and why ?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>linux</category>
    </item>
    <item>
      <title>How many monitors do you use when programming?</title>
      <dc:creator>Jean-Michel Plourde</dc:creator>
      <pubDate>Tue, 02 Jul 2019 19:21:46 +0000</pubDate>
      <link>https://dev.to/jmplourde/how-many-monitors-do-you-use-when-programming-17e3</link>
      <guid>https://dev.to/jmplourde/how-many-monitors-do-you-use-when-programming-17e3</guid>
      <description>&lt;p&gt;A good setup is one where everything needed is in front of you and quickly reachable. Some swear two is the minimum while others only use one.&lt;/p&gt;

&lt;p&gt;How many monitors do you use when programming?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Interesting stuff I stumbled on #2</title>
      <dc:creator>Jean-Michel Plourde</dc:creator>
      <pubDate>Tue, 25 Jun 2019 00:18:32 +0000</pubDate>
      <link>https://dev.to/jmplourde/interesting-stuff-i-stumbled-on-2-3oah</link>
      <guid>https://dev.to/jmplourde/interesting-stuff-i-stumbled-on-2-3oah</guid>
      <description>&lt;h1&gt;
  
  
  Podcasts
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://ladybug.dev/" rel="noopener noreferrer"&gt;The ladybug podcast&lt;/a&gt;&lt;/strong&gt;: The new podcast by &lt;a href="https://dev.to/aspittel"&gt;@aspittel&lt;/a&gt;, &lt;a href="https://dev.to/emmawedekind"&gt;@emmawedekind&lt;/a&gt;, &lt;a href="https://dev.to/kelly"&gt;@kelly&lt;/a&gt; and &lt;a href="https://dev.to/lkopacz"&gt;@lkopacz&lt;/a&gt; is an all lady podcast where they talk about code and career. I already follow these four women for the past year and I have always enjoyed reading about their journey, their projects and their takes about various subjects regarding women and under represented people in tech. This will considerably brighten my mondays.&lt;/p&gt;

&lt;h1&gt;
  
  
  Articles
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.gamasutra.com/view/news/344672/How_id_built_Wolfenstein_3D_using_Commander_Keen_tech.php" rel="noopener noreferrer"&gt;Gamasutra: How ID built Wolfenstein 3D using Commander Keen tech&lt;/a&gt;&lt;/strong&gt;: Even if it's not from my time, I still remember the first time I played Wolfenstein 3D on a old computer in a friend's basement. I was hooked on it and played several hours. From time to time, I discover those gems that told the story of how the most popular games of the past were made. Written in C, Wolfenstein 3D was a hell of revolutionary game and its creators brought in some fresh takes at doing things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.nytimes.com/2019/06/28/business/evernote-what-happened.html" rel="noopener noreferrer"&gt;A Unicorn Lost in the Valley, Evernote Blows Up the ‘Fail Fast’ Gospel&lt;/a&gt;&lt;/strong&gt;: What happens to those Silicon Valley unicorns that become huge then stale? We don't hear a lot about them, but there is many companies that reach a peak then the hype slowly vanishes. Remember Yo, Ello, Peach, Meerkat, Stolen, Clinkle, Secret, Color, etc. ? They were big, praised for their novelty then became zombiecorn. A nice approach to this phenomenon with the last victim of the movement: Evernote.&lt;/p&gt;

&lt;h1&gt;
  
  
  Projects
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://learningsynths.ableton.com/" rel="noopener noreferrer"&gt;Learn Synths - Ableton.com&lt;/a&gt;&lt;/strong&gt;: I am not a huge fan of electronic music, but making it always mystified me. At first, I was delusional in thinking you only have to push some buttons and you have a hit. Yeah, no. It has a lot to do with electrical engineering and math, but there is a fun way (even tho math is fun to me) to learn how a synthesiser work. You will learn that with little units of sound modification up to the thing: a synth.&lt;/p&gt;

&lt;h1&gt;
  
  
  Books
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.goodreads.com/book/show/35564920-educated" rel="noopener noreferrer"&gt;Educated by Tara Westover&lt;/a&gt;&lt;/strong&gt;: Born in rural Idaho, Tara is raised in the mountains by very religious parents. Survivalists, the whole family prepares for the End of the Day and is determined to live off the grid, away from the evil government. When they get badly injured, by accident or violence, they don't go to a hospital. They treat everything with tinctures or oils and put their faith into god hands. From a young age, Tara destiny is already traced by God commandments and her father who has the real power over its family. Eventually, the little girl grows curious about the outside, how it feels to live a normal life. Fighting her identity from the mountains, she goes about earning a PhD despite having no previous high school education. The book is pure joy to read, it got me through lots of emotions from rage to happiness and relieves.&lt;/p&gt;

&lt;h1&gt;
  
  
  Videos
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.youtube.com/watch?v=ODaPkCehkkA" rel="noopener noreferrer"&gt;How do Braking Systems Work on a Formula One Car? - Mercedes motorsport&lt;/a&gt;&lt;/strong&gt;: In the past days, I clicked through some suggestions Youtube regarding F1. While not a fan or following F1 seasons, I really enjoy the engineering of these fast cars. I especially appreciated how the disk brakes work and how they are cooled.&lt;/p&gt;

&lt;h1&gt;
  
  
  Voila!
&lt;/h1&gt;

&lt;p&gt;If you ever read/listen/watch something in that list, tell me how you liked it.&lt;br&gt;
You can also &lt;a href="https://www.goodreads.com/user/show/80203337-jim" rel="noopener noreferrer"&gt;follow me on goodreads.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>weeklyretro</category>
      <category>discuss</category>
      <category>personalnews</category>
    </item>
    <item>
      <title>Interesting stuff I stumbled on #1</title>
      <dc:creator>Jean-Michel Plourde</dc:creator>
      <pubDate>Sun, 23 Jun 2019 03:14:10 +0000</pubDate>
      <link>https://dev.to/jmplourde/interesting-stuff-i-stumbled-on-1-1jhl</link>
      <guid>https://dev.to/jmplourde/interesting-stuff-i-stumbled-on-1-1jhl</guid>
      <description>&lt;p&gt;Every day, I have to walk 20-30 minutes to work and like everyone I have daily maintenance tasks so it's the perfect time to listen to podcasts. I also enforce a 1-hour no-screen time before bed so I mostly read books. Whenever I have free time, I either read articles in my Pocket list, open a book, listen to a podcast or watch videos.&lt;/p&gt;

&lt;p&gt;I just want to expose the interesting stuff I stumbled upon during my week.&lt;/p&gt;

&lt;h1&gt;
  
  
  Podcasts
&lt;/h1&gt;

&lt;p&gt;I love listening to Joe Rogan podcasts. The guy is brilliant and well articulated plus with a ton of episodes with very interesting guests. While the format is very long, I am never disappointed by the content&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://podcasts.joerogan.net/podcasts/naval-ravikant" rel="noopener noreferrer"&gt;Naval Ravikant on Joe Rogan Experience&lt;/a&gt;&lt;/strong&gt;: Ravikant is an entrepreneur and an angel investor in the tech industry. He is also a futurist and has interesting takes on how humanity will involve with tech. Ravikant his mostly active on twitter where you can read his most popular and very humble threads like &lt;a href="https://threadreaderapp.com/thread/1002103360646823936.html" rel="noopener noreferrer"&gt;How to get rich (without getting Lucky)&lt;/a&gt; and some of his &lt;a href="https://threadreaderapp.com/thread/877467629308395521.html" rel="noopener noreferrer"&gt;tech predictions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://podcasts.joerogan.net/podcasts/jamie-metzl" rel="noopener noreferrer"&gt;Jamie Metzl on Joe Rogan Experience&lt;/a&gt;&lt;/strong&gt;: Metzl is also a tech futurist, a geopolitics expert, a novelist and an entrepreneur. He discusses about how we will one day crack the code of our genome so we can make ourselves super human. He demonstrates that for the past several years we already engineered plants and animals. I will definitely get a look at his book &lt;em&gt;Hacking Darwin&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://softwareengineeringdaily.com/2019/05/29/netflix-early-days-with-greg-burrell/" rel="noopener noreferrer"&gt;Software Engineering Daily: Netflix Early Days with Greg Burrell&lt;/a&gt;&lt;/strong&gt;: Did you know that Netflix is already 14 years old ? And that it used to offer DVD-by-mail service ? Me neither! Burrell is one of the first employees Netflix hired and he is still working for them. He explains how it used to run in the early days and what challenges they faced when they pivoted to being a streaming platform. Great insight on how work with teams and different services inside a huge company. I was drawn to this podcast because in the last few months, I was really interested in &lt;em&gt;resilience engineering&lt;/em&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Articles
&lt;/h1&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/deepu105" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F178939%2F38a82d99-3a0c-4a47-84fc-76fff3144cda.png" alt="deepu105"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/deepu105/my-beautiful-linux-development-environment-2afc" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;My beautiful Linux development environment&lt;/h2&gt;
      &lt;h3&gt;Deepu K Sasidharan ・ Jun 16 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#linux&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#fedora&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#development&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#gnome&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
 It made me geek out about my Linux distro in a way I haven't in a while because of the routine of my work which is focusing more on delivering features than taking time to sharpen my tools. Lots of good tools and tweaks to try, in fact there is some I didn't know about so I tried them and was very surprised how awesome they are.

&lt;h1&gt;
  
  
  Projects
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://n64.icequake.net/doc/n64intro/kantan/step2/index1.html" rel="noopener noreferrer"&gt;Nintendo 64 Introductory Manual&lt;/a&gt;&lt;/strong&gt;: The N64 is without a doubt ingrained in our culture. As a kid, I had a blast playing Golden Eye with my friends. Now that I am a developer, I look back a tech that marked my childhood. This introductory manual explains how it works under the hood with great details, clear explanations and helpful schemes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/satwikkansal/wtfpython" rel="noopener noreferrer"&gt;WTF Python&lt;/a&gt;&lt;/strong&gt;: For me, I find the best way to learn things is by breaking them, literally. Python is an awesome programming language and like any tech it has its weird and unexpected behaviors. Reading these puzzling anomalies made me understand the language on a totally different angle.&lt;/p&gt;

&lt;h1&gt;
  
  
  Books
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.goodreads.com/book/show/34474869-spaceman" rel="noopener noreferrer"&gt;Spaceman: An Astronaut's Unlikely Journey to Unlock the Secrets of the Universe by Mike Massimino&lt;/a&gt;&lt;/strong&gt;: I just couldn't put the book town. I'm a fan of astronaut biographies because their journeys are full of hard work, ups and downs and adventures. You realise they are humans, they have their struggles and they kept fighting adversities. Truly inspiring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.goodreads.com/book/show/3149348-animal-farm" rel="noopener noreferrer"&gt;Animal farm by George Orwell&lt;/a&gt;&lt;/strong&gt;: I already had read 1984 and I was thrilled to read Animal Farm when I friend lended me the book. What would happen if animals on a farm made a rebellion and overthrew the owner to run the farm ? I always liked dystopian stories. There is a powerful quote in this book particularly shook me: &lt;em&gt;All animals are equal, but some animals are more equal than others&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Voila!
&lt;/h1&gt;

&lt;p&gt;If you ever read/listen/watch something in that list, tell me how you liked it.&lt;br&gt;
You can also &lt;a href="https://www.goodreads.com/user/show/80203337-jim" rel="noopener noreferrer"&gt;follow me on goodreads.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>weeklyretro</category>
      <category>discuss</category>
      <category>personalnews</category>
    </item>
    <item>
      <title>What tool can you not live without?</title>
      <dc:creator>Jean-Michel Plourde</dc:creator>
      <pubDate>Mon, 27 May 2019 19:42:01 +0000</pubDate>
      <link>https://dev.to/jmplourde/what-tool-can-you-not-live-without-14il</link>
      <guid>https://dev.to/jmplourde/what-tool-can-you-not-live-without-14il</guid>
      <description>&lt;p&gt;We all have a tool that is almost an extension of ourselves and not using it feels too weird. This thread is a great opportunity to discover new tools to power charge how you do things. &lt;/p&gt;

&lt;p&gt;What is yours ?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>productivity</category>
      <category>devtips</category>
    </item>
    <item>
      <title>Things To Expect When Attending Tech Meetups</title>
      <dc:creator>Jean-Michel Plourde</dc:creator>
      <pubDate>Wed, 22 May 2019 03:22:49 +0000</pubDate>
      <link>https://dev.to/jmplourde/your-local-meetup-has-so-much-to-offer-2ekf</link>
      <guid>https://dev.to/jmplourde/your-local-meetup-has-so-much-to-offer-2ekf</guid>
      <description>&lt;p&gt;There is nothing better than having a monthly gathering of people coming from different backgrounds that want to share their knowledge and what passionate them about a topic. It's the perfect occasion to meet new people, discover  things, find job opportunities, get inspired, etc. What is great about technologies is there is something for everyone, there is a ton of free learning material online and you can reach a lot of people from around the globe. &lt;/p&gt;

&lt;p&gt;Two years ago, I stumbled on an ad on social medias for an upcoming tech meetup in my city. The format is pretty straight forward: A nice place where you can buy food/beverages, sit around and chat. There is a period for networking/casual chats, another for attendees to present themselves then you have a couple of five or fifteen minutes presentations. Quite a simple setup yet the benefits for such minimal efforts (going outside and participating...) is just insane. &lt;/p&gt;

&lt;h1&gt;
  
  
  You will meet so many interesting people
&lt;/h1&gt;

&lt;p&gt;Before attending my first meetup, I did not have any expectations. I was greatly surprised when I realized how much it had to offer. I met a lot of new folks with unique and interesting journeys. Learning about their projects, their interests, their career path, their jobs, what makes them tick really inspired me. The setting is just perfect for expanding your contact network. You will be shocked to discover how many incredibly talented individuals there is in your community. &lt;/p&gt;

&lt;p&gt;I connected with people I never imagined I would met in this kind of events: A well-respected guy in the Erlang community who writes books and travel the world to give talks, guys who worked in silicon valley for successful startups and tech giants, a senior engineer who gives me a lot of insightful guiding advice, indie video game studio developers, professors, scientists, artists, etc. &lt;/p&gt;

&lt;h1&gt;
  
  
  No skills required
&lt;/h1&gt;

&lt;p&gt;A tech meetup should not have minimal requirements to attend except for an interest in technologies and anything related (a narrow scope for a meetup just reduce the number of opportunities). It should be clear the event is inclusive to people of all backgrounds and with all kind of skills. It is a great opportunity to promote and share the work of underrepresented groups. It could be the turning point for someone wanting to jump into the tech world, an IT newcomer to learn a ton of things or just a place to hangout for someone. &lt;/p&gt;

&lt;p&gt;My local meetup built a website and open-sourced it on GitHub so everyone is welcomed to contribute. In fact, it is a great opportunity for newcomers to make their first contribution. From time to time, they share updates through various channels to maximize the reach: Facebook, mailing lists, websites, Slack, LinkedIn, Twitter, etc. The process can be automated so it does not feel like an unpleasant task.&lt;/p&gt;

&lt;p&gt;A meetup really benefits from having its very own online place to chat and share outside of the meets. A free and easy setup such as Slack should be enough so the community can keep in touch with everyone, share and help each other out.&lt;/p&gt;

&lt;h1&gt;
  
  
  There is no such thing in my area
&lt;/h1&gt;

&lt;p&gt;You are very lucky! You could organize one and develop so many new non-tech skills. You will find nice places you never knew, learn to schedule, publicize and run an event, find interesting speakers, make friends, etc. &lt;/p&gt;

&lt;p&gt;Restaurants especially love these kinds of events because it brings clients and attendees really like having access to good food and beverages. Libraries and universities often have rooms they will happily let you use for free. Twice a year, the local meetup I attend host the event at my university where they get sponsored by the computer/video game department. &lt;/p&gt;

&lt;p&gt;Speaking of sponsors: for any company looking to hire new talents it is simply too easy. Letting attendees present themselves a bit (making small groups if the crowd is big) is a social facilitator and something head hunters are really fond of. Do not hesitate to include that in your selling pitch to companies when approaching them. The networking period, usually before and after the event, will do its magic. &lt;/p&gt;

&lt;p&gt;Ask companies to sponsor your event with money or by paying for the drinks and/or food or by hosting the event in their offices. Thank them in your opening speech and suggest to display their promotion material. Every now and then, my local meetup is sponsored by a company who will either host the event, pay for food/drinks, give swag or send their senior devs give a talk.&lt;/p&gt;

&lt;h1&gt;
  
  
  So many opportunities
&lt;/h1&gt;

&lt;p&gt;Of course, finding a job is not the sole purpose of a meetup but a great side effect. There is forming collabs, sharing interesting stuff, helping each others, mentorship, etc. By learning the journey of someone else, there is this immense sense of refreshment where everyone around feels very inspired. More than often, discovering a new discipline in tech lead to an interesting topic for a future presentation. &lt;/p&gt;

&lt;p&gt;If like me you live in a small city, knowing each other in this field is crucial. You feel like you belong to something, you can provide help and consultancy and you learn the well kept secret of your local scene. In my case, through classmates at a meetup, I met some people working at a company who later offered me an internship. Some of them already knew me so it helped for the hiring process and when I started the transition was smoother. &lt;/p&gt;

&lt;h1&gt;
  
  
  That's all folks!
&lt;/h1&gt;

&lt;p&gt;I hope you are inspired to try attending, giving a talk at or organizing such an event. Feel free to share any benefits that I might have missed, there is so much of them. Shout out to my amazing &lt;a href="https://www.facebook.com/saglacio/" rel="noopener noreferrer"&gt;local meetup SagLac IO&lt;/a&gt; who inspired me this article.&lt;/p&gt;

</description>
      <category>speaking</category>
      <category>community</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
