<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Django - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Django - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 25 May 2026 22:37:32 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/django/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Scoped Note-Taking API with Django Rest Framework and SimpleJWT ]]>
                </title>
                <description>
                    <![CDATA[ If you've built a Django API and you're wondering how to add authentication so that each user can only access their own data, you're in the right place. Most Django tutorials teach you session-based a ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-scoped-note-taking-api-with-django-rest-framework-and-simplejwt/</link>
                <guid isPermaLink="false">69fa4395a386d7f121cd3bfc</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                    <category>
                        <![CDATA[ REST API ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Programming Blogs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ django rest framework ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Prabodh Tuladhar ]]>
                </dc:creator>
                <pubDate>Tue, 05 May 2026 19:23:01 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/36921ffa-4741-4e11-8f16-2c84322ebceb.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you've built a Django API and you're wondering how to add authentication so that each user can only access their own data, you're in the right place.</p>
<p>Most Django tutorials teach you session-based authentication. That works fine when your frontend and backend live on the same server. But the moment you separate them – say, a React app on Netlify talking to a Django API on PythonAnywhere – then sessions start to break down.</p>
<p>Cookies don't travel well across different domains, and suddenly your login system stops working.</p>
<p>That's where JSON Web Tokens (JWT) come in. JWTs give you a stateless, cookie-free way to authenticate users. They work seamlessly across domains, devices, and platforms. The server doesn't need to remember anything. It just verifies the token's signature and knows exactly who's making the request.</p>
<p>But authentication is only half the problem. Once you know who a user is, you still need to control what they can see. This is where <strong>scoping</strong> comes in.</p>
<p>Scoping means ensuring that each user can only access their own data. User A should never be able to read, edit, or delete User B's data (notes in our case), even if they somehow guess the right ID.</p>
<p>In this tutorial, you'll build a a personal note-taking API where users can register, log in with JWT tokens, and store notes that only they can access.</p>
<p>Along the way, you'll implement a custom user model, configure SimpleJWT for token-based authentication, and write scoped views that lock each user's data behind their own credentials.</p>
<h3 id="heading-what-well-cover">What We'll Cover:</h3>
<ul>
<li><p><a href="#heading-prerequisities">Prerequisities</a></p>
</li>
<li><p><a href="#heading-what-is-jwt-and-why-use-it-over-session-authentication">What is JWT and Why Use It Over Session Authentication</a>?</p>
<ul>
<li><p><a href="#heading-how-session-authentication-works">How Session Authentication Works</a></p>
</li>
<li><p><a href="#heading-how-jwt-authentication-works">How JWT Authentication Works</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-1-how-to-set-up-the-project-and-install-the-dependecies">Step 1: How to Set Up the Project and Install the Dependecies</a></p>
<ul>
<li><p><a href="#heading-11-how-to-create-the-project">1.1 How to Create the Project</a></p>
</li>
<li><p><a href="#heading-12-how-to-create-a-virtual-environment-and-install-the-required-dependencies">1.2 How to Create a Virtual Environment and Install the Required Dependencies</a></p>
</li>
<li><p><a href="#heading-13-how-to-create-the-project-and-the-app">1.3 How to Create the Project and the App</a></p>
</li>
<li><p><a href="#heading-14-how-to-register-the-app-and-django-rest-framework-drf">1.4 How to Register the App and Django Rest Framework (DRF)</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-2-how-to-create-a-custom-user-model">Step 2: How to Create a Custom User Model</a></p>
<ul>
<li><p><a href="#heading-21-how-to-define-the-custom-user-model">2.1 How to Define the Custom User Model</a></p>
</li>
<li><p><a href="#heading-22-how-to-tell-django-to-use-your-custom-user-model">2.2 How to Tell Django to Use Your Custom User Model</a></p>
</li>
<li><p><a href="#heading-23-how-to-run-migrations">2.3 How to Run Migrations</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-3-how-to-define-the-note-model">Step 3: How to Define the Note Model</a></p>
<ul>
<li><p><a href="#heading-32-how-to-apply-migration">3.2 How to Apply Migration</a></p>
</li>
<li><p><a href="#heading-33-how-to-register-models-in-the-admin">3.3 How to Register Models in the Admin</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-4-how-to-create-the-serializer">Step 4: How to Create the Serializer</a></p>
<ul>
<li><p><a href="#heading-41-how-to-create-userserializer">4.1 How to Create UserSerializer</a></p>
</li>
<li><p><a href="#heading-42-how-to-create-noteserializer">4.2 How to Create NoteSerializer</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-5-how-to-configure-simplejwt">Step 5: How to Configure SimpleJWT</a></p>
<ul>
<li><p><a href="#heading-51-how-to-update-rest-framework-settings">5.1 How to Update REST Framework Settings</a></p>
</li>
<li><p><a href="#heading-52-how-to-add-token-url-endpoints">5.2 How to Add Token URL Endpoints</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-6-how-to-build-the-authentication-logic">Step 6: How to Build the Authentication Logic</a></p>
</li>
<li><p><a href="#heading-step-7-how-to-implement-scoped-views">Step 7: How to Implement Scoped Views</a></p>
<ul>
<li><p><a href="#heading-71-how-to-create-a-noteviewset">7.1 How to Create a NoteViewSet</a></p>
</li>
<li><p><a href="#heading-72-why-this-matters-preventing-id-enumeration-attacks">7.2 Why This Matters: Preventing ID Enumeration Attacks</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-8-how-to-connect-a-url">Step 8: How to Connect a URL</a></p>
<ul>
<li><p><a href="#heading-81-how-to-create-app-level-urls">8.1 How to Create App-level URLs</a></p>
</li>
<li><p><a href="#heading-82-how-to-verify-the-project-level-urls">8.2 How to Verify the Project-Level URLs</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-9-how-to-test-the-apis-with-postman">Step 9: How to Test the APIs with Postman</a></p>
<ul>
<li><p><a href="#heading-91-how-to-register-a-user">9.1 How to Register a User</a></p>
</li>
<li><p><a href="#heading-92-how-to-obtain-access-and-refresh-tokens">9.2 How to Obtain Access and Refresh Tokens</a></p>
</li>
<li><p><a href="#heading-93-how-to-create-a-note">9.3 How to Create a Note</a></p>
</li>
<li><p><a href="#heading-94-how-to-list-your-notes">9.4 How to List Your Notes</a></p>
</li>
<li><p><a href="#heading-95-how-to-demostrate-scoping">9.5 How to Demostrate Scoping</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-10-how-to-handle-token-expiration-with-refresh-tokens">Step 10: How to Handle Token Expiration with Refresh Tokens</a></p>
</li>
<li><p><a href="#heading-how-you-can-improve-this-project">How You Can Improve This Project</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<p>Here's what this tutorial covers:</p>
<ol>
<li><p>How to set up a custom user model (and why you should always do this)</p>
</li>
<li><p>How to configure SimpleJWT for access and refresh token authentication</p>
</li>
<li><p>How to build serializers that protect sensitive fields</p>
</li>
<li><p>How to scope your API views so users only see their own data</p>
</li>
<li><p>How to test the entire flow using Postman</p>
</li>
</ol>
<p>Let's get started</p>
<h2 id="heading-prerequisities">Prerequisities</h2>
<p>Before you begin, make sure you're comfortable with the following:</p>
<ol>
<li><p><strong>Django fundamentals</strong>: You should understand how Django projects and apps work, including models, views, URLs, and migrations.</p>
</li>
<li><p><strong>Django REST Framework basics</strong>: You should be familiar with serializers, viewsets or API views, and how DRF handles requests and responses.</p>
</li>
<li><p><strong>Basic command line usage</strong>: You'll run commands in your terminal throughout this tutorial.</p>
</li>
</ol>
<p>Tools you'll need installed:</p>
<ul>
<li><p>Python 3.8 or higher</p>
</li>
<li><p>pip (Python's package manager)</p>
</li>
<li><p>A code editor like Visual Studio Code</p>
</li>
<li><p>Postman (or any API testing tool) for testing your endpoints. You'll use this to send requests to your API.</p>
</li>
</ul>
<h2 id="heading-what-is-jwt-and-why-use-it-over-session-authentication">What is JWT and Why Use It Over Session Authentication?</h2>
<p>Before you write any code, it's important to understand what problem JWTs solve and why Django's built-in session authentication isn't always enough.</p>
<h3 id="heading-how-session-authentication-work">How Session Authentication Work</h3>
<p>Django ships with a session-based authentication system. Here's how it works at a high level:</p>
<ol>
<li><p>A user sends their username and password to the server.</p>
</li>
<li><p>The server verifies the credentials and creates a <strong>session</strong> which is a small record stored in the server's database that says "this user is logged in."</p>
</li>
<li><p>The server sends back a <strong>session ID</strong> as a cookie. The browser stores this cookie automatically.</p>
</li>
<li><p>On every subsequent request, the browser sends the cookie back to the server. The server looks up the session ID in its database and says "ah, this is User A. Let them through."</p>
</li>
</ol>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/2689a08a-f8a9-4b83-ad7b-cc4c2de90419.png" alt="The infographics shows the steps taken in Django session authentication" style="display:block;margin:0 auto" width="2708" height="1252" loading="lazy">

<p>This works perfectly when your frontend and backend live on the same domain. The browser handles cookies automatically, and Django manages sessions in the database without you thinking about it.</p>
<p>But this approach has some limitations.</p>
<ol>
<li><p><strong>The cross-domain problem:</strong> If your React frontend lives at app.example.com and your Django API lives at <a href="http://api.example.com">api.example.com</a>, cookies become tricky. Browsers enforce strict rules about which domains can send and receive cookies.</p>
<p>You can work around this with CORS (Cross-Origin Resource Sharing) headers and special cookie settings, but it adds complexity and can be fragile.</p>
</li>
<li><p><strong>The scalability problem:</strong> Every active session is stored in the server's database. If you have 10,000 users logged in at the same time, that's 10,000 session records the server has to look up on every single request. As your application grows, this lookup becomes a bottleneck.</p>
</li>
<li><p><strong>The mobile problem:</strong> Mobile apps don't handle cookies the same way browsers do. If you're building an API that will serve both a web app and a mobile app, session cookies create extra headaches.</p>
</li>
</ol>
<h3 id="heading-how-jwt-authentication-works">How JWT Authentication Works</h3>
<p>JWTs take a fundamentally different approach. Instead of storing session data on the server, they put the authentication information directly into the token itself.</p>
<p>Here's how the flow works:</p>
<ol>
<li><p>A user sends their username and password to the server.</p>
</li>
<li><p>The server verifies the credentials and creates a JWT – a long encoded string that contains information like the user's ID and when the token expires.</p>
</li>
<li><p>The server sends this token back to the client. The client stores it (usually in memory or local storage).</p>
</li>
<li><p>On every subsequent request, the client includes the token in the request header. The server reads the token, verifies its signature, and says "this is User A. Let them through."</p>
</li>
</ol>
<p>Notice the key difference: <strong>the server never stores anything</strong>.</p>
<p>It doesn't look up a session in a database. It simply reads the token, checks its cryptographic signature to make sure nobody tampered with it, and extracts the user information. That's why JWTs are called <strong>stateless</strong> – the server doesn't maintain any state about who is logged in.</p>
<p><strong>This solves the cross-domain problem</strong> because tokens are sent in the request header, not as cookies. Headers work the same way regardless of which domain the request comes from.</p>
<p><strong>This solves the scalability problem</strong> because the server doesn't store sessions. Verifying a token is a quick cryptographic check, not a database lookup.</p>
<p><strong>This solves the mobile problem</strong> because any client that can send HTTP headers can use JWT. Mobile apps, desktop apps, other servers – they all work the same way.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/41d60dbd-707c-4483-8374-8910024bda7f.png" alt="The infographics shows the steps taken in JWT authentication" style="display:block;margin:0 auto" width="2682" height="1272" loading="lazy">

<h2 id="heading-step-1-how-to-set-up-the-project-and-install-the-dependecies">Step 1: How to Set Up the Project and Install the Dependecies</h2>
<h3 id="heading-11-how-to-create-the-project">1.1 How to Create the Project</h3>
<p>Open your terminal, navigate to where you want your project to live, and run the following commands:</p>
<pre><code class="language-shell">mkdir notes-project

cd notes-project
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/594f6c90-ea92-4859-9d9b-442d2fd2f23d.png" alt="The image shows the creation of notes project folder" style="display:block;margin:0 auto" width="1642" height="429" loading="lazy">

<h3 id="heading-12-how-to-create-a-virtual-environment-and-install-the-required-dependencies">1.2 How to Create a Virtual Environment and Install the Required Dependencies</h3>
<p>You will create a virtual environment here. Type the following command:</p>
<pre><code class="language-shell">python3 -m venv venv
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/4c1c9c33-9ffb-433a-a8f0-60cf0f598e14.png" alt="The image shows the creation of the virtual environment folder after tying the command" style="display:block;margin:0 auto" width="1778" height="380" loading="lazy">

<p>The above command creates a virtual environment inside a folder called <code>venv</code>. The first <code>venv</code> is the command and the second <code>venv</code> represents the name of the folder. You can name the folder anything though <code>venv</code> is usually preferred.</p>
<p>To activate the virtual environment, we need to use the following command:</p>
<p>On macOS/Linux:</p>
<pre><code class="language-shell">source venv/bin/activate
</code></pre>
<p>On Windows:</p>
<pre><code class="language-shell">venv\Scripts\activate
</code></pre>
<p>You'll know it worked when you see <code>(venv)</code> at the beginning of your terminal prompt. From this point on, any Python packages you install will only exist inside this <strong>virtual environment</strong>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/0a58f106-684c-4286-91fc-3c60f6e45483.png" alt="The image shows virtual environment being activated" style="display:block;margin:0 auto" width="2072" height="558" loading="lazy">

<p>With the virutal environment activated, install Django, Django Rest Framework, and Simple JWT Framework using the command:</p>
<pre><code class="language-shell">pip install django djangorestframework djangorestframework-simplejwt 
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/af83ad3f-3201-4e59-9367-e95630ae3cb3.png" alt="The image shows the installation of the packages after running the pip command" style="display:block;margin:0 auto" width="2314" height="1326" loading="lazy">

<p>You can verify everything installed correctly by running:</p>
<pre><code class="language-shell">pip list
</code></pre>
<p>You should see all three packages listed along with their dependencies.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/b725778c-57ca-42cb-a0a2-486ec3e6286e.png" alt="The image shows a list of all the dependencies along with the dependencies installed just now" style="display:block;margin:0 auto" width="2316" height="1038" loading="lazy">

<h3 id="heading-13-how-to-create-the-project-and-the-app">1.3 How to Create the Project and the App</h3>
<p>Run the following command to create the Django project:</p>
<pre><code class="language-plaintext">django-admin startproject notes_core .
</code></pre>
<p>The dot at the end is important. It tells Django to create the project files in your current directory instead of creating an extra nested folder.</p>
<p>Now let's type this command to create the app:</p>
<pre><code class="language-shell">python manage.py startapp notes
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/f0f95ce1-b8b0-4acb-ae5a-194fa3d74e08.png" alt="The image shows the folder structure of django project and app" style="display:block;margin:0 auto" width="2860" height="1638" loading="lazy">

<h3 id="heading-14-how-to-register-the-app-and-django-rest-framework-drf">1.4 How to Register the App and Django Rest Framework (DRF)</h3>
<p>Open <code>notes_core/settings.py</code> and add <code>rest_framework</code> and <code>notes</code> in the <code>INSTALLED_APPS</code> list:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/35988eff-506c-43af-ac6c-ef698e843ad3.png" alt="The image show the DRF and notes app being added to installed app list" style="display:block;margin:0 auto" width="2740" height="1312" loading="lazy">

<p>Django now knows about your new app and the REST framework. Let's move on to the most important architectural decision you'll make for this project.</p>
<h2 id="heading-step-2-how-to-create-a-custom-user-model">Step 2: How to Create a Custom User Model</h2>
<p>If you've built Django projects before, you might have used Django's default User model. For quick prototypes, that works fine. But for any project you plan to grow or maintain, starting with a custom user model is a best practice you should never skip.</p>
<p>Here's why: Django's default <code>User</code> model uses a <code>username</code> field as the primary identifier. If you later decide you want users to log in with their email address instead, or you need to add a profile picture field, or a phone number, then you're stuck.</p>
<p>Using a custom user model gives you full control over what a "user" means in your app. Instead of being tied to a username, you can design login around something more practical, like email or phone_number for a fitness or mobile-based app. You can also include fields like role (doctor, patient, receptionist in a clinic system) or date of birth directly in the user model, instead of managing a separate profile.</p>
<p>It also helps future-proof your project. If you start with the default model and later decide to switch login from username to email, or add required fields, it becomes difficult and risky to change. Using a custom user model from the beginning avoids this problem and makes it much easier to adapt your authentication system as your app grows.</p>
<p>By creating a custom user model from the start, even if it's identical to the default one, you give yourself the freedom to make changes later without any of that pain.</p>
<h3 id="heading-21-how-to-define-the-custom-user-model">2.1 How to Define the Custom User Model</h3>
<p>Open <code>notes/models/py</code> and add the following code:</p>
<pre><code class="language-python">from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    pass
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/29310f13-cdc9-409a-9c36-23234882fd6e.png" alt="The image shows the code for the custom user model" style="display:block;margin:0 auto" width="2802" height="1194" loading="lazy">

<p>You are importing Django’s built-in <code>AbstractUser</code> class.</p>
<p>Think of <code>AbstractUser</code> as a ready-made blueprint for a user. It already includes fields like username, password, email, first name, last name , and authentication logic.</p>
<p>The <code>pass</code> statement means you're not adding any extra fields yet.</p>
<p>But the key point is that this model is yours. So this model behaves exactly like Django’s default user model, but with one <strong>big advantage</strong>: you now have the flexibility to customize it later.</p>
<p>If three months from now you need to add a <code>phone_number</code> field or switch to email-based login, you just add a field to this class and run a migration.</p>
<pre><code class="language-python">from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    phone_number = models.CharField(max_length=15)
</code></pre>
<p>You can also see all the fields that the <code>CustomUser</code> class has inherited from the <code>AbstractUser</code> class.</p>
<p>To do this we can use the Python shell. Type the following command:</p>
<pre><code class="language-shell">python manage.py shell
</code></pre>
<p>When you type this command, make sure that the virtual environment is active:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/3c958f7f-6403-4eaf-b339-43532a02af6a.png" alt="The image shows the command to enter into the python shell with the virtual environment being activated" style="display:block;margin:0 auto" width="2588" height="604" loading="lazy">

<p>After this, import the <code>CustomUser</code> model in the shell:</p>
<pre><code class="language-shell">from notes.models import CustomUser
</code></pre>
<p>After that, type the following code:</p>
<pre><code class="language-shell">[fields.name for field in CustomUser._meta.get_fields()]
</code></pre>
<p>The above statement lists out all the fields in the <code>CustomUser</code> class.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/5a1b3314-7b48-41f6-98a9-dc3133cfce4c.png" alt="The image shows the output of all the fileds inherited by the CustomUser model" style="display:block;margin:0 auto" width="2638" height="904" loading="lazy">

<h3 id="heading-22-how-to-tell-django-to-use-your-custom-user-model">2.2 How to Tell Django to Use Your Custom User Model</h3>
<p>Now comes the important bit. Open <code>notes_core/settings.py</code> and add this line:</p>
<pre><code class="language-python">AUTH_USER_MODEL = 'notes.CustomUser'
</code></pre>
<p>This setting tells Django to use your <code>CustomUser</code> model instead of the built-in one for everything authentication-related such as login, permissions, foreign keys, and so on.</p>
<p>There's no strict rule to where you need to add it, but the best practice is to add it near the end of the file.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/87c9b03c-908d-4b2d-a6d5-9528ff98d1ab.png" alt="The image shows the above code being added to the settings.py file" style="display:block;margin:0 auto" width="2870" height="1280" loading="lazy">

<p>You can see which user model Django is using by using the method <code>get_user_model()</code>.</p>
<p>Open the Python shell again and import the <code>get_user_model()</code> method:</p>
<pre><code class="language-shell">from django.contrib.auth import get_user_model 
</code></pre>
<p>Then use <code>get_user_model()</code> and print the output:</p>
<pre><code class="language-shell">user = get_user_model()
print(user)
</code></pre>
<p>You should see the name of our model being used:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/895d5bcc-6880-4c4d-9007-96d44e9fa496.png" alt="895d5bcc-6880-4c4d-9007-96d44e9fa496" style="display:block;margin:0 auto" width="1580" height="276" loading="lazy">

<p>If you hadn't added the <code>AUTH_USER_MODEL</code> in the <code>settings.py</code> file, then Django would have used the default user model:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/a89f013a-e5d8-4e59-90b8-7da594188c13.png" alt="The image shows the default user model being used by Django" style="display:block;margin:0 auto" width="1886" height="1126" loading="lazy">

<p><strong>Note:</strong> You'll need to do this before you run your first migration. If you run migrate before setting AUTH_USER_MODEL, Django creates tables for the default User model, and switching afterward becomes a headache.</p>
<h3 id="heading-23-how-to-run-migrations">2.3 How to Run Migrations</h3>
<p>Now create and apply the initial migrations:</p>
<pre><code class="language-shell">python manage.py makemigrations
python manage.py migrate
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/cbca499a-286c-4f07-9a3e-375a69b4c374.png" alt="The image shows the output after running the above commands" style="display:block;margin:0 auto" width="2072" height="1222" loading="lazy">

<p>Django will create the necessary tables for your custom user model along with all the built-in Django tables.</p>
<p>We can again peek under the hood to see the SQL queries that Django used to create the tables especially the <code>CustomUser</code> table.</p>
<p>Type this command:</p>
<pre><code class="language-shell">python manage.py sqlmigrate notes 0001
</code></pre>
<p>Here <code>notes</code> is the name of the app and <code>0001</code> represents the migration number.</p>
<p>And you should get this output:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/850bba6c-8e54-4beb-8b85-f30f193341d5.png" alt="The image shows the output after the sqlmigrate command is executed" style="display:block;margin:0 auto" width="2714" height="1486" loading="lazy">

<p>Let's also create a superuser so you can access the admin panel later for debugging:</p>
<pre><code class="language-shell">python manage.py createsuperuser
</code></pre>
<p>Fill in the username, email (optional), and password when prompted.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/43820470-86b4-4274-834a-19a97adbc208.png" alt="The image shows the super user being created" style="display:block;margin:0 auto" width="2172" height="596" loading="lazy">

<h2 id="heading-step-3-how-to-define-the-note-model">Step 3: How to Define the Note Model</h2>
<p>Now let's create the data model for the core of your application. First add a new import to use the <code>settings</code> object.</p>
<pre><code class="language-python">from django.conf import settings
</code></pre>
<p>Then add the following code below the <code>CustomUser</code> class:</p>
<pre><code class="language-python">class Notes(models.Model):
    owner = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='notes'
    )
    title = models.CharField(max_length=200)
    body = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    def __str__(self):
        return f"{self.title} (by {self.owner.username})"
</code></pre>
<p>Here's the complete <code>model.py</code> code:</p>
<pre><code class="language-python">from django.contrib.auth.models import AbstractUser
from django.db import models
from django.conf import settings

class CustomUser(AbstractUser):
    pass

class Notes(models.Model):
    owner = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='notes'
    )
    title = models.CharField(max_length=200)
    body = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    def __str__(self):
        return f"{self.title} (by {self.owner.username})"
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/24f1c5e3-6f9a-4dce-a7dc-d5d17aecc202.png" alt="The image shows the complete models.py file" style="display:block;margin:0 auto" width="2728" height="1314" loading="lazy">

<p>Let's walk through each field:</p>
<ol>
<li><p><code>owner = models.ForeignKey(settings.AUTH_USER_MODEL, ...)</code>: Creates a relationship between each note and a user. The <code>ForeignKey</code> field tells Django that each note belogs to exactly one user but a user can have many notes.</p>
<p>Notice that we use <code>settings.AUTH_USER_MODEL</code> instead of directly importing <code>CustomUser</code>. This is the recommended practice because it keeps your code flexible. If you ever change the user model reference in settings, this foreign key adapts automatically.</p>
<p>The <code>on_delete=models.CASCADE</code> means that if a user is deleted, all their notes are deleted too.</p>
<p>The <code>related_name='notes'</code> lets you access a user's notes with <code>user.notes.all()</code>.</p>
</li>
<li><p><code>title = models.CharField(max_length=200)</code>: Creates a text field for the task name, limited to 200 characters.</p>
</li>
<li><p><code>body = models.TextField()</code>: Holds the actual note content. <code>TextField</code> has no character limit, so users can write as much as they need.</p>
</li>
<li><p><code>created_at = models.DateTimeField(auto_now_add=True)</code>: Automatically records the date and time when a task is created. You never need to set this manually.</p>
<p>The <code>__str__()</code> method gives each note a readable representation. Instead of seeing "Note object (1)" in the admin panel or during debugging, you'll see something like "Meeting Notes (by Solina)."</p>
</li>
</ol>
<h3 id="heading-32-how-to-apply-migration">3.2 How to Apply Migration</h3>
<p>Run the migration commands to create the Note table:</p>
<pre><code class="language-shell">python manage.py makemigrations
python manage.py migrate
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/bf4b3469-89d8-4f8b-8b0d-1ea24eda5e2e.png" alt="The image shows the result of migrating the notes model" style="display:block;margin:0 auto" width="2594" height="1304" loading="lazy">

<p>As before, we can see the exact SQL query Django used to create the <code>notes</code> table:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/606d0d20-7c4f-40fb-a37b-de623ccee574.png" alt="The image shows the SQL query to create the notes table  and reference to the custom user table created earlier" style="display:block;margin:0 auto" width="2700" height="634" loading="lazy">

<h3 id="heading-33-how-to-register-models-in-the-admin">3.3 How to Register Models in the Admin</h3>
<p>Open <code>notes/admin.py</code> and register both models so you can inspect data through the admin panel:</p>
<pre><code class="language-python">from django.contrib import admin
from .models import CustomUser, Notes

admin.site.register(CustomUser)
admin.site.register(Notes)
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/482fc6de-33d2-4d09-8c6f-ce3df684eaa3.png" alt="The image shows the code for admin.py" style="display:block;margin:0 auto" width="2376" height="736" loading="lazy">

<p>This is helpful during development when you want to quickly check whether data is being saved correctly.</p>
<h2 id="heading-step-4-how-to-create-the-serializer">Step 4: How to Create the Serializer</h2>
<p>In DRF, a serializer is like a bridge between your database and the internet.</p>
<p>Django models store data as Python objects. But when you want to send that data to a frontend application (like React or a mobile app), you can't send Python objects. You need to send a format that everyone understands which is usually JSON.</p>
<p>Serializers perform three main jobs:</p>
<ol>
<li><p><strong>Serialization:</strong> Converting complex Python objects (Models) into Python dictionaries (which can be easily rendered into JSON).</p>
</li>
<li><p><strong>Deserialization:</strong> Converting JSON data coming from a user back into complex Python objects.</p>
</li>
<li><p><strong>Validation:</strong> Checking if the incoming data is correct before saving it to the database.</p>
</li>
</ol>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/a7339d6d-e338-4e12-a837-a780e85752a6.png" alt="The image shows the serialization deserialization process" style="display:block;margin:0 auto" width="800" height="287" loading="lazy">

<h3 id="heading-41-how-to-create-userserializer">4.1 How to Create UserSerializer</h3>
<p>Create a new file called <code>notes/serializers.py</code> and add the following code:</p>
<pre><code class="language-python">from rest_framework import serializers
from django.contrib.auth import get_user_model

User = get_user_model()
class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True)

    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'password']

    def create(self, validated_data):
        user = User.objects.create_user(
            username=validated_data['username'],
            email=validated_data.get('email', ''),
            password=validated_data['password']
        )
        return user
</code></pre>
<p>Let's break down this serializer.</p>
<ol>
<li><p>The <code>UserSerializer</code> handles user registration.</p>
</li>
<li><p><code>User = get_user_model()</code> gets the user model that you're using and stores in the variable <code>User</code>. In our case, we're using the <code>CustomUser</code> model</p>
</li>
<li><p><code>class UserSerializer(serializers.ModelSerializer):</code>: Here you've created the UserSerializer class, which inherits <code>ModelSerializer</code>.</p>
<p>A <code>ModelSerializer</code> is a shortcut that automatically creates a serializers class with fields that are in the model class.</p>
<p>When we use a <code>ModelSerializer</code>, DRF inspects the model and automatically does these things:</p>
<p>1. Generates fields from the model so you don't have to<br>2. Automatically adds field validations that are present in the model<br>3. Implements <code>create()</code> and <code>update()</code> methods. A <code>ModelSerializer</code> knows which model to use and how to update and create it. You can override <code>create()</code> and <code>update()</code> methods if you need customized behaviors. <strong>You have overridden the</strong> <code>create()</code> <strong>method in the above code.</strong></p>
</li>
<li><p><code>password = serializers.CharField(write_only=True)</code>: This line is crucial. The <code>write_only=True</code> flag means the password will be accepted during registration but will <strong>never</strong> appear in any API response. Without this, your API would send back the password (even if hashed) every time user data is returned.</p>
<p>So users can create accounts, but their passwords are never exposed back.</p>
</li>
<li><p><code>class Meta</code>: Inside the <code>Meta</code> class, you tell the serializer which model to use. In this case, the model to use is <code>User</code> and the fields to be handled.</p>
</li>
<li><p>The <code>create()</code> method: This is the most important part. This method runs when we create a new user. Instead of using the default <code>.create()</code> method you have overridden it.</p>
<p>It's important to understand why we have overridden this method. The default <code>create()</code> method is not suitable for creating users securely.</p>
<p>By default this method stores the password in plain text format. This is a serious problem because passwords should never be stored in raw form. They need to be <strong>hashed</strong> so that even if the database is compromised, the passwords are never exposed.</p>
<p>Django provides a special method called <code>create_user()</code> that automatically handles this by <strong>hashing the password</strong> and setting up the user properly for authentication.</p>
</li>
</ol>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/c1246e5d-104d-486c-965c-8edb04c850dc.png" alt="The image shows the annoated explanation of the code above" style="display:block;margin:0 auto" width="2340" height="1280" loading="lazy">

<h3 id="heading-42-how-to-create-noteserializer">4.2 How to Create NoteSerializer</h3>
<p>After the <code>UserSerializer</code> class, let's create the <code>NoteSerializer</code> class. The <code>NoteSerializer</code> handles the notes data</p>
<p>First of all, you need to add an import to the <code>Notes</code> class. Add the line <code>from .models import Notes</code> at the end of the last import.</p>
<p>Put this code below the <code>UserSerializer</code> class:</p>
<pre><code class="language-python">class NoteSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')
    class Meta:
        model = Notes
        fields = ['id', 'owner', 'title', 'body', 'created_at']
</code></pre>
<p>Now let's break it down:</p>
<ol>
<li><p><code>owner = serializers.ReadOnlyField(source='owner.username')</code>: This is the most important line in the code. This makes the <code>owner</code> field <strong>read-only</strong>. That means the API will display who owns a note (showing their username), but no one can set or change the owner through the API.</p>
<p>Without this protection, a malicious user could send a POST request with <code>"owner": 5</code> and assign their note to someone else's account, or worse, modify someone else's notes by reassigning ownership.</p>
<p>The <code>source='owner.username'</code> part tells DRF to display the owner's username instead of their numeric ID, which makes the API responses more readable.</p>
</li>
<li><p><code>class Meta:</code> ...: As before the <code>Meta</code> class contains the model which the serializer use and the fields that the API will expose.</p>
<p>Here is the complete code in the <code>serializers.py</code> file</p>
</li>
</ol>
<pre><code class="language-python">from rest_framework import serializers
from django.contrib.auth import get_user_model
from .models import Notes

User = get_user_model()
class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True)
    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'password']

    def create(self, validated_data):
        user = User.objects.create_user(
            username=validated_data['username'],
            email=validated_data.get('email', ''),
            password=validated_data['password']
        )
        return user

class NoteSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')
    class Meta:
        model = Notes
        fields = ['id', 'owner', 'title', 'body', 'created_at']
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/3073f560-79dc-4950-8acb-96d871e0511c.png" alt="The image shows the complete code for the serializers.py file" style="display:block;margin:0 auto" width="2006" height="1310" loading="lazy">

<h2 id="heading-step-5-how-to-configure-simplejwt">Step 5: How to Configure SimpleJWT</h2>
<p>Now let's set up the authentication system. This is where you tell DRF to use JWT for authentication instead of sessions. This step is crucial because without it, DRF will default to session-based auth.</p>
<p>SimpleJWT provides a complete JWT implementation for DRF, so you don't have to build token generation, signing, or verification from scratch.</p>
<p>The access token is what your client sends with every API request. It's short-lived by design. Think of it like a visitor badge at an office building: it gets you through the door, but it expires at the end of the day. If someone steals it, the damage is limited because it stops working soon.</p>
<p>The refresh token is longer-lived and has a single purpose: getting a new access token when the current one expires. The client stores it securely and only sends it to one specific endpoint. Think of it like your employee ID card. You use it to get a new visitor badge each morning, but you don't flash it at every door.</p>
<p>This separation exists for security. If the short-lived access token is compromised (which is more likely since it's sent with every request), the attacker has a narrow window before it expires. The refresh token, which is sent less frequently, has a lower risk of interception.</p>
<p>Let's look at how the access and refresh token work together</p>
<ol>
<li><p>User logs in, server gives both access token and refresh token</p>
</li>
<li><p>User makes requests using the access token</p>
</li>
<li><p>Access token expires</p>
</li>
<li><p>App sends refresh token to server</p>
</li>
<li><p>Server checks it and gives a new access token</p>
</li>
<li><p>User continues without logging in again</p>
</li>
</ol>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/9cf61a53-99c6-4665-bf5d-dccafa71ca8d.png" alt="The image shows the use of access and refresh tokens" style="display:block;margin:0 auto" width="1840" height="1006" loading="lazy">

<h3 id="heading-51-how-to-update-rest-framework-settings">5.1 How to Update REST Framework Settings</h3>
<p>Open <code>notes_core/settings.py</code> and add the following code:</p>
<pre><code class="language-python">from datetime import timedelta
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ),

    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
}

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
}
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/54e89c41-b456-4bbb-a129-ac3e92a09a6d.png" alt="The image shows the code being added to settings.py file" style="display:block;margin:0 auto" width="2492" height="1528" loading="lazy">

<p>Let's unpack what each section does.</p>
<p>The <code>DEFAULT_AUTHENTICATION_CLASSES</code> setting tells DRF to use JWT as the authentication method for all API endpoints. Every incoming request will be checked for a valid JWT token in the Authorization header.</p>
<p>The <code>DEFAULT_PERMISSION_CLASSES</code> setting sets <code>IsAuthenticated</code> as the global permission policy. This means every endpoint in your API is locked down by default. Only users with a valid token can access any endpoint.</p>
<p>This is a secure-by-default approach: instead of remembering to protect each view individually, everything is protected, and you explicitly open up the endpoints that need to be public <em>(like the registration endpoint, which you'll handle in the next step).</em></p>
<p>The <code>SIMPLE_JWT</code> dictionary controls token behavior. The access token lasts 30 minutes. This is the token clients include in every request. If someone intercepts it, the damage is limited to a 30-minute window. The refresh token lasts one day.</p>
<p>When the access token expires, the client can use the refresh token to get a new access token without forcing the user to log in again. The duration of the refresh token is 1 day. This means after 1 day, the user must log in again with their username and password. You'll see exactly how this works later when you test with Postman.</p>
<h3 id="heading-52-how-to-add-token-url-endpoints">5.2 How to Add Token URL Endpoints</h3>
<p>SimpleJWT provides ready-made views for obtaining and refreshing tokens. You just need to wire them up to URLs.</p>
<p>Open <code>notes_core/urls.py</code> and update it with the following code:</p>
<pre><code class="language-python">from django.contrib import admin
from django.urls import path, include
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('notes.urls')),
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
</code></pre>
<p>The <code>token/</code> endpoint accepts a username and password, and returns an access token and a refresh token.</p>
<p>The <code>token/refresh/</code> endpoint accepts a refresh token and returns a new access token. You'll see these in action during testing.</p>
<h2 id="heading-step-6-how-to-build-the-authentication-logic">Step 6: How to Build the Authentication Logic</h2>
<p>Open <code>notes/views.py</code> and add the following:</p>
<pre><code class="language-python">from rest_framework import generics, permissions
from django.contrib.auth import get_user_model
from .serializers import UserSerializer

User = get_user_model()

class RegisterView(generics.CreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [permissions.AllowAny]
</code></pre>
<p>Now let's walk through this code.</p>
<p>The first section are the imports and after that we have used the the <code>get_user_model()</code> method to get the <code>CustomUser</code> model.</p>
<p>Now the main part is <code>RegisterView</code> class. The class inherits from <code>generics.CreateAPIView</code> which is a built in DRF view designed specifically for handling POST requests that create new objects.</p>
<p>Because of this, you don’t have to manually write the logic for handling POST requests, validating data, or saving to the database. DRF does all of that for you behind the scenes.</p>
<p>Inside the class, <code>queryset = Users.objects.all()</code> defines the set of user objects this view can work with.</p>
<p>The <code>serializer_class = UserSerializer</code> tells the view which serializer to use for validating incoming data and creating the user.</p>
<p>Finally <code>permission_classes = [permissions.AllowAny]</code> overrides the global <code>IsAuthenticated</code> permission you set earlier in the value of <code>DEFAULT_PERMISSION_CLASSES</code> .</p>
<p>This means that anyone can access the registration endpoint, even if they aren't logged in. This makes sense for a registration endpoint because new users won’t have accounts yet.</p>
<p>Every other view in your API will inherit the global IsAuthenticated permission, so only this registration endpoint is open.</p>
<h2 id="heading-step-7-how-to-implement-scoped-views">Step 7: How to Implement Scoped Views</h2>
<p>This is the heart of the tutorial. You've set up authentication so the API knows <strong>who</strong> is making a request. Now you need to make sure each user can only interact with <strong>their</strong> <strong>own</strong> notes.</p>
<p>Think of it this way: authentication is the lock on the front door of an apartment building. It keeps strangers out. But scoping is the lock on each individual apartment. Just because you live in the building doesn't mean you can walk into your neighbor's apartment.</p>
<p>Without scoping, an authenticated user could potentially see every note in the database, or worse, modify notes that belong to someone else. Two method overrides on your viewset prevent this entirely.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/03747660-068c-4757-9fc2-d2a122f22f5f.png" alt="The image represents the differences of access resources with and without scoping" style="display:block;margin:0 auto" width="1024" height="559" loading="lazy">

<h3 id="heading-71-how-to-create-a-noteviewset">7.1 How to Create a NoteViewSet</h3>
<p>Now let's create the <code>NoteViewSet</code>. First add these imports to the top of the file. We're importing the viewsets, serializers, and model.</p>
<pre><code class="language-python">from .models import Note
from .serializers import UserSerializer, NoteSerializer
from rest_framework import generics, viewsets, permissions
</code></pre>
<p>Add the following to <code>notes/views.py</code>, below the RegisterView:</p>
<pre><code class="language-python">class NoteViewSet(viewsets.ModelViewSet):
    serializer_class = NoteSerializer

    def get_queryset(self):
        return Notes.objects.filter(owner=self.request.user).order_by('-created_at')

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)
</code></pre>
<p>Now let's talk about this code in detail.</p>
<p>You've created a new class called <code>NoteViewSet</code> which inherits from the DRF class <code>ModelViewSet</code>. This gives you full CRUD operations, meaning you can list notes and retrieve a single note, as well as create, update, and delete a note.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/25999ad8-3534-424a-ab35-cad9cecec8ef.png" alt="The image shows the Model Viewset being imported" style="display:block;margin:0 auto" width="1338" height="248" loading="lazy">

<p>The next part <code>serializer_class = NoteSerializer</code> tells Django to use the <code>NoteSerializer</code> class to convert between Python objects and JSON.</p>
<p>But the magic is the two methods that you are overriding: <code>get_queryset()</code> and <code>perform_create()</code>.</p>
<p>The <code>get_queryset()</code> method controls which notes the API returns. If you didn't override this method, it would return <code>Note.objects.all()</code> (which would give every user access to every note in the database).</p>
<p>But here, you've overridden this method so that it filters notes by the current user.</p>
<p>Next is the <code>perform_create()</code> method, which is called when the note is saved. You've overridden this method so that it saves the notes of the user who's currently logged in. If you hadn't overridden the this method, it would return all the notes regardless of the logged in user.</p>
<p>Notice that you have passed <code>self.request.user</code> parameters in to the <code>filter()</code> function. This is the code that attaches the logged-in user as the owner of the note.</p>
<p>Remember how you made the owner field read-only in the serializer? This is the other half of that security measure.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/6db94c2f-673f-480a-bf20-730ed4af4bdb.png" alt="6db94c2f-673f-480a-bf20-730ed4af4bdb" style="display:block;margin:0 auto" width="1718" height="710" loading="lazy">

<p>The user can't set the owner through the API request, and the server automatically sets it to whoever is authenticated. These two pieces work together to make ownership tamper-proof.</p>
<h3 id="heading-72-why-this-matters-preventing-id-enumeration-attacks">7.2 Why This Matters: Preventing ID Enumeration Attacks</h3>
<p>Without get_queryset filtering, your API might allow something like this: a user sends a GET request to <code>/api/notes/42/</code> and sees a note that belongs to someone else, simply because they guessed the ID.</p>
<p>This is called an <strong>ID enumeration attack</strong> — an attacker cycles through IDs (1, 2, 3, 4...) to discover and access other people's data.</p>
<p>With your scoped <code>get_queryset</code>, even if User B sends a request to <code>/api/notes/42/</code> and note 42 belongs to User A, the viewset won't find it in User B's filtered queryset. DRF will return a 404 — as far as User B is concerned, that note doesn't exist.</p>
<h2 id="heading-step-8-how-to-connect-a-url">Step 8: How to Connect a URL</h2>
<p>Now you need to wire up the views to URL paths so the API knows which view to call for each endpoint.</p>
<h3 id="heading-81-how-to-create-app-level-urls">8.1 How to Create App-level URLs</h3>
<p>Create a new file called <code>notes/urls.py</code> and add the following:</p>
<pre><code class="language-python">from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import RegisterView, NoteViewSet

router = DefaultRouter()
router.register(r'notes', NoteViewSet, basename='note')

urlpatterns = [
    path('register/', RegisterView.as_view(), name='register'),
    path('', include(router.urls)),
]
</code></pre>
<p>The <code>DefaultRouter</code> automatically generates URL patterns for the NoteViewSet. Since you're using a <code>ModelViewSet</code>, the router creates endpoints for listing all notes, creating a note, retrieving a single note, updating a note, and deleting a note — <strong>all from that single router.register call.</strong></p>
<p>The <code>basename='note'</code> parameter is required here because your viewset doesn't have a queryset attribute defined directly on the class <em>(you're using get_queryset instead)</em>. DRF uses the <code>basename</code> to generate the URL pattern names like <code>note-list</code> and <code>note-detail</code>.</p>
<h3 id="heading-82-how-to-verify-the-project-level-urls">8.2 How to Verify the Project-Level URLs</h3>
<p>Make sure your <code>notes_core/urls.py</code> looks like this (you set this up in Step 5, but let's confirm):</p>
<pre><code class="language-python">from django.contrib import admin
from django.urls import path, include
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('notes.urls')),
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
</code></pre>
<p>Here's the full picture of your API's URL structure:</p>
<table>
<thead>
<tr>
<th><strong>Endpoint</strong></th>
<th><strong>Method</strong></th>
<th><strong>Description</strong></th>
</tr>
</thead>
<tbody><tr>
<td><code>api/register/</code></td>
<td><strong>POST</strong></td>
<td>Create a new user account</td>
</tr>
<tr>
<td><code>api/token/</code></td>
<td><strong>POST</strong></td>
<td>Get access and refresh tokens</td>
</tr>
<tr>
<td><code>api/token/refresh/</code></td>
<td><strong>POST</strong></td>
<td>Get a new access token using a refresh token</td>
</tr>
<tr>
<td><code>api/notes/</code></td>
<td><strong>GET</strong></td>
<td>List all notes for the authenticated user</td>
</tr>
<tr>
<td><code>api/notes/</code></td>
<td><strong>POST</strong></td>
<td>Create a new note</td>
</tr>
<tr>
<td><code>api/notes/&lt;id&gt;/</code></td>
<td><strong>GET</strong></td>
<td>Retrieve a specific note</td>
</tr>
<tr>
<td><code>api/notes/&lt;id&gt;/</code></td>
<td><strong>PUT/PATCH</strong></td>
<td>Update a specific note</td>
</tr>
<tr>
<td><code>api/notes/&lt;id&gt;/</code></td>
<td><strong>DELETE</strong></td>
<td>Delete a specific note</td>
</tr>
</tbody></table>
<p>Start the development server to make sure everything runs without errors:</p>
<pre><code class="language-shell">python manage.py runserver
</code></pre>
<p>If the server starts without complaints, your code is wired up correctly.</p>
<h2 id="heading-step-9-how-to-test-the-apis-with-postman">Step 9: How to Test the APIs with Postman</h2>
<p>Building the API is one thing. Proving it works is another. Let's walk through the entire flow using Postman, from registering a user to demonstrating that scoping actually works.</p>
<p>If you haven't used Postman before, it's a tool that lets you send HTTP requests to your API and inspect the responses. You can download it from <a href="https://www.postman.com/downloads/">postman.com/downloads</a>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/cc154f8b-d3db-4c48-b884-1dbd3d517209.png" alt="Postman software download page" style="display:block;margin:0 auto" width="2518" height="1552" loading="lazy">

<p>Alternatively, you can use curl from the command line or any other API testing tool you're comfortable with.</p>
<p>Make sure your development server is running before proceeding.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/90524445-9d7c-43d0-bcf7-105f54626d85.png" alt="python server running" style="display:block;margin:0 auto" width="2078" height="678" loading="lazy">

<h3 id="heading-91-how-to-register-a-user">9.1 How to Register a User</h3>
<p>Open Postman:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/8951a205-ee01-4646-8e0b-d1d16d21c749.png" alt="opening Postman" style="display:block;margin:0 auto" width="2764" height="1792" loading="lazy">

<p>Create a new request:</p>
<table>
<thead>
<tr>
<th>Method</th>
<th>POST</th>
</tr>
</thead>
<tbody><tr>
<td><strong>URL</strong></td>
<td><code>http://127.0.0.1:8000/api/register/</code></td>
</tr>
<tr>
<td><strong>Body tab</strong></td>
<td>Select "raw" and choose "JSON" from the dropdown</td>
</tr>
<tr>
<td><strong>Body Content</strong></td>
<td>{ "username": "priya", "email": "<a href="mailto:priya@example.com">priya@example.com</a>", "password": "securepassword123" }</td>
</tr>
</tbody></table>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/f043164f-184c-4f29-8a9f-55f604d521fb.png" alt="postman UI for registering a new user" style="display:block;margin:0 auto" width="1918" height="770" loading="lazy">

<p>Click <strong>Send</strong>. You should get a <code>201 Created</code> response with the user data <strong>(without the password</strong>, thanks to your <code>write_only=True</code> field) which you wrote in the <code>UserSerializer</code> class.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/021a79db-418f-4057-97f8-f3c5c4b9761c.png" alt="response of registering a user" style="display:block;margin:0 auto" width="2024" height="1240" loading="lazy">

<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/c8fd6b4e-f539-4f16-85c8-72515abadf8f.png" alt="The image describes the codes of the User serializer classs" style="display:block;margin:0 auto" width="2340" height="1280" loading="lazy">

<h3 id="heading-92-how-to-obtain-access-and-refresh-tokens">9.2 How to Obtain Access and Refresh Tokens</h3>
<p>Now log in to get your JWTs:</p>
<table>
<thead>
<tr>
<th>Method</th>
<th>POST</th>
</tr>
</thead>
<tbody><tr>
<td><strong>URL</strong></td>
<td><code>http://127.0.0.1:8000/api/token/</code></td>
</tr>
<tr>
<td><strong>Body</strong></td>
<td>{"username" : "priya", "password" : "securepassword123"}</td>
</tr>
</tbody></table>
<p>You'll get a response with access and refresh tokens.</p>
<p><strong>Copy the access token.</strong> You'll need it for every subsequent request. Also save the refresh token, as you'll use it later.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/e5667e26-b11a-463e-85d4-ba99082bee21.png" alt="The image shows the api returning access and refresh token" style="display:block;margin:0 auto" width="2016" height="1224" loading="lazy">

<p>A JWT is only encoded and not encrypted. The encoding is merely a way to transform the data into a safe, standard string format that can be easily transmitted over the internet.</p>
<p>Any one can peel through the encoding to see the data. This is done using base64url encoding.</p>
<p>We can use the Python library <code>pyjwt</code> to decode JWTs or use any of the online sites to decode. It's important to note that you should use online sites with caution since JWTs may contain sensitive information.</p>
<p>For this demo, we'll use site called <a href="https://www.jwt.io">jwt.io</a>.</p>
<p>Open the site and paste in the access token that you have just created:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/f8b9f3e0-db1e-49fb-82be-2b4d27adc8bd.png" alt="The image describes the various sections after decoding the JWT token" style="display:block;margin:0 auto" width="2448" height="1392" loading="lazy">

<p>The JWT has three parts: the header, the payload, and the signature.</p>
<p>The header sections tells you how the header is signed. In this case it is signed using the <strong>HS256</strong> algorithm.</p>
<p>The payload is where the actual data or claim lives. It contains standard claims such as token types, expiration time ( <code>exp</code> ), issued at time ( <code>iat</code> ), and custom claims.</p>
<p>The signature section is used to verify integrity. You <strong>can't decode it to meaningful data.</strong> This section ensures that the token wasn't tampered with.</p>
<h3 id="heading-93-how-to-create-a-note">9.3 How to Create a Note</h3>
<p>Now use the access token to create a note:</p>
<table>
<thead>
<tr>
<th>Method</th>
<th>POST</th>
</tr>
</thead>
<tbody><tr>
<td><strong>URL</strong></td>
<td><code>http://127.0.0.1:8000/api/notes/</code></td>
</tr>
<tr>
<td><strong>Header tab:</strong></td>
<td>Add a new header:</td>
</tr>
<tr>
<td>Key: Authorization, Value: Bearer</td>
<td></td>
</tr>
<tr>
<td><strong>Body</strong></td>
<td>{'title': 'My note', 'body': 'This contains secret information'}</td>
</tr>
</tbody></table>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/d04e2bfb-edc6-4c12-ac93-65611ed0d805.png" alt="The image shows adding new header into postman" style="display:block;margin:0 auto" width="1762" height="1084" loading="lazy">

<p>Notice that you don't include an owner field. That's handled automatically by perform_create. You should get a <code>201 Created response</code>:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/19a8b564-4d49-4dc1-823c-d7d96fcb5319.png" alt="The image shows the output (response) after creating a note" style="display:block;margin:0 auto" width="1802" height="1494" loading="lazy">

<p>You can create a few more notes, so that we have some data to work with.</p>
<h3 id="heading-94-how-to-list-your-notes">9.4 How to List Your Notes</h3>
<p>Now to fetch all of Priya's notes:</p>
<table>
<thead>
<tr>
<th>Method</th>
<th>GET</th>
</tr>
</thead>
<tbody><tr>
<td><strong>URL</strong></td>
<td><code>http://127.0.0.1:8000/api/notes/</code></td>
</tr>
<tr>
<td><strong>Header tab:</strong></td>
<td>Same Authorization: Bearer header</td>
</tr>
</tbody></table>
<p>You should see all the notes created, sorted by most recent first.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/8b7107e6-44ef-465c-b1aa-076ec9de1979.png" alt="The image shows the response of getting list of notes" style="display:block;margin:0 auto" width="2122" height="1634" loading="lazy">

<h3 id="heading-95-how-to-demonstrate-scoping">9.5 How to Demonstrate Scoping</h3>
<p>Let's prove that a second user can't view the first user's notes.</p>
<p>First, register the second user.</p>
<p>Send a POST request to <code>http://127.0.0.1/api/register</code> with the following data:</p>
<table>
<thead>
<tr>
<th>Method</th>
<th>POST</th>
</tr>
</thead>
<tbody><tr>
<td><strong>URL</strong></td>
<td><code>http://127.0.0.1:8000/api/register/</code></td>
</tr>
<tr>
<td><strong>Body tab</strong></td>
<td>Select "raw" and choose "JSON" from the dropdown</td>
</tr>
<tr>
<td><strong>Body Content</strong></td>
<td>{ "username": "sujan", "email": "<a href="mailto:sujan@example.com">sujan@example.com</a>", "password": "anotherpassword123" }</td>
</tr>
</tbody></table>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/094af273-4220-416a-9c8f-f33079badf83.png" alt="The image shows a new user being created" style="display:block;margin:0 auto" width="2028" height="994" loading="lazy">

<p>Then get tokens for Sujan by sending a POST request to <code>http://127.0.0.1:8000/api/token/</code> with Sujan's credentials (username and password) and then copy Sujan's access token.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/8fe22f1b-f36e-4b35-a478-1b48ea0218c3.png" alt="8fe22f1b-f36e-4b35-a478-1b48ea0218c3" style="display:block;margin:0 auto" width="2118" height="1274" loading="lazy">

<p>Now send a GET request to <code>http://127.0.0.1:8000/api/notes/</code> using Sujan's token in the Authorization header.</p>
<p>The response should be an empty list since this user hasn't created any notes:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/ef8e17d0-4f8f-4a51-8bfd-01c078247075.png" alt="The image shows the response of the get query new user's access token" style="display:block;margin:0 auto" width="2094" height="1352" loading="lazy">

<p>More importantly, Priya's notes are completely invisible to him. Even if Sujan tries to access a specific note by ID – say, <code>http://127.0.0.1:8000/api/notes/1/</code> – he'll get a <code>404 Not Found</code> response, not a <code>403 Forbidden</code>.</p>
<p>This is intentional. A <code>404 Not Found</code> doesn't reveal that the note exists, while a <code>403 Forbidden</code> would confirm its existence to a potential attacker.</p>
<p>A <code>403 Forbidden</code> response is like a door with a sign: <em>“Authorized personnel only”.</em> You now know something important is inside. A <code>404 Not Found</code> response is like a blank wall. You don’t even know a room exists.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/a4619fbb-42ce-4f80-9207-31eb81336c7c.png" alt="The image shows the difference between a 403 and 404 response code" style="display:block;margin:0 auto" width="1860" height="910" loading="lazy">

<p>Now that you know why we've used the <code>404</code> response instead of <code>403</code>, let's demonstrate this.</p>
<p>First, I'll access Priya's individual note using her credentials and her access token:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/e68e33cc-93a4-4eb5-99fe-934b283defeb.png" alt="The image shows the result of accessing individual note using the first user (Priya)" style="display:block;margin:0 auto" width="2116" height="1170" loading="lazy">

<p>Now, I'll change the access token and put Sujan's (new user) access token:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/e5a6d07c-a7f0-4a50-ba05-94cc5159489c.png" alt="The image shows the response of accessing the second note using the new user's (sujan) credentials" style="display:block;margin:0 auto" width="2110" height="1166" loading="lazy">

<p>You can see that using the new user's token to access the previous user's note leads to <code>404 Not Found</code> response.</p>
<h2 id="heading-step-10-how-to-handle-token-expiration-with-refresh-tokens">Step 10: How to Handle Token Expiration with Refresh Tokens</h2>
<p>Access tokens are deliberately short-lived (30 minutes in your configuration). This limits the window of damage if a token is stolen.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/b86f4f24-b8b0-45d0-bcee-5e39e2268e21.png" alt="b86f4f24-b8b0-45d0-bcee-5e39e2268e21" style="display:block;margin:0 auto" width="1330" height="870" loading="lazy">

<p>But you don't want users to re-enter their credentials every 30 minutes. That's what refresh tokens are for.</p>
<p>When Priya's access token expires, her API requests will start returning <code>401 Unauthorized</code> responses. Instead of logging in again, the client sends the refresh token to get a fresh access token.</p>
<table>
<thead>
<tr>
<th>Method</th>
<th>POST</th>
</tr>
</thead>
<tbody><tr>
<td><strong>URL</strong></td>
<td><code>http://127.0.0.1:8000/api/token/refresh/</code></td>
</tr>
<tr>
<td><strong>Body tab</strong></td>
<td>Select "raw" and choose "JSON" from the dropdown</td>
</tr>
<tr>
<td><strong>Body Content</strong></td>
<td>{ refresh: &lt; Priya's refresh token &gt;}</td>
</tr>
</tbody></table>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/67d255ac-8696-45e6-be17-c80b2a6e8af0.png" alt="The image shows the response of getting a new access token using a refresh token" style="display:block;margin:0 auto" width="2110" height="1360" loading="lazy">

<p>Replace your old access token with this new one, and you're good for another 30 minutes. The refresh token itself lasts for one day, so the user only needs to fully log in again once every 24 hours.</p>
<p>In a real application, the frontend client handles this automatically. When an API call returns a <code>401</code>, the client catches it, sends the refresh token to get a new access token, and retries the original request — all without the user noticing.</p>
<p>Here's what that flow looks like in pseudocode:</p>
<ol>
<li><p>Client sends request with access token</p>
</li>
<li><p>Server responds with 401 (token expired)</p>
</li>
<li><p>Client sends refresh token to /api/token/refresh/</p>
</li>
<li><p>Server responds with a new access token</p>
</li>
<li><p>Client retries the original request with the new access token</p>
</li>
<li><p>Server responds with the data</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/f0fd3fd8-842b-4de3-8f5b-2f1d4ad2181f.png" alt="The image show the steps to get a new token after a previous one has expired" style="display:block;margin:0 auto" width="1868" height="906" loading="lazy"></li>
</ol>
<p>If the refresh token itself has expired (after 24 hours in your configuration), step 4 will also return a <code>401</code>. At that point, the user truly needs to log in again with their username and password. This is the intended behavior: it means even a stolen refresh token has a limited useful life.</p>
<h2 id="heading-how-you-can-improve-this-project">How You Can Improve This Project</h2>
<p>This API is functional and secure, but there's plenty of room to build on it. Here are some directions you could take.</p>
<ol>
<li><p><strong>Add search and filtering.</strong> Let users search their notes by title or body text. You can use DRF's SearchFilter and django-filter to add query parameters like <code>?search=meeting</code> to the notes list endpoint.</p>
</li>
<li><p><strong>Add categories or tags.</strong> Create a <code>Category</code> model and add a <strong>foreign key</strong> to <code>Note</code>, or use a many-to-many relationship for tags. This would let users organize their notes and filter by category.</p>
</li>
<li><p><strong>Add pagination.</strong> Once a user has hundreds of notes, returning them all in a single response becomes slow. DRF has built-in pagination classes that let you return notes in pages of 10, 20, or whatever size you choose.</p>
</li>
<li><p><strong>Deploy to a production server.</strong> The API currently runs on your local machine. You could deploy it to platforms like PythonAnywhere, Railway, or Render to make it accessible from anywhere. You'd need to configure a production database (like PostgreSQL), set a secure SECRET_KEY, and serve the application behind HTTPS.</p>
</li>
<li><p><strong>Build a frontend.</strong> Connect a React, Next.js, or Vue.js frontend to this API. Store the JWTs in the client and implement the token refresh flow so users stay logged in seamlessly.</p>
</li>
<li><p><strong>Add token blacklisting.</strong> SimpleJWT supports token blacklisting, which lets you invalidate refresh tokens when a user logs out. Without this, a refresh token remains valid until it expires, even after the user "logs out."</p>
</li>
</ol>
<p>Each of these improvements builds on the patterns you've already learned and will deepen your understanding of Django, DRF, and API design.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You've built a fully functional, secure note-taking API with Django, Django REST Framework, and SimpleJWT. Along the way, you learned some fundamental concepts that apply to any API you'll build in the future.</p>
<p>You started with a custom user model — a small decision at the beginning that saves you from a painful migration later. You configured JWT authentication so your API can serve mobile clients and decoupled frontends that can't rely on session cookies.</p>
<p>You built serializers that protect sensitive data by keeping passwords write-only and ownership read-only. Most importantly, you implemented scoped views that ensure each user's data is completely isolated from everyone else's.</p>
<p>The patterns you practiced here — overriding <code>get_queryset</code> to filter by the current user, overriding <code>perform_create</code> to assign ownership automatically, and using <code>read-only</code> fields to prevent data tampering — are the same patterns you'll use in production APIs handling real user data.</p>
<p>The best way to solidify what you've learned is to keep building. Try adding search and filtering, build a React frontend that consumes this API, or start a completely new project may be a task manager, a journal app, or a bookmarks API using the same JWT and scoping patterns. The core workflow stays the same. Only the models and business logic change.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build and Deploy a Fitness Tracker Using Python Django and PythonAnywhere - A Beginner Friendly Guide ]]>
                </title>
                <description>
                    <![CDATA[ If you've learned some Python basics but still feel stuck when it comes to building something real, you're not alone. Many beginners go through tutorials, learn about variables, functions, and loops,  ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-and-deploy-a-fitness-tracker-using-python-django-and-pythonanywhere/</link>
                <guid isPermaLink="false">69cfff6ce466e2b762506a84</guid>
                
                    <category>
                        <![CDATA[ Programming Blogs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                    <category>
                        <![CDATA[ deployment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Prabodh Tuladhar ]]>
                </dc:creator>
                <pubDate>Fri, 03 Apr 2026 17:57:00 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/a1ae273b-9f92-4fc2-89aa-1452fc0df895.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you've learned some Python basics but still feel stuck when it comes to building something real, you're not alone. Many beginners go through tutorials, learn about variables, functions, and loops, and then hit a wall when they try to create an actual project.</p>
<p>The gap between "I know Python syntax" and "I can build a working web app" can feel enormous. But it does not have to be.</p>
<p>In this tutorial, you'll build a fitness tracker web application from scratch using Django, one of the most popular Python web frameworks. By the end, you'll have a fully functional app running live on the internet – something you can show to friends, add to your portfolio, or keep building on.</p>
<p>Here's what you'll learn:</p>
<ul>
<li><p>How Django projects and apps are structured</p>
</li>
<li><p>How to define database models to store workout data</p>
</li>
<li><p>How to create views that handle user requests</p>
</li>
<li><p>How to build HTML templates that display your data</p>
</li>
<li><p>How to connect URLs to views so users can navigate your app</p>
</li>
<li><p>How to deploy your finished app to PythonAnywhere so anyone can access it</p>
</li>
</ul>
<p>The app itself is straightforward: you can log a workout by entering an activity name, duration, and date. You can then view all your logged workouts on a separate page. It's simple, but it covers the core Django concepts you need to build much bigger things later.</p>
<p>Let's get started.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-what-you-are-going-build">What You Are Going Build</a></p>
</li>
<li><p><a href="#heading-step-1-how-to-set-up-your-django-project">Step 1: How to Set Up Your Django Project</a></p>
<ul>
<li><p><a href="#heading-1-1-how-to-create-a-virtual-environment">1. 1 How to create a virtual environment</a></p>
</li>
<li><p><a href="#heading-12-how-to-install-django">1.2 How to install Django</a></p>
</li>
<li><p><a href="#heading-13-how-to-create-the-project">1.3 How to Create the Project</a></p>
</li>
<li><p><a href="#heading-14-how-to-run-the-development-server">1.4 How to run the development server</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-2-how-to-create-a-django-app">Step 2: How to Create a Django App</a></p>
<ul>
<li><p><a href="#heading-21-how-to-generate-the-app">2.1 How to Generate the App</a></p>
</li>
<li><p><a href="#heading-22-how-to-register-the-app">2.2 How to Register the App</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-3-how-to-create-a-workout-model">Step 3: How to create a Workout Model</a></p>
<ul>
<li><a href="#heading-31-how-to-define-the-model">3.1 How to Define the Model</a></li>
</ul>
</li>
<li><p><a href="#heading-step-4-how-to-apply-migrations">Step 4: How to Apply Migrations</a></p>
<ul>
<li><p><a href="#heading-41-how-to-generate-the-migration">4.1 How to Generate the Migration</a></p>
</li>
<li><p><a href="#heading-42-how-to-apply-the-migration">4.2 How to Apply the Migration</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-5-how-to-register-the-model-in-the-admin-panel">Step 5: How to Register the Model in the Admin Panel</a></p>
<ul>
<li><p><a href="#heading-52-how-to-create-a-superuser">5.2 How to Create a Superuser</a></p>
</li>
<li><p><a href="#heading-53-how-to-access-the-admin-panel">5.3 How to Access the Admin Panel</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-6-how-to-create-views-for-the-app">Step 6: How to Create Views for the App</a></p>
<ul>
<li><p><a href="#heading-61-how-to-create-a-form-class">6.1 How to Create a Form Class</a></p>
</li>
<li><p><a href="#heading-62-how-to-write-views">6.2 How to Write Views</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-7-how-to-create-templates">Step 7: How to Create Templates</a></p>
<ul>
<li><p><a href="#heading-71-how-to-set-up-the-template-directory">7.1 How to Set Up the Template Directory</a></p>
</li>
<li><p><a href="#heading-72-how-to-create-the-workout-list-template">7.2 How to Create the Workout List Template</a></p>
</li>
<li><p><a href="#heading-73-how-to-create-add-workout-template">7.3 How to Create Add Workout Template</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-8-how-to-connect-urls">Step 8: How to Connect URLs</a></p>
<ul>
<li><p><a href="#heading-81-how-to-create-app-level-urls">8.1 How to Create App Level URLs</a></p>
</li>
<li><p><a href="#heading-82-how-to-link-app-urls-to-project">8.2 How to Link App URLs to project</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-9-how-to-test-the-application-locally">Step 9: How to Test the Application Locally</a></p>
</li>
<li><p><a href="#heading-step-10-how-to-prepare-for-deployment">Step 10: How to Prepare for Deployment</a></p>
<ul>
<li><a href="#heading-101-how-to-update-settings-for-production">10.1 How to Update Settings for Production</a></li>
</ul>
</li>
<li><p><a href="#heading-step-11-how-to-deploy-your-django-app-on-pythonanywhere">Step 11: How to Deploy Your Django App on PythonAnywhere</a></p>
<ul>
<li><p><a href="#heading-111-how-to-create-a-pythonanywhere-account">11.1 How to Create a PythonAnywhere Account</a></p>
</li>
<li><p><a href="#heading-112-how-to-upload-your-project-files">11.2 How to Upload Your Project Files</a></p>
</li>
<li><p><a href="#heading-113-how-to-set-up-a-virtual-environment-in-pythonanywhere">11.3 How to Set Up a Virtual Environment in PythonAnywhere</a></p>
</li>
<li><p><a href="#heading-114-how-to-run-migrations-and-create-a-superuser-on-pythonanywhere">11.4 How to Run Migrations and Create a SuperUser on PythonAnywhere</a></p>
</li>
<li><p><a href="#heading-114-how-to-configure-the-web-app-in-pythonanywhere">11.4 How to Configure the Web App in Pythonanywhere</a></p>
</li>
<li><p><a href="#heading-115-how-to-set-the-virtual-environment-path">11.5 How to Set the Virtual Environment Path</a></p>
</li>
<li><p><a href="#heading-116-how-to-configure-the-wsgi-file">11.6 How to Configure the WSGI file</a></p>
</li>
<li><p><a href="#heading-117-how-to-set-up-static-files">11.7 How to Set Up Static Files</a></p>
</li>
<li><p><a href="#heading-118-how-to-view-your-live-application">11.8 How to View Your Live Application</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-common-mistakes-and-how-to-fix-them">Common Mistakes and How to Fix Them</a></p>
</li>
<li><p><a href="#heading-how-you-can-improve-this-project">How You Can Improve This Project</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before you begin, make sure you are comfortable with the following:</p>
<p><strong>Python fundamentals:</strong> You should understand variables, functions, lists, dictionaries, and basic control flow (if/else statements and loops).</p>
<p><strong>Basic command line usage:</strong> You'll be running commands in your terminal throughout this tutorial. You should know how to open a terminal, navigate between folders, and run commands. If you're on Windows, you can use Command Prompt or PowerShell. On macOS or Linux, the default Terminal app works well.</p>
<p><strong>Tools you'll need installed:</strong></p>
<ul>
<li><p><strong>Python 3.8 or higher.</strong> You can check your version by running <code>python --version</code> or <code>python3 --version</code> in your terminal.&nbsp; If you don't have Python installed, download it from <a href="https://www.python.org">python.org</a></p>
</li>
<li><p><strong>pip.</strong> This is Python's package manager. It usually comes bundled with Python. You can verify by running <code>pip --version</code> or pip3 --version. Note the commands <code>python3</code> and <code>pip3</code> tell the terminal that you are explicitly using <strong>Python Version 3</strong></p>
</li>
<li><p><strong>A code editor.</strong> Visual Studio Code is a great free option, but you can use any editor you're comfortable with.</p>
</li>
</ul>
<p>That's everything. You don't need prior Django experience or web development knowledge. This tutorial will walk you through each step.</p>
<h2 id="heading-what-you-are-going-build">What You Are Going Build</h2>
<p>The fitness tracker you will build has two main features:</p>
<ol>
<li><strong>A form to log workouts.</strong> You will enter the name of an activity (like "Running" or "Push-ups"), how long you did it (in minutes), and the date. When you submit the form, Django saves that workout to a database.</li>
</ol>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/fe6b2a89-fc29-4710-a640-ce2757267e38.png" alt="The image shows a form to log workouts" style="display:block;margin:0 auto" width="1864" height="1544" loading="lazy">

<ol>
<li><strong>A page to view all your workouts.</strong> This page displays every workout you have logged, showing the activity, duration, and date in a clean list.</li>
</ol>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/8f6bd09e-497a-4480-83e5-af162028a0a3.png" alt="The image shows a list of logged workouts" style="display:block;margin:0 auto" width="2144" height="1720" loading="lazy">

<p>Here's how data flows through the app at a high level:</p>
<ol>
<li><p>You fill out the workout form in your browser and click submit.</p>
</li>
<li><p>Your browser sends that data to Django.</p>
</li>
<li><p>Django's view function receives the data, validates it, and saves it to the database.</p>
</li>
<li><p>When you visit the workouts page, Django's view function pulls all saved workouts from the database.</p>
</li>
<li><p>Django passes that data to an HTML template, which renders it as a page your browser can display.</p>
</li>
</ol>
<img alt="The image shows the data flow of the fitness tracker app with 5 steps" style="display:block;margin-left:auto" width="600" height="400" loading="lazy">

<p>This request-response cycle is the foundation of how Django works. Once you understand it, you can build almost anything.</p>
<h2 id="heading-step-1-how-to-set-up-your-django-project">Step 1: How to Set Up Your Django Project</h2>
<p>Every Django project starts with a few setup steps. You'll create an isolated Python environment, install Django, and generate the initial project structure.</p>
<h3 id="heading-1-1-how-to-create-a-virtual-environment">1. 1 How to Create a Virtual Environment</h3>
<p>A virtual environment is a self-contained folder that contains its own Python interpreter and installed packages for a specific project. This keeps your project's dependencies separate from other Python projects on your computer. This separation prevents version conflicts and keeps setups consistent.</p>
<p>For example, one project might require an older version of Django, while another needs the latest version, and a virtual environment allows both to work smoothly on the same system.</p>
<p>Without it, global installations can clash, break projects, and make setups hard to reproduce. Over time, the system environment becomes cluttered with unused or incompatible packages making debugging and maintenance more difficult.</p>
<p>Now let's set it up.</p>
<p>Open your terminal, and navigate to where you want your project to live and run the following command</p>
<pre><code class="language-shell">mkdir fitness-tracker
cd fitness-tracker
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/b4715e51-c2e3-4e97-ad7b-b41066aeefd9.png" alt="An image of the terminal showing the commands mkdir (make directory) and cd (change directory) being typed " style="display:block;margin:0 auto" width="2442" height="542" loading="lazy">

<p>The first command creates a new folder called <code>fitness-tracker</code>. The second command moves you into that folder.</p>
<p>You'll create the Python virutal environment here.</p>
<pre><code class="language-shell">python3 -m venv venv
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/3d1723ae-d069-48fd-9954-440a191f585f.png" alt="The image shows the command to create the python virtual enviroment." style="display:block;margin:0 auto" width="2374" height="512" loading="lazy">

<p>The above command creates a virtual environment inside a folder called <code>venv</code>. The first <code>venv</code> is the command and the second <code>venv</code> represents the name of the folder. You can name the folder anything though <code>venv</code> is usually preferred.</p>
<p>By using the <code>ls</code> command, you can see that we've created the virtual environment folder.</p>
<p>To activate the virtual environment, we need to use the following command:</p>
<p>On macOS/Linux:</p>
<pre><code class="language-shell">source venv/bin/activate
</code></pre>
<p>On Windows:</p>
<pre><code class="language-shell">venv\Scripts\activate
</code></pre>
<p>You'll know it worked when you see <code>(venv)</code> at the beginning of your terminal prompt. From this point on, any Python packages you install will only exist inside this <strong>virtual environment</strong>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/dabe362d-2f50-4745-a0bc-e57ad3536723.png" alt="The image shows the virtual environment being activated" style="display:block;margin:0 auto" width="2434" height="1066" loading="lazy">

<h3 id="heading-12-how-to-install-django">1.2 How to Install Django</h3>
<p>With your virtual environment activated, install Django using pip:</p>
<pre><code class="language-shell">pip install django
</code></pre>
<p>This downloads and installs the latest stable version of Django. You can verify the installation by running:</p>
<pre><code class="language-shell">python3 -m django --version
</code></pre>
<p>After running both these commands, you should see Django being installed and the version number:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/bda2ed0d-44bf-439b-a9fd-1cf3fcaf35ca.png" alt="The image shows django being installed and the version of django that has been installed" style="display:block;margin:0 auto" width="2818" height="968" loading="lazy">

<h3 id="heading-13-how-to-create-the-project">1.3 How to Create the Project</h3>
<p>We have finished installing Django. Now let's create a Django project. Django provides a command line utility that generates the boilerplate files that you need. Type the following command:</p>
<pre><code class="language-shell">django-admin startproject fitness_project .
</code></pre>
<p>The command creates a folder named <code>fitness-project</code>. Notice the dot at the end of the command. The dot at the end is important. It tells Django to create the project files in your current directory instead of creating an extra nested folder.</p>
<p>Now that we've created our Django project, let's open the project in your favourite text editor and look at folder structure.</p>
<p>You'll notice that the folder already comes with a bunch of files.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/eaffe95e-7078-4c2e-91f4-88a6c3696e88.png" alt="The image show the list of files created by the django-admin startproject command" width="2362" height="1488" loading="lazy">

<h3 id="heading-14-how-to-run-the-development-server">1.4 How to Run the Development Server</h3>
<p>Now let's make sure everything is working. You'll need to run a server for this. Type the following command:</p>
<pre><code class="language-shell">python manage.py runserver
</code></pre>
<p>You can type this command in the terminal with the virtual environment activated or you can use the integrated terminal if you're using VS Code. I'll be using the integrated terminal from this point on.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/53dbba38-9863-4972-a899-1e6ff66fb3f5.png" alt="This is an image of the server running after typing the runserver command" style="display:block;margin:0 auto" width="2870" height="1662" loading="lazy">

<p>Open your browser and go to <a href="http://127.0.0.1:8000/">http://127.0.0.1:8000/</a>. You should see Django's default welcome page with a rocket ship graphic confirming that your project is set up correctly.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/25d2e3ce-ce72-44f6-9f5f-78aeaeb88b3e.png" alt="This is an image of Django's default homepage" style="display:block;margin:0 auto" width="2872" height="1788" loading="lazy">

<p>Press <code>Ctrl + C</code> in your terminal to stop the server when you're ready to move on.</p>
<h2 id="heading-step-2-how-to-create-a-django-app">Step 2: How to Create a Django App</h2>
<p>In Django, a project is the overall container for your entire web application, while an app is a smaller, self-contained module inside that project that focuses on a specific piece of functionality.</p>
<p>A useful way to picture this is to think of a house. The project is the whole house. Each app is like a room inside that house. One room might be a kitchen, another a bedroom, each designed with a clear purpose. In the same way, a Django app is built to handle one responsibility, such as authentication, payments, or in this case, workout tracking.</p>
<p>Now, here's the important part: why not just put everything into one big project instead of using apps? You technically could, especially for very small projects. But as your application grows, that approach quickly becomes difficult to manage.</p>
<p>By using apps, you naturally separate concerns. It also makes collaboration smoother, since different people can work on different apps without constantly stepping on each other’s code.</p>
<p>Another major benefit is reusability. Since apps are modular, you can take an app from one project and reuse it in another.</p>
<p>For example, if you build a workout tracking app once, you could plug it into a completely different Django project later without rebuilding it from scratch. Later, you might create a completely different project, say a fitness coaching platform or a health dashboard. Instead of rebuilding the tracking feature from scratch, you can reuse the same app.</p>
<p>For this project, you'll create a single app called <code>tracker</code> that handles everything related to logging and displaying workouts.</p>
<h3 id="heading-21-how-to-generate-the-app">2.1 How to Generate the App</h3>
<p>Make sure you're in the same directory as the <code>manage.py</code> file, then run the following code:</p>
<pre><code class="language-shell">python manage.py startapp tracker
</code></pre>
<p>This create a new folder called tracker with the following following structure:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/cb07105a-6e65-49f9-9c7a-d5a64db42b49.png" alt="The image shows the folder strucutre created by after running the startapp command" style="display:block;margin:0 auto" width="2860" height="1250" loading="lazy">

<p>Each file has its own purpose. You'll work with <code>models.py</code>, <code>views.py</code> and <code>admin.py</code> throughout this project.</p>
<h3 id="heading-22-how-to-register-the-app">2.2 How to Register the App</h3>
<p>Django doesn't automatically know about your new app. You need to tell it by adding the app to the <code>INSTALLED_APPS</code> list in <code>settings.py</code> file.</p>
<p>Open <code>fitness_project/settings.py</code> and find the <code>INSTALLED_APPS</code> list. Add the name of the app, that is <code>tracker</code>, to the end of the list:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/eec90a01-5219-449e-97f8-97465e4ac23f.png" alt="eec90a01-5219-449e-97f8-97465e4ac23f" style="display:block;margin:0 auto" width="2868" height="1306" loading="lazy">

<p>You'll notice that a number of apps have already been installed automatically by Django. This is part of Django’s “batteries-included” philosophy, where many common features are ready to use out of the box.</p>
<p>Here is a short summary of what each of the apps does.</p>
<table>
<thead>
<tr>
<th><strong>App Name</strong></th>
<th><strong>Purpose</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>django.contrib.admin</strong></td>
<td>Powers the built-in admin dashboard, letting you manage your data through a web interface.</td>
</tr>
<tr>
<td><strong>django.contrib.auth</strong></td>
<td>Handles users, login systems, permissions, and password management.</td>
</tr>
<tr>
<td><strong>django.contrib.contenttypes</strong></td>
<td>Helps Django track and manage relationships between different models.</td>
</tr>
<tr>
<td><strong>django.contrib.sessions</strong></td>
<td>Stores user session data, so users stay logged in across requests.</td>
</tr>
<tr>
<td><strong>django.contrib.messages</strong></td>
<td>Lets you show temporary notifications like success or error messages.</td>
</tr>
<tr>
<td><strong>django.contrib.staticfiles</strong></td>
<td>Manages static assets such as CSS, JavaScript, and images</td>
</tr>
</tbody></table>
<p>Now Django knows your <code>tracker</code> app exists and will include it when running the project.</p>
<h2 id="heading-step-3-how-to-create-a-workout-model">Step 3: How to Create a Workout Model</h2>
<p>A model in Django is a Python class that defines the structure of your data. Each model maps directly to a table in your database. Each attribute on the model becomes a column in that table.</p>
<p>Think of a model as a blueprint for a spreadsheet. The class name is the name of the spreadsheet, and each field is a column header. Every time you save a new workout, Django creates a new row in that spreadsheet.</p>
<h3 id="heading-31-how-to-define-the-model">3.1 How to Define the Model</h3>
<p>Open <code>tracker/models.py</code> and replace its contents with this code:</p>
<pre><code class="language-python">from django.db import models

class Workout(models.Model):
    activity = models.CharField(max_length=200)
    duration = models.IntegerField(help_text="Duration in minutes")
    date = models.DateField()

    def __str__(self):
        return f"{self.activity} - {self.duration} min on {self.date}"
</code></pre>
<p>Let's discuss what each part does:</p>
<ul>
<li><p><code>activity = models.CharField(max_length=200)</code> creates a text fields that can hold up to 200 characters. This is where you'll store the name of the exercise like "Running" or "Cycling".</p>
</li>
<li><p><code>duration = models.IntegerField(help_text="Duration in minutes")</code> creates a whole number field for storing how many minutes the workout lasted. The <code>help_text</code> parameter adds a hint that will appear in forms and the admin panel.</p>
</li>
<li><p><code>date = models.DateField()</code> creates a date field for recording when the workout happened.</p>
</li>
</ul>
<p>The <code>__str__()</code> method defines how a Workout object appears when printed or displayed in the admin panel. Instead of seeing something unhelpful like "<strong>Workout object (1)</strong>," you will see "<strong>Running - 30 min on 2025-03-15.</strong>"</p>
<h2 id="heading-step-4-how-to-apply-migrations">Step 4: How to Apply Migrations</h2>
<p>You've defined your model, but Django hasn't created the actual database table yet. To do that, you need to run migrations.</p>
<p>Migrations are Django's way of translating your Python model definitions into database instructions. Migrations are done in two steps.</p>
<p>When you change a model – maybe by adding a field, removing a field, or renaming one – you create a new migration that describes that change. You can do this using the <code>makemigrations</code> command.</p>
<p>Then you apply the migration using the <code>migrate</code> command and Django updates the database to match.</p>
<p>This two-step process of first detecting the change and then applying the change gives you a reliable record of every change to your database structure over time.</p>
<h3 id="heading-41-how-to-generate-the-migration">4.1 How to Generate the Migration</h3>
<p>Run the following command in the integrated terminal:</p>
<pre><code class="language-shell">python manage.py makemigrations
</code></pre>
<p>You should see output like this:</p>
<pre><code class="language-shell">Migrations for 'tracker': tracker/migrations/0001_initial.py 
    + Create model Workout
</code></pre>
<p>Django inspected your Workout model and created a migration file that describes how to build the corresponding database table. You can find this file at <code>tracker/migrations/0001_initial.py</code> if you want to look at it, but you don't need to edit it.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/fa46eed5-6ef3-408a-8c23-f39518b117f4.png" alt="The image shows the file creating after makemigrations command runs" style="display:block;margin:0 auto" width="2880" height="1424" loading="lazy">

<h3 id="heading-42-how-to-apply-the-migration">4.2 How to Apply the Migration</h3>
<p>Now tell Django to execute that migration and actually create the table in the database:</p>
<pre><code class="language-shell">python manage.py migrate
</code></pre>
<p>You'll see several lines of output as Django applies not just your migration, but also the default migrations for Django's built-in apps (authentication, sessions, and so on).</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/fcaae5fe-0cc7-4c1f-b4c3-a3b173fd2551.png" alt="The image shows the output after applying migrations" style="display:block;margin:0 auto" width="2864" height="1650" loading="lazy">

<p>When it finishes, your database has a table ready to store workouts.</p>
<p>When the migrate command runs, we can see the exact SQL commands that Django used to build and change the database. Though this isn't required for creating the application, it's always good to know what's happening under hood.</p>
<p>Run this command:</p>
<pre><code class="language-shell">python manage.py sqlmigrate tracker 001
</code></pre>
<p>And you should get this output:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/016b0a33-06d0-47e8-97de-580e79a7d0e3.png" alt="The image shows the command to view sql queries created by django" style="display:block;margin:0 auto" width="2824" height="1628" loading="lazy">

<p>The <code>001</code> you added at the end is the migration number and represents first version of the database schema.</p>
<p>In practice, your workflow usually looks like this: you change your models, run <code>makemigrations</code> to generate the migration files, and then run the <code>migrate</code> command to apply those changes to the database.</p>
<h2 id="heading-step-5-how-to-register-the-model-in-the-admin-panel">Step 5: How to Register the Model in the Admin Panel</h2>
<p>Django comes with a powerful admin interface built in. It gives you a graphical way to view, add, edit, and delete records in your database without writing any extra code. This is incredibly useful during development because you can quickly test your models and see your data.</p>
<p>But by default, it doesn’t know:</p>
<ul>
<li><p>Which models you want to manage</p>
</li>
<li><p>How you want them displayed</p>
</li>
</ul>
<p>So you <em>register</em> models in <code>admin.py</code> to tell Django to include the specific model in the admin interface.</p>
<h3 id="heading-51-how-to-add-model-to-admin">5.1 How to Add Model to Admin</h3>
<p>Open <code>tracker/admin.py</code> and add the following code:</p>
<pre><code class="language-python">from django.contrib import admin
from .models import Workout

admin.site.register(Workout)
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/ad017508-993a-4c28-ade1-8e73fa0c6a4a.png" alt="ad017508-993a-4c28-ade1-8e73fa0c6a4a" style="display:block;margin:0 auto" width="2854" height="816" loading="lazy">

<p>This single line tells Django to include the <code>Workout</code> model in the admin interface.</p>
<h3 id="heading-52-how-to-create-a-superuser">5.2 How to Create a Superuser</h3>
<p>To access the admin panel, you need an admin account. Create one by running:</p>
<pre><code class="language-python">python manage.py createsuperuser
</code></pre>
<p>Django will prompt you for a username, email address, and password. Choose something you will remember. The email is optional – you can press Enter to skip it.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/4bbc7a15-682e-497d-a4a4-3e2dc4b848ac.png" alt="The image shows the superuser being created by adding username, email and password" style="display:block;margin:0 auto" width="2878" height="1362" loading="lazy">

<h3 id="heading-53-how-to-access-the-admin-panel">5.3 How to Access the Admin Panel</h3>
<p>Start the development server:</p>
<pre><code class="language-python">python manage.py runserver
</code></pre>
<p>Then navigate to <a href="http://127.0.0.1:8000/admin/">http://127.0.0.1:8000/admin/</a> in your browser. Log in with the credentials you just created.</p>
<p>You should see the Django administration dashboard with a "<strong>Tracker</strong>" section containing your "<strong>Workouts</strong>" model.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/a1e576bf-45f6-40dc-b6b9-69899b2df9d5.png" alt="The image shows the Django admin panel and the Worker model of the Tracker app being added to the admin panel" style="display:block;margin:0 auto" width="2860" height="1406" loading="lazy">

<p>Try clicking "Add" to create a couple of test workouts. This will confirm that your model is working correctly before you build the rest of the app.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/944ea6a4-bc6f-4321-87c0-5c7bcb267e26.png" alt="The image show some workouts (running and cycling) being added to the admin panel" style="display:block;margin:0 auto" width="2876" height="1146" loading="lazy">

<h2 id="heading-step-6-how-to-create-views-for-the-app">Step 6: How to Create Views for the App</h2>
<p>A view in Django is a Python function (or class) that receives a web request and returns a web response. That response could be an HTML page, a redirect, a 404 error, or anything else a browser can handle.</p>
<p>Views are where your application logic lives. They decide what data to fetch, what processing to do, and what to show the user.</p>
<p>For this app, you need two views: one to display the form where users add a workout, and one to display the list of all saved workouts.</p>
<h3 id="heading-61-how-to-create-a-form-class">6.1 How to Create a Form Class</h3>
<p>Before writing the views, you need a Django form that handles the workout input.</p>
<p>Django forms are a built-in way to handle user input like login forms, contact forms, or anything that collects data from a user. Instead of manually writing HTML, validating inputs, and handling errors, Django gives you a structured way to do all of that in one place.</p>
<p>Most user inputs are based on the models you’ve created, and Django can automatically generate forms from those models using <code>ModelForms</code>, which speeds things up significantly.</p>
<p>Let's create a new file called <code>forms.py</code> in the <code>tracker</code> folder and add the following code:</p>
<pre><code class="language-python">from django import forms
from .models import Workout

class WorkoutForm(forms.ModelForm):

    class Meta:
        model = Workout
        fields = ['activity', 'duration', 'date']
        widgets = {
            'date': forms.DateInput(attrs={'type': 'date'}),
        }
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/e8bce19c-7184-45b3-9afe-3a5f73cff43b.png" alt="The image shows the file location of forms.py as well the code for forms.py file" style="display:block;margin:0 auto" width="2866" height="1232" loading="lazy">

<p>In the above code, the <code>ModelForm</code> automatically generates form fields based on the <code>Workout</code> model. The <code>widgets</code> dictionary tells Django to render the date field as an HTML date picker instead of a plain text input.</p>
<p>We can actually see the forms being automatically created by Django. For this we need to enter the shell. In the terminal, type the following command:</p>
<pre><code class="language-shell">python manage.py shell
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/951829fb-98e8-48f5-8187-80cc98346e06.png" alt="The image shows the python shell being activated" style="display:block;margin:0 auto" width="2244" height="932" loading="lazy">

<p>Now lets import the <code>WorkoutForm</code> class that we just created.</p>
<p>Type the following code:</p>
<pre><code class="language-shell">from tracker.forms import WorkoutForm
</code></pre>
<p>Notice that we've given the <strong>name of the app</strong> as well when we imported the form.</p>
<p>Then create an object of the <code>WorkoutForm</code> class and print it.</p>
<pre><code class="language-shell">from tracker.forms import WorkoutForm
workoutform = WorkoutForm()
print(workoutform) 
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/b81265a2-7c21-453b-ae57-3cfec97fbaf9.png" alt="The image shows the command to open the python shell where you can execute python statement throught the terminal" style="display:block;margin:0 auto" width="2236" height="652" loading="lazy">

<p>You should get the following output:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/094b0d89-b003-49b6-939d-07eacfb0c745.png" alt="This image shows the html generated from ModelForm" style="display:block;margin:0 auto" width="936" height="632" loading="lazy">

<p>You can see that all the model fields have been renderd as HTML forms and the date field has been created as a date type that is <code>type="date"</code> instead of plain text.</p>
<h3 id="heading-62-how-to-write-views">6.2 How to Write Views</h3>
<p>As we've discussed above, our project has two views: one to add a workout and the other to display all the saved workouts.</p>
<p>First, let's create a view to add a workout. In the <code>tracker/views.py</code> file, type the following code:</p>
<pre><code class="language-python">from django.shortcuts import render, redirect
from .models import Workout

# view to list all workouts
def workout_list(request):
    workouts = Workout.objects.all().order_by('-date')
    return render(request, 'tracker/workout_list.html', {'workouts': workouts})
</code></pre>
<p>Let's walk through this view:</p>
<ul>
<li><p>The <code>workout_list</code> view handles the page that displays all workouts.</p>
</li>
<li><p>It queries the database for every <code>Workout</code> object, orders them by date (most recent first, thanks to the <code>-</code> prefix), and passes that list to a template called <code>workout_list.html</code>.</p>
</li>
<li><p>The <code>render</code> function combines the template with the data and returns the finished HTML page.</p>
</li>
</ul>
<p>To create the logic to add a workout, first add the <code>Workout</code> form import at the end of the import section. Then add the following code after the <code>workout_list</code> view:</p>
<pre><code class="language-python">from django.shortcuts import render, redirect
from .models import Workout
from .forms import WorkoutForm

# view to list all the workouts
def workout_list(request):
    workouts = Workout.objects.all().order_by('-date')
    return render(request, 'tracker/workout_list.html', {'workouts': workouts})

# view to add a workout
def add_workout(request):
    if request.method == 'POST':
        form = WorkoutForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('workout_list')
    else:
        form = WorkoutForm()
    return render(request, 'tracker/add_workout.html', {'form': form})
</code></pre>
<ul>
<li><p>The <code>add_workout</code> view handles both displaying the empty form and processing submitted form data.</p>
</li>
<li><p>When a user first visits the page, the request method is GET, so Django creates a blank form and renders it.</p>
</li>
<li><p>When the user fills out the form and clicks submit, the request method is POST. Django then validates the submitted data, saves it to the database if everything is correct, and redirects the user to the workout list page.</p>
</li>
<li><p>If the data isn't valid, Django re-renders the form with error messages.</p>
</li>
</ul>
<p>Here is the complete views code:</p>
<pre><code class="language-python">from django.shortcuts import render, redirect
from .models import Workout
from .forms import WorkoutForm

# view to list all workouts
def workout_list(request):
    workouts = Workout.objects.all().order_by('-date')
    return render(request, 'tracker/workout_list.html', {'workouts': workouts})

# view to add a workout
def add_workout(request):
    if request.method == 'POST':
        form = WorkoutForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('workout_list')
    else:
        form = WorkoutForm()
    return render(request, 'tracker/add_workout.html', {'form': form})

</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/8a0878c1-f029-49a4-8a7f-308cdb843b62.png" alt="The image shows the complete code for views.py with explanation about the add workout view" style="display:block;margin:0 auto" width="2316" height="1076" loading="lazy">

<h2 id="heading-step-7-how-to-create-templates">Step 7: How to Create Templates</h2>
<p>Templates are HTML files that Django fills in with dynamic data. They're the front end of your application: the part users actually see in their browser.</p>
<h3 id="heading-71-how-to-set-up-the-template-directory">7.1 How to Set Up the Template Directory</h3>
<p>Django looks for templates inside a <code>templates</code> folder within each app. Create the following folder structure inside your <code>tracker</code> app.</p>
<p><code>tracker/templates/tracker</code></p>
<p>The double <code>tracker</code> folder name might look redundant, but it's a Django convention called <strong>template namespacing</strong>. It prevents naming conflicts if you have multiple apps with templates that share the same filename.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/cfbe85c3-36dc-413e-918a-aa64d706d2fc.png" alt="The image shows folder structure of the templates folder" style="display:block;margin:0 auto" width="832" height="474" loading="lazy">

<h3 id="heading-72-how-to-create-the-workout-list-template">7.2 How to Create the Workout List Template</h3>
<p>Create a file called <code>tracker/templates/tracker/workout_list.html</code> and add the following code:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;My Workouts&lt;/title&gt;
    &lt;style&gt;
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background-color: #f5f7fa;
            color: #333;
            line-height: 1.6;
            padding: 2rem;
        }

        .container {
            max-width: 700px;
            margin: 0 auto;
        }

        h1 {
            font-size: 1.8rem;
            margin-bottom: 1rem;
            color: #1a1a2e;
        }

        .add-link {
            display: inline-block;
            background-color: #4361ee;
            color: white;
            padding: 0.6rem 1.2rem;
            border-radius: 6px;
            text-decoration: none;
            margin-bottom: 1.5rem;
            font-size: 0.95rem;
        }

        .add-link:hover {
            background-color: #3a56d4;

        }

        .workout-card {
            background: white;
            border-radius: 8px;
            padding: 1rem 1.2rem;
            margin-bottom: 0.8rem;
            box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
            display: flex;
            justify-content: space-between;
            align-items: center;

        }

        .workout-activity {
            font-weight: 600;
            font-size: 1.05rem;

        }

        .workout-details {
            color: #666;
            font-size: 0.9rem;

        }

        .empty-state {
            text-align: center;
            padding: 3rem 1rem;
            color: #888;

        }

    &lt;/style&gt;
&lt;/head&gt;

&lt;body&gt;
    &lt;div class="container"&gt;
        &lt;h1&gt;My Workouts&lt;/h1&gt;
        &lt;a href="{% url 'add_workout' %}" class="add-link"&gt;+ Log a Workout&lt;/a&gt;
        {% if workouts %}
            {% for workout in workouts %}
                &lt;div class="workout-card"&gt;
                    &lt;div&gt;
                        &lt;div class="workout-activity"&gt;{{ workout.activity }}&lt;/div&gt;
                        &lt;div class="workout-details"&gt;{{ workout.duration }} minutes&lt;/div&gt;
                    &lt;/div&gt;
                    &lt;div class="workout-details"&gt;{{ workout.date }}&lt;/div&gt;
                &lt;/div&gt;
            {% endfor %}

        {% else %}
            &lt;div class="empty-state"&gt;
                &lt;p&gt;No workouts logged yet. Start by adding one!&lt;/p&gt;
            &lt;/div&gt;
        {% endif %}
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>There are a few things worth noting here:</p>
<p>If you look closely at the HTML, you'll spot some weird-looking tags wrapped in curly braces ( <code>{% %}</code> and <code>{{ }}</code> ). Think of them as special instructions for Django.</p>
<p>You use the double curly braces (<code>{{ }}</code>) when you want to output or display a piece of data directly on the page.</p>
<p>On the other hand, you use the brace-and-percent-sign combo ( <code>{% %}</code> ) when you need Django to actually perform an action or apply logic, like running a loop or checking a condition.</p>
<p>They allow us to inject dynamic data straight from our Python backend right into our otherwise static HTML.</p>
<p>Lets look at this code snippet for the <code>workout_list.html</code></p>
<pre><code class="language-html">&lt;body&gt;
    &lt;div class="container"&gt;
        &lt;h1&gt;My Workouts&lt;/h1&gt;
        &lt;a href="{% url 'add_workout' %}" class="add-link"&gt;+ Log a Workout&lt;/a&gt;
        {% if workouts %}
            {% for workout in workouts %}
                &lt;div class="workout-card"&gt;
                    &lt;div&gt;
                        &lt;div class="workout-activity"&gt;{{ workout.activity }}&lt;/div&gt;
                        &lt;div class="workout-details"&gt;{{ workout.duration }} minutes&lt;/div&gt;
                    &lt;/div&gt;
                    &lt;div class="workout-details"&gt;{{ workout.date }}&lt;/div&gt;
                &lt;/div&gt;
            {% endfor %}

        {% else %}
            &lt;div class="empty-state"&gt;
                &lt;p&gt;No workouts logged yet. Start by adding one!&lt;/p&gt;
            &lt;/div&gt;
        {% endif %}
    &lt;/div&gt;
&lt;/body&gt;
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/864ed3dc-ceda-44f8-ba3d-b061222714c7.png" alt="The image shows the the body section of the workout_list.html with the focus on django template tags" style="display:block;margin:0 auto" width="1988" height="1160" loading="lazy">

<p>There are a few things worth noting here.</p>
<p>Right under the main heading, you'll see this line:<br><code>&lt;a href="{% url 'add_workout' %}"&gt;</code></p>
<p>Instead of hardcoding a web link like <code>href="/add-workout/"</code>, Django uses the <code>{% url %}</code> tag to generate the link dynamically. You pass it the name of the route (in this case, <code>add_workout</code>), and Django automatically figures out the correct URL path.</p>
<p>If you ever change the URL structure in your Python code later, Django updates this link automatically. You never have to hunt through HTML files to fix broken links!</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/2dc56430-d146-4fc9-8ae5-a3fb5a0d7dd1.png" alt="The image highlights the code that generates dynamic url" style="display:block;margin:0 auto" width="1748" height="632" loading="lazy">

<p>The <code>{% if workouts %}</code> block checks whether there are any workouts to display. If the list is empty, it shows a friendly message instead of a blank page.</p>
<p>The <code>{% for workout in workouts %}</code> loop iterates over every workout in the list and renders a card for each one. The double curly braces <code>{{ workout.activity }}</code> insert the value of each field into the HTML</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/f75b5c7e-5cd0-457c-901f-e489c26a8175.png" alt="f75b5c7e-5cd0-457c-901f-e489c26a8175" style="display:block;margin:0 auto" width="2012" height="1002" loading="lazy">

<p>Inside the loop, you'll notice tags that look like this:</p>
<ul>
<li><p><code>{{ workout.activity }}</code></p>
</li>
<li><p><code>{{ workout.duration }}</code></p>
</li>
<li><p><code>{{ workout.date }}</code></p>
</li>
</ul>
<p>As Django loops through each workout object, it uses dot notation to peek inside that specific object and grab its details. It grabs the activity type (like "Running"), the duration ("30"), and the date ("March 30"), and prints that exact text directly onto the webpage for the user to see.</p>
<h3 id="heading-73-how-to-create-add-workout-template">7.3 How to Create Add Workout Template</h3>
<p>Create a file called <code>tracker/templates/tracker/add_workout.html</code> and add the following code:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;Log a Workout&lt;/title&gt;
    &lt;style&gt;
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;

        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background-color: #f5f7fa;
            color: #333;
            line-height: 1.6;
            padding: 2rem;
        }

        .container {
            max-width: 500px;
            margin: 0 auto;

        }

        h1 {
            font-size: 1.8rem;
            margin-bottom: 1.5rem;
            color: #1a1a2e;
        }

        .form-group {
            margin-bottom: 1.2rem;
        }

        label {
            display: block;
            margin-bottom: 0.3rem;
            font-weight: 600;
            font-size: 0.95rem;

        }

        input[type="text"],
        input[type="number"],
        input[type="date"] {
            width: 100%;
            padding: 0.6rem 0.8rem;
            border: 1px solid #ddd;
            border-radius: 6px;
            font-size: 1rem;
            transition: border-color 0.2s;
        }

        input:focus {
            outline: none;
            border-color: #4361ee;

        }

        .btn {
            background-color: #4361ee;
            color: white;
            padding: 0.7rem 1.5rem;
            border: none;
            border-radius: 6px;
            font-size: 1rem;
            cursor: pointer;
            margin-right: 0.5rem;
        }

        .btn:hover {
            background-color: #3a56d4;
        }

        .back-link {
            color: #4361ee;
            text-decoration: none;
            font-size: 0.95rem;
        }

        .back-link:hover {
            text-decoration: underline;
        }

        .actions {
            display: flex;
            align-items: center;
            gap: 1rem;
            margin-top: 0.5rem;
        }

        .error-list {
            color: #e74c3c;
            font-size: 0.85rem;
            margin-top: 0.3rem;

        }

    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div class="container"&gt;
       &lt;h1&gt;Log a Workout&lt;/h1&gt;
        &lt;form method="post"&gt;
            {% csrf_token %}
            &lt;div class="form-group"&gt;
                &lt;label for="id_activity"&gt;Activity&lt;/label&gt;
                {{ form.activity }}
                {% if form.activity.errors %}
                    &lt;div class="error-list"&gt;{{ form.activity.errors }}&lt;/div&gt;
                {% endif %}
            &lt;/div&gt;
            &lt;div class="form-group"&gt;
                &lt;label for="id_duration"&gt;Duration (minutes)&lt;/label&gt;
                {{ form.duration }}
                {% if form.duration.errors %}
                    &lt;div class="error-list"&gt;{{ form.duration.errors }}&lt;/div&gt;
                {% endif %}
            &lt;/div&gt;

            &lt;div class="form-group"&gt;
                &lt;label for="id_date"&gt;Date&lt;/label&gt;
                {{ form.date }}
                {% if form.date.errors %}
                    &lt;div class="error-list"&gt;{{ form.date.errors }}&lt;/div&gt;
                {% endif %}
            &lt;/div&gt;

            &lt;div class="actions"&gt;
                &lt;button type="submit" class="btn"&gt;Save Workout&lt;/button&gt;
                &lt;a href="{% url 'workout_list' %}" class="back-link"&gt;Cancel&lt;/a&gt;
            &lt;/div&gt;
        &lt;/form&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>In the previous template, we learned how to display data. Now, we're looking at a form that actually collects data. Handling forms manually in web development can get messy, but Django provides some powerful template tags to do the heavy lifting for us.</p>
<p>Let's look at the Django-specific logic powering this form:</p>
<p>First, right after opening the <code>&lt;form&gt;</code> tag, you'll spot a very important line: <code>{% csrf_token %}</code>. Whenever you submit data to a server using a "POST" method, malicious sites can potentially intercept or forge that request.</p>
<p>By including this <code>{% csrf_token %}</code>, you tell Django to generate a unique, hidden security key for the form. When the user clicks "Save Workout," Django checks this token to guarantee the request is legitimate. <strong>If you forget this tag, Django will simply reject your form!</strong></p>
<pre><code class="language-html">&lt;form method="post"&gt;
            {% csrf_token %}
            &lt;div class="form-group"&gt;
                &lt;label for="id_activity"&gt;Activity&lt;/label&gt;
                {{ form.activity }}
                {% if form.activity.errors %}
                    &lt;div class="error-list"&gt;{{ form.activity.errors }}&lt;/div&gt;
                {% endif %}
            &lt;/div&gt;
            &lt;div class="form-group"&gt;
                &lt;label for="id_duration"&gt;Duration (minutes)&lt;/label&gt;
                {{ form.duration }}
                {% if form.duration.errors %}
                    &lt;div class="error-list"&gt;{{ form.duration.errors }}&lt;/div&gt;
                {% endif %}
            &lt;/div&gt;

            &lt;div class="form-group"&gt;
                &lt;label for="id_date"&gt;Date&lt;/label&gt;
                {{ form.date }}
                {% if form.date.errors %}
                    &lt;div class="error-list"&gt;{{ form.date.errors }}&lt;/div&gt;
                {% endif %}
            &lt;/div&gt;

            &lt;div class="actions"&gt;
                &lt;button type="submit" class="btn"&gt;Save Workout&lt;/button&gt;
                &lt;a href="{% url 'workout_list' %}" class="back-link"&gt;Cancel&lt;/a&gt;
            &lt;/div&gt;
        &lt;/form&gt;
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/4ca2a034-119c-4dc7-a14a-cd0a81815c58.png" alt="The image shows a screenshot of the code and highlight the csrf token tag" style="display:block;margin:0 auto" width="1580" height="824" loading="lazy">

<p>Now let's talk about automatically generating the form fields. Instead of manually typing out all the HTML <code>&lt;input&gt;</code> tags for the activity, duration, and date, we let Django do it for us using display tags (<code>{{ }}</code>).</p>
<p>Each <code>{{ form.activity }}</code>, <code>{{ form.duration }}</code>, and <code>{{ form.date }}</code> tag renders the corresponding form input. Django handles the HTML attributes, input types, and validation for you based on the model and form definitions.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/c2403685-b78d-4aa7-876d-63ca53481e37.png" alt="This image shows the code that automatically generates HTML forms" style="display:block;margin:0 auto" width="936" height="572" loading="lazy">

<p>The error blocks below each field display validation messages if a user submits invalid data, like entering text in the duration field instead of a number. Users make mistakes. They might leave a required field blank or type text into a number field. Fortunately, Django validates the data for you and sends back errors if something goes wrong.</p>
<p>Underneath each input field, we use a logic block that looks like this:<br><code>{% if form.activity.errors %}</code></p>
<p>This code checks a simple condition: Did the user mess up this specific field? If Django found an error with the "activity" input, the code drops into the if block and uses<code>{{ form.activity.errors }}</code> block to print the exact error message (like "<strong>This field is required</strong>") right below the input box.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/97a61f9d-faf6-4ddd-ab17-d53da88e0d07.png" alt="This image displays the error blocks" style="display:block;margin:0 auto" width="1908" height="1116" loading="lazy">

<p>You may notice that both templates include inline CSS rather than a separate stylesheet. For a small project like this, inline styles keep things simple and self-contained. In a larger project, you would use Django's static files system to manage CSS separately.</p>
<h2 id="heading-step-8-how-to-connect-urls">Step 8: How to Connect URLs</h2>
<p>You have views and templates, but Django doesn't know when to use them yet. You need to map URLs to views so that visiting a specific address in the browser triggers the right view function.</p>
<h3 id="heading-81-how-to-create-app-level-urls">8.1 How to Create App Level URLs</h3>
<p>Create a new file called <code>tracker/urls.py</code> and add the following code:</p>
<pre><code class="language-python">from django.urls import path
from . import views

urlpatterns = [ 
    path('', views.workout_list, name='workout_list'), 
    path('add/', views.add_workout, name='add_workout'), 
]
</code></pre>
<p>Each path function takes three arguments.</p>
<p>The first is the route string that represents a URL pattern (an empty string means the root of the app).</p>
<p>The second is the view function to call when that URL is visited.</p>
<p>The third is a name you can use to reference this URL elsewhere in your code, like in the <code>{% url %}</code> template tags you used earlier.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/d9128270-eec3-43af-93de-08780b4a53f5.png" alt="The image contains the description of three arguments of the path function" style="display:block;margin:0 auto" width="2864" height="1056" loading="lazy">

<h3 id="heading-82-how-to-link-app-urls-to-project">8.2 How to Link App URLs to project</h3>
<p>Now that your app-level URLs are set up, the next step is to connect them to the main project so Django knows where to start routing requests. Think of it like linking a smaller map (your app) to a bigger map (your project), so everything works together smoothly.</p>
<p>Open <code>fitness_project.urls.py</code> and update it to include your app's URLs:</p>
<pre><code class="language-python">from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('tracker.urls')),
]
</code></pre>
<p>The <code>include()</code> function tells Django to look at the URL patterns defined in the <code>tracker/urls.py</code> file whenever someone visits your site. The empty string prefix means your tracker app handles requests at the root of the site.</p>
<p>Here's the full picture of how a request flows through the URL system.</p>
<p>When someone visits <a href="http://127.0.0.1:8000/add/">http://127.0.0.1:8000/add/</a>, Django first checks <code>fitness_project/urls.py</code>. It matches the empty prefix and delegates to <code>tracker/urls.py</code>. There, it matches <code>add/</code> and calls the <code>add_workout view</code>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/4590c4e3-0d32-4865-90db-a6504ee508c1.png" alt="The image shows the how the URL flows through the system" style="display:block;margin:0 auto" width="2764" height="1146" loading="lazy">

<h2 id="heading-step-9-how-to-test-the-application-locally">Step 9: How to Test the Application Locally</h2>
<p>At this point, your app has everything it needs to work. Let's test it.</p>
<p>Start the development server by running the command:</p>
<pre><code class="language-shell">python manage.py runserver
</code></pre>
<p>Open your browser and visit <a href="http://127.0.0.1:8000/">http://127.0.0.1:8000/</a>. You should see the workout list page with the heading "<strong>My Workouts</strong>" and a button that says "<strong>+ Log a Workout</strong>."</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/e15dc4cf-09b0-4ba9-95b1-baba32ce929f.png" alt="The image shows the My Workouts image with the button to log a workout" style="display:block;margin:0 auto" width="2186" height="1006" loading="lazy">

<p>Click that button. You should see the workout form with fields for activity, duration, and date.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/d9790d3e-0b6a-4e1b-b3d9-f305bc254cfc.png" alt="The image shows an empty form to log a workout" style="display:block;margin:0 auto" width="1146" height="838" loading="lazy">

<p>Fill in some test data:</p>
<ul>
<li><p>Activity: Skipping</p>
</li>
<li><p>Duration: 25</p>
</li>
<li><p>Date: Pick today's date from the date picker</p>
</li>
</ul>
<p>Click "<strong>Save Workout</strong>" You should be redirected back to the workout list page, and your new workout should appear as a card.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/3f1dbf8f-3547-45ec-961d-cff75092ec02.png" alt="The image shows the workout list after adding a new workout" style="display:block;margin:0 auto" width="1656" height="1042" loading="lazy">

<p>Try adding a few more workouts with different activities and dates. Make sure they all show up on the list page in the correct order (most recent first).</p>
<p>This is also a good time to experiment. Try submitting the form with missing fields and see how Django handles validation.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/542e32cf-abf5-4f11-9d4e-0733697c591b.png" alt="The image shows an incomplete form being submitted and a correspoding error message" style="display:block;margin:0 auto" width="1284" height="1012" loading="lazy">

<p>Try accessing the admin panel at <a href="http://127.0.0.1:8000/admin/">http://127.0.0.1:8000/admin/</a> to see your workouts there as well.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/cdfbcbf3-f7b6-4b97-92ee-dcc27e2ce60c.png" alt="This image shows the added workouts in Django admin" style="display:block;margin:0 auto" width="2876" height="848" loading="lazy">

<p>If everything works as expected, you're ready to put your app on the internet.</p>
<h2 id="heading-step-10-how-to-prepare-for-deployment">Step 10: How to Prepare for Deployment</h2>
<p>Running your app on localhost is great for development, but nobody else can see it. Deployment means putting your app on a server that's accessible from anywhere on the internet.</p>
<p>Before you deploy, you'll need to make a few changes to your project's settings.</p>
<h3 id="heading-101-how-to-update-settings-for-production">10.1 How to Update Settings for Production</h3>
<p>Open <code>fitness_project/settings.py</code> and make the following changes.</p>
<p>First, set <code>DEBUG</code> to <code>False</code>.</p>
<p>During development, <code>DEBUG = True</code> shows detailed error pages that help you fix problems. In production, these error pages would expose sensitive information about your code and server to anyone who triggers an error.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/6b8b491e-d0ae-4a93-80bf-92134e46ff22.png" alt="The image shows the DEBUG being set to False in the settings.py file" style="display:block;margin:0 auto" width="2866" height="1348" loading="lazy">

<p>Next, update <code>ALLOWED_HOSTS</code> to include <strong>PythonAnywhere's</strong> <strong>domain</strong>.</p>
<p>This setting tells Django which domain names are allowed to serve your app. Replace yourusername with the actual PythonAnywhere username you will create in the next step.</p>
<pre><code class="language-python">ALLOWED_HOSTS = ['yourusername.pythonanywhere.com']
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/050746f6-d076-41de-8810-08bf539bfda5.png" alt="The image shows the allowed host list being updated to add the pythonanywhere domain" style="display:block;margin:0 auto" width="1622" height="1068" loading="lazy">

<p>Finally, add a <code>STATIC_ROOT</code> setting so Django knows where to collect your static files (CSS, JavaScript, images) for production:</p>
<pre><code class="language-python">import os
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/712e0514-42f6-4292-ae52-99f49b6f162b.png" alt="The image shows the code to collect static files" style="display:block;margin:0 auto" width="2862" height="1054" loading="lazy">

<p>These are the minimum changes needed for a basic deployment.</p>
<p>💡 For a production app handling real user data, you would also want to set a secure SECRET_KEY, configure a proper database like PostgreSQL, and set up HTTPS. But for a learning project, these changes are enough.</p>
<h2 id="heading-step-11-how-to-deploy-your-django-app-on-pythonanywhere">Step 11: How to Deploy Your Django App on PythonAnywhere</h2>
<p>PythonAnywhere is a hosting platform designed specifically for Python web applications. It offers a free tier that's perfect for beginner projects, and it handles much of the server configuration that would otherwise be complex to set up on your own.</p>
<h3 id="heading-111-how-to-create-a-pythonanywhere-account">11.1 How to Create a PythonAnywhere Account</h3>
<p>Go to <a href="http://pythonanywhere.com">pythonanywhere.com</a> and sign up for a free "Beginner" account. Remember the username you choose, because your app will be available at <a href="http://yourusername.pythonanywhere.com"><strong>yourusername.pythonanywhere.com</strong></a><strong>.</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/e64e7ca4-7d7d-40a7-a56b-259bb706461f.png" alt="The image shows the homepage of pythonanywhere" style="display:block;margin:0 auto" width="2854" height="1782" loading="lazy">

<p>Now signup to the website. Fill in the username, email and password and click on the free tier or now.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/fde55ec6-16f4-4a4f-8927-7b01d3e36a96.png" alt="The image shows the various tiers of python anywhere websites" style="display:block;margin:0 auto" width="2758" height="1418" loading="lazy">

<h3 id="heading-112-how-to-upload-your-project-files">11.2 How to Upload Your Project Files</h3>
<p>After logging in, you have two options for getting your project files onto PythonAnywhere.</p>
<h4 id="heading-option-a-upload-using-git">Option A: Upload using Git</h4>
<p>If your project is in a Git repository, open a Bash console from the PythonAnywhere dashboard by clicking "Consoles" and then "Bash." Then clone your repository:</p>
<p>git clone <a href="https://github.com/yourusername/fitness-tracker.git">https://github.com/yourusername/fitness-tracker.git</a></p>
<p>In this tutorial, we won't be using Git. Instead we'll follow the second option.</p>
<h4 id="heading-option-b-upload-files-manually">Option B: Upload files manually</h4>
<p>First go your project folder in your computer and created a compressed version of the project.</p>
<p>IMPORTANT NOTE: When you create the compressed file, make sure to first create a copy of the project somewhere and remove the venv and pycache folder before you compress it.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/169dcd52-50f0-44c0-82d4-1dea1196ac88.png" alt="The image shows the project folder being compressed" style="display:block;margin:0 auto" width="1564" height="422" loading="lazy">

<p>Navigate to your home directory and click on upload file tab and upload the compressed file.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/633c7b1f-0088-4d2d-8911-b45e86aa39c2.png" alt="The image shows the compressed file being uploaded to pythonanywhere" style="display:block;margin:0 auto" width="2668" height="1096" loading="lazy">

<p>Now we need to unzip the compressed file. To do this, go to the Consoles tab and click on Bash console.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/7774a88e-362e-4680-a19d-32e0ef09fe04.png" alt="The image shows the Consoles tab and bash option" style="display:block;margin:0 auto" width="2692" height="1198" loading="lazy">

<p>The bash console should open. Then type the following command in the console to unzip the folder:</p>
<pre><code class="language-shell">unzip fitness-tracker.zip
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/1ae044ad-8996-4191-868a-33566c2483d9.png" alt="The image shows the result of the unzip command" style="display:block;margin:0 auto" width="2330" height="1078" loading="lazy">

<h3 id="heading-113-how-to-set-up-a-virtual-environment-in-pythonanywhere">11.3 How to Set Up a Virtual Environment in PythonAnywhere</h3>
<p>Open a Bash console from the PythonAnywhere dashboard. Navigate to your project directory and create a fresh virtual environment:</p>
<pre><code class="language-shell">cd fitness-tracker
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/c8113d5d-68a6-400f-ab9f-7c8903485eda.png" alt="The image shows changing the directory to fitness tracker" style="display:block;margin:0 auto" width="2852" height="468" loading="lazy">

<p>Type the following command to install a virtual environment as we've done before and then activate the virtual environment:</p>
<pre><code class="language-shell">python3 -m venv venv

source venv/bin/activate
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/724767df-181c-4fcf-91e0-77c6dac566b0.png" alt="The image shows the virtual environment being created and activated" style="display:block;margin:0 auto" width="2108" height="624" loading="lazy">

<p>Now install Django as before using <code>pip install django</code> command:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/d6c37d87-7497-4ca1-a90f-6cd66422c2e5.png" alt="The image shows django being installed" style="display:block;margin:0 auto" width="1980" height="952" loading="lazy">

<h3 id="heading-114-how-to-run-migrations-and-create-a-superuser-on-pythonanywhere">11.4 How to Run Migrations and Create a SuperUser on PythonAnywhere</h3>
<p>While you're still in the Bash console with your virtual environment activated, run the migrations to create the database tables on the server:</p>
<pre><code class="language-shell">python manage.py makemigrations

python manage.py migrate

python manage.py createsuperuser
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/0b763118-5a1c-4c3f-87f4-693cc7de0da2.png" alt="The image shows the make migrations and migrate commands running" style="display:block;margin:0 auto" width="2026" height="768" loading="lazy">

<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/77183d3c-b4bc-40b7-b00f-5f03d2aea2ab.png" alt="The image shows the super user being created" style="display:block;margin:0 auto" width="1814" height="506" loading="lazy">

<h3 id="heading-114-how-to-configure-the-web-app-in-pythonanywhere">11.4 How to Configure the Web App in Pythonanywhere</h3>
<p>Go to the "Web" tab on the PythonAnywhere dashboard and click "Add a new web app." Follow the setup wizard:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/e1296e93-3e52-4327-b12a-4d4555e80845.png" alt="The image shows the web tab and add a new web app button" style="display:block;margin:0 auto" width="2880" height="846" loading="lazy">

<p>Click "Next" on the domain name step (<em>remember the free tier uses</em> <a href="http://yourusername.pythonanywhere.com"><em>yourusername.pythonanywhere.com</em></a>).</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/24495b99-e1dc-437d-8ce8-47c0c962349f.png" alt="The image shows the web console where you specify the domain name" style="display:block;margin:0 auto" width="2870" height="1302" loading="lazy">

<p>Select "Manual configuration" (not "Django" – the manual option gives you more control).</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/db470bd1-bbd1-4c61-b846-b7147d8f820f.png" alt="The image highlight the manual configuration option which should be selected" style="display:block;margin:0 auto" width="2872" height="1326" loading="lazy">

<p>Then choose the Python version that matches what you installed. In my case it's 3.13, so I'll choose 3.13</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/dc51d44e-531c-4871-a225-e68b2db5dc65.png" alt="The image shows the Python version what is being selected" style="display:block;margin:0 auto" width="2780" height="1384" loading="lazy">

<p>Click on Next button and a WSGI (Web Server Gateway Interface) will be created.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/3565512b-9c84-4b0d-82d9-3964cb0ff46b.png" alt="The image shows the final page before the web app is created" style="display:block;margin:0 auto" width="2878" height="1422" loading="lazy">

<p>With this we've created the web app:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/eda9fe63-01f4-48bc-98f1-07696bb798bf.png" alt="The image shows the final creation of the web app" style="display:block;margin:0 auto" width="2874" height="1568" loading="lazy">

<p>After you've set up the web app, you have to do two more things:</p>
<ul>
<li><p>Set the virtual environment path</p>
</li>
<li><p>Configure the WSGI file</p>
</li>
</ul>
<h3 id="heading-115-how-to-set-the-virtual-environment-path">11.5 How to Set the Virtual Environment Path</h3>
<p>On the <strong>Web</strong> tab, scroll down to the "<strong>Virtualenv</strong>" section and enter the path to your virtual enviroment. The path to the file should be like this:</p>
<pre><code class="language-shell">/home/yourusername/fitness-tracker/venv
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/413ed047-ecc1-440f-a931-4a57aa91348b.png" alt="The image shows the added path of virtual environment" style="display:block;margin:0 auto" width="2878" height="994" loading="lazy">

<h3 id="heading-116-how-to-configure-the-wsgi-file">11.6 How to Configure the WSGI file</h3>
<p>Still on the Web tab, scroll to the code section and click on the WSGI configuration file link:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/934d1542-79d6-4140-8bb2-a4389fc17770.png" alt="The image shows the Code section and the WSGI configuration file path" style="display:block;margin:0 auto" width="2264" height="548" loading="lazy">

<p>Delete all the contents and replace them with the content below and save the file:</p>
<pre><code class="language-python">import os
import sys
path = '/home/prabodhtuladhardev/fitness-tracker' #replace with your username
if path not in sys.path:
    sys.path.append(path)

os.environ['DJANGO_SETTINGS_MODULE'] = 'fitness_project.settings'

from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/416adac6-61d7-464d-b91f-b3b35272ef08.png" alt="The image shows the edited wsgi.py file and the highlights the save button" style="display:block;margin:0 auto" width="1812" height="986" loading="lazy">

<h3 id="heading-117-how-to-set-up-static-files">11.7 How to Set Up Static Files</h3>
<p>Still on the "Web" tab, scroll down to the "Static files" section. Add an entry:</p>
<ul>
<li><p>URL: <code>/static/</code></p>
</li>
<li><p>Directory: <code>/home/yourusername/fitness-tracker/staticfiles</code></p>
</li>
</ul>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/f128dbd2-4acc-4387-89f8-d79de8c1e66e.png" alt="The image shows the static files section of the Web tab" style="display:block;margin:0 auto" width="2488" height="728" loading="lazy">

<p>Then go back to your Bash console and run the following command:</p>
<pre><code class="language-shell">python manage.py collectstatic
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/207f8e67-2679-4ccc-8ebf-7d8aae5d7494.png" alt="The image shows the results of the collect static command" style="display:block;margin:0 auto" width="2448" height="600" loading="lazy">

<p>This copies all static files to the staticfiles directory so PythonAnywhere can serve them directly.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/a14b75c4-cc1e-48ad-baf2-cf4f00906b85.png" alt="The image shows the folder named static files that was created" style="display:block;margin:0 auto" width="2720" height="1026" loading="lazy">

<p>Go back to the "Web" tab and click the green "Reload" button at the top. This restarts your app with all the new configuration.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/08fab36e-8c8d-4d1b-8342-6e340fcf45d4.png" alt="The image shows the web tab with the reload button" style="display:block;margin:0 auto" width="2874" height="1046" loading="lazy">

<h3 id="heading-118-how-to-view-your-live-application">11.8 How to View Your Live Application</h3>
<p>Open a new browser tab and visit <a href="https://yourusername.pythonanywhere.com">https://yourusername.pythonanywhere.com</a>. You should see your fitness tracker, live on the internet.</p>
<p>Try adding a workout.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/db67dbb1-3cb9-4b86-bfaa-67427ef5eac0.png" alt="The image shows the workout list view being opened in python anywhere" style="display:block;margin:0 auto" width="2290" height="1278" loading="lazy">

<p>Visit the admin panel at <a href="https://yourusername.pythonanywhere.com/admin/">https://yourusername.pythonanywhere.com/admin/</a>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/bd80d1e8-cf23-4ac2-b6a7-35bfdf61d023.png" alt="The image shows the workout django admin being opened in pythonanywhere" style="display:block;margin:0 auto" width="2128" height="1310" loading="lazy">

<p>Everything should work just as it did on your local machine, but now anyone with the link can access it.</p>
<p>This is a meaningful milestone. You've gone from zero to a deployed Django application. Share the link with a friend or post it in a coding community. Seeing your work live on the internet is one of the most motivating experiences in learning to code.</p>
<h2 id="heading-common-mistakes-and-how-to-fix-them">Common Mistakes and How to Fix Them</h2>
<p>Even when you follow each step carefully, things can go wrong. Here are the most common issues beginners run into and how to solve them.</p>
<p><strong>"ModuleNotFoundError: No module named 'django'"</strong> – This usually means your virtual environment isn't activated. Run <code>source venv/bin/activate</code> (macOS/Linux) or <code>venv\Scripts\activate</code> (Windows) and try again. On PythonAnywhere, make sure the <strong>virtualenv</strong> path in the "<strong>Web</strong>" tab points to the correct location.</p>
<p><strong>"DisallowedHost" error</strong> – You forgot to add your domain to <code>ALLOWED_HOSTS</code> in <code>settings.py</code>, or there's a typo. Double-check that it matches your PythonAnywhere URL exactly.</p>
<p><strong>Static files not loading in production</strong> – Make sure you ran <code>python manage.py collectstatic</code> and that the static file mapping on PythonAnywhere points to the <strong>correct staticfiles</strong> directory. Also verify that <code>STATIC_ROOT</code> is set in <code>settings.py</code>.</p>
<p><strong>"No such table" or migration errors</strong> – You probably forgot to run <code>python manage.py migrate</code> after cloning or uploading your project to PythonAnywhere. Run the <code>migrate</code> command in the Bash console.</p>
<p><strong>Changes not showing up on PythonAnywhere</strong> – After making any code changes, you must click the "<strong>Reload</strong>" button on the "<strong>Web</strong>" tab. PythonAnywhere does not automatically detect file changes.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/08fab36e-8c8d-4d1b-8342-6e340fcf45d4.png" alt="The image shows the web tab and the reload buttton" style="display:block;margin:0 auto" width="2874" height="1046" loading="lazy">

<h2 id="heading-how-you-can-improve-this-project">How You Can Improve This Project</h2>
<p>The fitness tracker you built is intentionally simple. That's a feature, not a limitation. A working simple project is the perfect foundation for learning more.</p>
<p>Here are some ideas for expanding it.</p>
<ol>
<li><p><strong>Add user authentication:</strong> Right now, anyone who visits the site sees the same workout data. Django has a built-in authentication system that lets you add registration, login, and logout. Each user could then have their own private list of workouts.</p>
</li>
<li><p><strong>Add the ability to edit and delete workouts.</strong> Currently, once a workout is saved, there's no way to change or remove it from the interface (you can do it through the admin panel, but not the main app). Try creating new views and templates for editing and deleting.</p>
</li>
<li><p><strong>Add workout categories or tags.</strong> Let users categorize their workouts as "Cardio," "Strength," "Flexibility," and so on. This would involve adding a new field to the model or creating a separate Category model with a foreign key relationship.</p>
</li>
<li><p><strong>Add charts and progress tracking.</strong> Use a JavaScript charting library like Chart.js to display workout trends over time. For example, you could show a bar chart of total minutes exercised per week.</p>
</li>
<li><p><strong>Build an API with Django REST Framework.</strong> If you want to learn about building APIs, try installing Django REST Framework (DRF) and creating API endpoints for your workouts. This would let you build a mobile app or a separate front end that communicates with your Django back end.</p>
</li>
</ol>
<p>Each of these improvements will teach you something new about Django while building on the foundation you already have.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You've built a fully functional fitness tracker web app with Django and deployed it to the internet. That's no small achievement.</p>
<p>Along the way, you learned how Django projects and apps are structured, how models define the shape of your data, how migrations translate those models into database tables, how views handle the logic of your application, how templates render dynamic HTML, and how URLs tie everything together. You also went through the entire deployment process on PythonAnywhere.</p>
<p>These are the core building blocks of Django development. The patterns you practiced here – defining a model, creating a form, writing a view, building a template, and connecting a URL – are the same patterns you will use in every Django project, no matter how complex.</p>
<p>The best way to solidify what you have learned is to keep building. Try one of the improvements mentioned above, or start a completely new project. A calorie tracker, a habit tracker, an expense tracker, or a personal journal would all use the same Django concepts with slightly different models and views.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Optimize Django REST APIs for Performance: Profiling, Caching, and Scaling. ]]>
                </title>
                <description>
                    <![CDATA[ Performance problems in APIs rarely start as performance problems. They usually start as small design decisions that worked perfectly when the application had ten users, ten records, or a single developer testing locally. Over time, as traffic increa... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-optimize-django-rest-apis-for-performance/</link>
                <guid isPermaLink="false">6994b1d13e0696149c7c229c</guid>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                    <category>
                        <![CDATA[ django rest framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ REST API ]]>
                    </category>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Performance Optimization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scalability ]]>
                    </category>
                
                    <category>
                        <![CDATA[ backend ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mari ]]>
                </dc:creator>
                <pubDate>Tue, 17 Feb 2026 18:22:09 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1771352481135/11be538b-aaf5-4c1e-8ee2-99deea5f180e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Performance problems in APIs rarely start as performance problems. They usually start as small design decisions that worked perfectly when the application had ten users, ten records, or a single developer testing locally. Over time, as traffic increases and data grows, those same decisions begin to slow everything down.</p>
<p>In this article, we’ll walk step by step through how performance issues arise in Django REST APIs, how to see them clearly using profiling tools, and how to fix them using query optimization, caching, pagination, and basic scaling strategies.</p>
<p>This article will be most useful for developers who already understand Django, the Django REST Framework, and REST concepts, but are new to performance optimization.</p>
<h3 id="heading-what-well-cover">What we’ll cover:</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-django-rest-apis-become-slow">Why Django REST APIs Become Slow</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-profiling-finding-the-real-bottlenecks">Profiling: Finding the Real Bottlenecks</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-logging-sql-queries">Logging SQL Queries</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-summary-and-next-steps">Summary and Next Steps</a></p>
</li>
</ul>
<h2 id="heading-why-django-rest-apis-become-slow">Why Django REST APIs Become Slow</h2>
<p>Before optimizing anything, it’s important to understand why APIs become slow in the first place.</p>
<p>Most performance issues in Django REST APIs come from three main sources:</p>
<ol>
<li><p>Too many database queries</p>
</li>
<li><p>Doing expensive work repeatedly</p>
</li>
<li><p>Returning more data than necessary</p>
</li>
</ol>
<p>Django is fast by default, but it does exactly what you ask it to do. If your API endpoint triggers 300 database queries, Django will happily run all 300.</p>
<p>Now let’s look at some common causes of performance issues in Django REST APIs.</p>
<h3 id="heading-1-n1-query-problems-in-serializers">1. N+1 Query Problems in Serializers</h3>
<p>This happens when you loop over objects and access related fields, causing a separate query for each object.</p>
<pre><code class="lang-python"><span class="hljs-comment"># models.py</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Author</span>(<span class="hljs-params">models.Model</span>):</span>
    name = models.CharField(max_length=<span class="hljs-number">100</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Post</span>(<span class="hljs-params">models.Model</span>):</span>
    title = models.CharField(max_length=<span class="hljs-number">200</span>)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

<span class="hljs-comment"># views.py (naive approach)</span>
posts = Post.objects.all()
<span class="hljs-keyword">for</span> post <span class="hljs-keyword">in</span> posts:
    <span class="hljs-comment"># This triggers a query per post to fetch the author</span>
    print(post.author.name)
</code></pre>
<p>If you have 100 posts, this runs 101 queries: 1 for posts and 100 for authors. Django lazily loads related objects by default, so without intervention, your API performs repetitive database work that slows response times.</p>
<h3 id="heading-2-fetching-related-objects-inefficiently">2. Fetching Related Objects Inefficiently</h3>
<pre><code class="lang-python"><span class="hljs-comment"># Naive queryset fetching all related objects separately</span>
posts = Post.objects.all()
authors = [post.author <span class="hljs-keyword">for</span> post <span class="hljs-keyword">in</span> posts]  <span class="hljs-comment"># triggers extra queries per post</span>
</code></pre>
<p>Each access to <code>post.author</code> triggers a new query. Even though you already fetched all posts, Django lazily loads related objects by default. This creates many extra queries, slowing down your API.</p>
<h3 id="heading-3-serializing-large-datasets-without-pagination">3. Serializing Large Datasets Without Pagination</h3>
<p>Returning large query sets all at once can slow down your API and increase memory usage.</p>
<pre><code class="lang-python"><span class="hljs-comment"># views.py</span>
<span class="hljs-keyword">from</span> rest_framework.response <span class="hljs-keyword">import</span> Response
<span class="hljs-keyword">from</span> rest_framework.decorators <span class="hljs-keyword">import</span> api_view
<span class="hljs-keyword">from</span> .models <span class="hljs-keyword">import</span> Post
<span class="hljs-keyword">from</span> .serializers <span class="hljs-keyword">import</span> PostSerializer

<span class="hljs-meta">@api_view(['GET'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">all_posts</span>(<span class="hljs-params">request</span>):</span>
    posts = Post.objects.all()  <span class="hljs-comment"># retrieves all posts at once</span>
    serializer = PostSerializer(posts, many=<span class="hljs-literal">True</span>)
    <span class="hljs-keyword">return</span> Response(serializer.data)
</code></pre>
<p>If your database has thousands of posts, this endpoint fetches everything in memory, serializes it, and sends it over the network. It’s slow and can crash under load. Later, we’ll learn to paginate results efficiently.</p>
<h3 id="heading-4-recomputing-expensive-work-repeatedly">4. Recomputing Expensive Work Repeatedly</h3>
<p>Some endpoints calculate the same values on every request instead of caching or precomputing.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">expensive_view</span>(<span class="hljs-params">request</span>):</span>
    <span class="hljs-comment"># Simulate expensive computation</span>
    result = sum([i**<span class="hljs-number">2</span> <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">1000000</span>)])
    <span class="hljs-keyword">return</span> JsonResponse({<span class="hljs-string">"result"</span>: result})
</code></pre>
<p>Even if the data doesn’t change often, this computation happens on every request, consuming CPU time unnecessarily.  </p>
<p>Performance optimization is about reducing unnecessary work.  </p>
<p>At this point, it might be tempting to jump straight into fixes like caching responses or optimizing database queries. But doing that without evidence often leads to wasted effort or even new problems.</p>
<p>Before changing anything, you need to understand where your API is actually spending time. Is it the database? Is it serialization? Is it Python code running repeatedly on every request? This is where profiling becomes essential.</p>
<h2 id="heading-profiling-finding-the-real-bottlenecks">Profiling: Finding the Real Bottlenecks</h2>
<p>Optimizing without profiling is guessing. Profiling helps you answer one question:</p>
<blockquote>
<p>Where is my API actually spending time?</p>
</blockquote>
<p>In practice, profiling means observing an API while it runs and collecting data about what it’s doing. This includes how many database queries are executed, how long those queries take, and how much time is spent in Python code, such as serializers or business logic.</p>
<p>By profiling first, you avoid making assumptions and can focus on fixing the parts of your API that are truly slowing things down.</p>
<h3 id="heading-measuring-query-count-in-a-view">Measuring Query Count in a View</h3>
<p>During development, Django keeps track of all executed queries. You can inspect them directly:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.db <span class="hljs-keyword">import</span> connection
<span class="hljs-keyword">from</span> rest_framework.decorators <span class="hljs-keyword">import</span> api_view
<span class="hljs-keyword">from</span> rest_framework.response <span class="hljs-keyword">import</span> Response
<span class="hljs-keyword">from</span> .models <span class="hljs-keyword">import</span> Post
<span class="hljs-keyword">from</span> .serializers <span class="hljs-keyword">import</span> PostSerializer

<span class="hljs-meta">@api_view(["GET"])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">post_list</span>(<span class="hljs-params">request</span>):</span>
    posts = Post.objects.all()
    serializer = PostSerializer(posts, many=<span class="hljs-literal">True</span>)

    response = Response(serializer.data)

    print(<span class="hljs-string">f"Total queries executed: <span class="hljs-subst">{len(connection.queries)}</span>"</span>)

    <span class="hljs-keyword">return</span> response
</code></pre>
<p>If this prints 101 queries for 100 posts, you likely have an N+1 problem. This simple check confirms whether the database layer is the bottleneck.</p>
<p>One of the easiest ways to profile Django applications during development is by using tools that expose this information directly while requests are being processed.</p>
<h3 id="heading-using-the-django-debug-toolbar">Using the Django Debug Toolbar</h3>
<p>The Django Debug Toolbar is one of the simplest ways to understand performance during development. It acts as a lightweight profiling tool that shows what happens behind the scenes when a request is handled.</p>
<p>It shows you:</p>
<ul>
<li><p>How many SQL queries were executed</p>
</li>
<li><p>How long each query took</p>
</li>
<li><p>whether queries are duplicated</p>
</li>
<li><p>Which parts of the request lifecycle are slow</p>
</li>
</ul>
<h4 id="heading-how-to-install-and-enable-the-django-debug-toolbar">How to Install and Enable the Django Debug Toolbar</h4>
<p>First, install it:</p>
<pre><code class="lang-bash">pip install django-debug-toolbar
</code></pre>
<p>In settings.py:</p>
<pre><code class="lang-bash">INSTALLED_APPS = [
    ...
    <span class="hljs-string">"debug_toolbar"</span>,
]

MIDDLEWARE = [
    ...
    <span class="hljs-string">"debug_toolbar.middleware.DebugToolbarMiddleware"</span>,
]

INTERNAL_IPS = [
    <span class="hljs-string">"127.0.0.1"</span>,
]
</code></pre>
<p>In urls.py:</p>
<pre><code class="lang-bash">import debug_toolbar
from django.urls import path, include

urlpatterns = [
    ...
    path(<span class="hljs-string">"__debug__/"</span>, include(debug_toolbar.urls)),
]
</code></pre>
<p>When you load an endpoint in the browser during development, the toolbar displays total SQL queries, execution time, and duplicate queries. This makes inefficiencies immediately visible.</p>
<p>When you load an API endpoint and see 150 SQL queries for a single request, that’s a strong signal that something is wrong, often an N+1 query problem or inefficient serializer behavior.</p>
<h3 id="heading-logging-sql-queries">Logging SQL Queries</h3>
<p>Django allows you to log all executed SQL queries. This is especially useful when debugging API views.</p>
<p>Seeing the raw SQL makes inefficiencies obvious, such as repeated <code>SELECT</code> statements for the same table.</p>
<h4 id="heading-how-to-enable-sql-query-logging">How to Enable SQL Query Logging</h4>
<p>You can configure Django to log all SQL queries in settings.py:</p>
<pre><code class="lang-bash">LOGGING = {
    <span class="hljs-string">"version"</span>: 1,
    <span class="hljs-string">"handlers"</span>: {
        <span class="hljs-string">"console"</span>: {
            <span class="hljs-string">"class"</span>: <span class="hljs-string">"logging.StreamHandler"</span>,
        },
    },
    <span class="hljs-string">"loggers"</span>: {
        <span class="hljs-string">"django.db.backends"</span>: {
            <span class="hljs-string">"handlers"</span>: [<span class="hljs-string">"console"</span>],
            <span class="hljs-string">"level"</span>: <span class="hljs-string">"DEBUG"</span>,
        },
    },
}
</code></pre>
<p>With this configuration, every SQL query will be printed to the console when your API runs. Repeated SELECT statements or unexpected queries become obvious.</p>
<h3 id="heading-profiling-api-response-time">Profiling API Response Time</h3>
<p>Database queries are only one part of API performance. Beyond queries, it’s also important to measure the total response time of an endpoint.</p>
<p>Profiling response time helps you understand whether delays are caused by database access or by other parts of the request lifecycle. For example, if an endpoint takes 1.2 seconds to respond but only 50 milliseconds are spent on database queries, the bottleneck is likely in serialization, business logic, or repeated computations in Python.</p>
<p>By comparing query time and total response time, profiling helps you identify what to fix first instead of optimizing the wrong layer of the system.</p>
<h4 id="heading-how-to-measure-total-response-time">How to Measure Total Response Time</h4>
<pre><code class="lang-bash">import time
from rest_framework.decorators import api_view
from rest_framework.response import Response

@api_view([<span class="hljs-string">"GET"</span>])
def example_view(request):
    start_time = time.time()

    <span class="hljs-comment"># Simulate work</span>
    data = {<span class="hljs-string">"message"</span>: <span class="hljs-string">"Hello world"</span>}

    response = Response(data)

    end_time = time.time()
    <span class="hljs-built_in">print</span>(f<span class="hljs-string">"Response time: {end_time - start_time:.4f} seconds"</span>)

    <span class="hljs-built_in">return</span> response
</code></pre>
<p>If database queries are fast but the total response time is high, the bottleneck may be serialization or expensive Python logic.  </p>
<p>Once you’ve identified that database access is a significant contributor to slow response times, the next step is to look more closely at how Django retrieves related data.</p>
<h3 id="heading-sql-query-optimization-in-django-rest-apis">SQL Query Optimization in Django REST APIs</h3>
<p>One of the most common reasons Django REST APIs become slow is inefficient access to related objects. This often manifests as the N+1 query problem, where fetching related objects triggers a separate database query for each item. Identifying and fixing this problem can significantly reduce the number of queries and improve API performance.</p>
<h4 id="heading-understanding-the-n1-query-problem">Understanding the N+1 Query Problem</h4>
<p>Consider a simple example:</p>
<ul>
<li><p>You fetch a list of posts</p>
</li>
<li><p>Each post has an author</p>
</li>
<li><p>For every post, Django fetches the author separately</p>
</li>
</ul>
<p>If you have 100 posts, this results in 101 queries: 1 for the posts and 100 for the authors. This happens because Django lazily loads related objects by default. Without intervention, your API performs repetitive database work that slows down response times.</p>
<h4 id="heading-solving-the-problem-with-selectrelated-and-prefetchrelated">Solving the Problem with <code>select_related</code> and <code>prefetch_related</code></h4>
<p>Django provides built-in tools to control how related objects are loaded efficiently: <code>select_related</code> and <code>prefetch_related</code>.</p>
<p><strong>1. Using</strong> <code>select_related</code></p>
<p><code>select_related</code> is designed for foreign key and one-to-one relationships. It performs an SQL join and retrieves related objects in a single query.</p>
<p>Use it when:</p>
<ul>
<li><p>You know you will access related objects</p>
</li>
<li><p>The relationship is one-to-one or many-to-one</p>
</li>
</ul>
<pre><code class="lang-bash">posts = Post.objects.select_related(<span class="hljs-string">"author"</span>)

<span class="hljs-keyword">for</span> post <span class="hljs-keyword">in</span> posts:
    <span class="hljs-built_in">print</span>(post.author.name)  <span class="hljs-comment"># No additional queries</span>
</code></pre>
<p>This performs a SQL JOIN and retrieves posts and authors in a single query, eliminating the N+1 problem.</p>
<p>It reduces multiple queries into just one, avoiding repeated database hits.</p>
<p><strong>2. Using</strong> <code>prefetch_related</code></p>
<p><code>prefetch_related</code> is used for many-to-many and reverse foreign key relationships. It performs separate queries for each related table but combines the results in Python.</p>
<p>Use it when:</p>
<ul>
<li><p>A SQL join would produce too much duplicated data</p>
</li>
<li><p>You are dealing with collections of related objects</p>
</li>
</ul>
<h4 id="heading-example-how-to-optimize-a-many-to-many-relationship">Example: How to Optimize a Many-to-Many Relationship</h4>
<p>Consider a blog application where posts can have multiple tags:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># models.py</span>
class Tag(models.Model):
    name = models.CharField(max_length=50)

class Post(models.Model):
    title = models.CharField(max_length=200)
    tags = models.ManyToManyField(Tag)
</code></pre>
<p>Now imagine fetching posts and accessing their tags:</p>
<pre><code class="lang-bash">posts = Post.objects.all()

<span class="hljs-keyword">for</span> post <span class="hljs-keyword">in</span> posts:
    <span class="hljs-built_in">print</span>(post.tags.all())  <span class="hljs-comment"># Triggers additional queries</span>
</code></pre>
<p>If you have 100 posts, Django may execute:</p>
<ul>
<li><p>1 query to fetch posts</p>
</li>
<li><p>1 query per post to fetch related tags</p>
</li>
</ul>
<p>This results in many unnecessary database hits.</p>
<p>You can optimize this using <code>prefetch_related</code>:</p>
<pre><code class="lang-bash">posts = Post.objects.prefetch_related(<span class="hljs-string">"tags"</span>)

<span class="hljs-keyword">for</span> post <span class="hljs-keyword">in</span> posts:
    <span class="hljs-built_in">print</span>(post.tags.all())  <span class="hljs-comment"># Uses prefetched data</span>
</code></pre>
<p>With this approach, Django performs one query for posts and one query for all related tags. It then matches them in Python, eliminating repeated database queries.</p>
<p>Together, these tools allow you to optimize your queries and eliminate the N+1 problem efficiently.</p>
<h4 id="heading-common-beginner-mistakes">Common Beginner Mistakes</h4>
<p>Even after applying these optimizations, it’s easy to make mistakes. Watch out for:</p>
<ul>
<li><p>Forgetting that serializers can trigger additional queries</p>
</li>
<li><p>Using <code>select_related</code> on many-to-many relationships</p>
</li>
<li><p>Assuming Django automatically optimizes queries</p>
</li>
<li><p>Not checking the query count after adding serializers</p>
</li>
</ul>
<p>Paying attention to these pitfalls ensures your API remains fast and scalable.</p>
<h3 id="heading-caching-in-django-rest-apis">Caching in Django REST APIs</h3>
<p>Even after optimizing database queries, API performance can still suffer if the same computations or database lookups are performed repeatedly. This is where caching comes in. Caching is a technique for storing the results of expensive operations so they can be retrieved more quickly the next time they are needed.</p>
<p>At its core, caching exists because computers have multiple layers of memory with different speeds:</p>
<ul>
<li><p>CPU registers (fastest)</p>
</li>
<li><p>L1, L2, L3 caches</p>
</li>
<li><p>Main memory (RAM)</p>
</li>
<li><p>SSD storage</p>
</li>
<li><p>HDD storage (slowest)</p>
</li>
</ul>
<p>Each layer trades speed for size: the closer the data is to the CPU, the faster it can be accessed. Software systems use the same principle; by storing frequently accessed data in a “closer” or faster location, applications can respond more quickly.</p>
<h4 id="heading-cache-eviction">Cache Eviction</h4>
<p>Caches are limited in size, so when a cache is full, some data must be removed to make room for new data. This process is called cache eviction.</p>
<p>Common eviction strategies include:</p>
<ul>
<li><p><strong>Least Recently Used (LRU):</strong> removes the data that hasn’t been accessed for the longest time</p>
</li>
<li><p><strong>Random Replacement:</strong> removes a random item from the cache</p>
</li>
</ul>
<p>The goal is to keep the data that is most likely to be requested again while freeing space for new data. Understanding this helps developers use caching effectively.</p>
<h4 id="heading-caching-in-application-architectures">Caching in Application Architectures</h4>
<p>Caching exists at several levels in modern software systems:</p>
<ul>
<li><p><strong>Client-side caching:</strong> Web browsers cache HTTP responses to reduce the need for repeated network requests. This is controlled with HTTP headers like <code>Cache-Control</code>.</p>
</li>
<li><p><strong>CDN caching:</strong> Content Delivery Networks store static assets closer to users, reducing latency and server load.</p>
</li>
<li><p><strong>Backend caching:</strong> Backend services cache results from database queries, computed values, or API responses. This is where Django caching is most commonly applied.</p>
</li>
</ul>
<p>By applying caching strategically at the backend, APIs can serve data faster while reducing computation and database load.</p>
<h4 id="heading-caching-in-django">Caching in Django</h4>
<p>Django provides a flexible caching framework that supports multiple backends, including in-memory, file-based, database-backed, and third-party stores like Redis. The main types of caching in Django are:</p>
<ol>
<li><p><strong>Per-view caching:</strong> caches the entire output of a view. Ideal for endpoints where responses rarely change.</p>
<pre><code class="lang-python"> <span class="hljs-keyword">from</span> django.views.decorators.cache <span class="hljs-keyword">import</span> cache_page

<span class="hljs-meta"> @cache_page(60 * 15)  # cache for 15 minutes</span>
 <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">my_view</span>(<span class="hljs-params">request</span>):</span>
</code></pre>
<ol start="2">
<li><p>Template fragment caching: caches specific parts of a template to avoid repeated rendering.</p>
</li>
<li><p>Low-level caching: gives full control over what is cached and for how long, making it ideal for API responses.</p>
</li>
</ol>
</li>
</ol>
<p>    By combining these approaches, you can reduce repeated work in your API, lower database load, and speed up response times.</p>
<h3 id="heading-when-to-use-redis">When to Use Redis</h3>
<p>While Django’s built-in caching backends are sufficient for many projects, high-traffic APIs often require a shared, in-memory cache. This is where Redis excels. Redis is designed for fast access, low latency, and can handle frequent reads across multiple servers.</p>
<p>You should consider using Redis when:</p>
<ul>
<li><p>Data is read frequently but changes infrequently</p>
</li>
<li><p>Low latency is important for API responses</p>
</li>
<li><p>You need cache expiration and eviction policies</p>
</li>
<li><p>You want a shared cache across multiple servers or services</p>
</li>
</ul>
<p>Redis is particularly effective for API endpoints that serve the same data to many users, such as frequently accessed lists or computed results.</p>
<h3 id="heading-common-beginner-mistakes-1">Common Beginner Mistakes</h3>
<p>Caching is powerful, but it’s easy to misuse. Some common pitfalls include:</p>
<ul>
<li><p><strong>Caching everything blindly:</strong> not all data benefits from caching</p>
</li>
<li><p><strong>Forgetting cache invalidation:</strong> stale data can lead to incorrect responses</p>
</li>
<li><p><strong>Using cache where query optimization would suffice:</strong> sometimes optimizing database queries is a better solution than caching.</p>
</li>
</ul>
<p>Remember: caching should complement good database design, not replace it.</p>
<h3 id="heading-pagination-and-limiting-expensive-datasets">Pagination and Limiting Expensive Datasets</h3>
<p>Even with caching, returning large datasets in a single request can slow down your API and increase memory usage. Pagination is a simple and effective way to limit the amount of data returned at once.</p>
<p>Pagination helps by reducing:</p>
<ul>
<li><p>Database load</p>
</li>
<li><p>Memory usage</p>
</li>
<li><p>Serialization time</p>
</li>
<li><p>Network transfer size</p>
</li>
</ul>
<p>Django REST Framework provides built-in pagination classes that make it easy to paginate endpoints. As a rule of thumb, always paginate list endpoints unless there is a strong reason not to.</p>
<h3 id="heading-load-testing-and-measuring-improvement">Load Testing and Measuring Improvement</h3>
<p>Optimizations are only meaningful if you can measure their impact. Load testing simulates multiple users accessing your API simultaneously, helping you answer key questions:</p>
<ul>
<li><p>How many requests per second can my API handle?</p>
</li>
<li><p>Where does the API start to break under load?</p>
</li>
<li><p>Did caching, query optimization, and pagination actually improve performance?</p>
</li>
</ul>
<p>By running load tests before and after optimization, you can validate that your changes have the desired effect and avoid optimizing the wrong parts of your system.</p>
<h2 id="heading-summary-and-next-steps">Summary and Next Steps</h2>
<p>Optimizing Django REST APIs isn’t about chasing every tiny micro-optimization. It’s about reducing unnecessary work and focusing on the parts of your API that actually slow down performance.</p>
<h4 id="heading-key-takeaways">Key Takeaways</h4>
<ul>
<li><p><strong>Profile before optimizing:</strong> Identify the real bottlenecks before making changes.</p>
</li>
<li><p><strong>Reduce database queries:</strong> Use techniques like <code>select_related</code>, <code>prefetch_related</code>, and avoid N+1 queries.</p>
</li>
<li><p><strong>Cache frequently accessed data:</strong> Use Django caching and Redis to reduce repeated computations.</p>
</li>
<li><p><strong>Paginate large datasets:</strong> Limit memory usage and network load by returning data in chunks.</p>
</li>
<li><p><strong>Measure performance changes:</strong> Always verify that your optimizations have a real impact.</p>
</li>
</ul>
<h4 id="heading-next-steps-for-your-apis">Next Steps for Your APIs</h4>
<ol>
<li><p><strong>Add profiling to your existing APIs</strong> to understand where time is spent.</p>
</li>
<li><p><strong>Identify one slow endpoint</strong> and focus on optimizing it first.</p>
</li>
<li><p><strong>Optimize database queries</strong> using Django ORM best practices.</p>
</li>
<li><p><strong>Introduce caching carefully</strong>; avoid caching everything blindly.</p>
</li>
<li><p><strong>Measure the results</strong> with load testing and performance metrics.</p>
</li>
</ol>
<p>Remember: Performance optimization is not a one-time task. It’s a habit built by continuously observing how your system works, testing improvements, and applying changes where they make the most impact.</p>
<h2 id="heading-read-more">Read More</h2>
<ol>
<li><p><a target="_blank" href="https://www.django-rest-framework.org/topics/performance/">DRF Performance</a></p>
</li>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/topics/db/optimization/">Django ORM Optimization</a></p>
</li>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/topics/db/optimization/#select-related">Understanding N+1 queries</a></p>
</li>
</ol>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use the Django REST Framework - Build Backend APIs with DRF ]]>
                </title>
                <description>
                    <![CDATA[ When you click on most backend development tutorials, they often teach you what to do, not how to think.That’s why many developers only realize their mistakes after they start building. So, how does one actually think like a backend developer? Before... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-the-django-rest-framework/</link>
                <guid isPermaLink="false">6920d5f802099ac646b401c5</guid>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                    <category>
                        <![CDATA[ backend ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mari ]]>
                </dc:creator>
                <pubDate>Fri, 21 Nov 2025 21:13:28 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763759552021/cc57d91b-c2b9-4a40-8bb9-52c517dbbc35.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you click on most backend development tutorials, they often teach you <em>what</em> to do, not <em>how to think</em>.<br>That’s why many developers only realize their mistakes after they start building.</p>
<p>So, how does one actually think like a backend developer? Before answering that, let’s start with the basics: what exactly is backend development?</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-backend-development">What is Backend Development?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-django-rest-framework">Why Django REST Framework?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-flask">Flask</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-fastapi">FastAPI</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-django-rest-framework">Django REST Framework</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-think-like-a-backend-developer">How to Think Like a Backend Developer</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-think-in-systems-not-lines-of-code">1. Think in Systems, Not Lines of Code</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-separate-concerns-keep-things-organized">2. Separate Concerns — Keep Things Organized</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-anticipate-problems-before-they-happen">3. Anticipate Problems Before They Happen</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-make-your-code-predictable-and-readable">4. Make Your Code Predictable and Readable</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-5-think-in-the-request-logic-response-cycle">5. Think in the Request → Logic → Response Cycle</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-6-practice-thinking-like-a-backend-developer">6. Practice Thinking Like a Backend Developer</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-install-the-django-rest-framework">How to Install the Django REST Framework</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-step-1-install-python">Step 1: Install Python</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-create-a-project-folder">Step 2: Create a Project Folder</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-create-a-virtual-environment">Step 3: Create a Virtual Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-install-django">Step 4: Install Django</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-create-a-django-project">Step 5: Create a Django Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-6-install-django-rest-framework">Step 6: Install Django REST Framework</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-7-add-drf-to-installed-apps">Step 7: Add DRF to Installed Apps</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-8-run-initial-migrations">Step 8: Run Initial Migrations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-9-start-the-server">Step 9: Start the Server</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-10-verify-drf-installation">Step 10: Verify DRF Installation</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-the-backend-developers-mindset">The Backend Developer’s Mindset</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-common-mistakes-beginners-make">Common Mistakes Beginners Make</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-further-reading">Further Reading</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-backend-development">What is Backend Development?</h2>
<p>Backend development is the foundation of most web and mobile applications. It focuses on everything that happens behind the scenes, from processing logic and handling data to connecting with databases and APIs.</p>
<p>While it’s true that backend developers build APIs that communicate with the frontend, the job goes far beyond that. The backend is where data is validated, protected, stored, and retrieved.</p>
<p>In short: backend development is about building systems that ensure data integrity, performance, and scalability.</p>
<p>Backend developers are the ones responsible for designing and maintaining those systems. They ensure that every user request is processed efficiently and securely.</p>
<p>Now, how does the Django REST Framework (DRF) fit into all this?</p>
<h2 id="heading-why-django-rest-framework">Why Django REST Framework?</h2>
<p>A beginner-friendly tutorial must use a tool that:</p>
<ul>
<li><p>Teaches good structure</p>
</li>
<li><p>Encourages best practices</p>
</li>
<li><p>Hides unnecessary complexity</p>
</li>
<li><p>Helps you learn backend fundamentals correctly</p>
</li>
</ul>
<p>That’s why this guide uses the <strong>Django REST Framework (DRF)</strong>. Here’s how it compares to other popular Python frameworks.</p>
<h3 id="heading-flask">Flask</h3>
<p>Flask is a lightweight and flexible microframework. It is great for small projects, but:</p>
<ul>
<li><p>You have to set up everything manually (routing, JSON handling, database handling).</p>
</li>
<li><p>You need extra libraries for authentication, validation, or serialization.</p>
</li>
<li><p>Beginners often create unstructured projects because Flask doesn’t enforce architecture.</p>
</li>
</ul>
<p>Flask teaches freedom, not structure.</p>
<h3 id="heading-fastapi">FastAPI</h3>
<p>FastAPI is modern, fast, and async-first. However:</p>
<ul>
<li><p>It assumes you already understand APIs.</p>
</li>
<li><p>It requires understanding Python type hints deeply.</p>
</li>
<li><p>The ecosystem is still growing.</p>
</li>
<li><p>Beginners may not understand its underlying concepts (dependency injection, async IO).</p>
</li>
</ul>
<p>FastAPI teaches speed, not fundamentals.</p>
<h3 id="heading-django-rest-framework">Django REST Framework</h3>
<p>DRF is ideal for beginners because:</p>
<ul>
<li><p>It sits on top of Django, a very stable full-stack framework.</p>
</li>
<li><p>It encourages good architecture from day one.</p>
</li>
<li><p>It handles serialization, authentication, routing, validation, and permissions for you.</p>
</li>
<li><p>It gives you structure instead of chaos.</p>
</li>
</ul>
<p><strong>Bottom line:</strong> DRF can help you learn how backend systems work from scratch.</p>
<h2 id="heading-how-to-think-like-a-backend-developer">How to Think Like a Backend Developer</h2>
<p>Thinking like a backend developer is not about memorizing code. It’s about learning to see the bigger picture, how data moves, how logic flows, and how to build systems that work reliably and can grow.</p>
<p>Backend thinking can be summarized into six main principles:</p>
<h3 id="heading-1-think-in-systems-not-lines-of-code">1. Think in Systems, Not Lines of Code</h3>
<p>Many beginners focus on writing code that works for one feature. A backend developer thinks about the entire system.</p>
<p><strong>Analogy:</strong> Imagine a factory. Each machine (function or endpoint) does one task, but the factory only works efficiently if every machine is arranged correctly and communicates properly.</p>
<p><strong>Example:</strong> When a user submits a form to create a task:</p>
<ul>
<li><p>The request reaches the server.</p>
</li>
<li><p>The backend validates the data.</p>
</li>
<li><p>The backend stores it in the database.</p>
</li>
<li><p>The backend sends a response to the user.</p>
</li>
</ul>
<p>A backend developer doesn’t just write a function to save data. They ask:</p>
<ul>
<li><p>Where should this logic live — view, serializer, or service layer?</p>
</li>
<li><p>How will the data be validated and cleaned?</p>
</li>
<li><p>How will the system scale if thousands of users submit tasks at the same time?</p>
</li>
</ul>
<p>Seeing the system first makes code predictable, maintainable, and scalable.</p>
<h3 id="heading-2-separate-concerns-keep-things-organized">2. Separate Concerns — Keep Things Organized</h3>
<p>Backend thinking is about <strong>structure</strong>. Every piece of code should have a clear responsibility:</p>
<ul>
<li><p><strong>Models</strong>: Store and define your data</p>
</li>
<li><p><strong>Serializers</strong>: Convert data to a format the client understands (like JSON)</p>
</li>
<li><p><strong>Views</strong>: Apply the business logic and respond to requests</p>
</li>
</ul>
<p><strong>Why this matters:</strong> Without separation, code becomes messy and hard to debug. You might find yourself mixing database queries with validation or formatting, which leads to errors later.</p>
<p><strong>Simple analogy:</strong> Think of a restaurant.</p>
<ul>
<li><p>The <strong>chef</strong> prepares the food (model/data).</p>
</li>
<li><p>The <strong>waiter</strong> delivers the food to customers in a presentable way (serializer).</p>
</li>
<li><p>The <strong>manager</strong> decides who gets what and handles special requests (view/logic).</p>
</li>
</ul>
<p>Each role is separate but connected. This is exactly how backend developers structure code.</p>
<h3 id="heading-3-anticipate-problems-before-they-happen">3. Anticipate Problems Before They Happen</h3>
<p>Backend developers don’t just code for today. They <strong>think ahead</strong>:</p>
<ul>
<li><p>What if the user sends invalid data?</p>
</li>
<li><p>What if two users try to edit the same record at the same time?</p>
</li>
<li><p>How will the system handle millions of requests in the future?</p>
</li>
</ul>
<p><strong>Example:</strong> If a user tries to create a task without a title, a beginner might just let it crash. A backend developer writes validation rules to catch this and return a clear error message.</p>
<p><strong>Rule of thumb:</strong> Always ask, <em>“What could go wrong here?”</em> and design your code to handle it gracefully.</p>
<h3 id="heading-4-make-your-code-predictable-and-readable">4. Make Your Code Predictable and Readable</h3>
<p>Backend development is about <strong>writing code for humans, not just computers</strong>.</p>
<ul>
<li><p>Use clear variable names (<code>task_title</code> instead of <code>x</code>).</p>
</li>
<li><p>Keep functions short and focused.</p>
</li>
<li><p>Document your code.</p>
</li>
</ul>
<p>This way, <strong>anyone can pick up your code and understand it</strong>, including your future self.</p>
<p><strong>Tip:</strong> A backend system that is easy to read and predict is easier to debug, extend, and scale.</p>
<h3 id="heading-5-think-in-the-request-logic-response-cycle">5. Think in the Request → Logic → Response Cycle</h3>
<p>Every backend action fits into this pattern:</p>
<ul>
<li><p><strong>Request</strong>: The client sends data.</p>
</li>
<li><p><strong>Logic</strong>: The server validates, processes, and decides what to do.</p>
</li>
<li><p><strong>Response</strong>: The server sends data back in a structured way.</p>
</li>
</ul>
<p><strong>Example:</strong> User creates a task:</p>
<ul>
<li><p>Request: <code>{ "title": "Learn DRF" }</code></p>
</li>
<li><p>Logic: Check title is not empty → save to database → mark completed as <code>False</code></p>
</li>
<li><p>Response: <code>{ "id": 1, "title": "Learn DRF", "completed": false }</code></p>
</li>
</ul>
<p>Thinking in this cycle makes debugging and designing systems intuitive.</p>
<h3 id="heading-6-practice-thinking-like-a-backend-developer">6. Practice Thinking Like a Backend Developer</h3>
<ul>
<li><p><strong>Ask questions before coding:</strong> “Where should this logic live? How will this affect other parts of the system?”</p>
</li>
<li><p><strong>Break down problems into steps:</strong> Don’t just code the solution; code the process.</p>
</li>
<li><p><strong>Visualize data flow:</strong> Draw diagrams if necessary, from user request to database and back.</p>
</li>
<li><p><strong>Learn by doing:</strong> Build small projects and reflect on each component’s role.</p>
</li>
</ul>
<p>Check out Andy Harris’s video on how to think like a programmer.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/azcrPFhaY9k" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>Now that you understand how backend developers think, let’s walk through setting up a real backend environment using Django REST Framework.</p>
<h2 id="heading-how-to-install-the-django-rest-framework">How to Install the Django REST Framework</h2>
<p>Here’s how to get the Django REST framework running on your machine from scratch.</p>
<h3 id="heading-step-1-install-python">Step 1: Install Python</h3>
<p>Make sure you have <strong>Python 3.8+</strong> installed. You can check if Python is installed with this command:</p>
<pre><code class="lang-bash">python --version
</code></pre>
<p>If it’s not installed, download it from the <a target="_blank" href="https://docs.python.org/3/">official Python documentation</a>.</p>
<h3 id="heading-step-2-create-a-project-folder">Step 2: Create a Project Folder</h3>
<p>Choose a location on your computer and create a folder for your project:</p>
<pre><code class="lang-bash">mkdir my_drf_project
<span class="hljs-built_in">cd</span> my_drf_project
</code></pre>
<p>This keeps all your files organized in one place.</p>
<h3 id="heading-step-3-create-a-virtual-environment">Step 3: Create a Virtual Environment</h3>
<p>A virtual environment keeps your project dependencies separate from other projects.</p>
<p>Create a virtual environment:</p>
<pre><code class="lang-bash">python -m venv venv
</code></pre>
<p>Next, activate it. For Windows (PowerShell):</p>
<pre><code class="lang-powershell">.\venv\Scripts\Activate.ps1
</code></pre>
<p>For Mac/Linux:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> venv/bin/activate
</code></pre>
<p>You’ll know it’s active when your terminal prompt starts with <code>(venv)</code>.</p>
<h3 id="heading-step-4-install-django">Step 4: Install Django</h3>
<p>Now install Django inside the virtual environment:</p>
<pre><code class="lang-bash">pip install django
</code></pre>
<p>Check that Django is installed:</p>
<pre><code class="lang-bash">python -m django --version
</code></pre>
<h3 id="heading-step-5-create-a-django-project">Step 5: Create a Django Project</h3>
<p>Create a new Django project:</p>
<pre><code class="lang-bash">django-admin startproject core .
</code></pre>
<p>The <code>.</code> at the end means “create the project here.” Run the server to make sure it works:</p>
<pre><code class="lang-bash">python manage.py runserver
</code></pre>
<p>Visit <a target="_blank" href="http://127.0.0.1:8000/"><code>http://127.0.0.1:8000/</code></a> in your browser. You should see the Django welcome page.</p>
<h3 id="heading-step-6-install-django-rest-framework">Step 6: Install Django REST Framework</h3>
<p>Install DRF using pip:</p>
<pre><code class="lang-bash">pip install djangorestframework
</code></pre>
<h3 id="heading-step-7-add-drf-to-installed-apps">Step 7: Add DRF to Installed Apps</h3>
<p>Open <code>core/</code><a target="_blank" href="http://settings.py"><code>settings.py</code></a> and find the <code>INSTALLED_APPS</code> list. Add:</p>
<pre><code class="lang-bash"><span class="hljs-string">'rest_framework'</span>,
</code></pre>
<p>It should look like this:</p>
<pre><code class="lang-bash">INSTALLED_APPS = [
    <span class="hljs-string">'django.contrib.admin'</span>,
    <span class="hljs-string">'django.contrib.auth'</span>,
    <span class="hljs-string">'django.contrib.contenttypes'</span>,
    <span class="hljs-string">'django.contrib.sessions'</span>,
    <span class="hljs-string">'django.contrib.messages'</span>,
    <span class="hljs-string">'django.contrib.staticfiles'</span>,
    <span class="hljs-string">'rest_framework'</span>,
]
</code></pre>
<h3 id="heading-step-8-run-initial-migrations">Step 8: Run Initial Migrations</h3>
<p>Set up your database:</p>
<pre><code class="lang-bash">python manage.py migrate
</code></pre>
<p>Create a superuser for accessing the admin panel:</p>
<pre><code class="lang-bash">python manage.py createsuperuser
</code></pre>
<p>Follow the prompts for username, email, and password.</p>
<h3 id="heading-step-9-start-the-server">Step 9: Start the Server</h3>
<p>Run your development server again:</p>
<pre><code class="lang-bash">python manage.py runserver
</code></pre>
<p>Visit:</p>
<ul>
<li><p><a target="_blank" href="http://127.0.0.1:8000/"><code>http://127.0.0.1:8000/</code></a> → Django welcome page</p>
</li>
<li><p><a target="_blank" href="http://127.0.0.1:8000/admin/"><code>http://127.0.0.1:8000/admin/</code></a> → Admin panel (login with superuser)</p>
</li>
</ul>
<p>You now have Django + DRF installed and ready for API development.</p>
<h3 id="heading-step-10-verify-drf-installation">Step 10: Verify DRF Installation</h3>
<p>The easiest way to confirm that Django REST Framework is installed correctly is to build a very small test API. Each part of the setup helps you verify that DRF is working end-to-end.</p>
<p>Create a new app:</p>
<pre><code class="lang-bash">python manage.py startapp api
</code></pre>
<p>This creates an <code>api</code> folder where you’ll place your test model, serializer, and view. Adding it to <code>INSTALLED_APPS</code> tells Django to recognize the new app.</p>
<p>Add it to <code>INSTALLED_APPS</code>:</p>
<pre><code class="lang-bash"><span class="hljs-string">'api'</span>,
</code></pre>
<p>Create a simple <a target="_blank" href="http://models.py"><code>models.py</code></a> in the <code>api</code> app:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.db <span class="hljs-keyword">import</span> models

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Task</span>(<span class="hljs-params">models.Model</span>):</span>
    title = models.CharField(max_length=<span class="hljs-number">200</span>)
    completed = models.BooleanField(default=<span class="hljs-literal">False</span>)
</code></pre>
<p>This model represents a basic task with a title and a completion status. Creating even a simple model lets you test whether DRF can serialize and expose database objects as API responses.</p>
<p>Run migrations:</p>
<pre><code class="lang-bash">python manage.py makemigrations
python manage.py migrate
</code></pre>
<p>These commands generate and apply database tables for the <code>Task</code> model. Without migrations, DRF won’t have anything to fetch and serialize.</p>
<p>Create a serializer (<code>api/</code><a target="_blank" href="http://serializers.py"><code>serializers.py</code></a>):</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> rest_framework <span class="hljs-keyword">import</span> serializers
<span class="hljs-keyword">from</span> .models <span class="hljs-keyword">import</span> Task

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TaskSerializer</span>(<span class="hljs-params">serializers.ModelSerializer</span>):</span>
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Meta</span>:</span>
        model = Task
        fields = <span class="hljs-string">'__all__'</span>
</code></pre>
<p>A serializer converts your <code>Task</code> model into JSON so it can be returned as an API response. This step confirms that DRF’s serializer tools are working.</p>
<p>Create a view (<code>api/</code><a target="_blank" href="http://views.py"><code>views.py</code></a>):</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> rest_framework <span class="hljs-keyword">import</span> viewsets
<span class="hljs-keyword">from</span> .models <span class="hljs-keyword">import</span> Task
<span class="hljs-keyword">from</span> .serializers <span class="hljs-keyword">import</span> TaskSerializer

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TaskViewSet</span>(<span class="hljs-params">viewsets.ModelViewSet</span>):</span>
    queryset = Task.objects.all()
    serializer_class = TaskSerializer
</code></pre>
<p><code>ModelViewSet</code> automatically creates the CRUD API endpoints for your model. If this loads correctly, it means DRF’s generic views and viewsets are functioning.</p>
<p>Wire it to URLs (<code>core/</code><a target="_blank" href="http://urls.py"><code>urls.py</code></a>):</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.urls <span class="hljs-keyword">import</span> path, include
<span class="hljs-keyword">from</span> rest_framework.routers <span class="hljs-keyword">import</span> DefaultRouter
<span class="hljs-keyword">from</span> api.views <span class="hljs-keyword">import</span> TaskViewSet

router = DefaultRouter()
router.register(<span class="hljs-string">'tasks'</span>, TaskViewSet)

urlpatterns = [
    path(<span class="hljs-string">'admin/'</span>, admin.site.urls),
    path(<span class="hljs-string">'api/'</span>, include(router.urls)),
]
</code></pre>
<p>The router generates routes <code>/api/tasks/</code> for you. If routing works, DRF is properly integrated into your Django project.</p>
<p>Test the API by visiting:</p>
<pre><code class="lang-bash">http://127.0.0.1:8000/api/tasks/
</code></pre>
<p>If everything is set up correctly, you’ll see Django REST Framework’s browsable API. This confirms that DRF is installed, your project recognizes it, and it can serialize and return data successfully.</p>
<h2 id="heading-the-backend-developers-mindset">The Backend Developer’s Mindset</h2>
<p>When writing backend code, your goal isn’t just to make something <em>work</em>; it’s to make it predictable, scalable, and maintainable.</p>
<p>Professional backend developers focus on:</p>
<ul>
<li><p><strong>Predictability over cleverness</strong> — Code should be clear to others.</p>
</li>
<li><p><strong>Separation of concerns</strong> — Keep logic, data, and presentation layers distinct.</p>
</li>
<li><p><strong>Validation</strong> — Never trust user input; always validate.</p>
</li>
<li><p><strong>Consistency</strong> — Stick to naming conventions and reusable patterns.</p>
</li>
</ul>
<p>This mindset is what separates backend <em>coders</em> from backend <em>engineers</em>.</p>
<h2 id="heading-common-mistakes-beginners-make">Common Mistakes Beginners Make</h2>
<ul>
<li><p><strong>Writing too much logic in views:</strong> Keep views light. Move business logic into services or serializers.</p>
</li>
<li><p><strong>Ignoring validation</strong>: Always define validation rules in your serializers.</p>
</li>
<li><p><strong>Not planning for scalability:</strong> Even small projects grow. Build like you expect more users.</p>
</li>
</ul>
<h2 id="heading-further-reading">Further Reading</h2>
<p><a target="_blank" href="https://docs.djangoproject.com/en/5.2/">Django official documentation</a></p>
<p><a target="_blank" href="https://www.freecodecamp.org/news/how-to-build-a-rest-api-in-django/">How to Build a REST API in Django</a></p>
<p><a target="_blank" href="https://www.freecodecamp.org/news/what-is-serialization/">What is Serialization?</a></p>
<p><a target="_blank" href="https://www.freecodecamp.org/news/rest-api-best-practices-rest-endpoint-design-examples/">REST API Best Practices – REST Endpoint Design Examples</a></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Thinking like a backend developer isn’t about memorizing syntax; it’s about understanding how systems behave.</p>
<p>When you start reasoning through requests, logic, and responses, you begin to see the bigger picture, and that’s when you stop writing code and start building systems.</p>
<p>With Django REST Framework, that process becomes easier, cleaner, and more intuitive.</p>
<p>As you continue learning, build small APIs and gradually add features. The more you understand how data flows through a system, the more naturally backend thinking will come.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Revert a Migration in Django ]]>
                </title>
                <description>
                    <![CDATA[ So, you're working with Django, you've run a migration, and now something’s broken. Maybe you added a field that shouldn't be there. Maybe you renamed a model, and suddenly your database is a mess. Or maybe you're just experimenting and want to roll ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-revert-a-migration-in-django/</link>
                <guid isPermaLink="false">68781860a9407a5fe14ed384</guid>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Udemezue John ]]>
                </dc:creator>
                <pubDate>Wed, 16 Jul 2025 21:23:44 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745937479722/d02a1abc-33b1-4506-8001-033b4ec43130.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>So, you're working with Django, you've run a migration, and now something’s broken. Maybe you added a field that shouldn't be there.</p>
<p>Maybe you renamed a model, and suddenly your database is a mess. Or maybe you're just experimenting and want to roll things back.</p>
<p>That’s where reverting migrations comes in.</p>
<p>Knowing how to undo a migration in Django is just as important as knowing how to make one. It’s not about being perfect – it’s about being able to fix mistakes fast, without panic. I've been there.</p>
<p>A single migration can break everything if it goes wrong. But the good news is, Django gives you tools to take a step back safely.</p>
<p>Let me walk you through how this works, using plain language and clear steps.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-exactly-is-a-migration-in-django">What Exactly Is a Migration in Django?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-revert-a-migration-in-django">How to Revert a Migration in Django</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-case-1-undo-a-migration-thats-already-been-applied">Case 1: Undo a Migration That’s Already Been Applied</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-case-2-undo-a-migration-you-havent-applied-yet">Case 2: Undo a Migration You Haven’t Applied Yet</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-special-case-reverting-all-migrations-reset-everything">Special Case: Reverting All Migrations (Reset Everything)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-example-scenario-fixing-a-broken-migration">Example Scenario: Fixing a Broken Migration</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-common-pitfalls-and-how-to-avoid-them">Common Pitfalls (And How to Avoid Them)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-faqs">FAQs</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-can-i-revert-more-than-one-migration-at-a-time">Can I revert more than one migration at a time?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-do-i-know-which-migration-names-to-use">How do I know which migration names to use?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-is-reverting-migrations-safe">Is reverting migrations safe?</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-further-resources">Further Resources</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-exactly-is-a-migration-in-django">What Exactly Is a Migration in Django?</h2>
<p>Before we can talk about undoing a migration, let’s make sure we’re on the same page.</p>
<p>A migration in Django is a record of changes to your database. It tracks what models you’ve added or changed, and applies those changes to your actual database using SQL (behind the scenes).</p>
<p>You usually create a migration with this command:</p>
<pre><code class="lang-bash">python manage.py makemigrations
</code></pre>
<p>And apply it like this:</p>
<pre><code class="lang-bash">python manage.py migrate
</code></pre>
<p>That’s when Django updates your database tables to match your models.</p>
<p>Now, what if you want to undo that last step?</p>
<h2 id="heading-how-to-revert-a-migration-in-django">How to Revert a Migration in Django</h2>
<p>Alright, here’s the main part. Let’s say you just ran a migration and want to undo it. There are two situations:</p>
<ol>
<li><p><strong>You applied the migration already and want to reverse it</strong></p>
</li>
<li><p><strong>You haven’t applied it yet and just want to delete it</strong></p>
</li>
</ol>
<p>Let’s handle both.</p>
<h3 id="heading-case-1-undo-a-migration-thats-already-been-applied">Case 1: Undo a Migration That’s Already Been Applied</h3>
<p>If you've already run <code>python manage.py migrate</code>, Django has changed your database.</p>
<p>To reverse that migration, use this:</p>
<pre><code class="lang-bash">python manage.py migrate your_app_name migration_name_before
</code></pre>
<p>Let me break that down:</p>
<ul>
<li><p><code>your_app_name</code> is the name of your Django app (like <code>blog</code>, <code>users</code>, or <code>store</code>)</p>
</li>
<li><p><code>migration_name_before</code> is the name of the migration <em>before</em> the one you want to undo</p>
</li>
</ul>
<p>Let’s go through an example.</p>
<p>Say you have these migrations for an app called <code>store</code>:</p>
<pre><code class="lang-python"><span class="hljs-number">0001</span>_initial.py  
<span class="hljs-number">0002</span>_add_price_to_product.py  
<span class="hljs-number">0003</span>_change_price_field.py
</code></pre>
<p>If you want to undo the <code>0003_change_price_field.py</code> migration, you’d run:</p>
<pre><code class="lang-bash">python manage.py migrate store 0002
</code></pre>
<p>That tells Django to roll back to migration <code>0002</code>, effectively undoing everything in <code>0003</code>.</p>
<p>Once that’s done, you’ll see output like:</p>
<pre><code class="lang-python">Operations to reverse:
   - Alter field price on product
</code></pre>
<p>And your database is back the way it was before <code>0003</code>.</p>
<h3 id="heading-case-2-undo-a-migration-you-havent-applied-yet">Case 2: Undo a Migration You Haven’t Applied Yet</h3>
<p>Maybe you ran <code>makemigrations</code>, but not <code>migrate</code>. So you just created the migration file and haven’t actually touched the database yet.</p>
<p>In that case, you can safely delete the migration file.</p>
<p>Just go into your app’s <code>migrations/</code> folder, and delete the unwanted migration file (for example: <code>0003_change_price_field.py</code>).</p>
<p>Then you can re-run <code>makemigrations</code> with the correct changes.</p>
<p>Quick tip: Don't delete <code>__init__.py</code> or the <code>0001_initial.py</code> file unless you know what you’re doing. That first one is usually required.</p>
<h2 id="heading-special-case-reverting-all-migrations-reset-everything">Special Case: Reverting All Migrations (Reset Everything)</h2>
<p>Sometimes you just want to wipe all the migrations and start over.</p>
<p>This is common when you're still in development, and your database structure is messy.</p>
<p>Here's how I usually do it:</p>
<ol>
<li><p><strong>Delete the migration files</strong> inside the <code>migrations/</code> folder of your app (except for <code>__init__.py</code>)</p>
</li>
<li><p><strong>Drop the database</strong> or just clear the tables if you're using SQLite or a test DB</p>
</li>
<li><p>Run:</p>
</li>
</ol>
<pre><code class="lang-bash">python manage.py makemigrations
python manage.py migrate
</code></pre>
<p>If you're using SQLite, you can also just delete the <code>.sqlite3</code> file and start fresh.</p>
<p>For PostgreSQL or MySQL, you'll need to drop and recreate the database, or reset it using a tool like <code>pgAdmin</code> or <code>DBeaver</code>.</p>
<h2 id="heading-example-scenario-fixing-a-broken-migration">Example Scenario: Fixing a Broken Migration</h2>
<p>Let’s say you added a new field to a model:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Product</span>(<span class="hljs-params">models.Model</span>):</span>
    name = models.CharField(max_length=<span class="hljs-number">100</span>)
    price = models.DecimalField(max_digits=<span class="hljs-number">6</span>, decimal_places=<span class="hljs-number">2</span>, default=<span class="hljs-number">0.00</span>)
</code></pre>
<p>You made a typo:</p>
<pre><code class="lang-python">price = models.DecimalField(max_digits=<span class="hljs-number">6</span>, decimal_places=<span class="hljs-number">2</span>, default=<span class="hljs-string">'free'</span>)
</code></pre>
<p>Oops. Django lets you do this:</p>
<pre><code class="lang-bash">python manage.py makemigrations store
python manage.py migrate
</code></pre>
<p>Then it breaks.</p>
<p>You can fix this by reverting:</p>
<pre><code class="lang-bash">python manage.py migrate store 0001
</code></pre>
<p>Then fix the typo in your model and run:</p>
<pre><code class="lang-bash">python manage.py makemigrations
python manage.py migrate
</code></pre>
<p>Back on track!</p>
<h2 id="heading-common-pitfalls-and-how-to-avoid-them">Common Pitfalls (And How to Avoid Them)</h2>
<ul>
<li><p><strong>Don’t just delete a migration without reversing it first</strong>. This can confuse Django.</p>
</li>
<li><p><strong>Always check which migrations are applied</strong> using:</p>
</li>
</ul>
<pre><code class="lang-bash">python manage.py showmigrations
</code></pre>
<ul>
<li><p><strong>Keep backups of your database</strong>, especially in production. Use tools like <code>pg_dump</code> or <code>mysqldump</code> if needed.</p>
</li>
<li><p><strong>Don't reset migrations in a live app</strong> unless you absolutely must. It can mess up production data.</p>
</li>
</ul>
<h2 id="heading-faqs">FAQs</h2>
<h3 id="heading-can-i-revert-more-than-one-migration-at-a-time">Can I revert more than one migration at a time?</h3>
<p>Yes! You just migrate back to the point <em>before</em> the migrations you want to undo.</p>
<p><strong>Example:</strong></p>
<p>You’ve applied:</p>
<pre><code class="lang-python">[X] <span class="hljs-number">0001</span>_initial  
[X] <span class="hljs-number">0002</span>_add_price_to_product  
[X] <span class="hljs-number">0003</span>_change_price_field  
[X] <span class="hljs-number">0004</span>_add_discount_field
</code></pre>
<p>To undo both <code>0004</code> and <code>0003</code>, run:</p>
<pre><code class="lang-python">python manage.py migrate store <span class="hljs-number">0002</span>
</code></pre>
<p>This rolls back both 0004 and 0003, leaving only 0001 and 0002 applied.</p>
<h3 id="heading-how-do-i-know-which-migration-names-to-use"><strong>How do I know which migration names to use?</strong></h3>
<p>Run <code>python manage.py showmigrations</code> And you’ll see a list like:</p>
<pre><code class="lang-python"> [X] <span class="hljs-number">0001</span>_initial
 [X] <span class="hljs-number">0002</span>_add_price_to_product
 [X] <span class="hljs-number">0003</span>_change_price_field
</code></pre>
<p>The <code>[X]</code> shows applied migrations. To undo <code>0003</code>, migrate back to <code>0002</code>.</p>
<h3 id="heading-is-reverting-migrations-safe"><strong>Is reverting migrations safe?</strong></h3>
<p>It is, as long as you haven’t made changes to data that depend on the migration. Always test in development before trying in production.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Reverting migrations in Django isn't scary once you get the hang of it. It's like using undo in a Word document – you just need to know how far back to go.</p>
<p>So now that you know how to revert a migration in Django, what’s the trickiest migration issue you've run into—and how did you fix it?</p>
<p>Shoot me a <a target="_blank" href="http://x.com/_udemezue/">message</a> – I’d love to hear your story.</p>
<h3 id="heading-further-resources">Further Resources</h3>
<ul>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/topics/migrations/">Official Django Migration Docs</a></p>
</li>
<li><p><a target="_blank" href="https://realpython.com/django-migrations-a-primer/">Django Migrations Primer by RealPython</a></p>
</li>
<li><p><a target="_blank" href="https://adamj.eu/tech/2021/04/27/django-migrations-dos-and-donts/">Common Django Mistakes and How to Avoid Them</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Activate Your Django Virtual Environment ]]>
                </title>
                <description>
                    <![CDATA[ If you’re starting with Django, one of the first steps you’ll hear about is activating a virtual environment. And if that sounds a little technical, don’t worry – I’m going to walk you through exactly what that means, why it matters, and how to do it... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-activate-your-django-virtual-environment/</link>
                <guid isPermaLink="false">6877da442945848c5461548c</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                    <category>
                        <![CDATA[ virtualization ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Udemezue John ]]>
                </dc:creator>
                <pubDate>Wed, 16 Jul 2025 16:58:44 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746123776834/337004ca-692e-4df9-89db-81e78a16c7fe.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you’re starting with Django, one of the first steps you’ll hear about is <em>activating a virtual environment</em>. And if that sounds a little technical, don’t worry – I’m going to walk you through exactly what that means, why it matters, and how to do it step-by-step, without any confusing terms.</p>
<p>I’ve helped a lot of people get started with Python and Django, and trust me: understanding virtual environments early on can save you tons of headaches later.</p>
<p>A virtual environment can help you keep your Django projects organized. It also avoids conflicts between different versions of packages, and gives you a cleaner way to manage your development tools.</p>
<p>By the end of this guide, you’ll not only know how to activate your virtual environment, but also why you should.</p>
<p>Let's get into it.</p>
<h3 id="heading-heres-what-well-cover">Here’s what we’ll cover:</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-a-virtual-environment-in-python">What Is a Virtual Environment in Python?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-use-a-virtual-environment">Why Use a Virtual Environment?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-and-activate-a-django-virtual-environment">How to Set Up and Activate a Django Virtual Environment</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-install-python-if-you-havent-yet">1. Install Python (If You Haven’t Yet)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-install-virtualenv-optional-but-useful">2. Install virtualenv (Optional but Useful)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-create-a-virtual-environment">3. Create a Virtual Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-activate-the-virtual-environment">4. Activate the Virtual Environment</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-what-can-you-do-after-activating-it">What Can You Do After Activating It?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-deactivate-the-virtual-environment">How to Deactivate the Virtual Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-faqs">FAQs</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-do-i-need-to-activate-the-environment-every-time">Do I need to activate the environment every time?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-if-activate-doesnt-work">What if activate Doesn’t work?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-can-i-use-vs-code-or-another-editor-with-this">Can I use VS Code or another editor with this?</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-bonus-tips">Bonus Tips</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-add-a-gitignore-file">Add a .gitignore File</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-requirementstxt">Use requirements.txt</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-helpful-resources">Helpful Resources</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-further-learning">Further Learning</a></p>
</li>
</ol>
<h2 id="heading-what-is-a-virtual-environment-in-python">What Is a Virtual Environment in Python?</h2>
<p>A virtual environment is like a private workspace just for your project. Instead of installing packages (like Django) globally for your whole computer, you install them inside this little bubble. That way, different projects don’t mess with each other.</p>
<p>Imagine you’re working on two Django projects: one needs Django 3.2 and the other needs Django 4.1. Without a virtual environment, you'd run into version conflicts. But with virtual environments, each project stays separate and clean.</p>
<h2 id="heading-why-use-a-virtual-environment">Why Use a Virtual Environment?</h2>
<p>Here’s why I <em>always</em> use one when working with Django:</p>
<ul>
<li><p>Keeps your project dependencies isolated.</p>
</li>
<li><p>Prevents version conflicts between different projects.</p>
</li>
<li><p>Makes it easy to manage and uninstall packages.</p>
</li>
<li><p>Most importantly, <strong>Django expects it</strong>, especially if you want to follow best practices.</p>
</li>
</ul>
<h2 id="heading-how-to-set-up-and-activate-a-django-virtual-environment">How to Set Up and Activate a Django Virtual Environment</h2>
<p>Let’s walk through the process from start to finish.</p>
<h3 id="heading-1-install-python-if-you-havent-yet">1. <strong>Install Python (If You Haven’t Yet)</strong></h3>
<p>You need Python 3.8 or later. You can check what version you have by opening your terminal and typing:</p>
<pre><code class="lang-bash">python --version
</code></pre>
<p>If you see something like <code>Python 3.11.7</code>You’re good.</p>
<p>If you don’t have Python, download it here:</p>
<p>👉 <a target="_blank" href="https://www.python.org/downloads/">https://www.python.org/downloads/</a></p>
<p>Make sure to check the box <strong>“Add Python to PATH”</strong> during installation if you're on Windows.</p>
<h3 id="heading-2-install-virtualenv-optional-but-worth-knowing">2. Install <code>virtualenv</code> (Optional but Worth Knowing)</h3>
<p>Python includes a built-in tool called <code>venv</code>, and that’s what we’ll use in this tutorial.</p>
<p>However, some developers prefer <code>virtualenv</code> because:</p>
<ul>
<li><p>It works with older Python versions</p>
</li>
<li><p>It can be slightly faster in larger environments</p>
</li>
<li><p>It offers some additional flexibility</p>
</li>
</ul>
<p>To install <code>virtualenv</code> just run:</p>
<pre><code class="lang-bash">pip install virtualenv
</code></pre>
<p><strong>Note:</strong> You don’t need <code>virtualenv</code> for this tutorial, but it’s good to know about. We'll be using Python’s built-in <code>venv</code> going forward.</p>
<h3 id="heading-3-create-a-virtual-environment">3. <strong>Create a Virtual Environment</strong></h3>
<p>Now go to your Django project folder (or make one):</p>
<pre><code class="lang-bash">mkdir my_django_project
<span class="hljs-built_in">cd</span> my_django_project
</code></pre>
<p>Then run:</p>
<pre><code class="lang-bash">python -m venv venv
</code></pre>
<ul>
<li><p><code>python -m venv</code> uses Python’s built-in virtual environment module</p>
</li>
<li><p><code>venv</code> is the name of the folder that will store your environment (you can call it anything)</p>
</li>
</ul>
<p>This creates a folder called <code>venv/</code> in your project directory. That folder contains everything your virtual environment needs.</p>
<h3 id="heading-4-activate-the-virtual-environment">4. <strong>Activate the Virtual Environment</strong></h3>
<p>Here’s the part everyone asks about.</p>
<p>Activation depends on your operating system.</p>
<h4 id="heading-on-windows-cmd">On <strong>Windows (CMD)</strong>:</h4>
<pre><code class="lang-bash">venv\Scripts\activate
</code></pre>
<h4 id="heading-on-windows-powershell">On <strong>Windows (PowerShell)</strong>:</h4>
<pre><code class="lang-bash">.\venv\Scripts\Activate.ps1
</code></pre>
<h4 id="heading-on-mac-or-linux">On <strong>Mac or Linux</strong>:</h4>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> venv/bin/activate
</code></pre>
<p>After you activate it, your terminal prompt will change. It’ll look something like this:</p>
<pre><code class="lang-bash">(venv) your-computer-name:my_django_project username$
</code></pre>
<p>That <code>(venv)</code> at the beginning means the virtual environment is active.</p>
<h2 id="heading-what-can-you-do-after-activating-it">What Can You Do After Activating It?</h2>
<p>Now that it’s active, you can install Django (or anything else) just for this project:</p>
<pre><code class="lang-bash">pip install django
</code></pre>
<p>This installs Django inside the virtual environment, not globally.</p>
<p>To double-check:</p>
<pre><code class="lang-bash">pip list
</code></pre>
<p>You’ll see Django and any other installed packages listed there.</p>
<h2 id="heading-how-to-deactivate-the-virtual-environment">How to Deactivate the Virtual Environment</h2>
<p>When you’re done working, just type:</p>
<pre><code class="lang-bash">deactivate
</code></pre>
<p>That’s it. Your terminal goes back to normal, and your system’s Python is no longer linked to the project.</p>
<h2 id="heading-faqs">FAQs</h2>
<h3 id="heading-do-i-need-to-activate-the-environment-every-time"><strong>Do I need to activate the environment every time?</strong></h3>
<p>Yes, every time you open a new terminal session and want to work on your Django project, activate it again using the command for your OS.</p>
<h3 id="heading-what-if-activate-doesnt-work"><strong>What if</strong> <code>activate</code> <strong>Doesn’t work?</strong></h3>
<p>If you’re on Windows, PowerShell might block the script. Run this:</p>
<pre><code class="lang-powershell"><span class="hljs-built_in">Set-ExecutionPolicy</span> <span class="hljs-literal">-ExecutionPolicy</span> RemoteSigned <span class="hljs-literal">-Scope</span> CurrentUser
</code></pre>
<p>Then try activating again.</p>
<h3 id="heading-can-i-use-vs-code-or-another-editor-with-this"><strong>Can I use VS Code or another editor with this?</strong></h3>
<p>Absolutely. VS Code even detects your virtual environment automatically. You can select the interpreter from the bottom-left or by pressing <code>Ctrl+Shift+P</code> → “Python: Select Interpreter.”</p>
<h2 id="heading-bonus-tips">Bonus Tips</h2>
<h3 id="heading-add-a-gitignore-file">Add a <code>.gitignore</code> File</h3>
<p>If you're using Git, you don’t want to upload the <code>venv</code> folder to GitHub. Add this line to your <code>.gitignore</code> file:</p>
<pre><code class="lang-python">venv/
</code></pre>
<h3 id="heading-use-requirementstxt">Use <code>requirements.txt</code></h3>
<p>Once you’ve installed your project’s packages, freeze them like this:</p>
<pre><code class="lang-bash">pip freeze &gt; requirements.txt
</code></pre>
<p>Then later, you (or someone else) can install them with:</p>
<pre><code class="lang-bash">pip install -r requirements.txt
</code></pre>
<p>This is useful for team projects or for moving your app to a server.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Activating your Django virtual environment might seem like a small thing, but it’s a big step toward becoming a confident and organized developer.</p>
<p>Once you get the hang of it, it becomes second nature – and your future self will thank you for learning it the right way from the start.</p>
<p>Would you love to connect with me? You can do so on <a target="_blank" href="https://X.com/_udemezue">X.com/_udemezue</a></p>
<h3 id="heading-helpful-resources">Helpful Resources</h3>
<ul>
<li><p><a target="_blank" href="https://docs.python.org/3/library/venv.html">Official Python Docs on <code>venv</code></a></p>
</li>
<li><p><a target="_blank" href="https://www.djangoproject.com/">Django Official Website</a></p>
</li>
<li><p><a target="_blank" href="https://realpython.com/python-virtual-environments-a-primer/">Python Virtual Environments Tutorial (Real Python)</a></p>
</li>
<li><p><a target="_blank" href="https://stackoverflow.com/questions/63443862/activate-ps1-cannot-be-loaded-because-running-scripts-is-disabled">How to Fix “activate.ps1 cannot be loaded” in PowerShell</a></p>
</li>
</ul>
<h3 id="heading-further-learning">Further Learning</h3>
<p>If you're serious about Django, here are some free and paid resources I recommend:</p>
<ul>
<li><p><a target="_blank" href="https://djangoforbeginners.com/">Django for Beginners by William S. Vincent</a></p>
</li>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=F5mRW0jo-U4">FreeCodeCamp’s Django Crash Course on YouTube</a></p>
</li>
<li><p><a target="_blank" href="https://cs50.harvard.edu/web/2020/">CS50 Web Programming with Python and JavaScript</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Django Crash Course for Beginners ]]>
                </title>
                <description>
                    <![CDATA[ Django is a high-level web framework built with Python that encourages rapid development and clean, pragmatic design. Django handles much of the heavy lifting involved in web development, so you can focus more on writing your app and less on reinvent... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/django-crash-course-for-beginners/</link>
                <guid isPermaLink="false">6813a8237eaea66216eb6819</guid>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Thu, 01 May 2025 16:58:11 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746118671895/d37fe9e1-af3b-4419-a39d-26cb85d583eb.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Django is a high-level web framework built with Python that encourages rapid development and clean, pragmatic design. Django handles much of the heavy lifting involved in web development, so you can focus more on writing your app and less on reinventing the wheel. Whether you want to build a blog, an e-commerce platform, or a custom web service, Django provides the structure and tools to help you get the job done efficiently.</p>
<p>What makes Django especially powerful is its “batteries-included” philosophy. This means it comes with a wide range of features out of the box, including an ORM (Object-Relational Mapper) for interacting with databases, a templating engine for rendering dynamic HTML pages, robust form handling, user authentication, an admin interface, and much more. Django is also built with security in mind, offering protection against common web vulnerabilities like SQL injection, cross-site scripting, and cross-site request forgery. Plus, Django’s active community and extensive documentation make it one of the most accessible frameworks for beginners.</p>
<p>We just published a course on the <a target="_blank" href="http://freeCodeCamp.org">freeCodeCamp.org</a> YouTube channel that will teach you all about getting started with Django. Taught by Abel Gideon, this Django crash course for beginners offers a step-by-step introduction to the framework and its core components. You’ll begin with setting up your development environment and understanding how to use essential tools like <code>django-admin</code> and <a target="_blank" href="http://manage.py"><code>manage.py</code></a>. From there, the course introduces the fundamental concepts of Django’s architecture, particularly the Model-View-Template (MVT) pattern, which separates data handling, logic, and presentation.</p>
<p>The course covers things like how to create Django apps, define views and handle requests, work with models and databases, and design user-facing pages using Django’s template system. You’ll also explore how forms work in Django, how to use the Django Admin panel for content management, and how to integrate a MySQL database into your project. The course culminates in building a complete Django project, giving you a real-world example of how everything fits together.</p>
<p>By the end of the course, you’ll have a clear understanding of how Django works and how to use it to create your own dynamic web applications. Whether you’re looking to build a personal website, a data-driven dashboard, or the backend for a mobile app, this course will give you the foundation you need to move forward confidently in Django development.</p>
<p>Watch the full course on <a target="_blank" href="https://youtu.be/0roB7wZMLqI">the freeCodeCamp.org YouTube channel</a> (1-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/0roB7wZMLqI" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Get the User Model in Django – A Simple Guide With Examples ]]>
                </title>
                <description>
                    <![CDATA[ When I’m working with Django, one of the first things I often need to do is work with users – like getting the logged-in user, creating a new one, or extending the default user model to add more information. Now, Django has a built-in User model, but... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-get-user-model-in-django/</link>
                <guid isPermaLink="false">68123f859249b83d2215937d</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Udemezue John ]]>
                </dc:creator>
                <pubDate>Wed, 30 Apr 2025 15:19:33 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746026362647/7b47e9e7-6baf-409a-8654-0ad1eb528e31.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When I’m working with Django, one of the first things I often need to do is work with users – like getting the logged-in user, creating a new one, or extending the default user model to add more information.</p>
<p>Now, Django has a built-in <code>User</code> model, but sometimes you might want a custom one. That's where things can get a little confusing if you're just starting.</p>
<p>The good news? Getting the user model in Django is very simple once you understand how Django is set up.</p>
<p>Today, I’ll walk you through exactly how to get the user model in Django, explain why it matters, show you real code you can use, and answer a few common questions people usually have around this topic.</p>
<p>Let's jump right into it.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-getting-the-user-model-correctly-matters">Why Getting the User Model Correctly Matters</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-get-the-user-model-in-django">How to Get the User Model in Django</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-full-example-using-the-user-model">Full Example: Using the User Model</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-to-use-settingsauthusermodel">When to Use settings.AUTH_USER_MODEL</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-quick-summary">Quick Summary</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-common-mistakes-to-avoid">Common Mistakes to Avoid</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-faqs">FAQs</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-can-i-access-requestuser-it-directly">1. Can I access request.user it directly?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-what-happens-if-i-call-getusermodel-multiple-times">2. What happens if I call get_user_model() Multiple times?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-how-do-i-know-if-im-using-a-custom-user-model">3. How do I know if I’m using a custom user model?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-when-should-i-create-a-custom-user-model">4. When should I create a custom user model?</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-further-resources">Further Resources</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-why-getting-the-user-model-correctly-matters">Why Getting the User Model Correctly Matters</h2>
<p>Before anything else, it’s important to know why this even matters.</p>
<p>Django projects depend heavily on user information – not just for logins, but for permissions, profiles, admin management, and much more.</p>
<p>If you get the user model the wrong way, you can easily run into problems later, especially if you customize your user model.</p>
<p>Django even warns you about this in its <a target="_blank" href="https://docs.djangoproject.com/en/stable/topics/auth/customizing/">official documentation</a>. If you don't use the right way to access the user model, your project could break when you change or extend it.</p>
<p>That’s why it’s super important to always get the user model the <em>recommended</em> way, which I’ll show you next.</p>
<h2 id="heading-how-to-get-the-user-model-in-django">How to Get the User Model in Django</h2>
<p>Alright, here’s the simplest way to get the user model in Django:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.contrib.auth <span class="hljs-keyword">import</span> get_user_model

User = get_user_model()
</code></pre>
<p><strong>What’s happening here?</strong></p>
<ul>
<li><p><code>get_user_model()</code> is a built-in Django function.</p>
</li>
<li><p>It returns the correct User model – whether you're using the default one or a custom one you created.</p>
</li>
</ul>
<p>If you’re wondering why not just import <code>from django.contrib.auth.models import User</code>, the reason is this: if you ever swap out the default User model for a custom one, directly importing like that will break your code.</p>
<p>By using <code>get_user_model()</code>, you stay safe and future-proof your project.</p>
<h2 id="heading-full-example-using-the-user-model">Full Example: Using the User Model</h2>
<p>Let’s look at a full working example, not just a little snippet.</p>
<p>Imagine you want to create a new user inside a Django view:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.contrib.auth <span class="hljs-keyword">import</span> get_user_model
<span class="hljs-keyword">from</span> django.http <span class="hljs-keyword">import</span> HttpResponse

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_user_view</span>(<span class="hljs-params">request</span>):</span>
    User = get_user_model()
    user = User.objects.create_user(username=<span class="hljs-string">'newuser'</span>, password=<span class="hljs-string">'securepassword123'</span>)
    <span class="hljs-keyword">return</span> HttpResponse(<span class="hljs-string">f"Created user: <span class="hljs-subst">{user.username}</span>"</span>)
</code></pre>
<p>In this example:</p>
<ul>
<li><p>First, I get the user model with <code>get_user_model()</code>.</p>
</li>
<li><p>Then, I use Django’s built-in <code>create_user</code> method to create a user safely.</p>
</li>
<li><p>Finally, I send back a simple HTTP response showing the created username.</p>
</li>
</ul>
<p>Notice how clean and flexible it is – no matter what user model you're using under the hood.</p>
<h2 id="heading-when-to-use-settingsauthusermodel">When to Use <code>settings.AUTH_USER_MODEL</code></h2>
<p>Another thing you’ll often see in Django projects is something like this:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.conf <span class="hljs-keyword">import</span> settings

settings.AUTH_USER_MODEL
</code></pre>
<p>This doesn’t <strong>get</strong> the user model. Instead, it gives you the <strong>string</strong> path to the user model, like <code>"auth.User"</code> (for default) or <code>"myapp.MyCustomUser"</code> if you customized it.</p>
<p>You usually use <code>settings.AUTH_USER_MODEL</code> inside your <strong>models.py</strong> when you want to link to the User model in a ForeignKey, OneToOneField, or ManyToManyField.</p>
<p>For example:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.conf <span class="hljs-keyword">import</span> settings
<span class="hljs-keyword">from</span> django.db <span class="hljs-keyword">import</span> models

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Profile</span>(<span class="hljs-params">models.Model</span>):</span>
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    bio = models.TextField()
</code></pre>
<p>Here, the <code>Profile</code> model is tied to the correct user model. Again, this keeps your project flexible and future-proof.</p>
<h2 id="heading-quick-summary">Quick Summary</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Situation</td><td>What to Use</td></tr>
</thead>
<tbody>
<tr>
<td>Getting the actual User model in Python code (views, forms, admin, and so on)</td><td><code>get_user_model()</code></td></tr>
<tr>
<td>Referring to the User model in database relationships (ForeignKey, OneToOneField, and so on)</td><td><code>settings.AUTH_USER_MODEL</code></td></tr>
</tbody>
</table>
</div><p>Remember this table – it saves a lot of headaches later!</p>
<h2 id="heading-common-mistakes-to-avoid">Common Mistakes to Avoid</h2>
<ul>
<li><p><strong>Directly importing</strong> <code>User</code>: Never just do <code>from django.contrib.auth.models import User</code> unless you are 100% sure you're sticking with the default model forever (not recommended).</p>
</li>
<li><p><strong>Hardcoding relationships</strong>: If you write something like <code>ForeignKey('auth.User')</code> instead of using <code>settings.AUTH_USER_MODEL</code>, it will break if you ever switch to a custom user model.</p>
</li>
<li><p><strong>Not creating custom user models early</strong>: If you think you might ever need a custom user model (like adding phone numbers, extra profile fields), set it up early. Switching later is painful once you have a database full of users.</p>
</li>
</ul>
<h2 id="heading-faqs">FAQs</h2>
<h3 id="heading-1-can-i-access-requestuser-directly">1. Can I access <code>request.user</code> directly?</h3>
<p>Yes! Inside views, <code>request.user</code> gives you the current logged-in user object. Behind the scenes, Django is using the correct user model, whether it’s custom or default.</p>
<h3 id="heading-2-what-happens-if-i-call-getusermodel-multiple-times">2. What happens if I call <code>get_user_model()</code> multiple times?</h3>
<p>No problem at all. Django caches it internally, so it’s efficient. Feel free to call it wherever you need it.</p>
<h3 id="heading-3-how-do-i-know-if-im-using-a-custom-user-model">3. How do I know if I’m using a custom user model?</h3>
<p>Check your Django settings file (<code>settings.py</code>) and look for <code>AUTH_USER_MODEL</code>. If it’s set (like <code>'myapp.MyCustomUser'</code>, you are using a custom model. If it’s not there, Django is using the default.</p>
<h3 id="heading-4-when-should-i-create-a-custom-user-model">4. When should I create a custom user model?</h3>
<p>If you even <em>think</em> you’ll need fields like phone number, date of birth, profile pictures, and so on, it’s better to set up a custom model early.</p>
<p>Here’s a great guide from Django’s official docs on <a target="_blank" href="https://docs.djangoproject.com/en/stable/topics/auth/customizing/">customizing user models</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Working with users in Django doesn’t have to be tricky. Once you know to use <code>get_user_model()</code> when you need the model and <code>settings.AUTH_USER_MODEL</code> for database relationships, your code stays clean, safe, and ready for whatever changes come your way.</p>
<p>Now that you know how to get the user model in Django, what’s one thing you'd love to customize about your users in your project? Shoot me a message on <a target="_blank" href="http://x.com/_udemezue/">X</a>.</p>
<p>If you want me to show you how to <strong>build</strong> a custom user model from scratch, let me know – it’s not hard once you know the steps.</p>
<h3 id="heading-further-resources">Further Resources</h3>
<ul>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/topics/auth/customizing/">Official Django documentation: Using a custom user model</a></p>
</li>
<li><p><a target="_blank" href="https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html">Simple explanation on AbstractBaseUser vs AbstractUser</a></p>
</li>
<li><p><a target="_blank" href="https://stackoverflow.com/questions/29725217/django-custom-user-model-best-practice">StackOverflow: Best practices for Django user models</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Register Models in Django Admin ]]>
                </title>
                <description>
                    <![CDATA[ When you're building a website or an app with Django, one of the most exciting moments is when your database models finally come to life. But to manage your data easily – adding, editing, or deleting entries – you need Django’s Admin panel. Now, here... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-register-models-in-django-admin/</link>
                <guid isPermaLink="false">6810e5a2dc9845a2ab207f06</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Udemezue John ]]>
                </dc:creator>
                <pubDate>Tue, 29 Apr 2025 14:43:46 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745937579596/e8aed227-b7c3-4bf6-a448-a66782aeea42.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you're building a website or an app with Django, one of the most exciting moments is when your database models finally come to life.</p>
<p>But to manage your data easily – adding, editing, or deleting entries – you need Django’s Admin panel.</p>
<p>Now, here’s the catch: just creating a model isn’t enough. If you want it to show up in the Admin panel, you have to <strong>register</strong> it.</p>
<p>And honestly, registering models in Django Admin is one of the simplest but most important steps. If you miss it, it feels like your model doesn’t even exist.</p>
<p>In this guide, I’ll walk you through exactly how to register your models in Django Admin, step-by-step, with easy-to-understand code examples.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-django-admin-matters">Why Django Admin Matters</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-register-models-in-django-admin">How to Register Models in Django Admin</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-step-1-make-sure-you-have-a-model">Step 1: Make Sure You Have a Model</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-register-your-model-in-admin">Step 2: Register Your Model In Admin</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-optional-customize-how-your-model-looks-in-admin">Step 3: (Optional) Customize How Your Model Looks in Admin</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-faqs">FAQS</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-i-added-a-model-but-its-not-showing-up-in-admin-what-happened">1. I added a model, but it’s not showing up in Admin. What happened?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-do-i-have-to-register-every-model-separately">2. Do I have to register every model separately?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-how-do-i-unregister-a-model">3. How do I unregister a model?</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-helpful-links-and-resources">Helpful Links and Resources</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ul>
<h2 id="heading-why-django-admin-matters">Why Django Admin Matters</h2>
<p>Django Admin is like your personal dashboard for the backend of your website. Once you register your models, you can manage your app's content without touching any code.</p>
<p>Imagine being able to add new blog posts, approve users, update product listings – all with a few clicks. That’s the magic of Django Admin.</p>
<p>Without properly registering your models, you’re stuck managing everything manually, which can get messy real quick.</p>
<p>Plus, Django Admin saves developers hours of time. It’s one of the reasons Django is such a powerful framework.</p>
<h2 id="heading-how-to-register-models-in-django-admin">How to Register Models in Django Admin</h2>
<h3 id="heading-step-1-make-sure-you-have-a-model">Step 1: Make Sure You Have a Model</h3>
<p>Before you can register anything, you need a model. Here’s a super basic example of a model inside a Django app called <code>blog</code>.</p>
<p>Inside <code>blog/models.py</code>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.db <span class="hljs-keyword">import</span> models

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Post</span>(<span class="hljs-params">models.Model</span>):</span>
    title = models.CharField(max_length=<span class="hljs-number">200</span>)
    body = models.TextField()
    date_created = models.DateTimeField(auto_now_add=<span class="hljs-literal">True</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__str__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self.title
</code></pre>
<p>In this model:</p>
<ul>
<li><p><code>title</code> is a short text field.</p>
</li>
<li><p><code>body</code> is for longer content.</p>
</li>
<li><p><code>date_created</code> automatically stores the time when the post is created.</p>
</li>
</ul>
<p>And that <code>__str__</code> method? That’s just telling Django how to show each Post in the Admin – it’ll display the post’s title instead of something like <code>Post object (1)</code>.</p>
<p><strong>Quick tip</strong>: Always add a <code>__str__</code> method to your models. It makes your Admin interface much cleaner.</p>
<h3 id="heading-step-2-register-your-model-in-admin">Step 2: Register Your Model in Admin</h3>
<p>Alright, your model is ready. Time to register it!</p>
<p>Open <code>blog/admin.py</code>. When you create a new Django app, this file is empty by default.</p>
<p>Here’s how to register the <code>Post</code> model:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.contrib <span class="hljs-keyword">import</span> admin
<span class="hljs-keyword">from</span> .models <span class="hljs-keyword">import</span> Post

admin.site.register(Post)
</code></pre>
<p><strong>What’s happening here?</strong></p>
<ul>
<li><p>First, you import Django’s admin module.</p>
</li>
<li><p>Then, you import your model (<code>Post</code>).</p>
</li>
<li><p>Finally, you use <code>admin.site.register()</code> to tell Django, "Hey, I want this model to show up in the Admin panel."</p>
</li>
</ul>
<p>Save the file. Now if you go to your Admin site (usually at <code>http://127.0.0.1:8000/admin</code>), you’ll see <strong>Posts</strong> listed there.</p>
<h3 id="heading-step-3-optional-customize-how-your-model-looks-in-admin">Step 3: (Optional) Customize How Your Model Looks in Admin</h3>
<p>By default, Django Admin shows your models in a very basic table. But you can make it so much better with a little customization.</p>
<p>Here’s how you can make Posts show the title and creation date at a glance.</p>
<p>Still inside <code>blog/admin.py</code>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.contrib <span class="hljs-keyword">import</span> admin
<span class="hljs-keyword">from</span> .models <span class="hljs-keyword">import</span> Post

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PostAdmin</span>(<span class="hljs-params">admin.ModelAdmin</span>):</span>
    list_display = (<span class="hljs-string">'title'</span>, <span class="hljs-string">'date_created'</span>)

admin.site.register(Post, PostAdmin)
</code></pre>
<p>Now:</p>
<ul>
<li><p><code>list_display</code> tells Django which fields you want to show in the list view.</p>
</li>
<li><p>You create a <code>PostAdmin</code> class that describes how the <code>Post</code> model should behave in Admin.</p>
</li>
<li><p>When you register, you pass both the model (<code>Post</code>) and the admin class (<code>PostAdmin</code>).</p>
</li>
</ul>
<p><strong>Quick tip</strong>: Customizing your Admin improves your workflow <em>a lot</em> – especially when you’re managing many entries.</p>
<h2 id="heading-faqs">FAQS</h2>
<h3 id="heading-1-i-added-a-model-but-its-not-showing-up-in-admin-what-happened">1. I added a model, but it’s not showing up in Admin. What happened?</h3>
<p>Make sure you:</p>
<ul>
<li><p>Registered the model inside <code>admin.py</code>.</p>
</li>
<li><p>Ran migrations (<code>python manage.py makemigrations</code> and <code>python manage.py migrate</code>) if you changed anything in the model.</p>
</li>
</ul>
<p>Also, check if the app is listed in your <code>INSTALLED_APPS</code> inside <code>settings.py</code>.</p>
<h3 id="heading-2-do-i-have-to-register-every-model-separately">2. Do I have to register every model separately?</h3>
<p>Yes. Each model you want to manage in Admin needs to be registered. But you can register multiple models together too:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> .models <span class="hljs-keyword">import</span> Post, Comment, Category

admin.site.register([Post, Comment, Category])
</code></pre>
<h3 id="heading-3-how-do-i-unregister-a-model">3. How do I unregister a model?</h3>
<p>You can use:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.contrib <span class="hljs-keyword">import</span> admin
<span class="hljs-keyword">from</span> .models <span class="hljs-keyword">import</span> Post

admin.site.unregister(Post)
</code></pre>
<p>But honestly, most of the time, you just stop registering it if you don't want it there.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Registering models in Django Admin might seem like a tiny step, but it has a huge impact on how you work with your data.</p>
<p>It turns your database into a friendly dashboard that anyone can use – even non-technical people.</p>
<p>Once you get comfortable with registering and customising your models, you’ll move faster and feel a lot more in control of your app.</p>
<p>Now I’m curious — <strong>which model are you most excited to register in your Django Admin?</strong> Let’s chat on <a target="_blank" href="http://x.com/_udemezue">X</a>.</p>
<h3 id="heading-helpful-links-and-resources">Helpful Links and Resources</h3>
<ul>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/ref/contrib/admin/">Django Official Documentation – Admin Site</a></p>
</li>
<li><p><a target="_blank" href="https://realpython.com/get-started-with-django-1/">Understanding Django Models (Real Python)</a></p>
</li>
<li><p><a target="_blank" href="https://tutorial.djangogirls.org/en/django_admin/">Django Girls Tutorial – Introduction to Django Admin</a></p>
</li>
</ul>
<p>These are great places to go if you want to dive even deeper into Django Admin customization.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Enable CORS in Django ]]>
                </title>
                <description>
                    <![CDATA[ If you've ever tried to connect your frontend app to your Django backend and suddenly hit an error that looks something like "has been blocked by CORS policy", you're not alone. It’s frustrating, especially when your code seems fine. So what’s going ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-enable-cors-in-django/</link>
                <guid isPermaLink="false">680fa40e9096ceb5eeedd90e</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CORS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Udemezue John ]]>
                </dc:creator>
                <pubDate>Mon, 28 Apr 2025 15:51:42 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745855234567/f09d3338-c824-4cd8-a26f-93bb485f925a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you've ever tried to connect your frontend app to your Django backend and suddenly hit an error that looks something like <strong>"has been blocked by CORS policy"</strong>, you're not alone. It’s frustrating, especially when your code seems fine.</p>
<p>So what’s going on?</p>
<p>This is where <strong>CORS</strong> (Cross-Origin Resource Sharing) comes in. It's a browser security feature that blocks web pages from making requests to a different domain than the one that served the web page.</p>
<p>It’s there to protect users, but if it’s not configured correctly, it can stop your app from working the way you want.</p>
<p>Let’s fix that.</p>
<p>In this article, I’ll walk you through everything you need to know to enable CORS in Django without headaches.</p>
<h3 id="heading-heres-what-well-cover">Here’s what we’ll cover:</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-cors-and-why-should-you-care">What is CORS and Why Should You Care?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-enable-cors-in-django">How to Enable CORS in Django</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-install-django-cors-headers">1. Install django-cors-headers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-add-it-to-installedapps">2. Add It to INSTALLED_APPS</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-add-middleware">3. Add Middleware</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-set-the-allowed-origins">4. Set the Allowed Origins</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-optional-settings-you-might-need">Optional Settings You Might Need</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-allow-all-origins-not-recommended-for-production">Allow All Origins (Not Recommended for Production)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-allow-credentials-cookies-auth">Allow Credentials (Cookies, Auth)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-allow-specific-headers">Allow Specific Headers</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-example-full-settings-file-snippet">Example: Full Settings File Snippet</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-common-errors-and-how-to-fix-them">Common Errors (And How to Fix Them)</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-cors-not-working-at-all">1. CORS Not Working At All?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-preflight-request-fails-options-method">2. Preflight Request Fails (OPTIONS method)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-using-django-rest-framework">3. Using Django Rest Framework?</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-faqs">FAQs</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-can-i-allow-multiple-frontend-urls">Can I allow multiple frontend URLs?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-does-cors-affect-local-development-only">Does CORS affect local development only?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-is-it-secure-to-allow-all-origins">Is it secure to allow all origins?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-do-i-need-to-change-anything-on-the-frontend">Do I need to change anything on the frontend?</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-further-resources">Further Resources</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ul>
<h2 id="heading-what-is-cors-and-why-should-you-care">What is CORS and Why Should You Care?</h2>
<p>Before you start changing settings, it’s important to understand what CORS is.</p>
<p>Imagine you have a frontend built with React running on <code>http://localhost:3000</code> and a Django API running on <code>http://localhost:8000</code>.</p>
<p>When the frontend tries to talk to the backend, your browser sees that they’re not the same origin (they have different ports), and it blocks the request.</p>
<p>That’s CORS doing its job. It assumes you might be trying to do something unsafe – like stealing cookies or user data – so it steps in.</p>
<p>Now, as a developer, if you trust the frontend and you own both ends, then it’s safe to let those requests through. You just need to tell Django it’s okay.</p>
<h2 id="heading-how-to-enable-cors-in-django">How to Enable CORS in Django</h2>
<p>You’re going to need a third-party package called <code>django-cors-headers</code>. It’s widely used and actively maintained. Here’s how to set it up:</p>
<h3 id="heading-1-install-django-cors-headers">1. Install <code>django-cors-headers</code></h3>
<p>Run this in your terminal:</p>
<pre><code class="lang-bash">pip install django-cors-headers
</code></pre>
<p>This adds the package to your environment so Django can use it.</p>
<h3 id="heading-2-add-it-to-installedapps">2. Add It to <code>INSTALLED_APPS</code></h3>
<p>Open your <code>settings.py</code> file and find the <code>INSTALLED_APPS</code> section. Add this line:</p>
<pre><code class="lang-python">INSTALLED_APPS = [
    ...
    <span class="hljs-string">'corsheaders'</span>,
]
</code></pre>
<p>This registers the app with Django.</p>
<h3 id="heading-3-add-middleware">3. Add Middleware</h3>
<p>Now scroll down to the <code>MIDDLEWARE</code> section in <code>settings.py</code>. Add this <strong>at the top of the list</strong>:</p>
<pre><code class="lang-python">MIDDLEWARE = [
    <span class="hljs-string">'corsheaders.middleware.CorsMiddleware'</span>,
    <span class="hljs-string">'django.middleware.common.CommonMiddleware'</span>,
    ...
]
</code></pre>
<p><strong>Why at the top?</strong> Because middleware in Django runs in order. If you don’t place it at the top, the CORS headers might not be added correctly, and your browser will still block your requests.</p>
<h3 id="heading-4-set-the-allowed-origins">4. Set the Allowed Origins</h3>
<p>This is where you tell Django which origins are allowed to talk to your backend.</p>
<p>Still in <code>settings.py</code>, add:</p>
<pre><code class="lang-python">CORS_ALLOWED_ORIGINS = [
    <span class="hljs-string">"http://localhost:3000"</span>,
]
</code></pre>
<p>Replace <code>localhost:3000</code> with whatever domain or port your frontend is using. If you're using HTTPS or deploying, make sure to include the correct URL, like <code>https://yourfrontend.com</code>.</p>
<p>And that’s it! You’re now allowing your frontend to access your backend.</p>
<h2 id="heading-optional-settings-you-might-need">Optional Settings You Might Need</h2>
<p>Depending on your project, you might run into other issues. Here are some extra settings you might find useful:</p>
<h3 id="heading-allow-all-origins-not-recommended-for-production">Allow All Origins (Not Recommended for Production)</h3>
<p>If you’re just testing and want to allow everything (be careful with this), you can use:</p>
<pre><code class="lang-python">CORS_ALLOW_ALL_ORIGINS = <span class="hljs-literal">True</span>
</code></pre>
<p>Again, don’t use this in production unless you understand the risks. It can open up your API to abuse.</p>
<h3 id="heading-allow-credentials-cookies-auth">Allow Credentials (Cookies, Auth)</h3>
<p>If your frontend is sending authentication credentials like cookies or tokens, you also need this:</p>
<pre><code class="lang-python">CORS_ALLOW_CREDENTIALS = <span class="hljs-literal">True</span>
</code></pre>
<p>And make sure you <strong>don’t</strong> use <code>CORS_ALLOW_ALL_ORIGINS</code> with this setting – it won’t work due to security rules. Stick to <code>CORS_ALLOWED_ORIGINS</code>.</p>
<h3 id="heading-allow-specific-headers">Allow Specific Headers</h3>
<p>By default, common headers are allowed, but if you’re sending custom ones (like <code>X-Auth-Token</code>), you can add:</p>
<pre><code class="lang-python">CORS_ALLOW_HEADERS = [
    <span class="hljs-string">"content-type"</span>,
    <span class="hljs-string">"authorization"</span>,
    <span class="hljs-string">"x-auth-token"</span>,
    ...
]
</code></pre>
<h2 id="heading-example-full-settings-file-snippet">Example: Full Settings File Snippet</h2>
<p>Here’s a mini version of what your <code>settings.py</code> might look like after setup:</p>
<pre><code class="lang-python">INSTALLED_APPS = [
    ...
    <span class="hljs-string">'corsheaders'</span>,
    ...
]

MIDDLEWARE = [
    <span class="hljs-string">'corsheaders.middleware.CorsMiddleware'</span>,
    <span class="hljs-string">'django.middleware.common.CommonMiddleware'</span>,
    ...
]

CORS_ALLOWED_ORIGINS = [
    <span class="hljs-string">"http://localhost:3000"</span>,
]

CORS_ALLOW_CREDENTIALS = <span class="hljs-literal">True</span>
</code></pre>
<p>You can tweak this based on your needs, but that’s the basic structure.</p>
<h2 id="heading-common-errors-and-how-to-fix-them">Common Errors (And How to Fix Them)</h2>
<h3 id="heading-1-cors-not-working-at-all">1. CORS Not Working At All?</h3>
<p>Double check:</p>
<ul>
<li><p>You added <code>corsheaders.middleware.CorsMiddleware</code> it <strong>at the top</strong> of the middleware list.</p>
</li>
<li><p>Your frontend origin matches exactly, including port and protocol.</p>
</li>
<li><p>You restarted your server after changing the settings.</p>
</li>
</ul>
<h3 id="heading-2-preflight-request-fails-options-method">2. Preflight Request Fails (OPTIONS method)</h3>
<p>Sometimes your browser sends an <code>OPTIONS</code> request first to check if the server will allow the real request. Make sure your views or Django setup allow that method, or Django will return a 405 error.</p>
<p>You don’t usually need to do anything here unless you’re using a custom middleware or view decorator that blocks it.</p>
<h3 id="heading-3-using-django-rest-framework">3. Using Django Rest Framework?</h3>
<p>No problem – <code>django-cors-headers</code> works out of the box. Just make sure it’s installed and the middleware is set up correctly.</p>
<h2 id="heading-faqs">FAQs</h2>
<h3 id="heading-can-i-allow-multiple-frontend-urls"><strong>Can I allow multiple frontend URLs?</strong></h3>
<p>Yes! Just add more items to the list:</p>
<pre><code class="lang-python">CORS_ALLOWED_ORIGINS = [
    <span class="hljs-string">"http://localhost:3000"</span>,
    <span class="hljs-string">"https://myfrontend.com"</span>,
]
</code></pre>
<h3 id="heading-does-cors-affect-local-development-only"><strong>Does CORS affect local development only?</strong></h3>
<p>No, it applies in production too. Any time your frontend and backend are on different origins (different domain or port), you need to handle CORS.</p>
<h3 id="heading-is-it-secure-to-allow-all-origins"><strong>Is it secure to allow all origins?</strong></h3>
<p>No. Only do that temporarily during development. Always restrict access in production to just the domains you trust.</p>
<h3 id="heading-do-i-need-to-change-anything-on-the-frontend"><strong>Do I need to change anything on the frontend?</strong></h3>
<p>Sometimes. If you're sending credentials (like cookies), you’ll need to set <code>credentials: "include"</code> in your fetch or Axios requests.</p>
<p>Example with fetch:</p>
<pre><code class="lang-js">fetch(<span class="hljs-string">"http://localhost:8000/api/data"</span>, {
  <span class="hljs-attr">method</span>: <span class="hljs-string">"GET"</span>,
  <span class="hljs-attr">credentials</span>: <span class="hljs-string">"include"</span>,
})
</code></pre>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>CORS can feel like a wall you keep running into when building web apps. But once you get the hang of how it works – and how to set it up in Django – it becomes a small thing you configure and move on.</p>
<p>Just remember:</p>
<ul>
<li><p>Be specific in production</p>
</li>
<li><p>Always restart the server after changes</p>
</li>
<li><p>Don’t ignore warnings in your browser console – they’re your friends</p>
</li>
</ul>
<p>Now you know how to enable CORS in Django the right way.</p>
<h3 id="heading-further-resources">Further Resources</h3>
<ul>
<li><p><a target="_blank" href="https://github.com/adamchainz/django-cors-headers">django-cors-headers GitHub page</a> – for full documentation.</p>
</li>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">MDN CORS Overview</a> – to understand how CORS works under the hood.</p>
</li>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/topics/http/middleware/">Official Django Middleware Docs</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create Models in Your Django Project ]]>
                </title>
                <description>
                    <![CDATA[ If you're building something with Django, there's one thing you can't skip: creating models. Models are the heart of any Django app. They define how your data is structured, how it's stored in the database, and how Django can interact with it. Now, i... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-models-in-your-django-project/</link>
                <guid isPermaLink="false">680be6e3dfe4e9c64b6b0997</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Udemezue John ]]>
                </dc:creator>
                <pubDate>Fri, 25 Apr 2025 19:47:47 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745610452559/e009644b-bfef-4e43-9f1b-5f2e4deebdfa.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you're building something with Django, there's one thing you can't skip: creating models. Models are the heart of any Django app. They define how your data is structured, how it's stored in the database, and how Django can interact with it.</p>
<p>Now, if you're new to Django or still wrapping your head around the basics, don’t worry. I’ve been there too. Models might sound a bit intimidating at first, but they’re pretty straightforward once you see how they work.</p>
<p>I’ll walk you through it all – step by step – so by the end of this post, you’ll not only know how to create models, but also how to use them in real projects.</p>
<p>Let’s get into it.</p>
<h3 id="heading-heres-what-well-cover">Here’s what we’ll cover:</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-a-model-in-django">What is a Model in Django?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-models-in-django">How to Create Models in Django</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-step-1-start-a-django-project-if-you-havent-already">Step 1: Start a Django Project (if you haven’t already)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-define-your-model">Step 2: Define Your Model</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-register-the-app-and-create-the-database">Step 3: Register the App and Create the Database</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-create-and-use-objects">Step 4: Create and Use Objects</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-extra-model-features-that-youll-use">Extra Model Features That You’ll Use</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-default-values">1. Default Values</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-auto-timestamps">2. Auto Timestamps</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-model-meta-options">3. Model Meta Options</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-using-models-in-django-admin">Using Models in Django Admin</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-faqs">FAQs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ol>
<h2 id="heading-what-is-a-model-in-django">What is a Model in Django?</h2>
<p>A model in Django is just a Python class that tells Django how you want your data to look. Django takes care of the hard part (talking to the database), so you can focus on describing your data in simple Python code.</p>
<p>Here’s a quick example of a basic model:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.db <span class="hljs-keyword">import</span> models

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Book</span>(<span class="hljs-params">models.Model</span>):</span>
    title = models.CharField(max_length=<span class="hljs-number">100</span>)
    author = models.CharField(max_length=<span class="hljs-number">50</span>)
    published_date = models.DateField()
    price = models.DecimalField(max_digits=<span class="hljs-number">5</span>, decimal_places=<span class="hljs-number">2</span>)
</code></pre>
<p>Let me break it down:</p>
<ul>
<li><p><code>title</code> and <code>author</code> are just short pieces of text, so I’m using <code>CharField</code>.</p>
</li>
<li><p><code>published_date</code> is a date – easy enough, that’s what <code>DateField</code> is for.</p>
</li>
<li><p><code>price</code> is a number with decimals, so <code>DecimalField</code> does the job.</p>
</li>
</ul>
<p>Each line describes one piece of data I want to store for every book. Simple, right?</p>
<h2 id="heading-how-to-create-models-in-django">How to Create Models in Django</h2>
<h3 id="heading-step-1-start-a-django-project-if-you-havent-already">Step 1: Start a Django Project (if you haven’t already)</h3>
<p>If you’re brand new, first you need a Django project:</p>
<pre><code class="lang-bash">django-admin startproject mysite
<span class="hljs-built_in">cd</span> mysite
python manage.py startapp books
</code></pre>
<p>Now you’ve got a Django app called <code>books</code> where you can put your models.</p>
<h3 id="heading-step-2-define-your-model">Step 2: Define Your Model</h3>
<p>Inside your app folder (<code>books</code>), open <code>models.py</code>. That’s where you’ll define your model.</p>
<p>Here’s a slightly more real-world example:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.db <span class="hljs-keyword">import</span> models

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Author</span>(<span class="hljs-params">models.Model</span>):</span>
    name = models.CharField(max_length=<span class="hljs-number">100</span>)
    birthdate = models.DateField()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__str__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self.name


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Book</span>(<span class="hljs-params">models.Model</span>):</span>
    title = models.CharField(max_length=<span class="hljs-number">200</span>)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    summary = models.TextField()
    isbn = models.CharField(max_length=<span class="hljs-number">13</span>, unique=<span class="hljs-literal">True</span>)
    published = models.DateField()
    price = models.DecimalField(max_digits=<span class="hljs-number">6</span>, decimal_places=<span class="hljs-number">2</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__str__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self.title
</code></pre>
<p>What’s happening here:</p>
<ul>
<li><p>I’ve created two models: <code>Author</code> and <code>Book</code>.</p>
</li>
<li><p><code>Book</code> has a relationship with <code>Author</code> using <code>ForeignKey</code>. That means one author can have many books.</p>
</li>
<li><p>I’m using <code>__str__()</code> to return a nice name when I look at objects in the Django admin.</p>
</li>
</ul>
<h3 id="heading-step-3-register-the-app-and-create-the-database">Step 3: Register the App and Create the Database</h3>
<p>Before Django can use your models, make sure your app is added to the project settings.</p>
<p>Open <code>mysite/settings.py</code> and find the <code>INSTALLED_APPS</code> list. Add <code>'books',</code> to it:</p>
<pre><code class="lang-python">INSTALLED_APPS = [
    <span class="hljs-comment"># other apps</span>
    <span class="hljs-string">'books'</span>,
]
</code></pre>
<p>Now, run migrations to create the database tables for your models:</p>
<pre><code class="lang-bash">python manage.py makemigrations
python manage.py migrate
</code></pre>
<p>This is how Django turns your Python code into actual database tables. The first command makes a migration file (basically, instructions for the database), and the second applies it.</p>
<h3 id="heading-step-4-create-and-use-objects">Step 4: Create and Use Objects</h3>
<p>Now you can use these models in your code. Open the Django shell:</p>
<pre><code class="lang-bash">python manage.py shell
</code></pre>
<p>Then try this out:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> books.models <span class="hljs-keyword">import</span> Author, Book
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> date

<span class="hljs-comment"># Create an author</span>
jane = Author.objects.create(name=<span class="hljs-string">"Jane Austen"</span>, birthdate=date(<span class="hljs-number">1775</span>, <span class="hljs-number">12</span>, <span class="hljs-number">16</span>))

<span class="hljs-comment"># Create a book</span>
book = Book.objects.create(
    title=<span class="hljs-string">"Pride and Prejudice"</span>,
    author=jane,
    summary=<span class="hljs-string">"A novel about manners and marriage in early 19th-century England."</span>,
    isbn=<span class="hljs-string">"1234567890123"</span>,
    published=date(<span class="hljs-number">1813</span>, <span class="hljs-number">1</span>, <span class="hljs-number">28</span>),
    price=<span class="hljs-number">9.99</span>
)

print(book)
</code></pre>
<p>Django will save these to your database automatically.</p>
<h2 id="heading-extra-model-features-that-youll-use">Extra Model Features That You’ll Use</h2>
<h3 id="heading-1-default-values">1. Default Values</h3>
<p>You can give a field a default value:</p>
<pre><code class="lang-python">is_published = models.BooleanField(default=<span class="hljs-literal">False</span>)
</code></pre>
<h3 id="heading-2-auto-timestamps">2. Auto Timestamps</h3>
<p>These are super useful when tracking created or updated times:</p>
<pre><code class="lang-python">created_at = models.DateTimeField(auto_now_add=<span class="hljs-literal">True</span>)
updated_at = models.DateTimeField(auto_now=<span class="hljs-literal">True</span>)
</code></pre>
<h3 id="heading-3-model-meta-options">3. Model Meta Options</h3>
<p>You can add class Meta to customize things like the default ordering:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Book</span>(<span class="hljs-params">models.Model</span>):</span>
    <span class="hljs-comment"># fields...</span>

    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Meta</span>:</span>
        ordering = [<span class="hljs-string">'published'</span>]
</code></pre>
<h2 id="heading-using-models-in-django-admin">Using Models in Django Admin</h2>
<p>Django’s built-in admin panel is one of the best parts of the framework. But your models won’t show up there unless you register them.</p>
<p>In <code>books/admin.py</code>, add:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.contrib <span class="hljs-keyword">import</span> admin
<span class="hljs-keyword">from</span> .models <span class="hljs-keyword">import</span> Author, Book

admin.site.register(Author)
admin.site.register(Book)
</code></pre>
<p>Now run:</p>
<pre><code class="lang-bash">python manage.py createsuperuser
</code></pre>
<p>Then go to <a target="_blank" href="http://127.0.0.1:8000/admin">http://127.0.0.1:8000/admin</a>, log in, and boom – your models are there, with a full interface.</p>
<h2 id="heading-faqs">FAQs</h2>
<h3 id="heading-can-i-change-a-model-after-ive-made-it"><strong>Can I change a model after I’ve made it?</strong></h3>
<p>Yes, but you’ll need to make a new migration:</p>
<pre><code class="lang-bash">python manage.py makemigrations
python manage.py migrate
</code></pre>
<h3 id="heading-what-databases-work-with-django"><strong>What databases work with Django?</strong></h3>
<p>Django works with PostgreSQL, MySQL, SQLite (default), and more. Most people start with SQLite when learning because it's easy and works out of the box.</p>
<h3 id="heading-whats-the-difference-between-charfield-and-textfield"><strong>What’s the difference between CharField and TextField?</strong></h3>
<p>Use <code>CharField</code> for short text with a max length (like a name or title). Use <code>TextField</code> for longer text (like a blog post or summary).</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Once you understand models, the rest of Django starts to click into place. Everything – forms, views, templates – eventually connects back to the model. It's how your app stores and works with real data.</p>
<p>The best way to learn is by building something. Start small, maybe a book catalog, a task manager, or a personal blog. Add models one at a time and play with them in the admin.</p>
<h3 id="heading-further-resources">Further Resources</h3>
<ul>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/topics/db/models/">Official Django Docs – Models</a></p>
</li>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/ref/models/fields/">Django Model Field Reference</a></p>
</li>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django">Simple Django Tutorial – Mozilla Developer Network</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Change Your Django Secret Key (Without Breaking Your App) ]]>
                </title>
                <description>
                    <![CDATA[ If you're working on a Django project, you've probably come across the SECRET_KEY in your settings file. It might seem like just another line of code, but it's one of the most important pieces of your project. SECRET_KEY keeps your app secure by sign... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-change-your-django-secret-key-without-breaking-your-app/</link>
                <guid isPermaLink="false">680b9ed80564f7c150cbf95b</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Udemezue John ]]>
                </dc:creator>
                <pubDate>Fri, 25 Apr 2025 14:40:23 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745592003292/023f4ddd-61d7-4e06-b616-31de7924f6a9.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you're working on a Django project, you've probably come across the <code>SECRET_KEY</code> in your settings file. It might seem like just another line of code, but it's one of the most important pieces of your project.</p>
<p><code>SECRET_KEY</code> keeps your app secure by signing cookies, passwords, and other sensitive data. And if it ever gets exposed or leaked – yeah, that’s a problem.</p>
<p>Changing your Django <code>SECRET_KEY</code> is something you should do carefully. Maybe your key was committed to GitHub (we’ve all been there), or you just want to refresh it for better security.</p>
<p>Whatever the reason, I’ll walk you through how to do it safely without breaking anything. I’ll explain everything in plain English so you’re not left wondering what just happened.</p>
<p>Let’s get into it.</p>
<h2 id="heading-what-is-the-django-secretkey">What Is The Django <code>SECRET_KEY</code>?</h2>
<p>The <code>SECRET_KEY</code> is a long string of random characters stored in your <code>settings.py</code> file. It’s used internally by Django to:</p>
<ul>
<li><p>Securely sign session cookies</p>
</li>
<li><p>Generate password reset tokens</p>
</li>
<li><p>Protect data using cryptographic signing</p>
</li>
</ul>
<p>Here’s what it looks like in your Django project:</p>
<pre><code class="lang-python"><span class="hljs-comment"># settings.py</span>
SECRET_KEY = <span class="hljs-string">'django-insecure-12345supersecretrandomstring'</span>
</code></pre>
<p>If someone gets access to your <code>SECRET_KEY</code>, they could potentially:</p>
<ul>
<li><p>Forge session cookies and impersonate users</p>
</li>
<li><p>Reset passwords or tamper with signed data</p>
</li>
<li><p>Compromise the entire app</p>
</li>
</ul>
<p>So yeah – it’s kind of a big deal.</p>
<h2 id="heading-when-should-you-change-your-django-secret-key">When Should You Change Your Django Secret Key?</h2>
<p>You should change your <code>SECRET_KEY</code> if:</p>
<ul>
<li><p>You accidentally shared it in public code (like GitHub)</p>
</li>
<li><p>It was hardcoded in a file, and you want to switch to environment variables</p>
</li>
<li><p>You’re rotating keys as part of a security policy</p>
</li>
<li><p>You suspect it’s been compromised</p>
</li>
</ul>
<p>Still not sure if it’s necessary? If the key has ever been shared or stored where someone else could access it, just change it.</p>
<h2 id="heading-how-to-change-your-django-secretkey-safely">How to Change Your Django <code>SECRET_KEY</code> Safely</h2>
<h3 id="heading-1-generate-a-new-secret-key">1. <strong>Generate a New Secret Key</strong></h3>
<p>The key needs to be long, random, and secure. Django doesn’t provide a command for this out of the box, but you can generate one using Python.</p>
<p>Here’s a simple script:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.core.management.utils <span class="hljs-keyword">import</span> get_random_secret_key

print(get_random_secret_key())
</code></pre>
<p>To run this:</p>
<ol>
<li><p>Open your terminal</p>
</li>
<li><p>Run the Django shell with <code>python manage.py shell</code></p>
</li>
<li><p>Paste in the script</p>
</li>
</ol>
<p>It’ll return something like:</p>
<pre><code class="lang-python">x3%<span class="hljs-number">6</span>kn$mlg58+<span class="hljs-keyword">as</span>!rcvnmvd8%(<span class="hljs-number">2</span>p!p<span class="hljs-comment">#&amp;yk@r)+tdlj*w9kx!5gx</span>
</code></pre>
<p>Copy this. You’ll need it in a second.</p>
<h3 id="heading-2-store-the-key-securely-dont-hardcode-it">2. <strong>Store the Key Securely (Don’t Hardcode It)</strong></h3>
<p>Instead of pasting it into <code>settings.py</code>, it’s better to use an environment variable. That way, you don’t risk exposing it if you ever share your code.</p>
<p>Here’s how:</p>
<ol>
<li>Open your <code>.env</code> file (create one if it doesn’t exist):</li>
</ol>
<pre><code class="lang-python"><span class="hljs-comment"># .env</span>
SECRET_KEY=<span class="hljs-string">'x3%6kn$mlg58+as!rcvnmvd8%(2p!p#&amp;yk@r)+tdlj*w9kx!5gx'</span>
</code></pre>
<ol start="2">
<li>Install <code>python-decouple</code> if you haven’t already:</li>
</ol>
<pre><code class="lang-bash">pip install python-decouple
</code></pre>
<ol start="3">
<li>Update your <code>settings.py</code>:</li>
</ol>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> decouple <span class="hljs-keyword">import</span> config

SECRET_KEY = config(<span class="hljs-string">'SECRET_KEY'</span>)
</code></pre>
<p>Now your key is stored outside your code. Much safer.</p>
<h3 id="heading-3-commit-carefully">3. <strong>Commit Carefully</strong></h3>
<p>Make sure:</p>
<ul>
<li><p>Your <code>.env</code> file is added to <code>.gitignore</code></p>
</li>
<li><p>You never push it to your repository</p>
</li>
</ul>
<p>Here’s how <code>.gitignore</code> should look:</p>
<pre><code class="lang-python"><span class="hljs-comment"># .gitignore</span>
.env
</code></pre>
<p>You’d be surprised how often <code>.env</code> files get pushed by accident. Always double-check before you commit.</p>
<h3 id="heading-4-restart-your-app">4. <strong>Restart Your App</strong></h3>
<p>After changing the key, restart your server. If you’re using a platform like Heroku or Docker, make sure you update the <code>SECRET_KEY</code> in your environment variables dashboard.</p>
<p>For Heroku:</p>
<pre><code class="lang-bash">heroku config:<span class="hljs-built_in">set</span> SECRET_KEY=<span class="hljs-string">'your-new-key'</span>
</code></pre>
<p>For Docker:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># docker-compose.yml</span>
<span class="hljs-attr">environment:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">SECRET_KEY=your-new-key</span>
</code></pre>
<h3 id="heading-5-re-log-in-and-ask-users-to-do-the-same">5. <strong>Re-Log In (and Ask Users To Do the Same)</strong></h3>
<p>Changing the secret key invalidates all old sessions. So, everyone (including you) will be logged out. This is expected behaviour. If you're running a public site, it’s a good idea to notify users in advance.</p>
<h2 id="heading-what-happens-if-you-dont-change-it">What Happens If You Don't Change It?</h2>
<p>If your key is compromised, attackers can:</p>
<ul>
<li><p>Forge data</p>
</li>
<li><p>Hijack accounts</p>
</li>
<li><p>Break authentication systems</p>
</li>
</ul>
<p>It’s not just about best practices. It’s about real-world security.</p>
<h2 id="heading-faqs">FAQs</h2>
<h3 id="heading-will-this-break-my-app">Will this break my app?</h3>
<p>No, as long as you restart your app and store the key properly, everything will work fine. Just remember: all users will be logged out.</p>
<h3 id="heading-can-i-use-the-same-key-for-multiple-projects">Can I use the same key for multiple projects?</h3>
<p>Nope. Each project should have its unique secret key.</p>
<h3 id="heading-can-i-rotate-the-key-regularly">Can I rotate the key regularly?</h3>
<p>Yes, just be mindful that changing it too often will log users out repeatedly.</p>
<h3 id="heading-i-forgot-to-add-env-to-gitignore-what-now">I forgot to add <code>.env</code> to <code>.gitignore</code>. What now?</h3>
<p>Regenerate the key, update your project, and make sure the new <code>.env</code> file isn’t tracked.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Changing your Django <code>SECRET_KEY</code> might feel intimidating the first time, but it’s pretty simple when you break it down. As long as you generate a secure key, store it safely, and don’t expose it publicly, you’re doing great.</p>
<p>One last thing—<strong>when was the last time you checked if your secret key was accidentally pushed to GitHub?</strong> It might be a good time to take a quick look.</p>
<h3 id="heading-helpful-resources">Helpful Resources</h3>
<ul>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/ref/settings/#std-setting-SECRET_KEY">Django Docs – SECRET_KEY</a></p>
</li>
<li><p><a target="_blank" href="https://www.gitguardian.com/">GitGuardian – Secrets Detection</a></p>
</li>
<li><p><a target="_blank" href="https://12factor.net/config">12 Factor App – Config</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/henriquebastos/python-decouple">Python-Decouple GitHub</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What Is Q in Django? (And Why It's Super Useful) ]]>
                </title>
                <description>
                    <![CDATA[ If you're working with Django and writing queries, chances are you’ve bumped into a situation where you need to combine filters in a way that’s just... not straightforward. Maybe you're trying to search for users with a username or an email that matc... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-q-in-django-and-why-its-super-useful/</link>
                <guid isPermaLink="false">680a4adad2926105c84b08ae</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Udemezue John ]]>
                </dc:creator>
                <pubDate>Thu, 24 Apr 2025 14:29:46 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745504806983/58c92bf9-b2e6-486a-8722-d364decc5d1a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you're working with Django and writing queries, chances are you’ve bumped into a situation where you need to combine filters in a way that’s just... not straightforward.</p>
<p>Maybe you're trying to search for users with a username <em>or</em> an email that matches something. Or maybe you're trying to filter results where one condition is true <em>but</em> another is false.</p>
<p>That’s where <code>Q</code> comes in.</p>
<p>I remember the first time I ran into this problem – trying to use <code>or</code> in a <code>.filter()</code> and realizing quickly that regular Python logic doesn’t play nice there.</p>
<p>The error messages were confusing, and the docs didn’t help much. So let me break it down for you in a simple, practical way.</p>
<p>By the end of this guide, you’ll understand exactly what <code>Q</code> is, how it works, and how it can make your Django queries cleaner, more powerful, and a lot more flexible.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-whats-q-all-about">What's Q All About?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-q-in-django">How to Use Q in Django</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-example-1-or-logic">Example 1: OR Logic</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-example-2-and-logic-still-useful-with-q">Example 2: AND Logic (Still Useful with Q)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-example-3-not-logic">Example 3: NOT Logic</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-when-should-you-use-q">When Should You Use Q?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-mixing-q-and-regular-filters">Mixing Q and Regular Filters</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-real-world-example-filtering-products">Real-World Example: Filtering Products</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-gotchas-things-to-watch-out-for">Gotchas (Things To Watch Out For)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-frequently-asked-questions">Frequently Asked Questions</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-is-using-q-slower-than-a-regular-filter">Is using Q slower than a regular filter()?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-can-i-use-q-with-annotate-or-aggregate">Can I use Q with annotate() or aggregate()?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-can-i-build-q-objects-dynamically">Can I build Q objects dynamically?</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-further-resources">Further Resources</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-whats-q-all-about">What's Q All About?</h2>
<p>In Django, the <code>Q</code> object (from <code>django.db.models</code>) lets you build complex queries using <strong>OR</strong>, <strong>AND</strong>, and <strong>NOT</strong> logic – something that’s hard to do using just regular <code>.filter()</code> calls.</p>
<p>Normally, when you use <code>.filter()</code> in Django, it adds AND logic like this:</p>
<pre><code class="lang-python">MyModel.objects.filter(name=<span class="hljs-string">'Alice'</span>, age=<span class="hljs-number">30</span>)
</code></pre>
<p>This will get all rows where the <code>name</code> is <code>'Alice'</code> <em>and</em> the <code>age</code> is <code>30</code>. But what if you want:</p>
<blockquote>
<p>Get all rows where name is 'Alice' <strong>or</strong> age is 30?</p>
</blockquote>
<p>You can’t just do this:</p>
<pre><code class="lang-python">MyModel.objects.filter(name=<span class="hljs-string">'Alice'</span> <span class="hljs-keyword">or</span> age=<span class="hljs-number">30</span>)  <span class="hljs-comment"># ❌ This won't work!</span>
</code></pre>
<p>That’s where <code>Q</code> comes in.</p>
<h2 id="heading-how-to-use-q-in-django">How to Use Q in Django</h2>
<p>Here’s the basic import:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.db.models <span class="hljs-keyword">import</span> Q
</code></pre>
<p>Now, you can use <code>Q</code> to create conditions and combine them using the <code>|</code> (OR), <code>&amp;</code> (AND), and <code>~</code> (NOT) operators.</p>
<p>Let’s say you have a model like this:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.db <span class="hljs-keyword">import</span> models

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Person</span>(<span class="hljs-params">models.Model</span>):</span>
    name = models.CharField(max_length=<span class="hljs-number">100</span>)
    age = models.IntegerField()
    city = models.CharField(max_length=<span class="hljs-number">100</span>)
</code></pre>
<h3 id="heading-example-1-or-logic">Example 1: OR Logic</h3>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.db.models <span class="hljs-keyword">import</span> Q

people = Person.objects.filter(Q(name=<span class="hljs-string">'Alice'</span>) | Q(age=<span class="hljs-number">30</span>))
</code></pre>
<p>This will return anyone whose name is 'Alice' <strong>or</strong> whose age is 30. That’s clean and readable, right?</p>
<h3 id="heading-example-2-and-logic-still-useful-with-q">Example 2: AND Logic (Still Useful with Q)</h3>
<pre><code class="lang-python">people = Person.objects.filter(Q(name=<span class="hljs-string">'Alice'</span>) &amp; Q(age=<span class="hljs-number">30</span>))
</code></pre>
<p>This will return people where <strong>both</strong> conditions are true. Technically, this gives the same result as using:</p>
<pre><code class="lang-python">Person.objects.filter(name=<span class="hljs-string">'Alice'</span>, age=<span class="hljs-number">30</span>)
</code></pre>
<p>So why bother with Q here?</p>
<p>The real power of <code>Q</code> with <code>AND</code> is when you start <strong>nesting more complex conditions</strong>. For instance, suppose you want to find people who are named Alice and either live in Paris or are under 25. Here’s how you could write that:</p>
<pre><code class="lang-python">people = Person.objects.filter(
    Q(name=<span class="hljs-string">'Alice'</span>) &amp; (Q(city=<span class="hljs-string">'Paris'</span>) | Q(age__lt=<span class="hljs-number">25</span>))
)
</code></pre>
<p>Without <code>Q</code>, this logic would be hard (and messy) to express. <code>Q</code> lets you group conditions logically and write flexible, readable queries.</p>
<h3 id="heading-example-3-not-logic">Example 3: NOT Logic</h3>
<p>What if you want everyone <em>except</em> people named Alice?</p>
<pre><code class="lang-python">people = Person.objects.filter(~Q(name=<span class="hljs-string">'Alice'</span>))
</code></pre>
<p>The <code>~</code> operator flips the condition – it's saying “not this”.</p>
<h2 id="heading-when-should-you-use-q">When Should You Use Q?</h2>
<p>You can reach for <code>Q</code> when:</p>
<ul>
<li><p>You need OR conditions</p>
</li>
<li><p>You want to combine filters dynamically (for example, building a query based on user input)</p>
</li>
<li><p>You need to write complex conditional logic</p>
</li>
<li><p>You want to exclude certain things using <code>~Q(...)</code></p>
</li>
</ul>
<h3 id="heading-but-are-there-times-you-shouldnt-use-q"><strong>But are there times you shouldn't use Q?</strong></h3>
<p>Yes – if you're writing a straightforward filter with only AND logic (like <code>name='Alice'</code> and <code>age=30</code>), using <code>Q</code> doesn't add much value. It can make your code unnecessarily verbose. Stick with plain <code>.filter()</code> unless you need more flexibility.</p>
<h2 id="heading-mixing-q-and-regular-filters">Mixing Q and Regular Filters</h2>
<p>You can mix <code>Q</code> objects with normal keyword arguments in a filter. Just be careful with parentheses and order.</p>
<pre><code class="lang-python">Person.objects.filter(Q(name=<span class="hljs-string">'Alice'</span>) | Q(city=<span class="hljs-string">'Paris'</span>), age__gte=<span class="hljs-number">25</span>)
</code></pre>
<p>This translates to:</p>
<p><strong>(name = 'Alice' OR city = 'Paris') AND age &gt;= 25</strong></p>
<p>But here’s where <strong>parentheses</strong> make a big difference.</p>
<p>Take this incorrect example:</p>
<pre><code class="lang-python">Person.objects.filter(Q(name=<span class="hljs-string">'Alice'</span>) | Q(city=<span class="hljs-string">'Paris'</span>) &amp; Q(age__gte=<span class="hljs-number">25</span>))
</code></pre>
<p>Due to operator precedence, this will evaluate as:</p>
<p><strong>name = 'Alice' OR (city = 'Paris' AND age &gt;= 25)</strong></p>
<p>Which is not what you probably intended!</p>
<p>So when in doubt, <strong>use parentheses</strong> to clearly define your logic:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Correct: (name = 'Alice' OR city = 'Paris') AND age &gt;= 25</span>
Person.objects.filter((Q(name=<span class="hljs-string">'Alice'</span>) | Q(city=<span class="hljs-string">'Paris'</span>)) &amp; Q(age__gte=<span class="hljs-number">25</span>))
</code></pre>
<h2 id="heading-real-world-example-filtering-products">Real-World Example: Filtering Products</h2>
<p>Say you’ve got a <code>Product</code> model with <code>price</code>, <code>in_stock</code>, and <code>category</code>.</p>
<p>You want all products that are either:</p>
<ul>
<li><p>cheaper than $20 and in stock<br>  <strong>or</strong></p>
</li>
<li><p>In the 'Books' category</p>
</li>
</ul>
<p>Here’s how that might look:</p>
<pre><code class="lang-python">Product.objects.filter(
    (Q(price__lt=<span class="hljs-number">20</span>) &amp; Q(in_stock=<span class="hljs-literal">True</span>)) | Q(category=<span class="hljs-string">'Books'</span>)
)
</code></pre>
<p>Without <code>Q</code>You’d have to write separate queries and merge them, or use more complicated logic. This way is faster and more efficient.</p>
<h2 id="heading-things-to-watch-out-for">Things to Watch Out For</h2>
<ul>
<li><p><strong>Use parentheses</strong>: Just like in math, they control how things combine. Don’t trust default operator precedence unless you know it well.</p>
</li>
<li><p><strong>Don’t use</strong> <code>or</code>/<code>and</code> keywords: Python’s logical operators don’t work with Django ORM queries. Use <code>|</code> and <code>&amp;</code> instead.</p>
</li>
<li><p><strong>Mixing</strong> <code>Q</code> with <code>.exclude()</code>? Be extra careful. Why? Because <code>.exclude()</code> <strong>inverts</strong> the logic of the entire filter. That means if you write:</p>
<pre><code class="lang-python">  Person.objects.exclude(Q(name=<span class="hljs-string">'Alice'</span>) &amp; Q(city=<span class="hljs-string">'Paris'</span>))
</code></pre>
<p>  It’s saying: <strong>Exclude anyone who is named Alice <em>and</em> lives in Paris.</strong></p>
<p>  But what if you wrote:</p>
<pre><code class="lang-python">  Person.objects.exclude(Q(name=<span class="hljs-string">'Alice'</span>) | Q(city=<span class="hljs-string">'Paris'</span>))
</code></pre>
<p>  Now it excludes anyone named Alice <strong>or</strong> who lives in Paris – a much broader exclusion! So always double-check what you're excluding.</p>
</li>
<li><p>You might need to invert specific parts of your logic using <code>~Q(...)</code> <strong>before</strong> passing them to <code>.exclude()</code> Rather than excluding the whole expression.</p>
</li>
</ul>
<h2 id="heading-frequently-asked-questions">Frequently Asked Questions</h2>
<h3 id="heading-is-using-q-slower-than-a-regular-filter"><strong>Is using Q slower than a regular</strong> <code>filter()</code><strong>?</strong></h3>
<p>Nope! Under the hood, Django converts your query into optimized SQL. Whether you use <code>filter(name='Alice')</code> or <code>filter(Q(name='Alice'))</code>Performance is almost the same. What matters more is <em>how complex</em> your query is.</p>
<h3 id="heading-can-i-use-q-with-annotate-or-aggregate"><strong>Can I use Q with</strong> <code>annotate()</code> <strong>or</strong> <code>aggregate()</code><strong>?</strong></h3>
<p>Yep. You can use <code>Q</code> with <code>annotate()</code> to apply conditional logic for things like counting or filtering within annotations.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.db.models <span class="hljs-keyword">import</span> Count

<span class="hljs-comment"># Count users with more than one blog post</span>
User.objects.annotate(
    post_count=Count(<span class="hljs-string">'posts'</span>, filter=Q(posts__published=<span class="hljs-literal">True</span>))
)
</code></pre>
<h3 id="heading-can-i-build-q-objects-dynamically"><strong>Can I build Q objects dynamically?</strong></h3>
<p>Absolutely. That’s one of the best parts! You can build up a list of <code>Q()</code> objects and combine them however you want:</p>
<pre><code class="lang-python">filters = Q()
<span class="hljs-keyword">if</span> search_name:
    filters |= Q(name__icontains=search_name)
<span class="hljs-keyword">if</span> search_city:
    filters |= Q(city__icontains=search_city)

results = Person.objects.filter(filters)
</code></pre>
<p>This is especially useful for search forms or APIS where users can pass different combinations of filters.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>So that’s <code>Q</code> In Django. It’s not some scary, abstract concept – it’s just a powerful way to control how your queries behave.</p>
<p>Once you get used to using <code>Q</code>, your code becomes cleaner, easier to read, and more flexible when handling complex filters.</p>
<p>Honestly, I can’t imagine writing Django queries without it anymore.</p>
<h3 id="heading-further-resources">Further Resources</h3>
<p>Want to go deeper?</p>
<ul>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/topics/db/queries/#complex-lookups-with-q-objects">Django Q Object Official Docs</a></p>
</li>
<li><p><a target="_blank" href="https://realpython.com/tutorials/django/">Real Python's Guide to Q Objects</a></p>
</li>
<li><p><a target="_blank" href="https://books.agiliq.com/projects/django-orm-cookbook/en/latest/q_objects.html">Django ORM Cookbook</a> – solid practical examples</p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Change the Password of a Superuser in Django ]]>
                </title>
                <description>
                    <![CDATA[ Changing a superuser password in Django might sound like a big task, but it’s one of the easiest things to do once you know how. If you’re working on a Django project – whether it’s a hobby blog, a client’s website, or a bigger web application – mana... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-change-the-password-of-a-superuser-in-django/</link>
                <guid isPermaLink="false">6808f23b332c138efcea040d</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Udemezue John ]]>
                </dc:creator>
                <pubDate>Wed, 23 Apr 2025 13:59:23 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745416742960/89cce35f-2e91-4329-8fea-0d1551bea2c7.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Changing a superuser password in Django might sound like a big task, but it’s one of the easiest things to do once you know how.</p>
<p>If you’re working on a Django project – whether it’s a hobby blog, a client’s website, or a bigger web application – managing your admin accounts safely is a must.</p>
<p>And one key part of that? Making sure your superuser password is strong, secure, and easy for <em>you</em> to update.</p>
<p>You might be doing this because you forgot the old password, you're handing the project off to someone else, or you're tightening security after a team change.</p>
<p>Whatever your reason is, this guide will walk you through the easiest and safest ways to change a superuser password in Django.</p>
<p>I’ll break everything down in simple language, no heavy tech lingo or assumptions.</p>
<p>Let’s dive in.</p>
<h3 id="heading-what-well-cover">What we’ll cover:</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-changing-the-superuser-password-matters">Why Changing the Superuser Password Matters</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-simple-ways-to-change-a-django-superuser-password">3 Simple Ways to Change a Django Superuser Password</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-method-1-use-djangos-built-in-command">Method 1: Use Django’s Built-In Command</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-method-2-use-the-django-shell">Method 2: Use the Django Shell</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-method-3-use-django-admin-if-youre-logged-in">Method 3: Use Django Admin (If You’re Logged In)</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-bonus-forgot-your-superuser-username">Bonus: Forgot Your Superuser Username?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-faqs">FAQs</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-if-i-forgot-both-the-username-and-password">What if I forgot both the username and password?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-will-this-log-out-other-users">Will this log out other users?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-can-i-change-the-password-from-the-database-directly">Can I change the password from the database directly?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-do-i-know-if-my-new-password-is-secure">How do I know if my new password is secure?</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-further-reading-and-tools">Further Reading and Tools</a></p>
</li>
</ul>
<h2 id="heading-why-changing-the-superuser-password-matters">Why Changing the Superuser Password Matters</h2>
<p>Your Django superuser has full access to the admin dashboard. This means they can add or delete users, edit data, manage settings – everything. If that account gets compromised, the whole site is at risk.</p>
<p>Here’s what could go wrong if the password is weak or outdated:</p>
<ul>
<li><p>Someone could delete your database.</p>
</li>
<li><p>A hacker could inject malicious data.</p>
</li>
<li><p>Private user info could be exposed.</p>
</li>
</ul>
<p>According to <a target="_blank" href="https://www.verizon.com/business/en-sg/resources/articles/analyzing-covid-19-data-breach-landscape/">Verizon’s Data Breach Investigations Report</a>, over <strong>80%</strong> of hacking-related breaches are due to compromised or weak passwords. That’s a huge risk for something that’s easy to fix in a few minutes.</p>
<p>So let’s make sure your Django admin is locked down tight – without breaking anything.</p>
<h2 id="heading-3-simple-ways-to-change-a-django-superuser-password">3 Simple Ways to Change a Django Superuser Password</h2>
<p>I’ll show you three different ways to update your superuser password. You only need to pick one that fits your current setup.</p>
<h3 id="heading-method-1-use-djangos-built-in-command">Method 1: Use Django’s Built-In Command</h3>
<p>If you have access to the command line and your project’s virtual environment, this is the cleanest way.</p>
<h4 id="heading-activate-your-virtual-environment"><strong>Activate your virtual environment</strong></h4>
<p>This depends on your setup, but if you're using <code>venv</code> it might look like this:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> venv/bin/activate
</code></pre>
<p>Or on Windows:</p>
<pre><code class="lang-python">venv\Scripts\activate
</code></pre>
<h4 id="heading-navigate-to-your-project-folder"><strong>Navigate to your project folder</strong></h4>
<p>This is where <code>manage.py</code> lives:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> your_project_folder
</code></pre>
<h4 id="heading-run-the-following-command"><strong>Run the following command:</strong></h4>
<pre><code class="lang-bash">python manage.py changepassword your_superuser_username
</code></pre>
<p>Example:</p>
<pre><code class="lang-bash">python manage.py changepassword admin
</code></pre>
<p>Django will then ask you to enter a new password. Type it in, hit enter, confirm it again, and you’re done.</p>
<p>That’s it. You just changed your superuser password!</p>
<h3 id="heading-method-2-use-the-django-shell">Method 2: Use the Django Shell</h3>
<p>Maybe you don’t remember the username or want more control. The Django shell lets you interact directly with your database using Python.</p>
<p>Here’s how:</p>
<p>First, open the shell:</p>
<pre><code class="lang-bash">python manage.py shell
</code></pre>
<p>Then run the following code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.contrib.auth <span class="hljs-keyword">import</span> get_user_model

User = get_user_model()

user = User.objects.get(username=<span class="hljs-string">"admin"</span>)  <span class="hljs-comment"># Replace 'admin' with your username</span>
user.set_password(<span class="hljs-string">"new_secure_password"</span>)   <span class="hljs-comment"># Replace with your new password</span>
user.save()
</code></pre>
<p>Now exit the shell:</p>
<pre><code class="lang-python">exit()
</code></pre>
<p>That’s it. This method is especially helpful if you’re working in a staging environment or doing things programmatically.</p>
<h3 id="heading-method-3-use-django-admin-if-youre-logged-in">Method 3: Use Django Admin (If You’re Logged In)</h3>
<p>This one only works if you can still log in with the current superuser account.</p>
<ol>
<li><p>Go to your Django admin page, usually at <code>http://127.0.0.1:8000/admin/</code>.</p>
</li>
<li><p>Log in with your current credentials.</p>
</li>
<li><p>Click on <strong>Users</strong>.</p>
</li>
<li><p>Find your superuser account and click on it.</p>
</li>
<li><p>Scroll down to the “Password” section and click <strong>"this form"</strong> under the "Raw passwords are not stored..." message.</p>
</li>
<li><p>Enter your new password twice and save.</p>
</li>
</ol>
<p>This method is super quick and doesn’t require any code at all.</p>
<h2 id="heading-bonus-forgot-your-superuser-username">Bonus: Forgot Your Superuser Username?</h2>
<p>If you don’t remember the exact username of your superuser, no worries. You can list all users like this:</p>
<pre><code class="lang-bash">python manage.py shell
</code></pre>
<p>Then:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.contrib.auth <span class="hljs-keyword">import</span> get_user_model

User = get_user_model()

<span class="hljs-keyword">for</span> user <span class="hljs-keyword">in</span> User.objects.all():
    print(user.username)
</code></pre>
<p>This will print out all usernames in your system, including your superuser.</p>
<h2 id="heading-faqs">FAQs</h2>
<h3 id="heading-what-if-i-forgot-both-the-username-and-password"><strong>What if I forgot both the username and password?</strong></h3>
<p>Use the shell method above to list all usernames, then reset it using either the shell or the <code>changepassword</code> command.</p>
<h3 id="heading-will-this-log-out-other-users"><strong>Will this log out other users?</strong></h3>
<p>Changing your superuser password won’t affect other users unless you have custom logic tied to sessions. For most projects, everything else keeps running just fine.</p>
<h3 id="heading-can-i-change-the-password-from-the-database-directly"><strong>Can I change the password from the database directly?</strong></h3>
<p>Technically yes, but <strong>don’t do it</strong>. Passwords in Django are hashed using PBKDF2 by default. If you enter something manually in the database, it won’t work unless it's hashed the right way. Always use the Django shell or admin panel instead.</p>
<h3 id="heading-how-do-i-know-if-my-new-password-is-secure"><strong>How do I know if my new password is secure?</strong></h3>
<p>Django checks password strength by default. But if you want to be extra safe, use a tool like <a target="_blank" href="https://bitwarden.com/password-generator/">Bitwarden Password Generator</a> or <a target="_blank" href="https://1password.com/password-generator/">1Password’s Generator</a>.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>That’s pretty much everything you need to know to change your superuser password in Django. It’s quick, safe, and once you’ve done it once, it’ll be second nature.</p>
<p>It’s small actions like this that go a long way in keeping your Django projects secure. And since it only takes a minute or two, there’s no reason to put it off.</p>
<p>Let’s keep the conversation going, Connect with me on <a target="_blank" href="http://X.com/_udemezue">x.com/_udemezue</a></p>
<h3 id="heading-further-reading-and-tools">Further Reading and Tools</h3>
<ul>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/ref/django-admin/#changepassword">Official Django change password documentation</a></p>
</li>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/topics/auth/passwords/">How Django stores passwords securely</a></p>
</li>
<li><p><a target="_blank" href="https://owasp.org/www-community/vulnerabilities/Using_Insufficiently_Random_Values">PBKDF2 explained on OWASP</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use a Foreign Key in Django ]]>
                </title>
                <description>
                    <![CDATA[ When you're building something in Django – whether it's a blog, a to-do app, or even something way more complex – at some point, you'll want to connect different pieces of data. That’s where ForeignKey comes in. It helps link one model to another, li... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-a-foreign-key-in-django/</link>
                <guid isPermaLink="false">6807a227b77713cc8e0f6059</guid>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Udemezue John ]]>
                </dc:creator>
                <pubDate>Tue, 22 Apr 2025 14:05:27 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745330646960/e2fc7f1d-73f9-4e25-b870-e0928833e7a5.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you're building something in Django – whether it's a blog, a to-do app, or even something way more complex – at some point, you'll want to connect different pieces of data.</p>
<p>That’s where <code>ForeignKey</code> comes in. It helps link one model to another, like tying a comment to a post, or an order to a customer.</p>
<p>It’s one of those things in Django that can seem confusing at first, but once it clicks, you’ll wonder how you ever built apps without it.</p>
<p>So let’s break it all down. I’ll walk you through everything from what a ForeignKey is, to how to use it in your Django project.</p>
<h2 id="heading-heres-what-well-cover">Here’s what we’ll cover:</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-a-foreign-key-in-django">What is a Foreign Key in Django?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-use-a-foreignkey-instead-of-storing-ids-manually">Why Use a ForeignKey Instead of Storing IDs Manually?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-real-world-example-blog-posts-and-comments">Real-World Example: Blog Posts and Comments</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-ondelete-and-why-does-it-matter">What is on_delete and Why Does It Matter?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-access-related-objects-in-django">How to Access Related Objects in Django</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-foreign-key-relationships-in-django-admin">How to Create Foreign Key Relationships in Django Admin</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-what-happens-in-the-database">What Happens in the Database?</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-query-with-foreignkey">How to Query with ForeignKey</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-get-all-comments-for-a-post-with-id1">Get all comments for a post with id=1:</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-get-all-posts-that-have-at-least-one-comment-by-a-specific-user">Get all posts that have at least one comment by a specific user:</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-faqs">FAQs</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-can-a-foreignkey-be-optional">Can a ForeignKey be optional?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-can-a-foreignkey-point-to-the-same-model-self">Can a ForeignKey point to the same model (self)?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-can-a-model-have-more-than-one-foreignkey">Can a model have more than one ForeignKey?</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-further-resources">Further Resources</a></li>
</ul>
</li>
</ul>
<h2 id="heading-what-is-a-foreign-key-in-django">What is a Foreign Key in Django?</h2>
<p>In the simplest terms, a Foreign Key in Django creates a many-to-one relationship between two models. This means many rows in one table can be related to a single row in another.</p>
<p>For example:</p>
<ul>
<li><p>One blog post can have <strong>many comments</strong></p>
</li>
<li><p>One customer can have <strong>many orders</strong></p>
</li>
<li><p>One author can write <strong>many books</strong></p>
</li>
</ul>
<p>If you're coming from a spreadsheet background, think of it like linking two sheets using a shared column. In Django, you do this in your model definitions.</p>
<h2 id="heading-why-use-a-foreignkey-instead-of-storing-ids-manually">Why Use a ForeignKey Instead of Storing IDs Manually?</h2>
<p>You might be wondering, “Why not just store the ID of the related object in a plain integer field?”</p>
<p>Well, you could – but you'd lose a ton of power. Without a ForeignKey:</p>
<ul>
<li><p>You don’t get automatic validation that the related object exists.</p>
</li>
<li><p>You can't follow relationships easily in queries (for example, <code>post.comments.all()</code> wouldn’t be possible).</p>
</li>
<li><p>The Django admin can’t provide dropdowns or inline forms for related data.</p>
</li>
<li><p>You lose out on helpful features like <code>on_delete</code> behaviour and related object naming.</p>
</li>
</ul>
<p>ForeignKey fields automate and enforce these relationships, making your code cleaner, more secure, and easier to maintain.</p>
<h2 id="heading-real-world-example-blog-posts-and-comments">Real-World Example: Blog Posts and Comments</h2>
<p>Let’s say you’re building a blog. You’ll probably have a <code>Post</code> model and a <code>Comment</code> model. Each comment needs to be linked to a specific post.</p>
<p>Here’s how that looks in code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.db <span class="hljs-keyword">import</span> models

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Post</span>(<span class="hljs-params">models.Model</span>):</span>
    title = models.CharField(max_length=<span class="hljs-number">200</span>)
    content = models.TextField()
    published_at = models.DateTimeField(auto_now_add=<span class="hljs-literal">True</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__str__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self.title

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Comment</span>(<span class="hljs-params">models.Model</span>):</span>
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
    author = models.CharField(max_length=<span class="hljs-number">100</span>)
    text = models.TextField()
    created_at = models.DateTimeField(auto_now_add=<span class="hljs-literal">True</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__str__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f'Comment by <span class="hljs-subst">{self.author}</span>'</span>
</code></pre>
<p>Let me explain each part of that:</p>
<ul>
<li><p><code>models.ForeignKey(Post, on_delete=models.CASCADE)</code>: This line creates the connection. It means each comment is linked to one post. The <code>on_delete=models.CASCADE</code> part tells Django to delete all related comments if a post is deleted.</p>
</li>
<li><p><code>__str__</code> methods just make it easier to read things in the admin or shell.</p>
</li>
</ul>
<h2 id="heading-what-is-ondelete-and-why-does-it-matter">What is <code>on_delete</code> and Why Does It Matter?</h2>
<p>When you create a ForeignKey in Django, you have to include an <code>on_delete</code> argument. This controls what happens to the child rows (like comments) if the parent row (like a blog post) is deleted.</p>
<p>Here are the common options:</p>
<ul>
<li><p><code>models.CASCADE</code>: Delete the child rows, too (like deleting all comments when a post is deleted).</p>
</li>
<li><p><code>models.PROTECT</code>: Prevent deletion if there are related objects.</p>
</li>
<li><p><code>models.SET_NULL</code>: Set the ForeignKey to <code>NULL</code> instead of deleting.</p>
</li>
<li><p><code>models.SET_DEFAULT</code>: Set a default value.</p>
</li>
<li><p><code>models.DO_NOTHING</code>: Do nothing (not recommended unless you really know what you're doing).</p>
</li>
</ul>
<p>I usually go with <code>CASCADE</code> for simple apps, but it's worth thinking through depending on the situation.</p>
<h2 id="heading-how-to-access-related-objects-in-django">How to Access Related Objects in Django</h2>
<p>Once you’ve set up the ForeignKey, Django gives you a few nice tools to work with related data.</p>
<p>For example, let’s say you have a post object:</p>
<pre><code class="lang-python">post = Post.objects.get(id=<span class="hljs-number">1</span>)
</code></pre>
<p>You can get all comments for that post like this:</p>
<pre><code class="lang-python">comments = post.comment_set.all()
</code></pre>
<p>The <code>comment_set</code> is automatically created by Django, and you can customize the name with <code>related_name</code>:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Comment</span>(<span class="hljs-params">models.Model</span>):</span>
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name=<span class="hljs-string">'comments'</span>)
</code></pre>
<p>Now you can do:</p>
<pre><code class="lang-python">post.comments.all()
</code></pre>
<p>Much cleaner, right?</p>
<h2 id="heading-how-to-create-foreign-key-relationships-in-django-admin">How to Create Foreign Key Relationships in Django Admin</h2>
<p>The Django admin handles ForeignKeys well. If you’ve got both <code>Post</code> and <code>Comment</code> models registered in <code>admin.py</code>, you’ll get a dropdown in the comment form to select the post it belongs to.</p>
<p>You can also make inline forms, so you can add comments while editing a post. Here’s a quick example:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.contrib <span class="hljs-keyword">import</span> admin
<span class="hljs-keyword">from</span> .models <span class="hljs-keyword">import</span> Post, Comment

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CommentInline</span>(<span class="hljs-params">admin.TabularInline</span>):</span>
    model = Comment
    extra = <span class="hljs-number">1</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PostAdmin</span>(<span class="hljs-params">admin.ModelAdmin</span>):</span>
    inlines = [CommentInline]

admin.site.register(Post, PostAdmin)
admin.site.register(Comment)
</code></pre>
<p>Now when you're editing a post, you can add or edit comments right there on the same page.</p>
<h3 id="heading-what-happens-in-the-database">What Happens in the Database?</h3>
<p>Django uses a relational database (like PostgreSQL, MySQL, or SQLite), and ForeignKey creates a column in the database table that holds the ID of the related object.</p>
<p>If you run <code>python manage.py makemigrations</code> and then <code>python manage.py migrate</code>, Django will create the actual database tables with the proper relationships behind the scenes.</p>
<h2 id="heading-how-to-query-with-foreignkey">How to Query with ForeignKey</h2>
<p>You can also filter or search based on ForeignKey relationships:</p>
<h3 id="heading-get-all-comments-for-a-post-with-id1">Get all comments for a post with id=1:</h3>
<pre><code class="lang-python">Comment.objects.filter(post_id=<span class="hljs-number">1</span>)
</code></pre>
<p>Or, using the post object:</p>
<pre><code class="lang-python">post = Post.objects.get(id=<span class="hljs-number">1</span>)
comments = Comment.objects.filter(post=post)
</code></pre>
<h3 id="heading-get-all-posts-that-have-at-least-one-comment-by-a-specific-user">Get all posts that have at least one comment by a specific user:</h3>
<pre><code class="lang-python">Post.objects.filter(comments__author=<span class="hljs-string">'John'</span>)
</code></pre>
<p>That <code>comments__author</code> part is thanks to the <code>related_name='comments'</code> I added earlier.</p>
<h2 id="heading-faqs">FAQs</h2>
<h3 id="heading-can-a-foreignkey-be-optional"><strong>Can a ForeignKey be optional?</strong></h3>
<p>Yes, just add <code>null=True, blank=True</code> like this:</p>
<pre><code class="lang-python">post = models.ForeignKey(Post, on_delete=models.SET_NULL, null=<span class="hljs-literal">True</span>, blank=<span class="hljs-literal">True</span>)
</code></pre>
<p>You might want this if the related object isn't always required. For example, a <code>Comment</code> might <em>optionally</em> belong to a <code>Post</code>, or a <code>Task</code> might optionally have a related <code>Project</code>. It’s useful when building drafts, soft deletes, or handling legacy data.</p>
<h3 id="heading-can-a-foreignkey-point-to-the-same-model-self"><strong>Can a ForeignKey point to the same model (self)?</strong></h3>
<p>Absolutely. That’s called a self-referential ForeignKey, often used for things like threaded comments or categories.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Category</span>(<span class="hljs-params">models.Model</span>):</span>
    name = models.CharField(max_length=<span class="hljs-number">100</span>)
    parent = models.ForeignKey(<span class="hljs-string">'self'</span>, null=<span class="hljs-literal">True</span>, blank=<span class="hljs-literal">True</span>, on_delete=models.SET_NULL)
</code></pre>
<h3 id="heading-can-a-model-have-more-than-one-foreignkey"><strong>Can a model have more than one ForeignKey?</strong></h3>
<p>Totally. For example, an Order model could have one ForeignKey to a Customer, and another to a ShippingAddress.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>If you’re building anything in Django that deals with more than one model, understanding ForeignKey is essential. It makes your app more structured, easier to query, and way more powerful.</p>
<p>At first, it might feel like a lot, but once you build one or two relationships and see it all working in the admin and your views, it clicks.</p>
<p>And if something’s still unclear, that’s normal. I had to build a few mini-projects before it started to feel natural.</p>
<h3 id="heading-further-resources">Further Resources</h3>
<ul>
<li><p>Django Docs on <a target="_blank" href="https://docs.djangoproject.com/en/stable/ref/models/fields/#foreignkey">ForeignKey</a></p>
</li>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/ref/models/fields/">Django Model Field Reference</a></p>
</li>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/topics/db/models/">Writing Models in Django</a></p>
</li>
<li><p><a target="_blank" href="https://docs.djangoproject.com/en/stable/ref/contrib/admin/">Django Admin Docs</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
