<?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: Bharat Solanke</title>
    <description>The latest articles on DEV Community by Bharat Solanke (@bharat_solanke_8e45411fa6).</description>
    <link>https://dev.to/bharat_solanke_8e45411fa6</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%2F3257842%2Fd67dd88f-d8a5-4ee7-b70d-584c8816d168.jpg</url>
      <title>DEV Community: Bharat Solanke</title>
      <link>https://dev.to/bharat_solanke_8e45411fa6</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bharat_solanke_8e45411fa6"/>
    <language>en</language>
    <item>
      <title>Database Decoded: Navigating Queries in FastAPI &amp; Django (ORM vs. Raw SQL) 🚀</title>
      <dc:creator>Bharat Solanke</dc:creator>
      <pubDate>Tue, 24 Feb 2026 08:58:04 +0000</pubDate>
      <link>https://dev.to/bharat_solanke_8e45411fa6/database-decoded-navigating-queries-in-fastapi-django-orm-vs-raw-sql-37jl</link>
      <guid>https://dev.to/bharat_solanke_8e45411fa6/database-decoded-navigating-queries-in-fastapi-django-orm-vs-raw-sql-37jl</guid>
      <description>&lt;p&gt;So, you're building a web application or an API, and you need to talk to a database. Naturally, you're probably thinking about Python, and two of the biggest names that pop up are FastAPI and Django. But when it comes to database interactions, these frameworks approach things a little differently.&lt;/p&gt;

&lt;p&gt;Let's dive deep into the world of Object-Relational Mappers (ORMs) like SQLAlchemy (often paired with FastAPI) and Django's built-in ORM, compared to the raw power of SQL queries. By the end, you'll have a "foolproof" understanding of when and why to use each.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Big Three: SQLAlchemy, Django ORM, and Raw SQL 📊
&lt;/h2&gt;

&lt;p&gt;Imagine your database as a library. SQL is the librarian's native tongue. ORMs are like highly skilled translators who convert your Python requests into SQL, talk to the librarian, and then convert the answer back into Python objects you can easily use.&lt;/p&gt;

&lt;p&gt;Let's look at the core differences:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;SQLAlchemy (with FastAPI) ⚡&lt;/th&gt;
&lt;th&gt;Django ORM (with Django) 🚂&lt;/th&gt;
&lt;th&gt;Raw SQL 🔥&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Philosophy&lt;/td&gt;
&lt;td&gt;Explicit &amp;amp; Flexible. You're the architect! Define your models, sessions, and engines.&lt;/td&gt;
&lt;td&gt;"Batteries-Included" &amp;amp; Implicit. Django handles much of the plumbing behind the scenes.&lt;/td&gt;
&lt;td&gt;Direct &amp;amp; Unfiltered. No abstraction layer, full manual control.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flexibility&lt;/td&gt;
&lt;td&gt;Highly Adaptable. Works brilliantly with FastAPI, Flask, or even as a standalone library.&lt;/td&gt;
&lt;td&gt;Tightly Integrated. Best suited for the Django ecosystem.&lt;/td&gt;
&lt;td&gt;Universally Compatible. If a system speaks SQL, you can use it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async Support&lt;/td&gt;
&lt;td&gt;First-Class! Designed for async/await from the ground up, especially with asyncpg.&lt;/td&gt;
&lt;td&gt;Evolving. Traditionally synchronous, but making great strides with async views and ORM methods.&lt;/td&gt;
&lt;td&gt;Driver Dependent. Relies on the database driver (e.g., asyncpg for PostgreSQL).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query Style&lt;/td&gt;
&lt;td&gt;session.execute(select(User))&lt;/td&gt;
&lt;td&gt;User.objects.filter(is_active=True)&lt;/td&gt;
&lt;td&gt;SELECT * FROM users WHERE is_active = TRUE;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best For&lt;/td&gt;
&lt;td&gt;High-performance microservices, APIs, complex data manipulation.&lt;/td&gt;
&lt;td&gt;Rapid development of full-stack web apps, CMS, admin panels.&lt;/td&gt;
&lt;td&gt;Extreme optimization, complex analytics, database-specific features.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Why Django's ORM Feels Different (and often magical ✨)
&lt;/h2&gt;

&lt;p&gt;If you're migrating from Django to FastAPI, you'll immediately notice that database interaction feels more "hands-on" with FastAPI and SQLAlchemy.&lt;/p&gt;

&lt;h3&gt;
  
  
  In Django:
&lt;/h3&gt;

&lt;p&gt;You define your models, and Django takes care of creating database tables (migrations), providing a &lt;code&gt;.objects&lt;/code&gt; manager for queries, and even integrating with the admin panel. It's often "magic" that just works. ✨&lt;/p&gt;

&lt;p&gt;&lt;code&gt;User.objects.all()&lt;/code&gt; is a simple, intuitive way to fetch all users.&lt;/p&gt;

&lt;h3&gt;
  
  
  In FastAPI with SQLAlchemy:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You explicitly define your models (&lt;code&gt;Base.metadata.create_all(engine)&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;You manually manage &lt;code&gt;SessionLocal&lt;/code&gt; to get a database session and use &lt;code&gt;session.add()&lt;/code&gt;, &lt;code&gt;session.commit()&lt;/code&gt;, &lt;code&gt;session.refresh()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Fetching data involves constructs like &lt;code&gt;select(User)&lt;/code&gt;, &lt;code&gt;session.execute()&lt;/code&gt;, and &lt;code&gt;scalars()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Trade-off:&lt;/strong&gt; Django gives you incredible speed of development for many common web applications. FastAPI + SQLAlchemy gives you unparalleled speed of execution, especially in concurrent, asynchronous environments, and granular control over every aspect of your data interaction. It's about choosing the right tool for your project's specific needs! 🛠️&lt;/p&gt;




&lt;h2&gt;
  
  
  Optimization Masterclass: Squeezing Out Every Millisecond ⏱️
&lt;/h2&gt;

&lt;p&gt;No matter which path you choose, neglecting optimization is a fast track to slow APIs and frustrated users. Here are the golden rules:&lt;/p&gt;




&lt;h3&gt;
  
  
  🚀 For SQLAlchemy (FastAPI):
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Crush N+1 Queries (Your #1 Enemy! 😡)
&lt;/h4&gt;

&lt;p&gt;This is where you fetch a list of items (e.g., 10 users) and then, in a loop, fetch related data for each item (e.g., 10 separate queries for each user's address). That's 11 queries instead of 1!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Use &lt;code&gt;joinedload()&lt;/code&gt; (for foreign key relationships, like a SQL JOIN) or &lt;code&gt;subqueryload()&lt;/code&gt; (for collections, often using a subquery or IN clause).&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="c1"&gt;# Bad (N+1 example) 😱
&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&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;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;users&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;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;street&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Each .address access hits the DB again!
&lt;/span&gt;
&lt;span class="c1"&gt;# Good (Joined Load) ✅
&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&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;options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;joinedload&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;address&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;users&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;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;street&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# All addresses loaded in one go!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Async Bonus:&lt;/strong&gt; For asyncpg drivers, &lt;code&gt;selectinload()&lt;/code&gt; is often the champion for efficiently loading relationships.&lt;/p&gt;

&lt;h4&gt;
  
  
  Proper Session Management
&lt;/h4&gt;

&lt;p&gt;Always ensure your database sessions are opened and closed correctly. FastAPI's Dependency Injection system is a superhero here! 💪&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;sqlalchemy.orm&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.database&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SessionLocal&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SessionLocal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/items/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🚂 For Django ORM:
&lt;/h3&gt;

&lt;h4&gt;
  
  
  select_related() for Foreign Keys (SQL JOINs)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Bad (N+1 again!) 😱
&lt;/span&gt;&lt;span class="n"&gt;users&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;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;users&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;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Good (select_related) ✅
&lt;/span&gt;&lt;span class="n"&gt;users&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;select_related&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;profile&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;users&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;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  prefetch_related() for Many-to-Many &amp;amp; Reverse Foreign Keys
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Post&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;prefetch_related&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;comments&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;comment&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  .only() or .defer() to Limit Fields
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;users&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;only&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;username&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;email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🔥 For Raw SQL Queries (The "Break Glass" Option):
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Parameterized Queries (SQL Injection Shield! 🛡️)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Bad (SQL Injection waiting to happen!) 🚨
&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_params&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Good (Parameterized - safe and fast!) ✅
&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_params&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id = :user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&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;user_id&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_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Connection Pooling
&lt;/h4&gt;

&lt;p&gt;Creating a new database connection for every request is inefficient. Use a connection pool to reuse existing connections. Both SQLAlchemy and asyncpg offer robust pooling options. ♻️&lt;/p&gt;

&lt;h4&gt;
  
  
  Explicit Columns (SELECT column1, column2 vs. SELECT *)
&lt;/h4&gt;

&lt;p&gt;Only select the columns you absolutely need to reduce data transfer and processing overhead.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Hybrid Approach: Your Foolproof Strategy 💡
&lt;/h2&gt;

&lt;p&gt;Here’s the ultimate secret: Don't pick just one! The most robust and high-performing applications skillfully blend these approaches.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Start with the ORM (SQLAlchemy or Django ORM)
&lt;/h3&gt;

&lt;p&gt;For the vast majority of your standard CRUD operations and business logic, the ORM is your best friend. It offers rapid development, better security, and easier maintenance. This should be your default. 🤝&lt;/p&gt;

&lt;h3&gt;
  
  
  🧐 Optimize with ORM-specific Tools
&lt;/h3&gt;

&lt;p&gt;Before resorting to raw SQL, exhaust all the ORM's optimization features (e.g., joinedload, select_related, .only()). Many performance issues can be solved here.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚡ Deploy Raw SQL Strategically
&lt;/h3&gt;

&lt;p&gt;If, and only if, you hit a genuine performance bottleneck or need to execute a hyper-specific, complex query (like advanced reporting, graph traversals, or unique database features) that's clunky or slow with the ORM, then drop down to raw SQL.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With SQLAlchemy, you can execute raw SQL through your existing session using &lt;code&gt;session.execute(text("YOUR COMPLEX SQL HERE"))&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In Django, &lt;code&gt;Model.objects.raw()&lt;/code&gt; or &lt;code&gt;connection.cursor()&lt;/code&gt; allow raw SQL execution.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;By embracing this balanced, hybrid approach, you'll gain the best of all worlds: the development speed and safety of ORMs, combined with the raw power and optimization potential of direct SQL when it truly matters.&lt;/p&gt;

&lt;p&gt;Happy coding, and may your databases always be fast and furious! 🚀🏎️💨&lt;/p&gt;

</description>
      <category>sql</category>
      <category>sqlalchemy</category>
      <category>orm</category>
      <category>django</category>
    </item>
    <item>
      <title>🔴 Redis: Complete Beginner to Advanced Guide (With Real Lessons From Major Attacks)</title>
      <dc:creator>Bharat Solanke</dc:creator>
      <pubDate>Thu, 05 Feb 2026 16:41:36 +0000</pubDate>
      <link>https://dev.to/bharat_solanke_8e45411fa6/redis-complete-beginner-to-advanced-guide-with-real-lessons-from-major-attacks-58h4</link>
      <guid>https://dev.to/bharat_solanke_8e45411fa6/redis-complete-beginner-to-advanced-guide-with-real-lessons-from-major-attacks-58h4</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In modern backend development, performance and scalability are critical. Applications today serve millions of users, handle real-time updates, and process massive data quickly. Traditional databases alone often struggle to meet these speed requirements.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Redis&lt;/strong&gt; comes into the picture.&lt;/p&gt;

&lt;p&gt;Redis is widely used by companies like Twitter, GitHub, StackOverflow, and many large-scale SaaS platforms to handle caching, real-time messaging, session storage, and more.&lt;/p&gt;

&lt;p&gt;In this blog, we will cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What Redis is&lt;/li&gt;
&lt;li&gt;Why Redis is so fast&lt;/li&gt;
&lt;li&gt;Redis data structures&lt;/li&gt;
&lt;li&gt;Redis Streams&lt;/li&gt;
&lt;li&gt;Redis Geospatial&lt;/li&gt;
&lt;li&gt;Real-world use cases&lt;/li&gt;
&lt;li&gt;Alternatives to Redis&lt;/li&gt;
&lt;li&gt;Major Redis security attacks&lt;/li&gt;
&lt;li&gt;Production security lessons&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  What is Redis?
&lt;/h1&gt;

&lt;p&gt;Redis stands for &lt;strong&gt;Remote Dictionary Server&lt;/strong&gt;. It is an &lt;strong&gt;open-source, in-memory data store&lt;/strong&gt; used as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cache&lt;/li&gt;
&lt;li&gt;Database&lt;/li&gt;
&lt;li&gt;Message broker&lt;/li&gt;
&lt;li&gt;Real-time data store&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike traditional databases that store data on disk, Redis stores data in &lt;strong&gt;RAM (memory)&lt;/strong&gt; which makes it extremely fast.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Redis is So Fast
&lt;/h1&gt;

&lt;p&gt;Redis achieves high performance because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It stores data in memory.&lt;/li&gt;
&lt;li&gt;It uses optimized data structures.&lt;/li&gt;
&lt;li&gt;It is single-threaded which avoids locking overhead.&lt;/li&gt;
&lt;li&gt;It supports efficient network communication.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Redis can process millions of operations per second.&lt;/p&gt;




&lt;h1&gt;
  
  
  Redis Data Storage Model
&lt;/h1&gt;

&lt;p&gt;Redis stores data using &lt;strong&gt;key-value pairs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user:1001 → {"name": "Bharat", "role": "Engineer"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Redis Data Types
&lt;/h1&gt;

&lt;p&gt;Redis is more powerful than a simple key-value store because it supports multiple data structures.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Strings
&lt;/h2&gt;

&lt;p&gt;Used for caching, tokens, and counters.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Lists
&lt;/h2&gt;

&lt;p&gt;Ordered collection used for queues.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Sets
&lt;/h2&gt;

&lt;p&gt;Unordered unique values.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Sorted Sets
&lt;/h2&gt;

&lt;p&gt;Used for leaderboards and ranking systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Hashes
&lt;/h2&gt;

&lt;p&gt;Store objects like user profiles.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Streams
&lt;/h2&gt;

&lt;p&gt;Used for event-driven messaging systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Bitmaps and HyperLogLog
&lt;/h2&gt;

&lt;p&gt;Used for analytics and counting unique events.&lt;/p&gt;




&lt;h1&gt;
  
  
  Redis Persistence
&lt;/h1&gt;

&lt;p&gt;Although Redis stores data in memory, it supports persistence:&lt;/p&gt;

&lt;h3&gt;
  
  
  RDB (Snapshotting)
&lt;/h3&gt;

&lt;p&gt;Stores periodic snapshots of data.&lt;/p&gt;

&lt;h3&gt;
  
  
  AOF (Append Only File)
&lt;/h3&gt;

&lt;p&gt;Logs every write operation.&lt;/p&gt;

&lt;p&gt;Both can be used together for better reliability.&lt;/p&gt;




&lt;h1&gt;
  
  
  Redis Streams (Event Streaming)
&lt;/h1&gt;

&lt;p&gt;Redis Streams is a data structure designed for real-time event processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Time-ordered message storage&lt;/li&gt;
&lt;li&gt;Consumer groups&lt;/li&gt;
&lt;li&gt;Reliable message processing&lt;/li&gt;
&lt;li&gt;Message replay capability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Notification systems&lt;/li&gt;
&lt;li&gt;Chat applications&lt;/li&gt;
&lt;li&gt;Background job processing&lt;/li&gt;
&lt;li&gt;Microservices communication&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Redis Geospatial
&lt;/h1&gt;

&lt;p&gt;Redis provides geospatial indexing for location-based applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Supports
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Store coordinates&lt;/li&gt;
&lt;li&gt;Distance calculation&lt;/li&gt;
&lt;li&gt;Radius-based location search&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ride-sharing apps&lt;/li&gt;
&lt;li&gt;Food delivery services&lt;/li&gt;
&lt;li&gt;Fleet tracking&lt;/li&gt;
&lt;li&gt;Store locator applications&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Real-World Redis Use Cases
&lt;/h1&gt;

&lt;p&gt;Redis is widely used in production for:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Caching
&lt;/h3&gt;

&lt;p&gt;Reducing database load by storing frequently accessed data.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Session Storage
&lt;/h3&gt;

&lt;p&gt;Used by frameworks like Django and FastAPI.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Rate Limiting
&lt;/h3&gt;

&lt;p&gt;Prevent API abuse.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Real-Time Analytics
&lt;/h3&gt;

&lt;p&gt;Track user activity and counters.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Pub/Sub Messaging
&lt;/h3&gt;

&lt;p&gt;Used in live notification systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Distributed Locks
&lt;/h3&gt;

&lt;p&gt;Prevent race conditions in distributed systems.&lt;/p&gt;




&lt;h1&gt;
  
  
  Alternatives to Redis
&lt;/h1&gt;

&lt;p&gt;Although Redis is powerful, there are other tools available:&lt;/p&gt;

&lt;h2&gt;
  
  
  Memcached
&lt;/h2&gt;

&lt;p&gt;Simple in-memory cache.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apache Kafka
&lt;/h2&gt;

&lt;p&gt;Event streaming platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hazelcast
&lt;/h2&gt;

&lt;p&gt;In-memory distributed computing grid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apache Ignite
&lt;/h2&gt;

&lt;p&gt;Enterprise in-memory data grid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aerospike
&lt;/h2&gt;

&lt;p&gt;High-performance NoSQL database.&lt;/p&gt;

&lt;p&gt;Each alternative serves different use cases depending on scale and architecture.&lt;/p&gt;




&lt;h1&gt;
  
  
  Major Redis Security Attacks
&lt;/h1&gt;

&lt;p&gt;One of the most important lessons from Redis history comes from large-scale cyberattacks caused by misconfigured Redis servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Smominru Cryptomining Attack
&lt;/h2&gt;

&lt;p&gt;This was one of the largest attacks exploiting unsecured Redis instances.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Happened?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Thousands of Redis servers were exposed to the internet.&lt;/li&gt;
&lt;li&gt;Many had no password protection.&lt;/li&gt;
&lt;li&gt;Attackers scanned open Redis ports.&lt;/li&gt;
&lt;li&gt;Malware was installed to mine cryptocurrency.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Impact
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Over 500,000 servers infected.&lt;/li&gt;
&lt;li&gt;Massive cloud infrastructure cost.&lt;/li&gt;
&lt;li&gt;System slowdowns and crashes.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Redis Ransomware Attacks
&lt;/h2&gt;

&lt;p&gt;Another major attack involved deleting Redis databases and replacing them with ransom messages.&lt;/p&gt;

&lt;p&gt;Attackers executed commands like:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This command deletes all data stored in Redis.&lt;/p&gt;

&lt;p&gt;Victims were asked to pay cryptocurrency to recover their data.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why These Attacks Happened
&lt;/h1&gt;

&lt;p&gt;Redis itself was not vulnerable. The real issue was configuration mistakes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis exposed directly to the internet&lt;/li&gt;
&lt;li&gt;No authentication enabled&lt;/li&gt;
&lt;li&gt;Running Redis with root privileges&lt;/li&gt;
&lt;li&gt;No firewall protection&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Production Security Lessons
&lt;/h1&gt;

&lt;p&gt;Every backend developer using Redis must follow these practices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable Authentication
&lt;/h2&gt;

&lt;p&gt;Always configure strong passwords.&lt;/p&gt;

&lt;h2&gt;
  
  
  Restrict Network Access
&lt;/h2&gt;

&lt;p&gt;Redis should only be accessible inside private networks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Firewall Rules
&lt;/h2&gt;

&lt;p&gt;Allow Redis connections only from trusted servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disable Dangerous Commands
&lt;/h2&gt;

&lt;p&gt;Commands like FLUSHALL should be restricted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Access Control Lists (ACL)
&lt;/h2&gt;

&lt;p&gt;Modern Redis versions support role-based access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Never Run Redis as Root
&lt;/h2&gt;

&lt;p&gt;This prevents attackers from gaining system-level control.&lt;/p&gt;




&lt;h1&gt;
  
  
  When Should You Use Redis?
&lt;/h1&gt;

&lt;p&gt;Redis is ideal for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High-speed caching&lt;/li&gt;
&lt;li&gt;Real-time messaging&lt;/li&gt;
&lt;li&gt;Session storage&lt;/li&gt;
&lt;li&gt;Leaderboards&lt;/li&gt;
&lt;li&gt;Event streaming&lt;/li&gt;
&lt;li&gt;Rate limiting&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  When Redis May Not Be the Best Choice
&lt;/h1&gt;

&lt;p&gt;Redis may not be suitable when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Long-term data storage is required&lt;/li&gt;
&lt;li&gt;Large analytical queries are needed&lt;/li&gt;
&lt;li&gt;Complex relational data is required&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Redis is one of the most powerful tools available for backend developers. It significantly improves performance, enables real-time features, and simplifies distributed system design.&lt;/p&gt;

&lt;p&gt;However, Redis must be used responsibly. Many historical attacks occurred not because Redis was insecure, but because it was deployed without proper configuration.&lt;/p&gt;

&lt;p&gt;Understanding Redis functionality along with its security best practices ensures safe and efficient system design.&lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Redis is more than just a cache. It is a versatile real-time data platform used across modern applications.&lt;/p&gt;

&lt;p&gt;By learning Redis fundamentals, advanced features like Streams and Geospatial indexing, and production security practices, developers can build highly scalable and reliable systems.&lt;/p&gt;

&lt;p&gt;If you are working in backend development, mastering Redis is a valuable skill that directly impacts system performance and scalability.&lt;/p&gt;




&lt;p&gt;If you found this blog helpful, feel free to share it and connect with me for more backend and system design content.&lt;/p&gt;

</description>
      <category>redis</category>
      <category>advanced</category>
      <category>beginners</category>
    </item>
    <item>
      <title>GitHub Is Not Just About Commands: How to Manage It Properly Like a Professional</title>
      <dc:creator>Bharat Solanke</dc:creator>
      <pubDate>Mon, 02 Feb 2026 09:43:06 +0000</pubDate>
      <link>https://dev.to/bharat_solanke_8e45411fa6/github-is-not-just-about-commands-how-to-manage-it-properly-like-a-professional-3l9e</link>
      <guid>https://dev.to/bharat_solanke_8e45411fa6/github-is-not-just-about-commands-how-to-manage-it-properly-like-a-professional-3l9e</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7oro3mk8ivtq0vk11tob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7oro3mk8ivtq0vk11tob.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;br&gt;
When most developers think of GitHub, they think of commands like &lt;code&gt;git init&lt;/code&gt;, &lt;code&gt;git add&lt;/code&gt;, &lt;code&gt;git commit&lt;/code&gt;, and &lt;code&gt;git push&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But the truth is this: &lt;strong&gt;GitHub is not about commands at all.&lt;/strong&gt; Commands are just tools. GitHub is really about &lt;strong&gt;discipline, structure, communication, and long-term thinking&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you treat GitHub as only a place to push code, your project will eventually become messy, difficult to maintain, and painful to scale. If you treat it as a &lt;strong&gt;system&lt;/strong&gt;, it becomes one of your strongest engineering skills.&lt;/p&gt;

&lt;p&gt;This blog explains how to manage GitHub properly, especially when starting a project from scratch.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjdem0wuios71ttc17rs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjdem0wuios71ttc17rs.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. GitHub Is a Project Timeline, Not a Backup Folder
&lt;/h2&gt;

&lt;p&gt;Many developers use GitHub like cloud storage:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Let me just push whatever I have.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This mindset causes problems very quickly.&lt;/p&gt;

&lt;p&gt;Every commit you make becomes a &lt;strong&gt;permanent part of your project’s timeline&lt;/strong&gt;. Anyone reading your repository later (including your future self) should be able to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why something was added&lt;/li&gt;
&lt;li&gt;When it was added&lt;/li&gt;
&lt;li&gt;How the project evolved&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good GitHub management tells a clear story of how the project was built.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Branches Represent Intent, Not Just Code
&lt;/h2&gt;

&lt;p&gt;A branch is not just another copy of code. A branch answers one important question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What problem am I solving here?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is why professional projects follow structured branching patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;main&lt;/code&gt; → stable, release-ready code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;develop&lt;/code&gt; → active development and integration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;feature/*&lt;/code&gt; → one specific responsibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;feature/authentication&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;feature/database-setup&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;feature/workflow-engine&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even an empty branch has value because it clearly defines intent and future direction.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Never Treat &lt;code&gt;main&lt;/code&gt; as a Playground
&lt;/h2&gt;

&lt;p&gt;One of the most important rules in GitHub management is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Never commit directly to the &lt;code&gt;main&lt;/code&gt; branch.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;main&lt;/code&gt; branch represents trust. It should always be stable, readable, and deployable.&lt;/p&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do all work in feature branches&lt;/li&gt;
&lt;li&gt;Merge completed work into &lt;code&gt;develop&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Merge into &lt;code&gt;main&lt;/code&gt; only when the code is stable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Following this rule alone significantly improves code quality and project stability.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3t1wfn4yz0f0gb62mue1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3t1wfn4yz0f0gb62mue1.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Small, Meaningful Commits Matter
&lt;/h2&gt;

&lt;p&gt;Poor commit messages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;done&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;final&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fix&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;changes&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good commit messages explain intent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;chore: add gitignore for env and virtual environment&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;feat: setup database connection&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fix: handle token expiration logic&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good commit message answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What changed, and why?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If your commit cannot answer this clearly, it is probably doing too much.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0pgz5lldqh7pzrnd8ght.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0pgz5lldqh7pzrnd8ght.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. GitHub Is Also About What You Do NOT Commit
&lt;/h2&gt;

&lt;p&gt;Professional GitHub usage is not only about what you push, but also about what you intentionally ignore.&lt;/p&gt;

&lt;p&gt;Things that should almost never be committed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.env&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;Virtual environments (&lt;code&gt;venv&lt;/code&gt;, &lt;code&gt;.venv&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Secrets and credentials&lt;/li&gt;
&lt;li&gt;Machine-specific configuration files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commit &lt;code&gt;.env.example&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ignore real &lt;code&gt;.env&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;Inject secrets using environment variables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach keeps repositories secure, portable, and conflict-free.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Empty Branches Are Not a Problem
&lt;/h2&gt;

&lt;p&gt;Many developers think branches must contain code to be useful. This is incorrect.&lt;/p&gt;

&lt;p&gt;Empty branches are &lt;strong&gt;planning tools&lt;/strong&gt;. They help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define a roadmap&lt;/li&gt;
&lt;li&gt;Enable parallel development&lt;/li&gt;
&lt;li&gt;Avoid rushed architectural decisions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Creating branches like &lt;code&gt;feature/database-setup&lt;/code&gt; or &lt;code&gt;feature/authentication&lt;/code&gt; early makes the project easier to manage later.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. GitHub Is a Communication Tool
&lt;/h2&gt;

&lt;p&gt;Your GitHub repository communicates far more than just code. It shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How you think&lt;/li&gt;
&lt;li&gt;How you plan work&lt;/li&gt;
&lt;li&gt;How you structure systems&lt;/li&gt;
&lt;li&gt;How seriously you treat engineering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recruiters, teammates, and future maintainers often judge a project by its Git history before reading the code itself.&lt;/p&gt;

&lt;p&gt;Clean branches and meaningful commits silently demonstrate professionalism.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. GitHub Scales With Your Project
&lt;/h2&gt;

&lt;p&gt;As projects grow, complexity increases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More features&lt;/li&gt;
&lt;li&gt;More contributors&lt;/li&gt;
&lt;li&gt;More environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without proper GitHub management, this leads to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frequent merge conflicts&lt;/li&gt;
&lt;li&gt;Bug-prone releases&lt;/li&gt;
&lt;li&gt;Difficult rollbacks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Strong Git discipline early makes scaling smoother and less stressful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;GitHub is not about memorizing commands. Anyone can learn Git commands in a day.&lt;/p&gt;

&lt;p&gt;What truly matters is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thinking before committing&lt;/li&gt;
&lt;li&gt;Structuring work clearly&lt;/li&gt;
&lt;li&gt;Respecting the future of your project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When used properly, GitHub becomes more than a tool—it becomes a reflection of your engineering maturity.&lt;/p&gt;

</description>
      <category>github</category>
      <category>developermindset</category>
      <category>softwareengineering</category>
      <category>management</category>
    </item>
    <item>
      <title>😲 Most People Don’t Know You Can Log In by Copying a Password Hash 🔐 (And Why It’s NOT a Bug)</title>
      <dc:creator>Bharat Solanke</dc:creator>
      <pubDate>Tue, 06 Jan 2026 07:20:24 +0000</pubDate>
      <link>https://dev.to/bharat_solanke_8e45411fa6/most-people-dont-know-you-can-log-in-by-copying-a-password-hash-and-why-its-not-a-bug-2ljg</link>
      <guid>https://dev.to/bharat_solanke_8e45411fa6/most-people-dont-know-you-can-log-in-by-copying-a-password-hash-and-why-its-not-a-bug-2ljg</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzte4cec12g79oyaymn5j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzte4cec12g79oyaymn5j.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;😮 &lt;strong&gt;Most people don’t even know this is possible.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you are an admin or have database access, you can &lt;strong&gt;copy your own hashed password&lt;/strong&gt;, paste it into another user’s record, and then &lt;strong&gt;log in as that user using your password&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;⚡ When people discover this for the first time, the reaction is usually shock:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Wait… I didn’t know this was possible. That sounds like a huge security issue!”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;😰 It feels like something is very wrong.&lt;/p&gt;

&lt;p&gt;✅ In reality, this behavior is &lt;strong&gt;expected&lt;/strong&gt;, &lt;strong&gt;intentional&lt;/strong&gt;, and actually proves that the password system is working correctly.&lt;/p&gt;

&lt;p&gt;This article explains — from absolute basics — why this happens, what is really going on under the hood, and why it is &lt;strong&gt;not&lt;/strong&gt; a security flaw.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔰 Let’s Start From Absolute Basics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What Is a Password?
&lt;/h3&gt;

&lt;p&gt;A password is a &lt;strong&gt;secret known only to the user&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A secure system must:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Verify the password&lt;/li&gt;
&lt;li&gt;❌ Never store it in readable form&lt;/li&gt;
&lt;li&gt;❌ Never be able to retrieve it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a system can read your password later, it is already insecure.&lt;/p&gt;




&lt;h2&gt;
  
  
  ❌ The Most Common Wrong Assumption
&lt;/h2&gt;

&lt;p&gt;Many people believe:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The system stores my password and checks it during login.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is &lt;strong&gt;false&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A secure system &lt;strong&gt;never stores passwords&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔐 Then What Does the System Store?
&lt;/h2&gt;

&lt;p&gt;Instead of storing the password, systems store a &lt;strong&gt;hash&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A hash is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A one‑way mathematical transformation&lt;/li&gt;
&lt;li&gt;Impossible to reverse&lt;/li&gt;
&lt;li&gt;Always the same for the same input (with the same salt)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Password: admin@123
Hash:     X9A7f...Qm==
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can recreate the hash again — but you &lt;strong&gt;cannot get the password back&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Hashing Is NOT Encryption (Very Important)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Encryption&lt;/th&gt;
&lt;th&gt;Hashing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reversible&lt;/td&gt;
&lt;td&gt;One‑way&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Has a key&lt;/td&gt;
&lt;td&gt;No key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can be decrypted&lt;/td&gt;
&lt;td&gt;Cannot be reversed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Used for data&lt;/td&gt;
&lt;td&gt;Used for passwords&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Passwords must be &lt;strong&gt;hashed&lt;/strong&gt;, not encrypted.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Modern Frameworks Store Passwords (Example: Django)
&lt;/h2&gt;

&lt;p&gt;When a user is created:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The password is taken&lt;/li&gt;
&lt;li&gt;A random &lt;strong&gt;salt&lt;/strong&gt; is added&lt;/li&gt;
&lt;li&gt;A strong algorithm (PBKDF2 / bcrypt / Argon2) is applied&lt;/li&gt;
&lt;li&gt;The process is repeated thousands of times&lt;/li&gt;
&lt;li&gt;Only the final hash is stored&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example stored value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pbkdf2_sha256$870000$salt$hash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This value is &lt;strong&gt;not the password&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔄 How Login Actually Works
&lt;/h2&gt;

&lt;p&gt;When a user logs in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User enters a password&lt;/li&gt;
&lt;li&gt;The system:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Uses the same algorithm&lt;/li&gt;
&lt;li&gt;Uses the same salt&lt;/li&gt;
&lt;li&gt;Hashes the entered password

&lt;ol&gt;
&lt;li&gt;Compares the result with the stored hash&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If both hashes match → login succeeds.&lt;/p&gt;

&lt;p&gt;👉 The password is &lt;strong&gt;never read&lt;/strong&gt;, &lt;strong&gt;never retrieved&lt;/strong&gt;, &lt;strong&gt;never decrypted&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Now the Part That Confuses Most People
&lt;/h2&gt;

&lt;p&gt;As an admin, you may notice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can see hashed passwords in the database&lt;/li&gt;
&lt;li&gt;You copy &lt;strong&gt;your own hash&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You paste it into another user’s password field&lt;/li&gt;
&lt;li&gt;You log in as that user using &lt;strong&gt;your password&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;And it works&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This feels alarming.&lt;/p&gt;

&lt;p&gt;Many conclude:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Hashing must be broken.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It isn’t.&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 What Actually Happened (Key Insight)
&lt;/h2&gt;

&lt;p&gt;You did &lt;strong&gt;not&lt;/strong&gt; discover the other user’s password.&lt;/p&gt;

&lt;p&gt;You did this instead:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;You replaced their password with your password.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now both users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have the same hash&lt;/li&gt;
&lt;li&gt;Have the same password&lt;/li&gt;
&lt;li&gt;Use the same authentication secret&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  A Simple Real‑World Analogy
&lt;/h2&gt;

&lt;p&gt;Think of a password hash as a &lt;strong&gt;lock&lt;/strong&gt;, not a key.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You didn’t copy someone’s key&lt;/li&gt;
&lt;li&gt;You replaced their lock with your lock&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If two doors use the same lock, the same key opens both.&lt;/p&gt;

&lt;p&gt;That does &lt;strong&gt;not&lt;/strong&gt; mean the lock was cracked.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛡️ Why This Is NOT a Security Bug
&lt;/h2&gt;

&lt;p&gt;This behavior exists because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Passwords cannot be recovered&lt;/li&gt;
&lt;li&gt;Authentication works via &lt;strong&gt;hash comparison&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Admins must be able to reset access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this &lt;em&gt;didn’t&lt;/em&gt; work, the system would be trying to decrypt passwords — which would be a serious security flaw.&lt;/p&gt;

&lt;p&gt;So ironically:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This behavior proves the system is designed correctly.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What This Does NOT Mean
&lt;/h2&gt;

&lt;p&gt;This does NOT mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Passwords can be decrypted&lt;/li&gt;
&lt;li&gt;❌ Hashes are reversible&lt;/li&gt;
&lt;li&gt;❌ Frameworks like Django are insecure&lt;/li&gt;
&lt;li&gt;❌ Admins can see real passwords&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It only means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Hashes define access&lt;/li&gt;
&lt;li&gt;✅ Replacing a hash replaces the password&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚨 Why You Should NEVER Do This in Production
&lt;/h2&gt;

&lt;p&gt;Even though it works, manually copying hashes is &lt;strong&gt;bad practice&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Risks include:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Account takeover&lt;/li&gt;
&lt;li&gt;No audit trail&lt;/li&gt;
&lt;li&gt;Insider abuse&lt;/li&gt;
&lt;li&gt;Compliance violations&lt;/li&gt;
&lt;li&gt;Legal exposure&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Correct Way for Admins to Handle Passwords
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use password reset flows&lt;/li&gt;
&lt;li&gt;Set temporary passwords&lt;/li&gt;
&lt;li&gt;Force password change on next login&lt;/li&gt;
&lt;li&gt;Use built‑in admin tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Never:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copy hashes&lt;/li&gt;
&lt;li&gt;Share credentials&lt;/li&gt;
&lt;li&gt;Bypass reset mechanisms&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Hashed Passwords Are Still the Best Approach
&lt;/h2&gt;

&lt;p&gt;Even if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A database is leaked&lt;/li&gt;
&lt;li&gt;Backups are stolen&lt;/li&gt;
&lt;li&gt;Admin access is compromised&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Attackers still:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cannot read passwords&lt;/li&gt;
&lt;li&gt;Cannot reverse hashes&lt;/li&gt;
&lt;li&gt;Must brute‑force each account individually&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Banks use hashing&lt;/li&gt;
&lt;li&gt;Enterprises use hashing&lt;/li&gt;
&lt;li&gt;Frameworks use hashing by default&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧠 Final Takeaway
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Authentication systems never retrieve passwords.&lt;br&gt;
They only compare hashes.&lt;br&gt;
Copying a hash replaces a password — it does not reveal it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once this concept is understood, most password‑related confusion disappears.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this useful, share it — because this misunderstanding is far more common than it should be.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>password</category>
      <category>hashing</category>
      <category>security</category>
      <category>programming</category>
    </item>
    <item>
      <title>2025: Building Real Backend Systems, Not Just Writing Code</title>
      <dc:creator>Bharat Solanke</dc:creator>
      <pubDate>Tue, 23 Dec 2025 10:09:14 +0000</pubDate>
      <link>https://dev.to/bharat_solanke_8e45411fa6/2025-building-real-backend-systems-not-just-writing-code-ehn</link>
      <guid>https://dev.to/bharat_solanke_8e45411fa6/2025-building-real-backend-systems-not-just-writing-code-ehn</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmgmqdpr5k7gex85h4ojn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmgmqdpr5k7gex85h4ojn.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
Some years add experience to your resume.&lt;br&gt;&lt;br&gt;
Some years change how you &lt;strong&gt;think as an engineer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For me, &lt;strong&gt;2025&lt;/strong&gt; was that year. 🌱&lt;/p&gt;

&lt;p&gt;This wasn’t about experimenting with side projects or following tutorials. It was about &lt;strong&gt;building real backend systems&lt;/strong&gt;, handling production issues, and understanding what it truly means to own software that people depend on.&lt;/p&gt;




&lt;h2&gt;
  
  
  From Writing Code to Building Systems 🧠
&lt;/h2&gt;

&lt;p&gt;A major part of my 2025 journey involved building, stabilizing, and enhancing a &lt;strong&gt;dynamic logbook and workflow management system&lt;/strong&gt; using &lt;strong&gt;Python, Django, PostgreSQL, and Docker&lt;/strong&gt; 🐍🐘🐳.&lt;/p&gt;

&lt;p&gt;What made this system challenging was that it wasn’t static.&lt;/p&gt;

&lt;p&gt;Logbook templates—sections, tables, and fields—were &lt;strong&gt;generated at runtime&lt;/strong&gt;, and corresponding &lt;strong&gt;PostgreSQL tables were created dynamically&lt;/strong&gt; based on configuration.&lt;/p&gt;

&lt;p&gt;This meant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No hardcoded schemas
&lt;/li&gt;
&lt;li&gt;No predefined models for every table
&lt;/li&gt;
&lt;li&gt;Full control over runtime validations and inserts
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It pushed me beyond standard CRUD development and into &lt;strong&gt;real backend engineering&lt;/strong&gt;, where architectural decisions directly impact reliability and data integrity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Designing Workflows That Demand Accountability 🔄
&lt;/h2&gt;

&lt;p&gt;The system supported &lt;strong&gt;section-wise logbook submissions&lt;/strong&gt;, each following a strict lifecycle:&lt;/p&gt;

&lt;p&gt;➡️ Submit&lt;br&gt;&lt;br&gt;
➡️ Review&lt;br&gt;&lt;br&gt;
➡️ Approve&lt;br&gt;&lt;br&gt;
➡️ Reject  &lt;/p&gt;

&lt;p&gt;Each transition required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Role-based access control
&lt;/li&gt;
&lt;li&gt;Strong validations
&lt;/li&gt;
&lt;li&gt;Complete traceability
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To support this, I implemented &lt;strong&gt;audit logging, event tracking, and follow-up mechanisms&lt;/strong&gt;, ensuring that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every action is recorded
&lt;/li&gt;
&lt;li&gt;Every change has a user and timestamp
&lt;/li&gt;
&lt;li&gt;Every decision is traceable
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This reinforced an important lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In real-world systems, &lt;strong&gt;traceability is not optional—it’s foundational&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  When Databases Push Back 🗄️
&lt;/h2&gt;

&lt;p&gt;Dynamic systems quickly expose weaknesses—especially in databases.&lt;/p&gt;

&lt;p&gt;A significant portion of my time went into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fixing data consistency issues
&lt;/li&gt;
&lt;li&gt;Handling foreign key constraint failures
&lt;/li&gt;
&lt;li&gt;Preventing partial inserts during dynamic table operations
&lt;/li&gt;
&lt;li&gt;Managing edge cases where schema changes conflicted with live data
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These problems rarely appear in tutorials. They surface only when systems are actively used in production.&lt;/p&gt;

&lt;p&gt;Debugging them taught me patience, precision, and respect for the database as a &lt;strong&gt;core system component&lt;/strong&gt;, not just a storage layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Production: The Real Teacher 🚨
&lt;/h2&gt;

&lt;p&gt;Beyond application logic, 2025 took me deep into &lt;strong&gt;production infrastructure&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I worked hands-on with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker-based deployments 🐳
&lt;/li&gt;
&lt;li&gt;PostgreSQL backups and restores
&lt;/li&gt;
&lt;li&gt;Container networking and environment configuration
&lt;/li&gt;
&lt;li&gt;Fixing &lt;code&gt;ALLOWED_HOSTS&lt;/code&gt; issues
&lt;/li&gt;
&lt;li&gt;Nginx reverse proxy setup
&lt;/li&gt;
&lt;li&gt;SSL configuration 🔐
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also debugged &lt;strong&gt;live production issues&lt;/strong&gt;, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data not visible despite successful submissions
&lt;/li&gt;
&lt;li&gt;Network failures between services
&lt;/li&gt;
&lt;li&gt;Reliability issues under real user load
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing accelerates learning faster than debugging while users are waiting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Growing a System Design Mindset 📈
&lt;/h2&gt;

&lt;p&gt;Alongside day-to-day development, I focused on strengthening my &lt;strong&gt;system design fundamentals&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I explored:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kafka basics (topics, partitions, producers, consumers)
&lt;/li&gt;
&lt;li&gt;API testing strategies
&lt;/li&gt;
&lt;li&gt;Performance and scalability considerations
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even when not immediately applied, these concepts reshaped how I think about architecture—helping me design systems with &lt;strong&gt;decoupling, scalability, and future growth&lt;/strong&gt; in mind.&lt;/p&gt;




&lt;h2&gt;
  
  
  What 2025 Gave Me 💡
&lt;/h2&gt;

&lt;p&gt;In just a few months, my confidence changed—not because everything worked perfectly, but because I learned how to handle things when they didn’t.&lt;/p&gt;

&lt;p&gt;2025 taught me how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build backend systems that evolve dynamically
&lt;/li&gt;
&lt;li&gt;Debug production issues without panic
&lt;/li&gt;
&lt;li&gt;Design for reliability and auditability
&lt;/li&gt;
&lt;li&gt;Take true ownership of complex systems
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most importantly, it taught me this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Real engineering happens outside the comfort zone.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Closing Thoughts 🌱
&lt;/h2&gt;

&lt;p&gt;2025 wasn’t about shortcuts or flashy achievements.&lt;br&gt;&lt;br&gt;
It was about &lt;strong&gt;depth, ownership, and growth&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I didn’t just write code this year.&lt;br&gt;&lt;br&gt;
I learned how &lt;strong&gt;real systems behave&lt;/strong&gt;, how &lt;strong&gt;production humbles assumptions&lt;/strong&gt;, and how &lt;strong&gt;engineering maturity is earned through responsibility&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And that foundation will stay with me—well beyond 2025. 🚀&lt;/p&gt;

</description>
      <category>fullstack</category>
      <category>python</category>
      <category>docker</category>
      <category>postgres</category>
    </item>
    <item>
      <title>🚀 A Complete Guide to Deploying SSL-Enabled Django in Docker With Nginx, PostgreSQL &amp; Redis</title>
      <dc:creator>Bharat Solanke</dc:creator>
      <pubDate>Fri, 28 Nov 2025 06:39:19 +0000</pubDate>
      <link>https://dev.to/bharat_solanke_8e45411fa6/a-complete-guide-to-deploying-ssl-enabled-django-in-docker-with-nginx-postgresql-redis-8el</link>
      <guid>https://dev.to/bharat_solanke_8e45411fa6/a-complete-guide-to-deploying-ssl-enabled-django-in-docker-with-nginx-postgresql-redis-8el</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fux8ou2sux83t7o852hzk.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fux8ou2sux83t7o852hzk.jpeg" alt=" " width="273" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A practical journey of debugging, configuring, and enabling HTTPS on a production server.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deploying a secure Django application in production often looks simple on paper — until you meet the real-world challenges of certificates, Docker networking, Nginx proxying, and cloud firewalls.&lt;/p&gt;

&lt;p&gt;This blog walks through the end-to-end process we followed to bring a Dockerized Django system live over HTTPS. It covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How we diagnosed SSL misconfigurations&lt;/li&gt;
&lt;li&gt;Fixing invalid certificates&lt;/li&gt;
&lt;li&gt;Updating Docker + Nginx&lt;/li&gt;
&lt;li&gt;Firewall/NAT troubleshooting&lt;/li&gt;
&lt;li&gt;Final verification steps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not just a how-to; it is a real engineering story.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧩 1. Deployment Architecture&lt;/strong&gt;&lt;br&gt;
The application stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✔ Django (served with Gunicorn)&lt;/li&gt;
&lt;li&gt;✔ Nginx (reverse proxy + static/media)&lt;/li&gt;
&lt;li&gt;✔ PostgreSQL&lt;/li&gt;
&lt;li&gt;✔ Redis&lt;/li&gt;
&lt;li&gt;✔ Celery + Flower&lt;/li&gt;
&lt;li&gt;✔ Docker &amp;amp; docker-compose
HTTP worked perfectly.
HTTPS, however, would hang indefinitely and then time out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🎯 2. The Goal&lt;/strong&gt;&lt;br&gt;
We needed to successfully:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✔ Install valid SSL certificates&lt;/li&gt;
&lt;li&gt;✔ Configure Nginx to serve HTTPS correctly&lt;/li&gt;
&lt;li&gt;✔ Expose port 443 from Docker → host machine&lt;/li&gt;
&lt;li&gt;✔ Ensure firewall/NAT permitted inbound HTTPS&lt;/li&gt;
&lt;li&gt;✔ Validate secure access from any browser&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🔎 3. Debugging the SSL Failure&lt;/strong&gt;&lt;br&gt;
First step: check Nginx logs inside the container:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;We found:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cannot load certificate "/etc/nginx/ssl/domain.crt":
PEM_read_bio_X509_AUX() failed (SSL: ... no start line)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This error meant:&lt;br&gt;
👉 The certificate file existed — but its content was empty or invalid.&lt;/p&gt;

&lt;p&gt;We checked the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;head -n 2 nginx/ssl/domain.crt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🛠 4. Fixing the SSL Files&lt;/strong&gt;&lt;br&gt;
The correct .crt, .key, and CA bundle files were uploaded to the server using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scp -P &amp;lt;port&amp;gt; certificate.crt ca_bundle.crt user@server:/path/to/nginx/ssl/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We verified they were valid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ls -l nginx/ssl/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All files now had valid PEM content:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;domain.crt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;domain.key&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ca_bundle.crt&lt;/code&gt;
Nginx was reloaded afterward.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🧱 5. Configuring HTTPS in Nginx&lt;/strong&gt;&lt;br&gt;
We updated the configuration to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✔ Redirect HTTP → HTTPS&lt;/li&gt;
&lt;li&gt;✔ Serve static &amp;amp; media correctly&lt;/li&gt;
&lt;li&gt;✔ Proxy Django with correct headers&lt;/li&gt;
&lt;li&gt;✔ Load the correct certificates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Final Nginx HTTPS configuration&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;upstream django_app {
    server web:8000;
}

server {
    listen 80;
    server_name _;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name _;

    ssl_certificate /etc/nginx/ssl/domain.crt;
    ssl_certificate_key /etc/nginx/ssl/domain.key;
    ssl_trusted_certificate /etc/nginx/ssl/ca_bundle.crt;

    location /static/ {
        alias /usr/share/nginx/html/static/;
    }

    location /media/ {
        alias /usr/share/nginx/html/media/;
    }

    location / {
        proxy_pass http://django_app;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
    }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🐳 6. Updating Docker for HTTPS&lt;/strong&gt;&lt;br&gt;
We exposed port 443:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nginx:
  ports:
    - "80:80"
    - "443:443"
  volumes:
    - ./nginx/ssl:/etc/nginx/ssl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then restarted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose down
docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nginx was now correctly listening on port 443.&lt;br&gt;
But… HTTPS was still unreachable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧱 7. Host-Level Firewall Check&lt;/strong&gt;&lt;br&gt;
We checked:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Result: inactive.&lt;br&gt;
Next, iptables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo iptables -t nat -L -n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker’s NAT rules were correct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DNAT tcp dpt:443 → container_ip:443
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the machine itself wasn’t blocking port 443.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📡 8. External Network Test&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -Iv https://&amp;lt;server-ip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trying &amp;lt;server-ip&amp;gt;:443...
(hangs forever)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was the critical clue:&lt;br&gt;
❌ No traffic was reaching the server&lt;br&gt;
❌ Not even reaching Docker or Nginx&lt;br&gt;
This meant the cloud/datacenter firewall was blocking 443.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📩 9. Requesting Cloud Team to Open Port 443&lt;/strong&gt;&lt;br&gt;
We escalated the issue and requested:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Please enable inbound HTTPS (443) on the network firewall.&lt;br&gt;
The server is listening correctly, but incoming TLS traffic is blocked.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After the network team opened port 443…&lt;br&gt;
🔥 HTTPS started working instantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🎉 10. Final Verification&lt;/strong&gt;&lt;br&gt;
We tested again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -Iv https://&amp;lt;your-domain&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/2 200
server: nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browser showed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✔ HTTPS lock&lt;/li&gt;
&lt;li&gt;✔ Valid certificate&lt;/li&gt;
&lt;li&gt;✔ Full redirect from HTTP → HTTPS&lt;/li&gt;
&lt;li&gt;✔ Static/media loading correctly&lt;/li&gt;
&lt;li&gt;✔ Django + WebSockets running normally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deployment was successful.&lt;br&gt;
&lt;strong&gt;🏁 11. Key Lessons Learned&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SSL files must contain valid PEM data
Corrupted or empty certificates cause silent failures.&lt;/li&gt;
&lt;li&gt;Use a complete certificate chain
Both:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;domain.crt
ca_bundle.crt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Docker must expose HTTPS explicitly
Exposing only port 80 is not enough.&lt;/li&gt;
&lt;li&gt;Never forget cloud firewalls
Your VM might be open but the provider might still be blocking ports.&lt;/li&gt;
&lt;li&gt;Debug every layer
To solve HTTPS issues, inspect:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Browser&lt;/li&gt;
&lt;li&gt;DNS&lt;/li&gt;
&lt;li&gt;Cloud firewall&lt;/li&gt;
&lt;li&gt;Server firewall&lt;/li&gt;
&lt;li&gt;iptables&lt;/li&gt;
&lt;li&gt;Docker NAT routing&lt;/li&gt;
&lt;li&gt;Nginx configs&lt;/li&gt;
&lt;li&gt;SSL files&lt;/li&gt;
&lt;li&gt;Application headers
Solving it required checking each of these layers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;📘 12. Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deploying SSL in a production Docker environment is not just about placing certificate files in a folder.&lt;br&gt;
It requires a complete understanding of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✔ Nginx reverse proxying&lt;/li&gt;
&lt;li&gt;✔ SSL termination&lt;/li&gt;
&lt;li&gt;✔ Docker networking&lt;/li&gt;
&lt;li&gt;✔ Cloud firewall rules&lt;/li&gt;
&lt;li&gt;✔ Valid certificates&lt;/li&gt;
&lt;li&gt;✔ Correct header forwarding
Once all layers worked together, the system became fully HTTPS-enabled and production-ready.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ssl</category>
      <category>docker</category>
      <category>nginx</category>
      <category>redis</category>
    </item>
    <item>
      <title>Mastering Whitespace and Newlines in Django Templates: The Ultimate Guide 🎯</title>
      <dc:creator>Bharat Solanke</dc:creator>
      <pubDate>Thu, 18 Sep 2025 07:46:27 +0000</pubDate>
      <link>https://dev.to/bharat_solanke_8e45411fa6/mastering-whitespace-and-newlines-in-django-templates-the-ultimate-guide-5eii</link>
      <guid>https://dev.to/bharat_solanke_8e45411fa6/mastering-whitespace-and-newlines-in-django-templates-the-ultimate-guide-5eii</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gisakgk2jlqoe8h2bw0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gisakgk2jlqoe8h2bw0.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hidden bugs, mysterious broken outputs, and unexpected rendering errors in Django templates are often caused by invisible foes: whitespace and auto-generated newlines. For Django developers, especially those working with dynamic data, form-heavy UIs, or complex HTML tables, these subtle formatting issues can consume hours of debugging. This post provides an in-depth, practical guide to why whitespace matters in Django templates, how to spot and fix related issues, and best practices to keep your projects robust and professional. 🧑‍💻✨&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Why Whitespace and Newlines Cause Problems in Django Templates 🤔&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Django templates process the literal text, tags, and code—every character, space, and newline impacts the final output.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Broken logic tags: &lt;code&gt;{% if %}&lt;/code&gt;, &lt;code&gt;{{ value|date:"Y-m-d"|default:"-" }}&lt;/code&gt; split awkwardly, or with stray spaces, can cause silent bugs and missing output. ⚠️&lt;/li&gt;
&lt;li&gt;Blank cells, awkward layouts, or “mystery gaps”—especially in tables and forms where the template’s layout is whitespace-sensitive. 🕳️&lt;/li&gt;
&lt;li&gt;Unintended extra lines from loops or indentations, turning clean pages into confusing ones.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Best Practices: Writing Clean and Maintainable Django Templates 💡&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Keep Tags and Filter Chains on a Single Line&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;td&amp;gt;{{ value|date:"Y-m-d"|default:"-" }}&amp;lt;/td&amp;gt;  &amp;lt;!-- 👍 Best practice --&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;Never do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;td&amp;gt;
  {{ value
      |date:"Y-m-d"
      |default:"-" }}
&amp;lt;/td&amp;gt;   &amp;lt;!-- 👎 This can fail silently! --&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;Stray newlines in tags can cause perplexing bugs. Line it up—keep it clear! 🗂️&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Tame Your Editor 🛠️&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Configure VSCode/PyCharm to turn off auto-wrap for .html and Django template files.&lt;br&gt;
A perfectly formatted template today can break tomorrow if your editor “helps” a bit too much! ✍️&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Use &lt;code&gt;{% spaceless %}&lt;/code&gt; for Clean Output&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When whitespace creeps into your forms or table markup, Django’s built-in &lt;code&gt;{% spaceless %}&lt;/code&gt; tag is your friend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; {% spaceless %}
  &amp;lt;tr&amp;gt;
    &amp;lt;td&amp;gt;{{ user.username }}&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;{{ user.email }}&amp;lt;/td&amp;gt;
  &amp;lt;/tr&amp;gt;
{% endspaceless %}


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

&lt;/div&gt;



&lt;p&gt;No more unexpected gaps—just clean, professional HTML! 🧽&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;4. Modularize and Comment for Team Success&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;{% include %}&lt;/code&gt; and inheritance for complex layouts. 🔗&lt;/li&gt;
&lt;li&gt;Consistent block and filename conventions keep your project scalable. 🗃️&lt;/li&gt;
&lt;li&gt;Comment tricky sections with &lt;code&gt;{% comment %} ... {% endcomment %}&lt;/code&gt; for clarity, especially when revisiting code later. 💬&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Outsource Complexity: Views Over Template Logic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Templates should show data, not decide what data to show!&lt;br&gt;
Keep calculations, loops, and logic-heavy operations in your view/controller. The simpler your HTML, the fewer whitespace gremlins you’ll face. 🚀&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Debug Like a Pro 🔍&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Django Debug Toolbar to inspect how your template renders, step by step.&lt;/li&gt;
&lt;li&gt;Regularly “View Source” in your browser—spot empty cells, stray newlines, and hunt down those stealthy bugs. 👀&lt;/li&gt;
&lt;li&gt;Advanced: Profiling tools and middleware can help track performance and subtle render errors in production.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Advanced Whitespace Management 🥇&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For large apps: Use middleware or packages like django-spaceless-templates to systematically clean up whitespace during rendering.&lt;/li&gt;
&lt;li&gt;Needing even more control? Try Jinja2 templates, which offer fine-grained block-level whitespace trimming. 🧩&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Visual Debugging (Emoticon Style!)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📝 Compare before/after code samples to spot whitespace issues.&lt;/li&gt;
&lt;li&gt;🕵️ Use the browser’s inspect tool to understand the connection between source and output.&lt;/li&gt;
&lt;li&gt;🎨 Screenshots of both “messy” and “polished” UIs can make fixes tangible and convincing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Conclusion &amp;amp; Key Takeaways 🎉&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Whitespace and newlines are silent disruptors in Django templating—with just a line break or stray space, you can derail layout and logic. But a handful of best practices—careful formatting, &lt;code&gt;{% spaceless %}&lt;/code&gt; tags, editor vigilance, and a modular coding mindset—turns chaos into clarity.&lt;/p&gt;

&lt;p&gt;With these insights and tools, it’s easy to banish whitespace bugs for good—and ship Django templates that are as robust as they are beautiful. Good luck, and happy templating! 🥳🚀&lt;/p&gt;

</description>
      <category>django</category>
      <category>programming</category>
      <category>template</category>
    </item>
    <item>
      <title>Implementing PostgreSQL Replication and Automated Cloud Backups Using Docker and Rclone</title>
      <dc:creator>Bharat Solanke</dc:creator>
      <pubDate>Tue, 09 Sep 2025 06:58:08 +0000</pubDate>
      <link>https://dev.to/bharat_solanke_8e45411fa6/implementing-postgresql-replication-and-automated-cloud-backups-using-docker-and-rclone-115</link>
      <guid>https://dev.to/bharat_solanke_8e45411fa6/implementing-postgresql-replication-and-automated-cloud-backups-using-docker-and-rclone-115</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;In modern production environments, database replication and automated cloud backups are essential for ensuring high availability, fault tolerance, and disaster recovery. In this blog, I’ll walk through a step-by-step approach to set up a PostgreSQL replication system using Docker, create automated backups, and upload them to cloud storage using Rclone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This guide uses a dummy project structure to maintain confidentiality while illustrating a real-world implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why This Setup is Useful&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine a growing SaaS application that cannot afford downtime. You want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure continuous replication of your main database to multiple secondary databases.&lt;/li&gt;
&lt;li&gt;Automate daily backups to prevent data loss.&lt;/li&gt;
&lt;li&gt;Securely store backups on cloud storage like OneDrive, Google Drive, or S3.&lt;/li&gt;
&lt;li&gt;Have a system where restoring the database is straightforward if Docker containers fail.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach is ideal for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-environment deployments (staging, production).&lt;/li&gt;
&lt;li&gt;Teams managing sensitive data that requires offsite backups.&lt;/li&gt;
&lt;li&gt;Applications where downtime translates to business loss.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project_root/
├── app/
│   └── some_module/                
├── backups/                        
├── backup.sh                      
├── backup_to_cloud.sh             
├── db/
│   ├── parent/
│   │   ├── postgresql.conf         
│   │   └── pg_hba.conf             
│   ├── child1/
│   │   └── postgresql.conf         
│   ├── child2/
│   │   └── postgresql.conf         
│   └── child3/
│       └── postgresql.conf         
├── db_backups/                    
├── docker-init-scripts/             
├── entrypoint.sh                     
├── full_backup.sql.gz                
├── backup.sql                        
├── postgresql.conf                   
├── test_cloud_backup.sh              
├── manage.py                         
├── docker-compose.yml               
└── requirements.txt                  

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;db/childX: Config files for replicated child databases.&lt;/li&gt;
&lt;li&gt;backups/: Local storage for .sql or .dump files.&lt;/li&gt;
&lt;li&gt;cron_jobs/: Scripts scheduled to run periodic backups.&lt;/li&gt;
&lt;li&gt;backup.sh: Handles local database backup.&lt;/li&gt;
&lt;li&gt;backup_to_cloud.sh: Uploads backups to cloud storage via Rclone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Setting Up Docker Containers for PostgreSQL Replication&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We set up one parent database and three child databases. Each child replicates from the parent to ensure redundancy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
services:
  postgres_parent:
    image: postgres:15
    container_name: postgres_parent
    restart: always
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: admin
    volumes:
      - postgres_parent_data:/var/lib/postgresql/data
      - ./db/primary/postgresql.conf:/etc/postgresql/postgresql.conf
      - ./db/primary/pg_hba.conf:/etc/postgresql/pg_hba.conf
    command: postgres -c config_file=/etc/postgresql/postgresql.conf
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
      interval: 10s
      retries: 5
      timeout: 5s

  postgres_child1:
    image: postgres:15
    container_name: postgres_child1
    restart: always
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: admin
      PARENT_HOST: postgres_parent
    volumes:
      - postgres_child1_data:/var/lib/postgresql/data
      - ./db/child1/postgresql.conf:/etc/postgresql/postgresql.conf
    command: postgres -c config_file=/etc/postgresql/postgresql.conf
    depends_on:
      - postgres_parent

  postgres_child2:
    image: postgres:15
    container_name: postgres_child2
    restart: always
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: admin
      PARENT_HOST: postgres_parent
    volumes:
      - postgres_child2_data:/var/lib/postgresql/data
      - ./db/child2/postgresql.conf:/etc/postgresql/postgresql.conf
    command: postgres -c config_file=/etc/postgresql/postgresql.conf
    depends_on:
      - postgres_parent

  postgres_child3:
    image: postgres:15
    container_name: postgres_child3
    restart: always
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: admin
      PARENT_HOST: postgres_parent
    volumes:
      - postgres_child3_data:/var/lib/postgresql/data
      - ./db/child3/postgresql.conf:/etc/postgresql/postgresql.conf
    command: postgres -c config_file=/etc/postgresql/postgresql.conf
    depends_on:
      - postgres_parent

volumes:
  postgres_parent_data:
  postgres_child1_data:
  postgres_child2_data:
  postgres_child3_data:

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each child container depends on the parent.&lt;/li&gt;
&lt;li&gt;Custom PostgreSQL configs are mounted to ensure proper replication settings.&lt;/li&gt;
&lt;li&gt;Healthchecks ensure the parent is ready before children start.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Local Database Backups&lt;/strong&gt;&lt;br&gt;
To safeguard against data loss, we create daily backups.&lt;/p&gt;

&lt;p&gt;backup.sh Example:&lt;br&gt;
&lt;/p&gt;

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

BACKUP_DIR="./backups"
DATE=$(date +"%Y-%m-%d_%H-%M-%S")
DB_NAME="mydb"
USER="postgres"

mkdir -p $BACKUP_DIR
pg_dump -U $USER $DB_NAME &amp;gt; $BACKUP_DIR/${DB_NAME}_backup_$DATE.sql
echo "Backup created at $BACKUP_DIR/${DB_NAME}_backup_$DATE.sql"

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Stores timestamped .sql backups in backups/.&lt;/li&gt;
&lt;li&gt;Can be triggered manually or via cron.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Step 3: Automating Backups with Cron&lt;br&gt;
Set up a cron job to run the backup script daily.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# crontab -e
0 2 * * * /path/to/my_project/backup.sh &amp;gt;&amp;gt; /path/to/my_project/logs/backup.log 2&amp;gt;&amp;amp;1

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Runs every day at 2 AM.&lt;/li&gt;
&lt;li&gt;Logs are stored for auditing purposes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Uploading Backups to Cloud with Rclone&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We use Rclone to upload backups to OneDrive.&lt;br&gt;
backup_to_cloud.sh Example:&lt;br&gt;
&lt;/p&gt;

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

BACKUP_DIR="./backups"
REMOTE_NAME="onedrive"
REMOTE_PATH="DB_Backups/$(date +%Y-%m-%d)/"

rclone copy $BACKUP_DIR $REMOTE_NAME:$REMOTE_PATH --progress

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rclone Setup Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Rclone on the server.&lt;/li&gt;
&lt;li&gt;Run rclone config.&lt;/li&gt;
&lt;li&gt;Create a new remote (e.g., onedrive) and authorize it.&lt;/li&gt;
&lt;li&gt;Test connectivity using rclone lsf onedrive:.&lt;/li&gt;
&lt;li&gt;Tip: Always test rclone copy manually before automating.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Handling Failures and Recovery&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Scenario: Docker or a container fails.&lt;/p&gt;

&lt;p&gt;Recovery Steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stop all containers:
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Restore the database from the latest backup:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;psql -U postgres -d mydb &amp;lt; ./backups/mydb_backup_YYYY-MM-DD.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Restart containers:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Verify replication status in PostgreSQL:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT * FROM pg_stat_replication;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key Points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep backups outside Docker volumes to prevent accidental loss.&lt;/li&gt;
&lt;li&gt;Use versioned backups and cloud storage to recover from critical failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Real-World Example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Scenario: A SaaS application stores customer activity data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parent database: Accepts writes.&lt;/li&gt;
&lt;li&gt;Child databases: Provide read replicas for analytics and reporting.&lt;/li&gt;
&lt;li&gt;Backup scripts: Run nightly, stored locally and in the cloud.&lt;/li&gt;
&lt;li&gt;Failure recovery: If the parent container crashes, the latest backup can restore the database, minimizing downtime.&lt;/li&gt;
&lt;li&gt;This setup ensures high availability, data durability, and quick disaster recovery.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Summary&lt;br&gt;
In this blog, we covered:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Dockerized PostgreSQL replication with one parent and multiple children.&lt;/li&gt;
&lt;li&gt;Automated local backups using scripts and cron jobs.&lt;/li&gt;
&lt;li&gt;Cloud backup using Rclone and OneDrive.&lt;/li&gt;
&lt;li&gt;Recovery procedure in case Docker or database fails.&lt;/li&gt;
&lt;li&gt;Best practices for backup and replication management.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach is scalable, secure, and production-ready, suitable for both small teams and enterprise environments.&lt;/p&gt;

&lt;p&gt;✅ Next Steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extend Rclone scripts to support multiple cloud providers.&lt;/li&gt;
&lt;li&gt;Add monitoring alerts if backup or replication fails.&lt;/li&gt;
&lt;li&gt;Schedule incremental backups to optimize storage.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>postgres</category>
      <category>docker</category>
      <category>automation</category>
      <category>cloud</category>
    </item>
    <item>
      <title>⚡ Building a Real‑Time Collaborative Form Editing System with Django Channels, WebSockets &amp; Redis</title>
      <dc:creator>Bharat Solanke</dc:creator>
      <pubDate>Mon, 18 Aug 2025 17:08:36 +0000</pubDate>
      <link>https://dev.to/bharat_solanke_8e45411fa6/building-a-real-time-collaborative-form-editing-system-with-django-channels-websockets-redis-2egl</link>
      <guid>https://dev.to/bharat_solanke_8e45411fa6/building-a-real-time-collaborative-form-editing-system-with-django-channels-websockets-redis-2egl</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgxtuiu1mwgbduxttz8en.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgxtuiu1mwgbduxttz8en.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🏁 Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Today's fast-paced work environment. teams often need to &lt;strong&gt;edit shared forms&lt;/strong&gt; simultaneously. Whether it's a multi-user dashboard, a shared data entry sheet, or a confirmation panel - the challenge is the same:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Without real‑time sync, users may overwrite each other’s work 😓&lt;/li&gt;
&lt;li&gt;Constant page refreshes slow down productivity&lt;/li&gt;
&lt;li&gt;Communication gaps can lead to missing or stale updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💡 Real‑time collaborative editing solves this by ensuring that everyone connected to the same form sees changes instantly — just like in Google Sheets.&lt;/p&gt;

&lt;p&gt;This blog will walk through how I built a multi‑user, live‑updating form system using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Django Channels&lt;/strong&gt; for async WebSocket handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis&lt;/strong&gt; as the message broker&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript WebSockets&lt;/strong&gt; API for sending/receiving updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🔍 The Problem We’re Solving&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine a shared form where multiple people:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type into different fields at the same time ✏️&lt;/li&gt;
&lt;li&gt;Need to see each other’s changes immediately 👀&lt;/li&gt;
&lt;li&gt;Want visual indicators showing who’s editing what 💬&lt;/li&gt;
&lt;li&gt;Can dynamically add/remove form rows in real time ➕➖&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without real-time features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data can be out of sync between users&lt;/li&gt;
&lt;li&gt;There’s a risk of accidental overwrites&lt;/li&gt;
&lt;li&gt;The experience feels slow and outdated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🏗 High-Level Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The system follows this flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User edits a field → Browser sends WebSocket message to Django  
Django Channels sends it to a Redis Pub/Sub group  
Redis broadcasts to all connected clients in that group  
Other users’ browsers instantly update the field
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🗂 Room-based grouping&lt;/strong&gt; ensures only users editing the same form instance see each other’s updates&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚙ Tech Stack Overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technology&lt;/strong&gt;  --&amp;gt; &lt;strong&gt;Purpose&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;WebSockets --&amp;gt;  Enables instant 2‑way communication&lt;br&gt;
Django Channels --&amp;gt; Adds async WebSocket support to Django&lt;br&gt;
Redis --&amp;gt;   Distributes messages between server processes&lt;br&gt;
JavaScript --&amp;gt;  Captures inputs, sends updates, and applies received changes&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🖥 Backend – Django Channels Consumer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The consumer acts as the WebSocket handler — connecting users, receiving messages, and broadcasting to others.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
from channels.generic.websocket import AsyncWebsocketConsumer

class CollaborativeFormConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        # Identifiers define a unique group for the form session
        self.section_id = self.scope["url_route"]["kwargs"]["section_id"]
        self.template_id = self.scope["url_route"]["kwargs"]["template_id"]
        self.shift_id = self.scope["url_route"]["kwargs"]["shift_id"]

        self.group_name = f"form_{self.section_id}_{self.template_id}_{self.shift_id}"
        self.username = f"{self.scope['user'].first_name} {self.scope['user'].last_name}".strip()

        print(f"✅ Connected: {self.username} joined {self.group_name}")
        await self.channel_layer.group_add(self.group_name, self.channel_name)
        await self.accept()

    async def disconnect(self, close_code):
        print(f"❌ Disconnected: {self.username}")
        await self.channel_layer.group_discard(self.group_name, self.channel_name)

    async def receive(self, text_data):
        data = json.loads(text_data)
        event_type = data.get("type", "")

        # Broadcast to others in the group
        await self.channel_layer.group_send(
            self.group_name,
            {
                "type": "broadcast_message",
                "event": event_type,
                "sender": self.username,
                "sender_channel": self.channel_name,
                **{k: v for k, v in data.items() if k != "type"},
            },
        )

    async def broadcast_message(self, event):
        if event["sender_channel"] == self.channel_name:
            return  # Don’t send updates back to the sender

        payload = {k: v for k, v in event.items() if k not in ("sender_channel", "type")}
        payload["type"] = event["event"]

        await self.send(text_data=json.dumps(payload))

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

&lt;/div&gt;



&lt;p&gt;Why this works well:&lt;br&gt;
✅ Group-based rooms keep messages relevant&lt;br&gt;
✅ Echo prevention avoids duplicate updates for the sender&lt;br&gt;
✅ Works with Redis for multi-server scalability&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🌐 WebSocket Routing&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.urls import re_path
from . import consumers

websocket_urlpatterns = [
    re_path(
        r"ws/form/(?P&amp;lt;section_id&amp;gt;\d+)/(?P&amp;lt;shift_id&amp;gt;\d+)/(?P&amp;lt;template_id&amp;gt;\d+)/$",
        consumers.CollaborativeFormConsumer.as_asgi(),
    ),
]

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;📦 Redis: The Messaging Backbone&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add this to settings.py:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {"hosts": [("localhost", 6379)]},
    },
}

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

&lt;/div&gt;



&lt;p&gt;Redis ensures broadcasts work across multiple Django workers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✨ Frontend – JavaScript WebSocket Integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open the connection &amp;amp; sync fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function openFormWS(sectionId, shiftId, templateId, currentUser) {
  const protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
  const wsUrl = `${protocol}${window.location.host}/ws/form/${sectionId}/${shiftId}/${templateId}/`;
  const ws = new WebSocket(wsUrl);

  ws.onopen = () =&amp;gt; console.log(`🔗 Connected`);

  ws.onmessage = (e) =&amp;gt; {
    const data = JSON.parse(e.data);

    if (data.type === 'update') {
      document.querySelectorAll(`[data-template-id="${templateId}"][data-field="${data.field}"]`)
        .forEach(input =&amp;gt; {
          input.value = data.value;
          showLastEdited(input, data.sender);
        });
    }
    else if (data.type === 'focus') {
      indicateUserEditing(data.field, data.sender, templateId);
    }
    else if (data.type === 'blur') {
      removeUserEditing(data.field, templateId);
    }
  };

  wireRealtimeFields(templateId, ws, currentUser);
}

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

&lt;/div&gt;



&lt;p&gt;Attach event listeners:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function wireRealtimeFields(templateId, ws, currentUser) {
  document.querySelectorAll(
    `[data-template-id="${templateId}"].form-field:not([data-wired="1"])`
  ).forEach(input =&amp;gt; {
    input.dataset.wired = "1";

    input.addEventListener('input', () =&amp;gt; {
      ws.send(JSON.stringify({ type: 'update', field: input.dataset.field, value: input.value, sender: currentUser }));
    });

    input.addEventListener('focus', () =&amp;gt; {
      ws.send(JSON.stringify({ type: 'focus', field: input.dataset.field, sender: currentUser }));
    });

    input.addEventListener('blur', () =&amp;gt; {
      ws.send(JSON.stringify({ type: 'blur', field: input.dataset.field, sender: currentUser }));
    });
  });
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🎨 Visual Indicators for Collaboration&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function indicateUserEditing(field, user, templateId) {
  const el = document.querySelector(`[data-template-id="${templateId}"][data-field="${field}"]`);
  el.classList.add('being-edited');
  el.insertAdjacentHTML('afterend', `&amp;lt;span class="editing-badge"&amp;gt;✏ Editing: ${user}&amp;lt;/span&amp;gt;`);
}

function removeUserEditing(field, templateId) {
  const el = document.querySelector(`[data-template-id="${templateId}"][data-field="${field}"]`);
  el.classList.remove('being-edited');
  const badge = el.parentElement.querySelector('.editing-badge');
  if (badge) badge.remove();
}

function showLastEdited(input, user) {
  let badge = input.parentElement.querySelector('.last-edit-badge');
  if (!badge) {
    badge = document.createElement('span');
    badge.className = 'last-edit-badge';
    input.parentElement.appendChild(badge);
  }
  badge.textContent = `👤 Last edited by: ${user}`;
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CSS Highlight:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.being-edited {
  border: 2px solid orange;
  background: #fff3cd;
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;💾 Adding an Autosave Feature&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To ensure no data loss during network issues, we add a lightweight autosave API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@csrf_exempt
@login_required
def save_autosave_data(request):
    data = json.loads(request.body)
    template_id = data.get("template_id")
    autosave_data = data.get("autosave_data")

    MyAutosaveModel.objects.update_or_create(
        template_id=template_id,
        defaults={"data": autosave_data},
    )
    return JsonResponse({"status": "success"})

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🔥 Challenges &amp;amp; Solutions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Challenge&lt;/strong&gt;  -&amp;gt;    &lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Avoid feedback loops -&amp;gt; Skip sending updates to the origin user&lt;/p&gt;

&lt;p&gt;Two users editing same field -&amp;gt; Show “Currently Editing” badges&lt;/p&gt;

&lt;p&gt;Dynamic form rows -&amp;gt; Re‑wire event listeners after adding&lt;/p&gt;

&lt;p&gt;Offline scenarios -&amp;gt; Periodic autosave to server&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🎯 Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By pairing Django Channels + WebSockets + Redis, we’ve built a scalable, real‑time collaborative form system that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keeps all connected users instantly in sync&lt;/li&gt;
&lt;li&gt;Shows edit indicators for clarity&lt;/li&gt;
&lt;li&gt;Prevents frustrating overwrites&lt;/li&gt;
&lt;li&gt;Works for any multi-user form editing scenario&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This architecture can be used in dashboards, CRMs, multi-user admin tools, or any shared data entry system you build.&lt;/p&gt;

</description>
      <category>django</category>
      <category>websocket</category>
      <category>asgi</category>
      <category>wsgi</category>
    </item>
    <item>
      <title>🚀 Why ASGI Over WSGI? 🔄 Solving Multi-Device Login Conflicts in Django 📱💻🛡️</title>
      <dc:creator>Bharat Solanke</dc:creator>
      <pubDate>Wed, 30 Jul 2025 07:46:54 +0000</pubDate>
      <link>https://dev.to/bharat_solanke_8e45411fa6/why-asgi-over-wsgi-solving-multi-device-login-conflicts-in-django-1196</link>
      <guid>https://dev.to/bharat_solanke_8e45411fa6/why-asgi-over-wsgi-solving-multi-device-login-conflicts-in-django-1196</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;✅ A practical guide to implementing single-user login session enforcement with real-time logout notifications using Django, ASGI, Channels, WebSockets, and Redis.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;📌 Problem Statement&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We want to ensure that each user can only be logged in on one device at a time.&lt;/p&gt;

&lt;p&gt;Hear's the expected behaviour:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When a user logs in from a new device, any existing session on other devices should get:

&lt;ul&gt;
&lt;li&gt;❌ Logged out immediately, and&lt;/li&gt;
&lt;li&gt;🔔 Notified in real-time about the forced logout.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Traditional Django projects (which run on WSGI) can't do this in real-time - you'd need pulling or periodic refresh. We needed an instant notification system&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❓ Why WSGI Couldn’t Help&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;WSGI (Web Server Gateway Interface) is great for traditional request-response cycles but:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ WSGI Limitations&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;No support for WebSockets&lt;/li&gt;
&lt;li&gt;No long-lived connections&lt;/li&gt;
&lt;li&gt;No built-in async support&lt;/li&gt;
&lt;li&gt;Not scalable for concurrent real-time tasks&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Hence, we &lt;strong&gt;replaced WSGI with ASGI.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Why We Used ASGI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ASGI (Asynchronous Server Gateway Interface) allows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;✅ Real-time WebSocket support&lt;/li&gt;
&lt;li&gt;✅ Async communication&lt;/li&gt;
&lt;li&gt;✅ Push-based updates (no polling)&lt;/li&gt;
&lt;li&gt;✅ Integration with channels and channels_redis&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This made ASGI a perfect fit for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time session monitoring&lt;/li&gt;
&lt;li&gt;Force logout via socket&lt;/li&gt;
&lt;li&gt;Scalable async event-driven features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;📡 WebSockets (and Why They Matter)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A WebSocket is a protocol that enables bi-directional, full-duplex communication between the server and the client over a single TCP connection.&lt;br&gt;
Unlike HTTP, which is request-response based, WebSockets stay open, so the server can push data to the client without a new request.&lt;br&gt;
In our use case:&lt;/p&gt;

&lt;p&gt;When a user logs in from a new device, we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Save the new session key&lt;/li&gt;
&lt;li&gt;Broadcast a message to the old session’s WebSocket connection&lt;/li&gt;
&lt;li&gt;The frontend (already connected via WebSocket) receives a real-time message prompting the user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🔧 What We Implemented&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;✅ 1. WebSocket Connection Per User&lt;br&gt;
Every logged-in user opens a WebSocket connection. We assign each user to a Redis channel group named uniquely, like &lt;u&gt;user_{user.id}&lt;/u&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# consumers.py

class SessionConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.user = self.scope["user"]
        if self.user.is_authenticated:
            self.group_name = f"user_{self.user.id}"
            await self.channel_layer.group_add(self.group_name, self.channel_name)
            await self.accept()
        else:
            await self.close()

    async def disconnect(self, close_code):
        if self.user.is_authenticated:
            await self.channel_layer.group_discard(self.group_name, self.channel_name)

    async def force_logout(self, event):
        await self.send(text_data=json.dumps({
            "action": "force_logout"
        }))

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

&lt;/div&gt;



&lt;p&gt;✅ 2. Track Current Session ID in DB&lt;br&gt;
We extended Django’s AbstractUser to store the current session key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# models.py

from django.contrib.auth.models import AbstractUser

class UserMaster(AbstractUser):
    current_session_key = models.CharField(max_length=255, null=True, blank=True)

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

&lt;/div&gt;



&lt;p&gt;✅ 3. Check and Destroy Old Sessions on Login&lt;br&gt;
On login, we check if there is a previous session for the user. If yes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We &lt;u&gt;delete it&lt;/u&gt;
&lt;/li&gt;
&lt;li&gt;Notify the old WebSocket channel (group) to logout
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# login view

from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync

if user.current_session_key:
    old_session = Session.objects.filter(session_key=user.current_session_key).first()
    if old_session:
        old_session.delete()

    # 🔔 Notify via WebSocket
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)(
        f"user_{user.id}",
        {
            "type": "force_logout",
        }
    )

# Save new session
user.current_session_key = request.session.session_key
user.save()

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

&lt;/div&gt;


&lt;p&gt;✅ 4. Frontend WebSocket Listener&lt;br&gt;
When a logout message is sent, the frontend instantly logs the user out.&lt;br&gt;
&lt;/p&gt;

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

const socket = new WebSocket('ws://' + window.location.host + '/ws/session/');

socket.onmessage = function(event) {
    const data = JSON.parse(event.data);
    if (data.action === "force_logout") {
        alert("You have been logged out because your account was accessed elsewhere.");
        window.location.href = "/logout/";
    }
};

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

&lt;/div&gt;



&lt;p&gt;⚙️ Supporting Infrastructure: Redis &amp;amp; Channels&lt;br&gt;
We added these in &lt;u&gt;settings.py&lt;/u&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSTALLED_APPS += ["channels"]

ASGI_APPLICATION = "your_project.asgi.application"

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("redis", 6379)],
        },
    }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🧱 Docker Compose Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We added Redis and Uvicorn (ASGI server) to our &lt;u&gt;docker-compose.yml&lt;/u&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3.9"
services:
  web:
    build: .
    command: uvicorn your_project.asgi:application --host 0.0.0.0 --port 8000 --reload
    depends_on:
      - redis
    ports:
      - "8000:8000"
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🆚 ASGI vs WSGI Summary Table&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjhlpl4mkmna0bnjofq24.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjhlpl4mkmna0bnjofq24.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧠 Conclusion&lt;/strong&gt;&lt;br&gt;
With ASGI, we made our Django app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Real-time capable&lt;/li&gt;
&lt;li&gt;✅ WebSocket-enabled&lt;/li&gt;
&lt;li&gt;✅ Scalable for future interactive features&lt;/li&gt;
&lt;li&gt;✅ More secure with single-session login&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern is now ready to be reused for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔔 Notifications&lt;/li&gt;
&lt;li&gt;📡 Live dashboards&lt;/li&gt;
&lt;li&gt;🧾 Collaborative forms&lt;/li&gt;
&lt;li&gt;👨‍💻 Chat or support features&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>django</category>
      <category>websocket</category>
      <category>asgi</category>
      <category>wsgi</category>
    </item>
    <item>
      <title>Django Context Processors: Explained Step-by-Step</title>
      <dc:creator>Bharat Solanke</dc:creator>
      <pubDate>Thu, 24 Jul 2025 17:33:20 +0000</pubDate>
      <link>https://dev.to/bharat_solanke_8e45411fa6/django-context-processors-explained-step-by-step-3kg9</link>
      <guid>https://dev.to/bharat_solanke_8e45411fa6/django-context-processors-explained-step-by-step-3kg9</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6563zpykb63wn0zkg2fl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6563zpykb63wn0zkg2fl.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. What Are Context Processors?
&lt;/h2&gt;

&lt;p&gt;A context processor in Django is a special Python function that receives the request object as an argument and returns a dictionary of variables. These key-value pairs are added to the template context, making them available in every template rendered using Django's template engine&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Context processors are a handy way to provide common context data to all templates without having to include them in every view&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Why Do Context Processors Exist?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;DRY (Don't Repeat Yourself): They help developers avoid repeating the same context variables in multiple views. For example, side-wide settings, user info, or notification banners can be injected into templates automatically&lt;/li&gt;
&lt;li&gt;Consistency and Centralization: They offer a single location to control data that should be present in all or many templates, improving consistency across an application.&lt;/li&gt;
&lt;li&gt;Separation of Concerns: Logic for preparing context data can be separated from view logic, leading to cleaner, more maintainable code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. How Do Context Processors Work?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Definition: Write a function that returns a dictionary of context data.&lt;/li&gt;
&lt;li&gt;Registration: Add the function's path to the context_processors list inside the TEMPLATES setting in settings.py.&lt;/li&gt;
&lt;li&gt;Accessibility: After registration, variables from the context processor are automatically available in relevant templates, with no need to explicitly pass them in each view.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# myapp/context_processors.py
def site_defaults(request):
    return {
        'site_name': 'My Awesome Site',
        'current_year': 2025,
    }

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# settings.py
TEMPLATES = [
    {
        # ...
        'OPTIONS': {
            'context_processors': [
                # ...default processors...
                'myapp.context_processors.site_defaults',
            ],
        },
    },
]

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

&lt;/div&gt;



&lt;p&gt;Now, site_name and current_year are available in all templates.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. When Are Context Processors Used?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Automatic inclusion: Context processors are triggered whenever a template is rendered using Django's RequestContext (by default in Django’s render shortcut and generic views).&lt;/li&gt;
&lt;li&gt;Global context: Whenever variables need to appear in multiple or all templates, such as in navigation menus or footers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Common Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;User information: Displaying the current user’s name, avatar, or permissions site-wide.&lt;/li&gt;
&lt;li&gt;Navigation menus: Providing category lists or navigation items.&lt;/li&gt;
&lt;li&gt;Site metadata: Adding site title, company branding, or copyright.&lt;/li&gt;
&lt;li&gt;Notification banners: Global alerts, announcements, or maintenance messages.&lt;/li&gt;
&lt;li&gt;Cart details in e-commerce: Showing shopping cart count or summary everywhere.&lt;/li&gt;
&lt;li&gt;Settings and preference injection: Theme, language, or accessibility settings.&lt;/li&gt;
&lt;li&gt;Integration with middleware: Middleware can set attributes on the request object based on authentication, localization, or custom business rules, which context processors then expose to templates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Best Practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Only inject data that must truly be global, as context processors run on every template render.&lt;/li&gt;
&lt;li&gt;Keep context processors fast; avoid database-heavy logic inside them.&lt;/li&gt;
&lt;li&gt;Use unique variable names to prevent conflicts, especially when combining multiple processors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Summary Table
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2ita3kqqgaiwnjm98u3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2ita3kqqgaiwnjm98u3.png" alt=" " width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Django context processors are a foundational feature for sharing global variables across templates efficiently and cleanly in any scalable Django project.&lt;br&gt;
&lt;a href="https://docs.djangoproject.com/en/stable/ref/templates/api/#writing-your-own-context-processors" rel="noopener noreferrer"&gt;https://docs.djangoproject.com/en/stable/ref/templates/api/#writing-your-own-context-processors&lt;/a&gt;&lt;br&gt;
&lt;a href="https://realpython.com/django-template-context-processors/" rel="noopener noreferrer"&gt;https://realpython.com/django-template-context-processors/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>django</category>
      <category>python</category>
      <category>learning</category>
      <category>contextprocessors</category>
    </item>
    <item>
      <title>🧠 Smart Autosave: 💾 Leveraging localStorage Responsibly in a Multi-User World 🌍👥</title>
      <dc:creator>Bharat Solanke</dc:creator>
      <pubDate>Thu, 10 Jul 2025 10:06:39 +0000</pubDate>
      <link>https://dev.to/bharat_solanke_8e45411fa6/smart-autosave-leveraging-localstorage-responsibly-in-a-multi-user-world-1634</link>
      <guid>https://dev.to/bharat_solanke_8e45411fa6/smart-autosave-leveraging-localstorage-responsibly-in-a-multi-user-world-1634</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F90sh42wlujzmrk02911s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F90sh42wlujzmrk02911s.png" alt=" " width="316" height="159"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Autosave protects us from losing work due to crashes or power cuts. For one user, saving to localStorage works like magic. ✨&lt;br&gt;
But in a multi-user app with logins and private data? 🤔 It can lead to serious problems—like saving someone else’s data by mistake. 😱 That's when localStorage's simplicity can turn into a head-scratching, data-leaking nightmare. 🤯&lt;/p&gt;

&lt;p&gt;Join me on a journey through the evolution of an autosave feature, from its deceptively simple beginnings with client-side localStorage to a robust, secure system in a multi-user web application🕵️‍♀️&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 1: The Initial Approach – localStorage Autosave (So Simple, So Tempting! 🍬)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you hear "autosave," localStorage is often the first idea. Why not? It’s built-in, fast, persistent (even after browser restarts), and doesn’t need a server to work. 🙌&lt;/p&gt;

&lt;p&gt;🔁 The Workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User types → JS event (like oninput) triggers&lt;/li&gt;
&lt;li&gt;Save draft → Stored in &lt;strong&gt;localStorage&lt;/strong&gt; with a key&lt;/li&gt;
&lt;li&gt;On reload → Check &lt;strong&gt;localStorage&lt;/strong&gt;, repopulate field&lt;/li&gt;
&lt;li&gt;On submit → Clear the saved draft
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form id="myOnlineEditorForm"&amp;gt;
  &amp;lt;textarea id="editorContent" placeholder="Start writing..."&amp;gt;&amp;lt;/textarea&amp;gt;
&amp;lt;/form&amp;gt;

&amp;lt;script&amp;gt;
  const key = 'document_draft_form_A';
  const textarea = document.getElementById('editorContent');

  document.addEventListener('DOMContentLoaded', () =&amp;gt; {
    const draft = localStorage.getItem(key);
    if (draft) textarea.value = draft;
  });

  textarea.addEventListener('input', () =&amp;gt; {
    localStorage.setItem(key, textarea.value);
  });

  document.getElementById('myOnlineEditorForm').addEventListener('submit', () =&amp;gt; {
    localStorage.removeItem(key);
  });
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;✅ Why It Feels Great:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast ⚡ – No server, no delay&lt;/li&gt;
&lt;li&gt;Simple 🧠 – Easy to implement&lt;/li&gt;
&lt;li&gt;Offline-Friendly ✈️ – Works without internet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⚠️ But Here's the Catch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not User-Aware: Anyone on the same browser sees the same draft 😬&lt;/li&gt;
&lt;li&gt;Device-Locked: No access across devices&lt;/li&gt;
&lt;li&gt;Not Secure: No encryption—bad for sensitive data 🔓&lt;/li&gt;
&lt;li&gt;Storage Limits: 5–10MB max&lt;/li&gt;
&lt;li&gt;No Sync Logic: Doesn’t know if a newer draft exists elsewhere&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It worked fine for a personal notepad. But once user accounts came into play, we realized: this approach wouldn’t cut it. It was time to go server-side. 💻🔐&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 2: Backend to the Rescue – Secure, User-Specific Autosave 🌐🔐&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;localStorage&lt;/strong&gt; was great… until users, sessions, and privacy came into play. To fix the limitations—like shared browser storage and lack of syncing—we brought in the backend as the single source of truth. ✅&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🗂️ Backend Model&lt;/strong&gt;&lt;br&gt;
  We created a &lt;strong&gt;UserDraft&lt;/strong&gt; model with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;user_id → links the draft to the logged-in user&lt;/li&gt;
&lt;li&gt;form_identifier → uniquely identifies the form/editor&lt;/li&gt;
&lt;li&gt;draft_content → stores the actual draft (text or JSON)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🔌 APIs to Power Autosave&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;POST /api/save-draft/ → Save draft for the current user&lt;/li&gt;
&lt;li&gt;GET /api/load-draft/{form_identifier}/ → Load user’s draft for that form&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;💡 Updated Frontend Workflow&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On input: Send draft via AJAX to the backend&lt;/li&gt;
&lt;li&gt;On load: Fetch draft from the server&lt;/li&gt;
&lt;li&gt;If no server data? Fallback to localStorage… 😬
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Conceptual logic
if (serverDraft) {
    use(serverDraft);
} else if (localStorage.getItem(formIdentifier)) {
    use(localStorageDraft); // Just in case...
}

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

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;⚠️ The Sneaky Pitfall&lt;/strong&gt;&lt;br&gt;
This tiny fallback to localStorage seemed harmless—but it let old, possibly incorrect data sneak in. And that’s where things started getting tricky... 🕵️‍♀️💥&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 3: The Problem Unveiled – Cross-User Data Leakage! 😱&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Everything looked perfect—until we hit a chilling bug:&lt;br&gt;
&lt;strong&gt;“User B is seeing User A’s draft!” 🤯&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧩 What Happened?&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User A logs in and starts editing. Autosave stores the draft:&lt;/li&gt;
&lt;li&gt;✅ On the server (tied to User A)&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ In localStorage (document_draft_form_type_X)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;User A logs out.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;User B logs in on the same browser, opens the same editor.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Backend correctly returns no draft (User B hasn’t saved anything yet).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Our JS fallback kicks in:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;“No server draft? Let’s check localStorage!”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🔥 Boom—User A’s draft shows up for User B.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;🕵️ Root Cause: Misused Fallback + Persistent localStorage&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;localStorage belongs to the browser, not the user.&lt;/li&gt;
&lt;li&gt;Our “if server returns nothing, use localStorage” logic backfired.&lt;/li&gt;
&lt;li&gt;We accidentally loaded private data from one user into another’s session. 🚨&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Lesson Learned:&lt;/strong&gt;&lt;br&gt;
Even with perfect backend separation, client-side storage must be handled carefully in multi-user apps. A harmless fallback turned into a serious data leak. 🔒&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 4: The Robust Solution – Server as Authority, Frontend as Smart Manager ✅&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The fix was clear: the server must be the single source of truth for user data. localStorage? Just a fallback—for network issues only. 🔒&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧠 1. Smart Frontend Loading (Only Trust Server Data)&lt;/strong&gt;&lt;br&gt;
We rewrote our load logic to prioritize server data and treat localStorage as a last resort, never a fallback for "no server data."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function loadAutosaveFromServerAndPopulate(formIdentifier) {
    try {
        const res = await fetch(`/api/load-draft/${formIdentifier}/`);
        const result = await res.json();

        if (res.ok &amp;amp;&amp;amp; result.status === 'success') {
            if (result.data &amp;amp;&amp;amp; Object.keys(result.data).length &amp;gt; 0) {
                localStorage.setItem(`autosave_${formIdentifier}`, JSON.stringify(result.data));
                populateFormFields(formIdentifier, result.data); // Server wins
            } else {
                localStorage.removeItem(`autosave_${formIdentifier}`); // Clean stale data
                populateFormFields(formIdentifier, {});
            }
        } else {
            throw new Error(result.message || 'Server error');
        }
    } catch (err) {
        console.error('Error fetching autosave:', err);
        const local = localStorage.getItem(`autosave_${formIdentifier}`);
        populateFormFields(formIdentifier, local ? JSON.parse(local) : {});
    }
}

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

&lt;/div&gt;



&lt;p&gt;🔁 localStorage is now only used when the server is unreachable, not when it simply returns no data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧹 2. Backend Cleanup on Final Submission&lt;/strong&gt;&lt;br&gt;
When a user submits the form, the server:&lt;br&gt;
Saves the final document&lt;br&gt;
Deletes any associated draft&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def submit_final_document(request):
    submitted = parse_request_body(request)
    save_permanent_document_to_db(user=request.user, data=submitted)
    delete_user_draft_from_db(user_id=request.user.id, form_identifier=submitted.get('form_identifier'))
    return JsonResponse({"status": "success", "message": "Submitted &amp;amp; draft cleared."})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🧽 3. Frontend Cleanup on Submit &amp;amp; Logout&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;On Submit:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;form.addEventListener("submit", () =&amp;gt; {
    const formId = 'document_draft_form_type_A';
    localStorage.removeItem(`autosave_${formId}`);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On Logout:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function clearAllAutosaveLocalStorage() {
    Object.keys(localStorage).forEach((key) =&amp;gt; {
        if (key.startsWith('autosave_')) localStorage.removeItem(key);
    });
}
// Attach to logout action:
// &amp;lt;a href="/logout/" onclick="clearAllAutosaveLocalStorage();"&amp;gt;Logout&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;✅ Conclusion: Trust the Server, Manage the Client Smartly! 💪&lt;/strong&gt;&lt;br&gt;
This journey brought some key lessons for building reliable web apps:&lt;br&gt;
&lt;strong&gt;🛡️ Server is the Source of Truth&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always store user-specific or sensitive data on the backend—never rely on the browser alone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;💾 localStorage is a Resilience Layer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use it only for temporary caching, and only when the network fails—not as a fallback for missing server data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🧹 Clean Up Explicitly&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear autosaved data after final submission and on logout—from both server and localStorage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;👥 Think Multi-User&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always account for multiple users sharing the same browser. Design your client-side logic accordingly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By following these principles, your autosave system won’t just save work—it’ll protect privacy and deliver a seamless user experience.&lt;br&gt;
Happy coding! 🚀👨‍💻&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>security</category>
      <category>python</category>
    </item>
  </channel>
</rss>
