<?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: Pedro Campos</title>
    <description>The latest articles on DEV Community by Pedro Campos (@pcampos119104).</description>
    <link>https://dev.to/pcampos119104</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%2F782675%2F1b9ff306-5c1f-4d65-8878-79efc3aaf321.jpeg</url>
      <title>DEV Community: Pedro Campos</title>
      <link>https://dev.to/pcampos119104</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pcampos119104"/>
    <language>en</language>
    <item>
      <title>Django project - Part 5 Dev Tools</title>
      <dc:creator>Pedro Campos</dc:creator>
      <pubDate>Thu, 19 Dec 2024 19:40:19 +0000</pubDate>
      <link>https://dev.to/pcampos119104/django-project-part-5-dev-tools-2j62</link>
      <guid>https://dev.to/pcampos119104/django-project-part-5-dev-tools-2j62</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/pcampos119104/palindrome/tree/feature/part5" rel="noopener noreferrer"&gt;The source code from this part&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;In this part we are going to add a few tools to help in the development phase, like a hot reload for template when adding a tailwind class.&lt;/p&gt;

&lt;p&gt;There is more tools, but this three I judge to be essential for a development.&lt;/p&gt;

&lt;h3&gt;
  
  
  Django extension
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://django-extensions.readthedocs.io/en/latest/index.html" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;"Django Extensions is a collection of custom extensions for the Django Framework." -  Documentation.&lt;br&gt;
This lib can be used in production too.&lt;/p&gt;

&lt;p&gt;Install:&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="nv"&gt;$ &lt;/span&gt;poetry add django-extensions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the app to the INSTALLED_APPS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
    &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;django_extensions&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="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple as that, now we can use all the &lt;a href="https://django-extensions.readthedocs.io/en/latest/index.html" rel="noopener noreferrer"&gt;extensions features&lt;/a&gt; &lt;br&gt;
We can test an extension command, this prints the settings configuration variables:&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="nv"&gt;$ &lt;/span&gt;just mng print_settings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Django browser reload
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/adamchainz/django-browser-reload" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check if a template has changed and refresh the browser. It's a way for the server notify the page that something has been change and it's need to reload. Very, very useful when editing a template layout. Only works on DEBUG=True&lt;/p&gt;

&lt;p&gt;Install:&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="nv"&gt;$ &lt;/span&gt;poetry add django-browser-reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add to INSTALLED_APPS, it works only in DEBUG true:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;django_browser_reload&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;p&gt;We need to add to the url in the base urls.py endpoint for the js script talk to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;admin/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;accounts/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;allauth.urls&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;palindrome.base.urls&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__reload__/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;django_browser_reload.urls&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;p&gt;Add the middleware so the js script will be injected on the page, for the server communication and reload action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;MIDDLEWARE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;django_browser_reload.middleware.BrowserReloadMiddleware&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rebuild the image, run the container and make a change on the home.html, you will see the page reloading itself:&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="nv"&gt;$ &lt;/span&gt;just build
&lt;span class="nv"&gt;$ &lt;/span&gt;just runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Marimo
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.marimo.io/" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My favorite; it's like Jupyter Notebook, but using it for development is much better. I don't know about data science. &lt;br&gt;&lt;br&gt;
You can test a function, inspect forms, models, or whatever.&lt;/p&gt;

&lt;p&gt;It's a little more complex to install than the above libs, but it's worth it.&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="nv"&gt;$ &lt;/span&gt;poetry add marimo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need that our marimo server start in development like runserver and tailwindCSS, for that we need to edit our &lt;code&gt;start&lt;/code&gt; script to add the marimo command.&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;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; errexit
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; pipefail
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; nounset

&lt;span class="c"&gt;# Apply migrations if has new one.&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Running migrations..."&lt;/span&gt;
python manage.py migrate

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting marimo"&lt;/span&gt;
&lt;span class="nb"&gt;exec &lt;/span&gt;marimo edit &lt;span class="nt"&gt;--host&lt;/span&gt; 0.0.0.0 &lt;span class="nt"&gt;-p&lt;/span&gt; 2718 &lt;span class="nt"&gt;--no-token&lt;/span&gt; &lt;span class="nt"&gt;--headless&lt;/span&gt; &amp;amp;&amp;gt; /dev/null &amp;amp;

&lt;span class="c"&gt;# -i the input.css for some tailwind directives or pure css,&lt;/span&gt;
&lt;span class="c"&gt;# it will be added to the -o output.css in the&lt;/span&gt;
&lt;span class="c"&gt;# tailwind processing, --watch=always is the hot reload.&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting tailwindcss watcher"&lt;/span&gt;
&lt;span class="nb"&gt;exec &lt;/span&gt;npx tailwindcss &lt;span class="nt"&gt;-i&lt;/span&gt; ./palindrome/static/css/input.css &lt;span class="nt"&gt;-o&lt;/span&gt; ./palindrome/static/css/output.css &lt;span class="nt"&gt;--watch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;always &amp;amp;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting runserver"&lt;/span&gt;
&lt;span class="nb"&gt;exec &lt;/span&gt;python manage.py runserver 0.0.0.0:8000

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

&lt;/div&gt;



&lt;p&gt;We need to install &lt;code&gt;psutils&lt;/code&gt; on the container through &lt;code&gt;Dockerfile&lt;/code&gt;, It's a dependency for Marimo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Pull official base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.12.6-alpine3.20&lt;/span&gt;

&lt;span class="c"&gt;# Set working directory&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Set env variables&lt;/span&gt;

&lt;span class="c"&gt;# Don't write out pyc files&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONDONTWRITEBYTECODE 1&lt;/span&gt;
&lt;span class="c"&gt;# No buffering stdin/stdout&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONUNBUFFERED 1&lt;/span&gt;

&lt;span class="c"&gt;# update the alpine linux&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk update
&lt;span class="c"&gt;# install bash on image, could be the ash but the bash is more agnostic&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; bash
&lt;span class="c"&gt;# install npm on image&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; npm

&lt;span class="c"&gt;# NEW LINE&lt;/span&gt;
&lt;span class="c"&gt;# for psutil, a dependencie of marimo&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; gcc python3-dev musl-dev linux-headers

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

&lt;/div&gt;



&lt;p&gt;Open the marimo port on &lt;code&gt;compose.yaml&lt;/code&gt;:&lt;br&gt;
&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="c1"&gt;# The service to be created on docker&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt; 
    &lt;span class="s"&gt;# Open the port in localhost:8000 and 2718 for external access&lt;/span&gt;
    &lt;span class="s"&gt;ports&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8000:8000"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2718:2718"&lt;/span&gt; &lt;span class="c1"&gt;# marimo&lt;/span&gt;
  &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build and run:&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="nv"&gt;$ &lt;/span&gt;just build
&lt;span class="nv"&gt;$ &lt;/span&gt;just runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Marimo will be running on &lt;code&gt;localhost:2718&lt;/code&gt; and the Django app on &lt;code&gt;localhost:8000&lt;/code&gt; as usual.&lt;/p&gt;

&lt;h4&gt;
  
  
  Configure a Marimo notebook for django
&lt;/h4&gt;

&lt;p&gt;To use Django in the notebook, like models, forms, etc, we need to make some code in the first cell of all notebooks.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;local&lt;/code&gt; folder on the project for hosting the notebooks.&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="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new notebook using marimo and save in the &lt;code&gt;local&lt;/code&gt; folder as &lt;code&gt;template.py&lt;/code&gt;&lt;br&gt;&lt;br&gt;
This is the code for the &lt;code&gt;template.py&lt;/code&gt; to run the django:&lt;br&gt;
First cell to configure the django for marimo.&lt;br&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;marimo&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;mo&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;django&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="c1"&gt;# The templete it's on /local, whe need
&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/app&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DJANGO_SETTINGS_MODULE&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;palindrome.settings&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DJANGO_ALLOW_ASYNC_UNSAFE&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;true&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Second cell, is just for testing if everything is working.&lt;br&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;from&lt;/span&gt; &lt;span class="n"&gt;django.contrib.auth&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_user_model&lt;/span&gt;
&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;get_user_model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created&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="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_or_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Pedro&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;email@asdf.asdf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;user&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you google, you will find many other tools. Add as you need, not because it's shine.&lt;/p&gt;

&lt;p&gt;The next post will be about the scary deployment, buuuu.&lt;/p&gt;

</description>
      <category>marimo</category>
      <category>djangobrowserreload</category>
      <category>djangoextensions</category>
      <category>django</category>
    </item>
    <item>
      <title>Django project - Part 4 HTMX, TailwindCSS and AlpineJS</title>
      <dc:creator>Pedro Campos</dc:creator>
      <pubDate>Fri, 29 Nov 2024 12:29:18 +0000</pubDate>
      <link>https://dev.to/pcampos119104/django-project-part-4-htmx-tailwindcss-and-alpinejs-1bae</link>
      <guid>https://dev.to/pcampos119104/django-project-part-4-htmx-tailwindcss-and-alpinejs-1bae</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Now is the fun part, add HTMX, TailwindCSS, AlpineJS and change the home page to test the three libs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pcampos119104/palindrome/tree/feature/part4" rel="noopener noreferrer"&gt;The source code from this part&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Tailwind
&lt;/h3&gt;

&lt;p&gt;We are going to install the Tailwind through the container instance and then add the generated the json files to the Dockerfile, so it will be installed on the next build. This is made only once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# install npm on image&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; npm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enter on the container to install Taiwind. let's add a just command to help in the future&lt;br&gt;
justfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
# Enter in the container shell
shell:
  docker compose run --rm web sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute the command&lt;br&gt;
&lt;code&gt;$ just shell&lt;/code&gt;&lt;br&gt;
Enter on the container to install Taiwind. let's add a just command to help in the future&lt;br&gt;
justfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# npm install -D tailwindcss
# npx tailwindcss init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That will create 3 files: package.json, package-lock.json and tailwind.config.js. Let's add them to the Dockerfile and rebuild the container, except the tailwind.config.js, which will be added with the rest of the code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;...
# install npm on image
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; npm

&lt;span class="c"&gt;# tailwind stuff&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="c"&gt;# use the json to install the Tailwind&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the file &lt;code&gt;palindrome/static/css/input.css&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and config the tailwind.config.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;/**&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;import('tailwindcss').Config&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;*/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;module.exports&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;content:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"./palindrome/templates/**/*.{html,js}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"./palindrome/static/**/*.{html,js}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;theme:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;extend:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;plugins:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add STATICFILES_DIRS to the settings.py to reflect the custom directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
STATICFILES_DIRS = [BASE_DIR / 'palindrome/static']

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

&lt;/div&gt;



&lt;p&gt;Now we need to add the tailwind start on the start script, on docker/dev/start:&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 the input.css for some tailwind directives or pure css, it will be added to the -o output.css in the&lt;/span&gt;
&lt;span class="c"&gt;# tailwind processing, --watch=always is the hot reload.&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting tailwindcss watcher"&lt;/span&gt;
&lt;span class="nb"&gt;exec &lt;/span&gt;npx tailwindcss &lt;span class="nt"&gt;-i&lt;/span&gt; ./palindrome/static/css/input.css &lt;span class="nt"&gt;-o&lt;/span&gt; ./palindrome/static/css/output.css &lt;span class="nt"&gt;--watch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;always &amp;amp;


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

&lt;/div&gt;



&lt;p&gt;You can add a tailwind tag to a header, in the palindrome/templates/base/home.html, to test the configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    ...
    &lt;span class="c"&gt;&amp;lt;!-- Tailwind output file --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt;
            &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{% static 'css/output.css' %}"&lt;/span&gt;
            &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    ...
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
...

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Simple Header&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-3xl font-bold underline p-10"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Simple Tailwind header&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;hr&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  AlpineJS
&lt;/h3&gt;

&lt;p&gt;AlpineJS it's very simple, just download the lib and add to the page.&lt;/p&gt;

&lt;p&gt;Go to the &lt;a href="https://alpine.dev" rel="noopener noreferrer"&gt;alpine.dev&lt;/a&gt;, download the lib &lt;code&gt;cdn.mim.js&lt;/code&gt; in the palindrome/static/css  and change the name to &lt;code&gt;alpine.min.js&lt;/code&gt; . Should be something like this:&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="nv"&gt;$ &lt;/span&gt;wget https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mv &lt;/span&gt;cdn.min.js alpine.min.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add to the home header and write a test code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{% load static %}
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt;
            &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt;
            &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Tailwind output file --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt;
            &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{% static 'css/output.css' %}"&lt;/span&gt;
            &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script
            &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"{% static 'js/alpine.min.js' %}"&lt;/span&gt;
            &lt;span class="na"&gt;defer&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Title&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Simple Header&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-3xl font-bold underline p-10"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Simple Tailwind header&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;hr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-3xl font-bold underline pt-10 px-10"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Simple AlpineJS counter&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-data=&lt;/span&gt;&lt;span class="s"&gt;"{ count: 0 }"&lt;/span&gt;
     &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-3xl font-bold underline pb-10 px-10"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;x-on:click=&lt;/span&gt;&lt;span class="s"&gt;"count++"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Increment&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;x-text=&lt;/span&gt;&lt;span class="s"&gt;"count"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;hr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you have an increment button to test the Alpine configuration&lt;/p&gt;

&lt;h3&gt;
  
  
  HTMX
&lt;/h3&gt;

&lt;p&gt;Almost as easy as the AlpineJS, but with a little more work.&lt;br&gt;
Download the HTMX lib from &lt;a href="https://htmx.org/" rel="noopener noreferrer"&gt;HTMX website&lt;/a&gt; in the palindrome/static/js folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wget https://unpkg.com/browse/htmx.org@2.0.3/dist/htmx.min.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install an extension for django, the &lt;a href="https://django-htmx.readthedocs.io/en/latest/installation.html" rel="noopener noreferrer"&gt;django-htmx&lt;/a&gt;, it has a Middleware that provides a &lt;code&gt;request.htmx&lt;/code&gt;, which in used in htmx partial views. &lt;br&gt;
Create a View on the &lt;code&gt;base/view.py&lt;/code&gt; to respond to the HTMX call from the browser.&lt;br&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;from&lt;/span&gt; &lt;span class="n"&gt;django.http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;

&lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;htmx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;  
    A simple htmx view that return a partial html.    
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;    
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;span id=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;click-test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;HTMX is working&amp;lt;/span&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the View in the url.py&lt;br&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;from&lt;/span&gt; &lt;span class="n"&gt;django.urls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;palindrome.base.views&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HomeView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;htmx&lt;/span&gt;

&lt;span class="n"&gt;app_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;base&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HomeView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_view&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="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;htmx/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;htmx&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;htmx&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;p&gt;Create a HTMX call from the home.html.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{% load static %}  
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;  
   ...
    &lt;span class="nt"&gt;&amp;lt;script            
            &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"{% static 'js/htmx.min.js' %}"&lt;/span&gt;  
            &lt;span class="na"&gt;defer&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Title&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;  
...
&lt;span class="nt"&gt;&amp;lt;hr&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-3xl font-bold underline pt-10 px-10"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Simple HTMX&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex flex-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-blue-500 hover:bg-blue-700 text-white font-bold  
        py-2 px-4 mb-10 mx-10 rounded w-fit"&lt;/span&gt;            
            &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"{% url 'base:htmx' %}"&lt;/span&gt;  
            &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"click"&lt;/span&gt;  
            &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#click-test"&lt;/span&gt;  
            &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        Click Me!  
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"click-test"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Wraping up
&lt;/h3&gt;

&lt;p&gt;Add &lt;code&gt;/node_modules/&lt;/code&gt; to the &lt;code&gt;.gitignore&lt;/code&gt;.&lt;br&gt;
prepare for commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ just format
$ git add .
$ git commit -m 'nice description'

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

&lt;/div&gt;



</description>
      <category>htmx</category>
      <category>tailwindcss</category>
      <category>alpinejs</category>
      <category>django</category>
    </item>
    <item>
      <title>Django project - Part 2 Postgres</title>
      <dc:creator>Pedro Campos</dc:creator>
      <pubDate>Fri, 29 Nov 2024 12:28:24 +0000</pubDate>
      <link>https://dev.to/pcampos119104/django-project-part-2-postgres-2oj9</link>
      <guid>https://dev.to/pcampos119104/django-project-part-2-postgres-2oj9</guid>
      <description>&lt;p&gt;This is the part 2 of a serie of posts on how to structure a Dango project. This is the link to part 1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pcampos119104/palindrome/tree/feature/part2" rel="noopener noreferrer"&gt;The source code from this part&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Ok, we have our Django project running on docker, let's now configure the Postgres. Dispite the fact that we can &lt;a href="https://www.sqlite.org/whentouse.html" rel="noopener noreferrer"&gt;use sqlite in production&lt;/a&gt;, we are going to use the Postgres.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update compose.yaml
&lt;/h3&gt;

&lt;p&gt;We are going to add a new service for postgres and configure as a dependence to our web service.&lt;br&gt;
&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="c1"&gt;# The service to be created on docker&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;# Only start this image after postgres&lt;/span&gt;
    &lt;span class="s"&gt;depends_on&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;

  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# The postgres image from docker hub&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16.2&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;palindrome_postgres&lt;/span&gt;
    &lt;span class="c1"&gt;# The volume that will be used by this image&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;palindrome_postgres_data:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class="c1"&gt;# Open the port in localhost:5432 for external access&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5432:5432"&lt;/span&gt;
    &lt;span class="c1"&gt;# db NAME, USER and HOST are by default 'postgres', just configure the password here&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Volume used by the postgres container&lt;/span&gt;
    &lt;span class="na"&gt;palindrome_postgres_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update .env file
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
# DB_PASSWORD is configured on compose.yaml, the others is the image default 
DB_NAME=postgres
DB_USER=postgres
DB_PASSWORD=postgres
DB_HOST=postgres
DB_PORT=5432

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update the .env.template for first setup refenrece
&lt;/h3&gt;

&lt;p&gt;Add the template on git&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;... 
DB_NAME=
DB_USER=
DB_PASSWORD=
DB_HOST=
DB_PORT=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add psycopg on Poetry
&lt;/h3&gt;

&lt;p&gt;Add psycopg, a python adapter to postgres, so we can talk to the database.&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="nv"&gt;$ &lt;/span&gt;poetry add psycopg[binary]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update settings.py
&lt;/h3&gt;

&lt;p&gt;Configure settings.py for using postgres and remove the sqlite.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;DATABASES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# Remove the sqlite configuration
&lt;/span&gt;    &lt;span class="c1"&gt;# 'default': {
&lt;/span&gt;    &lt;span class="c1"&gt;#     'ENGINE': 'django.db.backends.sqlite3',
&lt;/span&gt;    &lt;span class="c1"&gt;#     'NAME': BASE_DIR / 'db.sqlite3',
&lt;/span&gt;    &lt;span class="c1"&gt;# }
&lt;/span&gt;
    &lt;span class="c1"&gt;# Add the postgres configuration
&lt;/span&gt;    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;default&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ENGINE&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;django.db.backends.postgresql&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="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DB_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;USER&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DB_USER&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;PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DB_PASSWORD&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;HOST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DB_HOST&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;PORT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DB_PORT&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="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update README.md
&lt;/h3&gt;

&lt;p&gt;Yes, this is part of the job. Just add the psycopg&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Palindrome project&lt;/span&gt;

Project used to explain my view on a django project architecture, explained on my &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;series of posts&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://dev.to/pcampos119104/django-project-setup-part-1-2e7a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="gu"&gt;## Tools, libs, etc. Some time related files.&lt;/span&gt;

Versions on Poetry.
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Python&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://www.python.org/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; Programming languange
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;django-environ&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://django-environ.readthedocs.io&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; Manage .envs in Django
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Poetry&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://python-poetry.org/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; Python packaging and dependency management
&lt;span class="p"&gt;    -&lt;/span&gt; poetry.lock
&lt;span class="p"&gt;    -&lt;/span&gt; pyproject.toml
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Django&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://www.djangoproject.com/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; Web framework written in Python
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Docker&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://www.docker.com/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; Manage containers for dev environment
&lt;span class="p"&gt;    -&lt;/span&gt; compose.yaml
&lt;span class="p"&gt;    -&lt;/span&gt; compose/dev/Dockerfile
&lt;span class="p"&gt;    -&lt;/span&gt; compose/dev/start
&lt;span class="p"&gt;    -&lt;/span&gt; .env
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Just&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://just.systems/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; encapsulate commands for easier use
&lt;span class="p"&gt;    -&lt;/span&gt; justfile
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;psycopg&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://www.psycopg.org/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; Python adapter for Postgres # &amp;lt;-- new line

&lt;span class="gu"&gt;## Dev environment setup&lt;/span&gt;
&lt;span class="p"&gt;
1.&lt;/span&gt; Install Just, Docker and Poetry(opcional).
&lt;span class="p"&gt;2.&lt;/span&gt; Copie .env.example to .env, no need for edtion. 
&lt;span class="p"&gt;3.&lt;/span&gt; &lt;span class="sb"&gt;`$ just build`&lt;/span&gt;

&lt;span class="gu"&gt;## Run the server for development&lt;/span&gt;
&lt;span class="p"&gt;
1.&lt;/span&gt; Certified that docker is up and running
&lt;span class="p"&gt;2.&lt;/span&gt; &lt;span class="sb"&gt;`$ just runserver`&lt;/span&gt;

You can access on http://0.0.0.0:8000/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rebuild an run the project
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;just build
&lt;span class="nv"&gt;$ &lt;/span&gt;just runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Django project - Part 3 Continuous Integration</title>
      <dc:creator>Pedro Campos</dc:creator>
      <pubDate>Thu, 17 Oct 2024 12:06:10 +0000</pubDate>
      <link>https://dev.to/pcampos119104/django-project-part-3-continuous-integration-8c5</link>
      <guid>https://dev.to/pcampos119104/django-project-part-3-continuous-integration-8c5</guid>
      <description>&lt;p&gt;This is the part 3 of a serie of posts on how to structure a Django project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;If you worked on a project with at least one other developer, you know the problems of sharing the same codebase on Github. One of them (and the worst one) is when someone updates a new feature and breaks a working code. Tests solve most of that problem, but we need a way to force the tests to run before merging to the &lt;code&gt;main&lt;/code&gt; and refuse any changes that don't pass the test. That is the GitHub Action.&lt;/p&gt;

&lt;p&gt;We are going to add a test and make that run on Github action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pcampos119104/palindrome/tree/feature/part3" rel="noopener noreferrer"&gt;The finished source code from this part&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;We will add pytest and pytest-django, write a test, make it run in the container for local validation, and run on github actions to protect the &lt;code&gt;main&lt;/code&gt; branch. I'll assume you are familiar with &lt;a href="https://docs.pytest.org/en/stable/" rel="noopener noreferrer"&gt;pytest&lt;/a&gt;, &lt;a href="https://pytest-django.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;pytest-django&lt;/a&gt; and &lt;a href="https://github.com/" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Install pytest and pytest-django
&lt;/h3&gt;

&lt;p&gt;We add the pytest dependence in the dev group on poetry, we don't need that in production, but only in development.&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="nv"&gt;$ &lt;/span&gt;poetry add &lt;span class="nt"&gt;--group&lt;/span&gt; dev pytest pytest-django
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes a separate block on poetry&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nn"&gt;[tool.poetry.dependencies]&lt;/span&gt;  
&lt;span class="py"&gt;python&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^3.11"&lt;/span&gt;  
&lt;span class="py"&gt;Django&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^5.0.3"&lt;/span&gt;  
&lt;span class="py"&gt;django-environ&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^0.11.2"&lt;/span&gt;  
&lt;span class="py"&gt;django-allauth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^65.0.2"&lt;/span&gt;  
&lt;span class="py"&gt;psycopg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="py"&gt;extras&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"binary"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^3.2.3"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;tool.poetry.group.dev.dependencies&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  
&lt;span class="py"&gt;pytest&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^8.3.3"&lt;/span&gt;  
&lt;span class="py"&gt;pytest-django&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^4.9.0"&lt;/span&gt;
&lt;span class="err"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Dockerfile we already have the flag to install the dev dependeces.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;...
# Copy our poetry artifacts to the building image  
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; poetry.lock pyproject.toml /app  &lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;pip3 &lt;span class="nb"&gt;install &lt;/span&gt;poetry  
&lt;span class="c"&gt;# No need to create a virtual env in the container  &lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;poetry config virtualenvs.create &lt;span class="nb"&gt;false&lt;/span&gt;  
&lt;span class="c"&gt;# Install dependencies with the dev dependecies  &lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;poetry &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--with&lt;/span&gt; dev
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create a test for the homepage
&lt;/h3&gt;

&lt;p&gt;Let's make a test for our homepage, just check if return a 200. We need that for our test on Github Action. &lt;br&gt;
Delete this file, if exists:&lt;br&gt;
/project/app/test.py&lt;/p&gt;

&lt;p&gt;Create the file:&lt;br&gt;
/project/app/tests/&lt;strong&gt;init&lt;/strong&gt;.py&lt;br&gt;
/project/app/tests/test_views.py&lt;/p&gt;

&lt;p&gt;In test_views.py&lt;br&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;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.urls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestBaseViews&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_home&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        Test if home page works
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="c1"&gt;# I'll assume you know how to configure an url in django 
&lt;/span&gt;        &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&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="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;base:home&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Add a pytest configuration block on pyproject.toml.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="c"&gt;# Configurations for pystest  &lt;/span&gt;
&lt;span class="nn"&gt;[tool.pytest.ini_options]&lt;/span&gt;  
&lt;span class="py"&gt;DJANGO_SETTINGS_MODULE&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"project.settings"&lt;/span&gt;  

&lt;span class="c"&gt;# find the tests:  &lt;/span&gt;
&lt;span class="py"&gt;python_files&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"test_*.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*_test.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"testing/python/*.py"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add Ruff
&lt;/h3&gt;

&lt;p&gt;Let's add ruff, configure it, and add the command in justfile.&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="nv"&gt;$ &lt;/span&gt;poetry add &lt;span class="nt"&gt;--group&lt;/span&gt; dev ruff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the configuration to pyproject.toml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="c"&gt;# Configuration for Ruff&lt;/span&gt;
&lt;span class="nn"&gt;[tool.ruff]&lt;/span&gt;
&lt;span class="c"&gt;# 80 it's the default but nowadays the common sense it's 120.&lt;/span&gt;
&lt;span class="py"&gt;line-length&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;
&lt;span class="c"&gt;# Show an enumeration of all fixed lint violations&lt;/span&gt;
&lt;span class="py"&gt;show-fixes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="nn"&gt;[tool.ruff.lint]&lt;/span&gt;
&lt;span class="c"&gt;# https://docs.astral.sh/ruff/linter/#rule-selection&lt;/span&gt;
&lt;span class="c"&gt;# which linter to run&lt;/span&gt;
&lt;span class="py"&gt;select&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c"&gt;# isort&lt;/span&gt;
    &lt;span class="s"&gt;"I"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c"&gt;# pycodestyle&lt;/span&gt;
    &lt;span class="s"&gt;"E"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;[tool.ruff.format]&lt;/span&gt;
&lt;span class="c"&gt;# https://docs.astral.sh/ruff/settings/#format_quote-style&lt;/span&gt;
&lt;span class="c"&gt;# There is a lot discussion on the internet about it, I use single quotes.&lt;/span&gt;
&lt;span class="py"&gt;quote-style&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"single"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create an alias on Just
&lt;/h3&gt;

&lt;p&gt;To simplify running the tests we need to update our justfile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
# Run the tests
test:
  docker compose run --rm web ruff check
  docker compose run --rm web pytest

# Run Ruff for fix errors
format:
  docker compose run --rm web ruff check --fix
  docker compose run --rm web ruff format

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;test&lt;/code&gt; command runs a &lt;code&gt;ruff check&lt;/code&gt; without changing the code. This is going to be used on github actions for code quality, as the &lt;code&gt;pytest&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;format&lt;/code&gt; command is used on development to fix the code before the Pull Request. Always run before the last commit before the PR&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the .github files
&lt;/h3&gt;

&lt;p&gt;Now we need to run this &lt;code&gt;just test&lt;/code&gt; on github every time we make a Pull Request to &lt;code&gt;main&lt;/code&gt; branch, or &lt;code&gt;dev&lt;/code&gt; branch, that is up to you or the company to choose a process.&lt;/p&gt;

&lt;p&gt;First, create the files.&lt;br&gt;
.github/workflows/ci.yml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# The name that will be show in Github&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI&lt;/span&gt;

&lt;span class="c1"&gt;# Enable Buildkit and let compose use it to speed up image building&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;DOCKER_BUILDKIT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;COMPOSE_DOCKER_CLI_BUILD&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;# This jobs will run when a pull request is made for the main branch.&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;


&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# This is the job that is going to run the `just test` command.&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

      &lt;span class="c1"&gt;# git clone the repo in this ubuntu runner&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout Code Repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;

      &lt;span class="c1"&gt;# Add just command for running the tests&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Add just&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;extractions/setup-just@v2&lt;/span&gt;

      &lt;span class="c1"&gt;# The .env is on .gitignore, so it's needed to be created here&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create env file&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;touch .env&lt;/span&gt;
          &lt;span class="s"&gt;echo DEBUG=true &amp;gt; .env&lt;/span&gt;
          &lt;span class="s"&gt;echo SECRET_KEY=0m8HMl9crvazciYYD58znKmuQaQAFT8q &amp;gt;&amp;gt; .env&lt;/span&gt;
          &lt;span class="s"&gt;echo ENVIRONMENT=dev &amp;gt;&amp;gt; .env&lt;/span&gt;
          &lt;span class="s"&gt;echo ALLOWED_HOSTS=* &amp;gt;&amp;gt; .env&lt;/span&gt;
          &lt;span class="s"&gt;echo DB_NAME=postgres &amp;gt;&amp;gt; .env&lt;/span&gt;
          &lt;span class="s"&gt;echo DB_USER=postgres &amp;gt;&amp;gt; .env&lt;/span&gt;
          &lt;span class="s"&gt;echo DB_PASSWORD=postgres &amp;gt;&amp;gt; .env&lt;/span&gt;
          &lt;span class="s"&gt;echo DB_HOST=postgres &amp;gt;&amp;gt; .env&lt;/span&gt;
          &lt;span class="s"&gt;echo DB_PORT=5432 &amp;gt;&amp;gt; .env&lt;/span&gt;
          &lt;span class="s"&gt;cat .env&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build the Stack&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker compose build&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run DB Migrations&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker compose run --rm web python manage.py migrate&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;just test&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Tear down the Stack&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker compose down&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run the test locally
&lt;/h3&gt;

&lt;p&gt;Rebuild the image to update the changes on pyproject.toml&lt;br&gt;
&lt;code&gt;just build&lt;/code&gt;&lt;br&gt;
Run the tests, should works.&lt;br&gt;
&lt;code&gt;just test&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Config the Github branchs.
&lt;/h3&gt;

&lt;p&gt;Protect your &lt;code&gt;main&lt;/code&gt; branch from accepting a direct push, configure the PR to only pass if the Action concludes without errors, so it will only accept updates through PR after running and passing the tests.&lt;br&gt;
The example below is a simple one:&lt;br&gt;
On github go to the Settings &amp;gt; Rules &amp;gt; Rulesets &amp;gt; New rulesets &amp;gt; New branch rulesets&lt;br&gt;
The important configuration is Target branches, choose to Include by pattern and add &lt;code&gt;main&lt;/code&gt;. Another configuration is Branch rules, check at least Require a pull request before merging.&lt;br&gt;
This is a complex subject you can dive into if you want to.&lt;/p&gt;
&lt;h3&gt;
  
  
  Open a Pull Request for test
&lt;/h3&gt;

&lt;p&gt;Make the push to your working branch, like &lt;code&gt;new_feature&lt;/code&gt;, and open a Pull Request to &lt;code&gt;main&lt;/code&gt;. You will see on the PR page the GitHub action working next to the merge/rebase button and if you click on it, it will open the logs of the Actions, in 'Run tests' should be printed something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Run just test
docker compose run --rm web ruff check
 Container palindrome_postgres  Running
All checks passed!
docker compose run --rm web pytest
 Container palindrome_postgres  Running
============================= test session starts ==============================
platform linux -- Python 3.12.6, pytest-8.3.3, pluggy-1.5.0
django: version: 5.0.3, settings: palindrome.settings (from ini)
rootdir: /app
configfile: pyproject.toml
plugins: django-4.9.0
collected 1 item
palindrome/base/tests/test_view.py .                                     [100%]
============================== 1 passed in 0.20s ===============================
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can merge the PR to the &lt;code&gt;main&lt;/code&gt; now.&lt;/p&gt;

&lt;p&gt;The CI is configured, we can work as adults now.&lt;/p&gt;

</description>
      <category>django</category>
      <category>githubactions</category>
      <category>ci</category>
      <category>testing</category>
    </item>
    <item>
      <title>Django project - Part 2 Postgres</title>
      <dc:creator>Pedro Campos</dc:creator>
      <pubDate>Fri, 04 Oct 2024 15:13:42 +0000</pubDate>
      <link>https://dev.to/pcampos119104/django-project-part-2-postgres-57cl</link>
      <guid>https://dev.to/pcampos119104/django-project-part-2-postgres-57cl</guid>
      <description>&lt;p&gt;This is the part 2 of a serie of posts on how to structure a Dango project. This is the &lt;a href="https://dev.to/pcampos119104/django-project-setup-part-1-2e7a"&gt;link to part 1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pcampos119104/palindrome/tree/feature/part1" rel="noopener noreferrer"&gt;The source code from this part&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Ok, we have our Django project running on docker, let's now configure the Postgres. Dispite the fact that we can &lt;a href="https://www.sqlite.org/whentouse.html" rel="noopener noreferrer"&gt;use sqlite in production&lt;/a&gt;, we are going to use the Postgres.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update compose.yaml
&lt;/h3&gt;

&lt;p&gt;We are going to add a new service for postgres and configure as a dependence to our web service.&lt;br&gt;
&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="c1"&gt;# The service to be created on docker&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;# Only start this image after postgres&lt;/span&gt;
    &lt;span class="s"&gt;depends_on&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;

  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# The postgres image from docker hub&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16.2&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;palindrome_postgres&lt;/span&gt;
    &lt;span class="c1"&gt;# The volume that will be used by this image&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;palindrome_postgres_data:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class="c1"&gt;# Open the port in localhost:5432 for external access&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5432:5432"&lt;/span&gt;
    &lt;span class="c1"&gt;# db NAME, USER and HOST are by default 'postgres', just configure the password here&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Volume used by the postgres container&lt;/span&gt;
    &lt;span class="na"&gt;palindrome_postgres_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update .env file
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
# DB_PASSWORD is configured on compose.yaml, the others is the image default 
DB_NAME=postgres
DB_USER=postgres
DB_PASSWORD=postgres
DB_HOST=postgres
DB_PORT=5432

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update the .env.template for first setup refenrece
&lt;/h3&gt;

&lt;p&gt;Add the template on git&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;... 
DB_NAME=
DB_USER=
DB_PASSWORD=
DB_HOST=
DB_PORT=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add psycopg on Poetry
&lt;/h3&gt;

&lt;p&gt;Add psycopg, a python adapter to postgres, so we can talk to the database.&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="nv"&gt;$ &lt;/span&gt;poetry add psycopg[binary]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update settings.py
&lt;/h3&gt;

&lt;p&gt;Configure settings.py for using postgres and remove the sqlite.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;DATABASES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# Remove the sqlite configuration
&lt;/span&gt;    &lt;span class="c1"&gt;# 'default': {
&lt;/span&gt;    &lt;span class="c1"&gt;#     'ENGINE': 'django.db.backends.sqlite3',
&lt;/span&gt;    &lt;span class="c1"&gt;#     'NAME': BASE_DIR / 'db.sqlite3',
&lt;/span&gt;    &lt;span class="c1"&gt;# }
&lt;/span&gt;
    &lt;span class="c1"&gt;# Add the postgres configuration
&lt;/span&gt;    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;default&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ENGINE&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;django.db.backends.postgresql&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="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DB_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;USER&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DB_USER&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;PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DB_PASSWORD&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;HOST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DB_HOST&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;PORT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DB_PORT&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="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update README.md
&lt;/h3&gt;

&lt;p&gt;Yes, this is part of the job. Just add the psycopg&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Palindrome project&lt;/span&gt;

Project used to explain my view on a django project architecture, explained on my &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;series of posts&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://dev.to/pcampos119104/django-project-setup-part-1-2e7a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="gu"&gt;## Tools, libs, etc. Some time related files.&lt;/span&gt;

Versions on Poetry.
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Python&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://www.python.org/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; Programming languange
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;django-environ&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://django-environ.readthedocs.io&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; Manage .envs in Django
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Poetry&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://python-poetry.org/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; Python packaging and dependency management
&lt;span class="p"&gt;    -&lt;/span&gt; poetry.lock
&lt;span class="p"&gt;    -&lt;/span&gt; pyproject.toml
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Django&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://www.djangoproject.com/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; Web framework written in Python
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Docker&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://www.docker.com/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; Manage containers for dev environment
&lt;span class="p"&gt;    -&lt;/span&gt; compose.yaml
&lt;span class="p"&gt;    -&lt;/span&gt; compose/dev/Dockerfile
&lt;span class="p"&gt;    -&lt;/span&gt; compose/dev/start
&lt;span class="p"&gt;    -&lt;/span&gt; .env
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Just&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://just.systems/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; encapsulate commands for easier use
&lt;span class="p"&gt;    -&lt;/span&gt; justfile
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;psycopg&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://www.psycopg.org/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; Python adapter for Postgres # &amp;lt;-- new line

&lt;span class="gu"&gt;## Dev environment setup&lt;/span&gt;
&lt;span class="p"&gt;
1.&lt;/span&gt; Install Just, Docker and Poetry(opcional).
&lt;span class="p"&gt;2.&lt;/span&gt; Copie .env.example to .env, no need for edtion. 
&lt;span class="p"&gt;3.&lt;/span&gt; &lt;span class="sb"&gt;`$ just build`&lt;/span&gt;

&lt;span class="gu"&gt;## Run the server for development&lt;/span&gt;
&lt;span class="p"&gt;
1.&lt;/span&gt; Certified that docker is up and running
&lt;span class="p"&gt;2.&lt;/span&gt; &lt;span class="sb"&gt;`$ just runserver`&lt;/span&gt;

You can access on http://0.0.0.0:8000/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rebuild an run the project
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;just build
&lt;span class="nv"&gt;$ &lt;/span&gt;just runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should be able to create a superuser normally now. Just run &lt;code&gt;just mng createsuperuser&lt;/code&gt;&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>django</category>
      <category>docker</category>
    </item>
    <item>
      <title>Django project - Part 1 Docker</title>
      <dc:creator>Pedro Campos</dc:creator>
      <pubDate>Mon, 30 Sep 2024 19:03:19 +0000</pubDate>
      <link>https://dev.to/pcampos119104/django-project-setup-part-1-2e7a</link>
      <guid>https://dev.to/pcampos119104/django-project-setup-part-1-2e7a</guid>
      <description>&lt;h4&gt;
  
  
  Introduction
&lt;/h4&gt;

&lt;p&gt;This is &lt;strong&gt;advanced content&lt;/strong&gt; on how I structure my django for a new project. I've skipped the basic startproject and startapp, there are plenty of content on that. So, the project started with Poetry, django, .gitignore, git repo, simple home page and django-allauth.&lt;/p&gt;

&lt;p&gt;This is for small/medium applications, like a startup/ or small business. After the business grows, you need to evolve the architecture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pcampos119104/palindromo/tree/feature/part1" rel="noopener noreferrer"&gt;The source code from this part&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;In this series of posts we are going to set up a django project for an efficient development environment(in my opinion). In the end, we are going to have a setup with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.python.org/" rel="noopener noreferrer"&gt;Python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://python-poetry.org/" rel="noopener noreferrer"&gt;Poetry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.djangoproject.com/" rel="noopener noreferrer"&gt;Django&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://django-environ.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;django-environ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://django-extensions.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;django-extentions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.allauth.org/en/latest/" rel="noopener noreferrer"&gt;django-allauth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://just.systems/" rel="noopener noreferrer"&gt;Just&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marimo.io/" rel="noopener noreferrer"&gt;marimo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.astral.sh/ruff/" rel="noopener noreferrer"&gt;ruff&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://django-debug-toolbar.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;django-debug-toolbar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.pytest.org/en/8.0.x/" rel="noopener noreferrer"&gt;pytest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pytest-django.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;pytest-django&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunicorn.org/" rel="noopener noreferrer"&gt;gunicorn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://whitenoise.readthedocs.io/en/stable/django.html" rel="noopener noreferrer"&gt;whitenoise&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.sentry.io/platforms/python/" rel="noopener noreferrer"&gt;sentry-sdk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/adamchainz/django-browser-reload" rel="noopener noreferrer"&gt;django-browser-reload&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://htmx.org/" rel="noopener noreferrer"&gt;htmx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://django-htmx.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;django-htmx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;tailwindcss&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://alpinejs.dev/" rel="noopener noreferrer"&gt;alpinejs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;Postgres&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;README.md, yes it's one of the pillars.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;git&lt;/a&gt;, &lt;a href="https://github.com/" rel="noopener noreferrer"&gt;github &lt;/a&gt;and &lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;github actions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Deploy the app on &lt;a href="https://fly.io/" rel="noopener noreferrer"&gt;fly.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Deploy the postgres on &lt;a href="https://neon.tech/" rel="noopener noreferrer"&gt;neon.tech&lt;/a&gt; 
... and possible more&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  This is a serie with 8 parts, this is the first one.
&lt;/h4&gt;

&lt;p&gt;Take note that not all parts are published yet, it's an ongoing work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: Docker setup. Simple dockerization, even before Postgres configuration.&lt;/li&gt;
&lt;li&gt;Part 2: Postgres. Configure Postgres to be used in Django and the configuration in the compose.yml.&lt;/li&gt;
&lt;li&gt;Part 3: Continuos integration. Add Pytest, ruff, and run it on github action on PR to prevent broken code and ensure code quality.&lt;/li&gt;
&lt;li&gt;Part 4: HTMX, TailwindCSS and AlpineJS. Configure HTMX in the project, there is a few tricks for django. Configure nodejs on the container and the tailwind watcher in development. Configure AlpineJS, more simple than HTMX and TailwindCSS.&lt;/li&gt;
&lt;li&gt;Part 5: Dev Tools, Add tools like Marimo, django-extensions, django-toolbar, django-browser-reload.&lt;/li&gt;
&lt;li&gt;Part 6: Continuos Delivery. Configure staticfiles, in this case with whitenoise. Deploy on &lt;a href="https://fly.io/" rel="noopener noreferrer"&gt;fly.io&lt;/a&gt; and &lt;a href="https://neon.tech/" rel="noopener noreferrer"&gt;neon.tech&lt;/a&gt;  and, of course, using github action for that.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 1 Docker setup
&lt;/h2&gt;

&lt;p&gt;I'm not going to explain what Docker is and why you should use it. I assume you know the basics.&lt;/p&gt;

&lt;h3&gt;
  
  
  What do we need to start?
&lt;/h3&gt;

&lt;p&gt;For now, install the &lt;a href="https://docs.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; and &lt;a href="https://just.systems/man/en/" rel="noopener noreferrer"&gt;Just&lt;/a&gt; and, of course, our django project with already a simple home page, in this case using poetry.&lt;br&gt;&lt;br&gt;
&lt;code&gt;Just&lt;/code&gt; encapsulate commands for easier use. Here is an example of a justfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build:
  docker compose build
runserver:
  docker compose up --build
mng command:
  docker compose run --rm web python manage.py {{command}}
sh:
  docker compose run --rm web sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then we can run&lt;br&gt;
&lt;code&gt;$ just build&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ just runserver&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ just mng createsuperuser&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Ok, let's create the files.
&lt;/h2&gt;

&lt;p&gt;Don't worry, I'll explain each one of them.&lt;br&gt;&lt;br&gt;
First create all those empty files:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;docker/dev/Dockerfile - Steps to create the image
&lt;/li&gt;
&lt;li&gt;docker/dev/start - Bash file to start the container through entrypoint on docker-compose
&lt;/li&gt;
&lt;li&gt;compose.yml - Simple orchestration of the containers, just one for now.
&lt;/li&gt;
&lt;li&gt;.env - Our environment variables injected into the container by compose.yml, add on .gitignore.
&lt;/li&gt;
&lt;li&gt;.env.template - Same key as .env but with empty values for reference, this one stays on the repo.
&lt;/li&gt;
&lt;li&gt;justfile - Wrapper for the commands.
&lt;/li&gt;
&lt;li&gt;README.md - You know why, don't play dead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Install django-environ
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://django-environ.readthedocs.io/en/latest/install.html" rel="noopener noreferrer"&gt;django-environ&lt;/a&gt; - To manage env var&lt;br&gt;
&lt;code&gt;$ poetry add django-environ&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Dockerfile file
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Pull official base image
FROM python:3.12.6-alpine3.19
# Set working directory in the image
WORKDIR /app

# Set env variables
# Don't write out pyc files
ENV PYTHONDONTWRITEBYTECODE 1
# No buffering stdin/stdout
ENV PYTHONUNBUFFERED 1

# update the alpine linux
RUN apk update
# install bash on image, alpine uses ash, but we have a script that uses bash
RUN apk add --no-cache bash

# Copy our poetry artifacts to the building image
COPY poetry.lock pyproject.toml /app

RUN pip3 install poetry
# No need to create a virtual env in the container
RUN poetry config virtualenvs.create false
# Install dependencies with the dev dependecies
RUN poetry install --with dev

# Copy start bash script with the instruction on how to start and serve Django.
COPY ./docker/dev/start /start
RUN sed -i 's/\r$//g' /start
RUN chmod +x /start

# Copy all project files to the image.
COPY . /app

# Not used, the app are going to be running through docker compose
CMD ["manage.py", "runserver", "0.0.0.0:8000"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  start
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

set -o errexit
set -o pipefail
set -o nounset

# Apply migrations if has a new one.
echo "Running migrations..."
python manage.py migrate

# Start the server for development
echo "Starting runserver"
exec python manage.py runserver 0.0.0.0:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  compose.yml
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
# The service to be created on docker
  web:
    # how to build the image for this service
    build:
      # Where is the directory to work on...
      context: .
      # ... with what Dockerfile instructions
      dockerfile: ./docker/dev/Dockerfile
    image: palindrome_local
    container_name: palindrome_local
    volumes:
      - .:/app:z
    env_file:
      - .env
    ports:
      - "8000:8000"
    # The bash file to execute, the one created above and added to Dockerfile
    command: /start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  justfile
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# List all just commands
default:
  just --list

# Build the docker image
build:
  docker compose build

# Run the Django app in development mode
runserver:
  docker compose up --build

# Run manage.py inside the container like createsuperuser
mng command:
  docker compose run --rm web python manage.py {{command}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  .env
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEBUG=True
SECRET_KEY='dev-key-(g!%yi9at$h6$*sz**^ld6+j)r305*=6i^3ho1bq=z@8c#b7ml'
ALLOWED_HOSTS=*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  .env.template
&lt;/h3&gt;

&lt;p&gt;Can be the same for now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEBUG=True
SECRET_KEY='dev-key-(g!%yi9at$h6$*sz**^ld6+j)r305*=6i^3ho1bq=z@8c#b7ml'
ALLOWED_HOSTS=*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update settings.py
&lt;/h3&gt;

&lt;p&gt;For now just these settings, the value come from .env file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import environ

...
env = environ.Env()
SECRET_KEY = env('SECRET_KEY')
DEBUG = env.bool('DEBUG', False)
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS')
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Update README.md
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Palindrome project

Project used to explain my view on a django project architecture

## Tools, libs, etc. Some time related files.

Versions on Poetry.

- [Python](https://www.python.org/) Programming languange
- [django-environ](https://django-environ.readthedocs.io) Manage .envs in Django
- [Poetry](https://python-poetry.org/) Python packaging and dependency management
    - poetry.lock
    - pyproject.toml
- [Django](https://www.djangoproject.com/) Web framework written in Python
- [Docker](https://www.docker.com/) Manage containers for dev environment
    - compose.yaml
    - compose/dev/Dockerfile
    - compose/dev/start
    - .env
- [Just](https://just.systems/) encapsulate commands for easier use
    - justfile

## Dev environment setup

1. Install Just, Docker and Poetry(opcional).
2. Copy .env.example to .env, no need for edition. 
3. Certified that docker is up and running
4. $ just build

## Run the server for development

1. Certified that docker is up and running
2. $ just runserver

You can access on http://0.0.0.0:8000/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build and run
&lt;/h3&gt;

&lt;p&gt;Well, with your docker running and everything setup, we can now go to &lt;strong&gt;part 2: Postgres&lt;/strong&gt;, configure Postgres on settings, docker etc.&lt;/p&gt;

&lt;p&gt;Good luck and remember what happened in 1971.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



</description>
      <category>django</category>
      <category>docker</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
