<?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>Forem: Samwit Adhikary</title>
    <description>The latest articles on Forem by Samwit Adhikary (@samwitadhikary).</description>
    <link>https://forem.com/samwitadhikary</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%2F563246%2Fa04cf6ae-0b3e-460a-8f39-64ff6b7c5a9a.jpeg</url>
      <title>Forem: Samwit Adhikary</title>
      <link>https://forem.com/samwitadhikary</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/samwitadhikary"/>
    <language>en</language>
    <item>
      <title>How I Built a Full Stack Community Platform from Scratch - And What I Learned</title>
      <dc:creator>Samwit Adhikary</dc:creator>
      <pubDate>Wed, 08 Apr 2026 19:15:25 +0000</pubDate>
      <link>https://forem.com/samwitadhikary/how-i-built-a-full-stack-community-platform-from-scratch-and-what-i-learned-3ljh</link>
      <guid>https://forem.com/samwitadhikary/how-i-built-a-full-stack-community-platform-from-scratch-and-what-i-learned-3ljh</guid>
      <description>&lt;p&gt;&lt;em&gt;A real story of building AskLoop - combining Medium + Dev.to + Stack Overflow with Django 5 + React 18&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem I Kept Seeing
&lt;/h2&gt;

&lt;p&gt;I've spent a lot of time on developer platforms. And I kept noticing the same pattern everywhere.&lt;/p&gt;

&lt;p&gt;You ask a question - it gets buried.&lt;br&gt;
You share something valuable - it disappears in 48 hours.&lt;br&gt;
A great discussion starts - nobody follows up.&lt;/p&gt;

&lt;p&gt;There's always content. But rarely real depth.&lt;br&gt;
There's always activity. But rarely meaningful interaction.&lt;/p&gt;

&lt;p&gt;That bothered me enough to build something about it.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;AskLoop&lt;/strong&gt; - a community platform that combines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✍️ Medium-style long-form articles&lt;/li&gt;
&lt;li&gt;❓ Stack Overflow-style Q&amp;amp;A with voting&lt;/li&gt;
&lt;li&gt;💬 Dev.to-style forum discussions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All in one place. No noise. No paywalls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live demo: &lt;a href="https://askloop-here.netlify.app" rel="noopener noreferrer"&gt;https://askloop-here.netlify.app&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Before diving into the technical stuff, here's what the platform does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;3 post types&lt;/strong&gt; - Article, Q&amp;amp;A, Forum Discussion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rich text editor&lt;/strong&gt; with image upload (TipTap)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nested comments&lt;/strong&gt; with threaded replies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Q&amp;amp;A voting&lt;/strong&gt; - upvote/downvote + accept answer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JWT Authentication&lt;/strong&gt; - register, login, refresh tokens&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User profiles&lt;/strong&gt; - bio, social links, reputation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Badge system&lt;/strong&gt; - auto-awarded at reputation milestones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time notifications&lt;/strong&gt; - likes, comments, follows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full-text search&lt;/strong&gt; with filters&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bookmarks, follows, reports&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Account deletion&lt;/strong&gt; with password confirmation&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;p&gt;I wanted to use tools I could actually understand and explain - not just copy-paste a boilerplate.&lt;/p&gt;
&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Django 5.0.6
Django REST Framework 3.15.2
Simple JWT (djangorestframework-simplejwt)
django-allauth + dj-rest-auth
PostgreSQL via Supabase
Gunicorn + Nginx
Whitenoise
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;React 18 + TypeScript
Vite
TipTap (rich text editor)
shadcn/ui + Tailwind CSS
Axios + React Router v6
date-fns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Infrastructure
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Backend → Linode (Ubuntu 22.04)
Frontend → Netlify
Database → Supabase PostgreSQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Total cost: &lt;strong&gt;$5/month&lt;/strong&gt; (just the Linode server)&lt;/p&gt;



&lt;p&gt;One thing I learned the hard way - Netlify is HTTPS but my Linode server is HTTP. Browsers block mixed content. The fix was proxying the API through Netlify using a &lt;code&gt;_redirects&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;/api/*&lt;/span&gt; &lt;span class="s"&gt;http://your-server-ip/api/:splat&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;span class="n"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;/index.html&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple. Elegant. Took me 2 hours to figure out 😅&lt;/p&gt;




&lt;h2&gt;
  
  
  Django REST Framework - What I Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Custom User Model
&lt;/h3&gt;

&lt;p&gt;Always use a custom user model from day one. Changing it later is painful.&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AbstractUser&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;bio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blank&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;reputation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IntegerField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;is_banned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BooleanField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Use email as login field
&lt;/span&gt;    &lt;span class="n"&gt;USERNAME_FIELD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;REQUIRED_FIELDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  JWT Authentication
&lt;/h3&gt;

&lt;p&gt;I used &lt;code&gt;dj-rest-auth&lt;/code&gt; with &lt;code&gt;Simple-JWT&lt;/code&gt;. The setup is straightforward but the token refresh flow took some work to get right.&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;# settings.py
&lt;/span&gt;&lt;span class="n"&gt;SIMPLE_JWT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ACCESS_TOKEN_LIFETIME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REFRESH_TOKEN_LIFETIME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ROTATE_REFRESH_TOKENS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the frontend, I added an Axios interceptor that automatically refreshes the token on 401 responses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interceptors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Refresh token and retry&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;refresh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;refresh_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/auth/token/refresh/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;refresh&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;access_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Authorization&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
              &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Slug-Based URLs
&lt;/h3&gt;

&lt;p&gt;Posts use slugs instead of IDs for better URLs.&lt;br&gt;
Auto-generated from the title on save:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SlugField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blank&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;slugify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;
            &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;while&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;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="n"&gt;slug&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Badge System
&lt;/h3&gt;

&lt;p&gt;Badges auto-award when a user crosses reputation milestones. The key was overriding &lt;code&gt;save()&lt;/code&gt; on the User model so it works regardless of how reputation is updated - even through Django admin:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&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="n"&gt;old&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;rep_changed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;old&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reputation&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reputation&lt;/span&gt;
        &lt;span class="k"&gt;except&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;DoesNotExist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;rep_changed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;rep_changed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rep_changed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_check_reputation_badges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_check_reputation_badge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;milestones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rising_star&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contributor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expert&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;legend&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;milestones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reputation&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;badge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Badge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;UserBadge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_or_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;badge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;badge&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  React — Interesting Challenges
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Edited Badge Problem
&lt;/h3&gt;

&lt;p&gt;Django sets &lt;code&gt;created_at&lt;/code&gt; and &lt;code&gt;updated_at&lt;/code&gt; in two separate operations —they're never exactly equal even on first save. Difference is ~0.5ms.&lt;/p&gt;

&lt;p&gt;My first check &lt;code&gt;updated_at !== created_at&lt;/code&gt; was always true, so everything showed "edited".&lt;/p&gt;

&lt;p&gt;Fix: Use 1 second tolerance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; 
   &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-xs italic text-muted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;edited&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Load More Pagination
&lt;/h3&gt;

&lt;p&gt;Django's paginated response returns &lt;code&gt;next&lt;/code&gt; as an &lt;strong&gt;absolute URL&lt;/strong&gt; like &lt;code&gt;http://localhost:8000/api/posts/?page=2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When I passed this to Axios, it double-prepended the base URL and got a 404.&lt;/p&gt;

&lt;p&gt;Fix: Strip the origin before passing to Axios:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;relPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Returns: /api/posts/?page=2&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;relPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextUrl&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  TipTap Image Alignment
&lt;/h3&gt;

&lt;p&gt;TipTap's default Image extension doesn't support alignment. I built a custom &lt;code&gt;FigureImage&lt;/code&gt; node that wraps &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; in &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; with alignment controlled by CSS margins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Center: margin: 0 auto&lt;/span&gt;
&lt;span class="c1"&gt;// Left:   margin-right: auto  &lt;/span&gt;
&lt;span class="c1"&gt;// Right:  margin-left: auto&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Text-align on a block element doesn't work for images — margin-based alignment does.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deployment — Things That Went Wrong
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Gunicorn 203/EXEC Error
&lt;/h3&gt;

&lt;p&gt;My first production deploy failed with &lt;code&gt;status=203/EXEC&lt;/code&gt;. The unix socket path didn't exist. Fix: switched to TCP binding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;... gunicorn --bind 127.0.0.1:8000 ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. InconsistentMigrationHistory
&lt;/h3&gt;

&lt;p&gt;Adding &lt;code&gt;django.contrib.sites&lt;/code&gt; to &lt;code&gt;INSTALLED_APPS&lt;/code&gt; after &lt;code&gt;socialaccount&lt;/code&gt; was already migrated caused this error. Fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python manage.py migrate sites &lt;span class="nt"&gt;--fake-initial&lt;/span&gt;
python manage.py migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Mixed Content (HTTPS → HTTP)
&lt;/h3&gt;

&lt;p&gt;Netlify frontend (HTTPS) calling Linode API (HTTP) gets blocked by browsers. Already covered above — Netlify proxy via &lt;code&gt;_redirects&lt;/code&gt; solves it.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Email Confirmation Template Error
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;allauth&lt;/code&gt; tried to render an email confirmation template that didn't exist. For now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ACCOUNT_EMAIL_VERIFICATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will add proper email confirmation later.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Would Do Differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Plan the data models first&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I added fields to models multiple times after the fact. Spending an extra hour designing models upfront saves days of migrations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Use environment variables from day one&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I hardcoded some values early and had to hunt them down later. Always use &lt;code&gt;.env&lt;/code&gt; from the first commit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Write API docs as you go&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Documenting endpoints after the fact is painful. I should have maintained a simple list as I built.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Test on mobile early&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The UI looked great on desktop but needed work on mobile. Test on real devices early and often.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Lines of code:  ~8,000
Time to build:  ~3 months (evenings + weekends)
Server cost:    $5/month (Linode)
Domain cost:    $0 (using free Netlify subdomain)
Total spent:    $15 so far
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Password reset via email&lt;/li&gt;
&lt;li&gt;HTTPS on the backend server&lt;/li&gt;
&lt;li&gt;OAuth (Google + GitHub login)&lt;/li&gt;
&lt;li&gt;Email notifications&lt;/li&gt;
&lt;li&gt;Mobile app (maybe)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Source Code Available
&lt;/h2&gt;

&lt;p&gt;If you want to build something similar or just learn from the code — I've made the complete source code available:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔗 &lt;a href="https://samwit.gumroad.com/l/askloop-app" rel="noopener noreferrer"&gt;https://samwit.gumroad.com/l/askloop-app&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complete Django backend&lt;/li&gt;
&lt;li&gt;Complete React frontend&lt;/li&gt;
&lt;li&gt;Production deployment guide&lt;/li&gt;
&lt;li&gt;Full API documentation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It Live
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://askloop-here.netlify.app" rel="noopener noreferrer"&gt;https://askloop-here.netlify.app&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create an account, write a post, ask a question. The community is just getting started — you'd be one of the first members.&lt;/p&gt;




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

&lt;p&gt;Building AskLoop taught me more than any tutorial ever could.&lt;/p&gt;

&lt;p&gt;Every bug was a real problem to solve.&lt;br&gt;
Every feature was a real decision to make.&lt;br&gt;
Every deployment error was a real lesson learned.&lt;/p&gt;

&lt;p&gt;If you're thinking about building something — stop thinking and start building.&lt;/p&gt;

&lt;p&gt;The first version will be ugly. Ship it anyway.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Questions? Drop them in the comments below — I read and respond to every one.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Follow me for more build-in-public content.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>django</category>
      <category>react</category>
      <category>webdev</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Part 3: Understanding Databases and Creating Your First User System</title>
      <dc:creator>Samwit Adhikary</dc:creator>
      <pubDate>Wed, 10 Dec 2025 16:26:29 +0000</pubDate>
      <link>https://forem.com/samwitadhikary/part-3-understanding-databases-and-creating-your-first-user-system-4an8</link>
      <guid>https://forem.com/samwitadhikary/part-3-understanding-databases-and-creating-your-first-user-system-4an8</guid>
      <description>&lt;p&gt;&lt;em&gt;This is Part 3 of the series "Learn Backend Development by Building a Social Media App.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Welcome to Part 3.&lt;br&gt;
So far you've understood what a backend is, created your first Django project, and seen your server run for the very first time. Now we move into one of the most important ideas in backend development: &lt;strong&gt;data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Everything in a social media app revolves around data. Users are data. Posts are data. Comments, likes, stories, notification, all data. You're going to learn how that data is stored, how Django interacts with it, and how we begin shaping something as fundamental as a user account.&lt;/p&gt;

&lt;p&gt;Today's chapter is long because this is the foundation of everything else. We're going to go step by step in a slow, friendly manner, and by the end, you'll truly understand what happens inside the backend whenever someone creates an account.&lt;/p&gt;
&lt;h2&gt;
  
  
  Before We Code: What Exactly Is a Database?
&lt;/h2&gt;

&lt;p&gt;A database is simply a place where your backend stores information. Think of it like a very organized notebook. Every time someone registers, a fresh entry is added to the notebook. Every time someone posts a picture, the notebook receives another entry. Every like, every comment, every follow, everything goes into this book.&lt;/p&gt;

&lt;p&gt;The difference between a real notebook and a database is that the database can handle millions of entries without getting tired.&lt;/p&gt;

&lt;p&gt;Your app might have thousands of users and thousands of posts. A database is where all of that lives, safely and permanently.&lt;/p&gt;

&lt;p&gt;Django communicates with the database for you. You don't write SQL. Django does the hard work.&lt;/p&gt;
&lt;h2&gt;
  
  
  Understanding Models in Django
&lt;/h2&gt;

&lt;p&gt;If a database is like a notebook full of organized information, then a &lt;strong&gt;model&lt;/strong&gt; is the template for how a particular kind of information should look.&lt;/p&gt;

&lt;p&gt;For example, if you're designing a user, you need to tell your backend what information a user should contain. Maybe a username, an email, a password, a profile picture, a date they joined, and so on. A model is how you describe that structure.&lt;/p&gt;

&lt;p&gt;A simple way to think of it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Model = Blueprint
Database Table = Building made using that blueprint
Database Row = One specific user built using that blueprint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You write the blueprint once in your Django code. Django creates the building (the table). Every time someone registers, Django adds a row to that table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Django's Built-In User Model (And Why We Don't Use It)
&lt;/h2&gt;

&lt;p&gt;Django comes with a built-in User model. It already includes username, password, email, and several other properties.&lt;/p&gt;

&lt;p&gt;But modern apps need more control. Social media especially needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;email login&lt;/li&gt;
&lt;li&gt;custom fields&lt;/li&gt;
&lt;li&gt;profile extension&lt;/li&gt;
&lt;li&gt;OTP verification&lt;/li&gt;
&lt;li&gt;unique behaviors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of this, many real projects don't use Django's default User. They create a &lt;strong&gt;custom user model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Creating it early is important because changing it later becomes very complicated. So in this part, we'll build a custom user model from the ground up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Your First Django App
&lt;/h2&gt;

&lt;p&gt;Inside your project folder, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python manage.py startapp accounts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a new app called &lt;code&gt;accounts&lt;/code&gt;. This app will contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the custom user model&lt;/li&gt;
&lt;li&gt;registration logic&lt;/li&gt;
&lt;li&gt;login logic&lt;/li&gt;
&lt;li&gt;verification features later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your folder structure now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;core/
accounts/
manage.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;accounts&lt;/code&gt; folder will soon become the home of your entire authentication system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the Custom User Model
&lt;/h2&gt;

&lt;p&gt;Let's open &lt;code&gt;accounts/models.py&lt;/code&gt;. Delete everything inside it and write this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AbstractUser&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AbstractUser&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;EmailField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;USERNAME_FIELD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;REQUIRED_FIELDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now let's understand what just happend, slowly.&lt;br&gt;
We're telling Django that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Our User is based on Django's built-in AbstractUser (so we still inherit many useful features like password handling).&lt;/li&gt;
&lt;li&gt;The default username is replaced with an email.&lt;/li&gt;
&lt;li&gt;Emails must be unique.&lt;/li&gt;
&lt;li&gt;When Django prints a user, it should show the email, not some random ID.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This gives us full control. Later parts of the series will add more fields like verification status, OTP codes, timestamps, and more.&lt;/p&gt;
&lt;h2&gt;
  
  
  Adding the Accounts App to Installed Apps (Important Step)
&lt;/h2&gt;

&lt;p&gt;You just created a new Django app called &lt;code&gt;accounts&lt;/code&gt;, but Django doesn't automatically know about it. You must tell Django to load it.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;core/settings.py&lt;/code&gt; and find &lt;code&gt;INSTALLED_APPS&lt;/code&gt;. Add &lt;code&gt;"accounts"&lt;/code&gt; there like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;INSTALLED&lt;/span&gt; &lt;span class="n"&gt;APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;django.contrib.admin&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;django.contrib.auth&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;django.contrib.contenttypes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;django.contrib.sessions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;django.contrib.messages&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;django.contrib.staticfiles&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;accounts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- Add here
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without this step, Django won't pick up your custom models at all, which would cause errors later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting the Custom User Model to Django
&lt;/h2&gt;

&lt;p&gt;Django won't magically use you new user model unless you tell it to. So open &lt;code&gt;core/settings.py&lt;/code&gt; and add this line at the bottom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;AUTH_USER_MODEL &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'accounts.User'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line is extremely important. It says, "Hey Django, use this new User model instead of the old one".&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Migrations (Very Important Concept)
&lt;/h2&gt;

&lt;p&gt;You created a model. But right now, the database knows nothing about it.&lt;br&gt;
A migration is like giving the database instructions about your blueprint. It's Django telling the database, "Please create a table for this model".&lt;/p&gt;

&lt;p&gt;We run two commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python manage.py makemigrations
python manage.py migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command reads your model and prepares instructions. The second command applies those instructions to the actual database.&lt;/p&gt;

&lt;p&gt;After running these commands, your User table now exists inside your database.&lt;/p&gt;

&lt;p&gt;You haven't created any users yet, but the table is ready to hold them. Here's a simple diagram of what happened:&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%2F4botdv07lvjxkicrv4rn.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%2F4botdv07lvjxkicrv4rn.png" alt="Migration Process" width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a process you'll repeat throughout this entire series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Your First User (Using the Admin System)
&lt;/h2&gt;

&lt;p&gt;Django has a built-in admin panel that lets you interact with your data. But before we can us it, we need to create an admin user.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python manage.py createsuperuser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will ask for an email , a username, and a password. Once done, start your server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python manage.py runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;http://127.0.0.1:8000/admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see a login page. Enter your credentials, and you'll step into Django's administrator dashboard.&lt;/p&gt;

&lt;p&gt;This dashboard will eventually show your posts, comments, users, stories, everything. Right now, it's empty, but that's perfect. It means we're ready to start filling it by building the actual registration and login system.&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%2Fih58espaxwl07dwonfvq.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%2Fih58espaxwl07dwonfvq.png" alt="Admin Dashboard" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where We Are Now
&lt;/h2&gt;

&lt;p&gt;At this moment, your backend has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a working Django server&lt;/li&gt;
&lt;li&gt;a custom user model&lt;/li&gt;
&lt;li&gt;a connected database&lt;/li&gt;
&lt;li&gt;Django admin access&lt;/li&gt;
&lt;li&gt;your first superuser account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You've taken the first real step towards building your social media backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Coming in Part 4
&lt;/h2&gt;

&lt;p&gt;In the next part, we begin turning this data into something users can actually interact with. You'll learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what JWT is&lt;/li&gt;
&lt;li&gt;how login really works&lt;/li&gt;
&lt;li&gt;how sessions differ from token authentication&lt;/li&gt;
&lt;li&gt;how to implement registration using real API endpoints&lt;/li&gt;
&lt;li&gt;how to implement login&lt;/li&gt;
&lt;li&gt;how to secure protected routes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where your backend starts becoming a real API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Till then, Happy Coding!!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>backend</category>
      <category>tutorial</category>
      <category>django</category>
    </item>
    <item>
      <title>Part 2: Setting Up Your First Django Project</title>
      <dc:creator>Samwit Adhikary</dc:creator>
      <pubDate>Tue, 09 Dec 2025 18:55:03 +0000</pubDate>
      <link>https://forem.com/samwitadhikary/post-2-setting-up-your-first-django-project-2ken</link>
      <guid>https://forem.com/samwitadhikary/post-2-setting-up-your-first-django-project-2ken</guid>
      <description>&lt;p&gt;&lt;em&gt;This is Part 2 of the series "Learn Backend Development by Building a Social Media App.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Welcome back.&lt;br&gt;
In the previous part, you learned what a backend really is and how it quietly powers almost everything inside a modern app. Now it's time to take the first real step: creating your backend project on your own computer.&lt;/p&gt;

&lt;p&gt;Before we start typing commands, let's go slowly. I want you to understand what each tool is, why we are installing it, and what your computer is actually doing. Many beginners jump straight into commands without knowing what's happening, and that usually leads to confusion later. We won't do that here. You'll know exactly what's going on.&lt;/p&gt;

&lt;p&gt;By the end of this chapter, you'll have a running Django project on your machine. Seeing that browser window open for the first time with your backend responding, that's when the journey becomes real.&lt;/p&gt;

&lt;p&gt;Let's begin.&lt;/p&gt;
&lt;h2&gt;
  
  
  Understanding the Tools We're About to Install
&lt;/h2&gt;

&lt;p&gt;Before we install anything, you should know what role each piece plays.&lt;/p&gt;
&lt;h3&gt;
  
  
  Python
&lt;/h3&gt;

&lt;p&gt;Python is the language Django runs on. Think of Python as the base ingredient in a recipe. Without it, nothing works.&lt;/p&gt;
&lt;h3&gt;
  
  
  Django
&lt;/h3&gt;

&lt;p&gt;Django is the framework that helps you create websites and backends quickly. It gives structure, tools, and a lot of built-in features.&lt;/p&gt;
&lt;h3&gt;
  
  
  Django REST Framework (DRF)
&lt;/h3&gt;

&lt;p&gt;This is the addon for Django. It helps you build APIs so your app (Android, iOS, Web) can talk to your backend easily.&lt;/p&gt;

&lt;p&gt;We won't install DRF today. We'll start with Django first so you understand its basics, then add DRF in the next parts.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Happens When You Install Django?
&lt;/h2&gt;

&lt;p&gt;When you install Django, your computer is basically getting a toolbox filled with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tools to talk to databases&lt;/li&gt;
&lt;li&gt;tools to manage users&lt;/li&gt;
&lt;li&gt;tools to build URLs&lt;/li&gt;
&lt;li&gt;tools to create models&lt;/li&gt;
&lt;li&gt;tools to render data&lt;/li&gt;
&lt;li&gt;tools to validate information&lt;/li&gt;
&lt;li&gt;tools to structure your entire project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Django is like a well-organized kitchen. Everything has a piece, everything has a purpose, and nothing is random.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1: Install Python
&lt;/h2&gt;

&lt;p&gt;If you already have Python 3.10 or above, you're good. If not, download it from &lt;a href="https://www.python.org/downloads/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While installing on Windows, &lt;strong&gt;make sure you check the box&lt;/strong&gt; that says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add Python to PATH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise your computer won't know Python exists.&lt;/p&gt;

&lt;p&gt;After installation, you can test by opening the terminal (Command Prompt, PowerShell, or Mac/Linux Terminal) and typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it prints something like &lt;code&gt;Python 3.10&lt;/code&gt; or &lt;code&gt;Python 3.11&lt;/code&gt;, you're ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Create a Project Folder
&lt;/h2&gt;

&lt;p&gt;Choose a place where you want your backend project to live and create a folder. Name it something simple:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Then open this folder in &lt;strong&gt;VS Code&lt;/strong&gt; or your preferred editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Create a Virtual Environment
&lt;/h2&gt;

&lt;p&gt;This is something many beginners don't understand at first, so let's go slow. A &lt;strong&gt;virtual environment&lt;/strong&gt; is like a small, private Python world inside your computer. Only this project will use it. Other projects won't interfere. It keeps your backend clean and avoid dependency problems.&lt;/p&gt;

&lt;p&gt;Here's the command to create it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a folder called &lt;code&gt;venv&lt;/code&gt; that holds your environment.&lt;/p&gt;

&lt;p&gt;To activate it:&lt;/p&gt;

&lt;h3&gt;
  
  
  On Windows
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;venv&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\a&lt;/span&gt;ctivate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  On macOS/Linux:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once activated, you'll see &lt;code&gt;(venv)&lt;/code&gt; appear in your terminal. That means your project is now living in its own safe little world.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Install Django
&lt;/h2&gt;

&lt;p&gt;With the virtual environment activate, install Django:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;django
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your computer will download Django and set up all its tools. To confirm it installed correctly, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;django-admin &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it shows a version number, everything worked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Create Your Django Project
&lt;/h2&gt;

&lt;p&gt;Now the fun part begins. We're going to create the base structure of your backend.&lt;br&gt;
Inside your project folder, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;django-admin startproject core &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break this down so you understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;django-admin&lt;/code&gt; is the Django command tool&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;startproject&lt;/code&gt; tells Django to create a new project&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;core&lt;/code&gt; is the name of the project folder Django will create inside your directory&lt;/li&gt;
&lt;li&gt;the dot &lt;code&gt;.&lt;/code&gt; means "create it here, not in another folder"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you run this command, Django generates several files.&lt;/p&gt;

&lt;p&gt;Here's how your project now looks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;social_media_backend/
│
├── core/
│    ├── __init.py__
│    ├── settings.py
│    ├── urls.py
│    ├── asgi.py
│    └── wsgi.py
│
└── manage.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this looks confusing, don't worry. We're going to understand every part of this folder structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding the Django Project Structure
&lt;/h3&gt;

&lt;p&gt;Let's go through the important files slowly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;manage.py&lt;/strong&gt;&lt;br&gt;
Think of this as your project's remote control. You'll use it to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;start the server&lt;/li&gt;
&lt;li&gt;apply database migrations&lt;/li&gt;
&lt;li&gt;create apps&lt;/li&gt;
&lt;li&gt;run commands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's the file you'll run the most.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;settings.py&lt;/strong&gt;&lt;br&gt;
This file controls everything Django does: database settings, installed apps, security, authentication, etc. You'll come back to this file many times as your backend grows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;urls.py&lt;/strong&gt;&lt;br&gt;
This is where you tell Django, "When someone visits this URL, run this code."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;asgi.py and wsgi.py&lt;/strong&gt;&lt;br&gt;
These connect your project to web servers. We don't need to touch them yet, but they become important when deploying.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 6: Run Your Backend for the First Time
&lt;/h2&gt;

&lt;p&gt;Now type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python manage.py runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your terminal will show something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Starting development server at http://127.0.0.1:8000/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now open your browser and visit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;http://127.0.0.1:8000/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the Django welcome page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F989zemyt3eo4btsl2ewc.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%2F989zemyt3eo4btsl2ewc.png" alt="Django Welcome Page" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a big moment. It means your backend is alive.&lt;br&gt;
Your journey as a backend developer has officially begun.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Simple Diagram: Your Project Right Now
&lt;/h2&gt;

&lt;p&gt;Here's a visual way to understand what you just created:&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%2Femky5i6epay2493idpn3.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%2Femky5i6epay2493idpn3.png" alt="System Flow" width="709" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything you will build later, login, posts, notifications, stories, will start from this simple running server.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next in Part 3
&lt;/h2&gt;

&lt;p&gt;Now that your project is created and running, the next step is understanding users. In Part 3, we'll take our first step towards real backend functionality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how databases work&lt;/li&gt;
&lt;li&gt;what models are&lt;/li&gt;
&lt;li&gt;how Django stores data&lt;/li&gt;
&lt;li&gt;how to design a custom user&lt;/li&gt;
&lt;li&gt;how to register a user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It will be your first real interaction with the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Till then happy coding!!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>backenddevelopment</category>
      <category>django</category>
      <category>socialmedia</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Part 1: What a Backend Really Is (A Gentle Introduction)</title>
      <dc:creator>Samwit Adhikary</dc:creator>
      <pubDate>Mon, 08 Dec 2025 19:13:25 +0000</pubDate>
      <link>https://forem.com/samwitadhikary/part-1-what-a-backend-really-is-a-gentle-introduction-2e0d</link>
      <guid>https://forem.com/samwitadhikary/part-1-what-a-backend-really-is-a-gentle-introduction-2e0d</guid>
      <description>&lt;p&gt;&lt;em&gt;This is Part 1 of the series "Learn Backend Development by Building a Social Media App.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the World Behind an App
&lt;/h2&gt;

&lt;p&gt;Before we start coding, I want you to picture something very simple.&lt;br&gt;
Imagine opening an app like Instagram. You scroll through posts, you leave a like, you comment on something funny, and you get a notification when someone follows you. All of this feels instant and smooth, almost magical.&lt;/p&gt;

&lt;p&gt;But here's the truth: your phone doesn't actually "know" anything. It doesn't store everyone's posts. It doesn't remember every comment. It doesn't send notifications by itself.&lt;/p&gt;

&lt;p&gt;Something else is doing all that work for every user around the world. That hidden system is called the &lt;strong&gt;backend&lt;/strong&gt;. Understanding this is the first step towards becoming a backend developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thinking of the Backend Like a Restaurant Kitchen
&lt;/h2&gt;

&lt;p&gt;A backend is easiest to understand when you think of a restaurant.&lt;/p&gt;

&lt;p&gt;Imagine you're sitting at a table with a menu. That menu is like the app you see on your phone. You tap buttons just like you point at items on a menu.&lt;/p&gt;

&lt;p&gt;The waiter who takes your order is the &lt;strong&gt;API&lt;/strong&gt;. He carries your request from the table to the kitchen.&lt;/p&gt;

&lt;p&gt;The kitchen is the &lt;strong&gt;backend&lt;/strong&gt;. You never see it, but that's where the action happens.&lt;/p&gt;

&lt;p&gt;If you ask for food, the waiter tells the kitchen what you want. If you like a post, your app tells the backend which post you liked.&lt;/p&gt;

&lt;p&gt;In both cases, the kitchen prepares something and sends it back through the waiter. &lt;/p&gt;

&lt;p&gt;That's the basic idea behind every app today.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Your App Talks to the Backend
&lt;/h2&gt;

&lt;p&gt;Whenever you do something like "open my feed", your app sends a message to a server. The message might simply say, "Please send all posts from today."&lt;/p&gt;

&lt;p&gt;The server reads it, looks into its database, collects the posts, and send them back. To keep things organized, all communication follows rules. These rules are called &lt;strong&gt;APIs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An API isn't complicated. It's basically a polite way for your app to say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Here's what I want. Here's who I am. Can you give me the right data?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The server replies with a structured data your app can understand. And just like that, your feed refreshes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Backend Exists in the First Place
&lt;/h2&gt;

&lt;p&gt;If you're building a real social media app, you quickly discover that the backend is the heart of everything. &lt;/p&gt;

&lt;p&gt;Your phone cannot store the world's posts. Your friend's phone cannot store everyone's comments. Your laptop cannot decide who should receive a notification.&lt;/p&gt;

&lt;p&gt;You need one central brain, a &lt;strong&gt;backend&lt;/strong&gt; that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stores all the data safely&lt;/li&gt;
&lt;li&gt;decides what each user should see&lt;/li&gt;
&lt;li&gt;verifies passwords securely&lt;/li&gt;
&lt;li&gt;prevents one user from deleting another user's posts&lt;/li&gt;
&lt;li&gt;sends notifications in real time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without a backend, your app is just a collection of buttons that don't actually &lt;em&gt;do&lt;/em&gt; anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why We Build With Django
&lt;/h2&gt;

&lt;p&gt;There are many backend frameworks out there, but &lt;strong&gt;Django&lt;/strong&gt; is one of the best for beginners because it takes care of a lot of complicated things for you.&lt;/p&gt;

&lt;p&gt;Imagine if you had to personally write code to manage users, handle passwords, connect to a database, check data, validate inputs, and secure everything.&lt;br&gt;
It would be overwhelming.&lt;/p&gt;

&lt;p&gt;Django puts all the basic building blocks in place, so you focus on understanding concepts instead of fighting with the foundation.&lt;/p&gt;

&lt;p&gt;Then, to create APIs, we add &lt;strong&gt;Django REST Framework&lt;/strong&gt;, usually called &lt;strong&gt;DRF&lt;/strong&gt;. It makes sending and receiving data simple. Think of Django as the house and DRF as the furniture that makes it usable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Our Final Social Media Backend Will Look Like
&lt;/h2&gt;

&lt;p&gt;We're not building a small demo. We are building something that resembles a real backend like the ones used in production apps.&lt;/p&gt;

&lt;p&gt;Your backend will eventually support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Account creation&lt;/li&gt;
&lt;li&gt;Login with JWT&lt;/li&gt;
&lt;li&gt;Email verification&lt;/li&gt;
&lt;li&gt;User profiles&lt;/li&gt;
&lt;li&gt;Uploads for images and videos&lt;/li&gt;
&lt;li&gt;Comments&lt;/li&gt;
&lt;li&gt;Likes&lt;/li&gt;
&lt;li&gt;Stories&lt;/li&gt;
&lt;li&gt;Follows and unfollows&lt;/li&gt;
&lt;li&gt;Notifications&lt;/li&gt;
&lt;li&gt;Real-time updates&lt;/li&gt;
&lt;li&gt;Group features&lt;/li&gt;
&lt;li&gt;Exporting your entire user data&lt;/li&gt;
&lt;li&gt;And a proper deployment setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This might sound like a lot right now, but we'll move slowly so that every piece makes sense before we add the next one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tools We Will Use Throughout This Journey
&lt;/h2&gt;

&lt;p&gt;Since this is a full project, we will use tools that real developers use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Django&lt;/strong&gt; will be the main framework.&lt;br&gt;
&lt;strong&gt;DRF&lt;/strong&gt; will help us build APIs.&lt;br&gt;
&lt;strong&gt;PostgreSQL&lt;/strong&gt; will store your data safely.&lt;br&gt;
&lt;strong&gt;Redis&lt;/strong&gt; will make instant notification possible.&lt;br&gt;
&lt;strong&gt;Django Channels&lt;/strong&gt; will let the server talk to user in real time.&lt;br&gt;
&lt;strong&gt;S3&lt;/strong&gt; will store images and videos.&lt;br&gt;
&lt;strong&gt;Docker&lt;/strong&gt; will help us run everything cleanly in production.&lt;/p&gt;

&lt;p&gt;You don't need to worry about these now. Each one will be introduced in the moment it becomes relevant, so you never feel lost.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Everything Works Together
&lt;/h2&gt;

&lt;p&gt;Here's a simple flowchart that shows how the final system works. Don't worry about understanding everything yet - this is just a picture of the destination.&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%2Foo3oehuj901an6tl3zeq.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%2Foo3oehuj901an6tl3zeq.png" alt="System Flowchart" width="800" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you understand this flow, you'll understand how real social media apps operate behind the scenes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You'll Gain From This Series
&lt;/h2&gt;

&lt;p&gt;By the time you finish all the parts, you will not only know how to build a backend, you'll understand the thinking behind backend development. You'll know why certain tools exist and where they fit. You'll know how apps actually communicate with servers. You'll know how data flows through systems.&lt;/p&gt;

&lt;p&gt;Most importantly, you'll gain the confidence to build apps of your own, without relying on tutorials.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Coming Next in Part 2
&lt;/h2&gt;

&lt;p&gt;In the next part, we will finally start coding. You'll install Python, install Django, set up your project, and run your first backend server.&lt;/p&gt;

&lt;p&gt;This is where your learning becomes real. You'll see something working on your screen because of code you wrote.&lt;/p&gt;

</description>
      <category>django</category>
      <category>backenddevelopment</category>
      <category>redis</category>
      <category>database</category>
    </item>
  </channel>
</rss>
