<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Nyawade</title>
    <description>The latest articles on DEV Community by Michael Nyawade (@mnyawade).</description>
    <link>https://dev.to/mnyawade</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3839529%2Fb336d897-0a7d-4a18-84b5-124af989e837.png</url>
      <title>DEV Community: Michael Nyawade</title>
      <link>https://dev.to/mnyawade</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mnyawade"/>
    <language>en</language>
    <item>
      <title>I Kept Recreating My Django Superuser Every Time I Switched Computers — Until I Finally Understood What Was Happening</title>
      <dc:creator>Michael Nyawade</dc:creator>
      <pubDate>Tue, 02 Jun 2026 12:24:36 +0000</pubDate>
      <link>https://dev.to/mnyawade/i-kept-recreating-my-django-superuser-every-time-i-switched-computers-until-i-finally-understood-2col</link>
      <guid>https://dev.to/mnyawade/i-kept-recreating-my-django-superuser-every-time-i-switched-computers-until-i-finally-understood-2col</guid>
      <description>&lt;p&gt;I’ve been building a Django project called &lt;strong&gt;SmartDuka&lt;/strong&gt;, a smart shop management system for small retailers and shop owners. It handles inventory tracking, low stock alerts, dead stock detection, sales tracking, credit sales, and daily business insights.&lt;/p&gt;

&lt;p&gt;This is also my first serious Django project.&lt;/p&gt;

&lt;p&gt;And for a while, every time I switched from my home laptop to my work laptop, I went through the exact same process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python manage.py migrate
python manage.py createsuperuser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I would open the admin panel and start entering products, customers, and sample sales all over again.&lt;/p&gt;

&lt;p&gt;Every. Single. Time.&lt;/p&gt;

&lt;p&gt;At first, I thought this was just part of working with Django. But eventually I realized I wasn’t actually understanding the relationship between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;my code&lt;/li&gt;
&lt;li&gt;migrations&lt;/li&gt;
&lt;li&gt;and the database itself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article is basically the explanation I wish someone had given me earlier.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Mistake I Wasn’t Really Making
&lt;/h2&gt;

&lt;p&gt;To be fair, I &lt;em&gt;was&lt;/em&gt; doing one important thing correctly:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;was inside my &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For a good reason SQLite databases are local files and usually shouldn’t be committed to Git.&lt;/p&gt;

&lt;p&gt;So technically my workflow looked “correct.”&lt;/p&gt;

&lt;p&gt;The problem was this:&lt;/p&gt;

&lt;p&gt;I assumed migrations would somehow preserve my data across machines.&lt;/p&gt;

&lt;p&gt;They don’t.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Django Migrations Actually Do
&lt;/h2&gt;

&lt;p&gt;This part finally clicked for me after some frustration.&lt;/p&gt;

&lt;p&gt;When you run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Django recreates the &lt;strong&gt;database structure&lt;/strong&gt;, not the actual records inside it.&lt;/p&gt;

&lt;p&gt;So migrations restore things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tables&lt;/li&gt;
&lt;li&gt;columns&lt;/li&gt;
&lt;li&gt;relationships&lt;/li&gt;
&lt;li&gt;constraints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But they do &lt;em&gt;not&lt;/em&gt; restore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;superusers&lt;/li&gt;
&lt;li&gt;products&lt;/li&gt;
&lt;li&gt;inventory&lt;/li&gt;
&lt;li&gt;sales records&lt;/li&gt;
&lt;li&gt;customers&lt;/li&gt;
&lt;li&gt;suppliers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That data lives inside the database itself.&lt;/p&gt;

&lt;p&gt;And in SQLite, that database is literally one file:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Once I understood that, everything made sense.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I Had to Keep Recreating My Superuser
&lt;/h2&gt;

&lt;p&gt;Because every new machine was getting:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;After migrations, Django had the tables it needed, but there were no actual records inside them yet.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;no users existed&lt;/li&gt;
&lt;li&gt;no inventory existed&lt;/li&gt;
&lt;li&gt;no sales existed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wasn’t losing my code.&lt;/p&gt;

&lt;p&gt;I was losing my database state.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Different Solutions I Considered
&lt;/h2&gt;

&lt;p&gt;Once I understood the problem properly, I realized there are several ways developers approach this.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Recreate Everything Every Time
&lt;/h3&gt;

&lt;p&gt;This was my original workflow.&lt;/p&gt;

&lt;p&gt;It technically works, but only for tiny projects.&lt;/p&gt;

&lt;p&gt;Once your app starts accumulating meaningful data, this becomes painful very quickly.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Commit &lt;code&gt;db.sqlite3&lt;/code&gt; to Git
&lt;/h3&gt;

&lt;p&gt;I considered this briefly.&lt;/p&gt;

&lt;p&gt;It solves the syncing issue, but introduces other problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;binary file conflicts&lt;/li&gt;
&lt;li&gt;messy Git history&lt;/li&gt;
&lt;li&gt;merge problems&lt;/li&gt;
&lt;li&gt;potential security risks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a real project, this didn’t feel right.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Use Fixtures
&lt;/h3&gt;

&lt;p&gt;Django supports:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;which export and import data as JSON fixtures.&lt;/p&gt;

&lt;p&gt;This is actually pretty useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;demo data&lt;/li&gt;
&lt;li&gt;seed data&lt;/li&gt;
&lt;li&gt;lookup tables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But for constantly changing business data like inventory and sales, it felt awkward as the main solution.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Copy &lt;code&gt;db.sqlite3&lt;/code&gt; Between Machines
&lt;/h3&gt;

&lt;p&gt;This ended up being the most practical short-term solution for me.&lt;/p&gt;

&lt;p&gt;Instead of recreating everything, I simply keep the latest SQLite database and transfer it between machines manually.&lt;/p&gt;

&lt;p&gt;That preserves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;my superuser&lt;/li&gt;
&lt;li&gt;inventory records&lt;/li&gt;
&lt;li&gt;customers&lt;/li&gt;
&lt;li&gt;sales history&lt;/li&gt;
&lt;li&gt;everything else&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The workflow now looks more like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git pull
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="c"&gt;# copy latest db.sqlite3&lt;/span&gt;
python manage.py migrate
python manage.py runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much better.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Important Detail About Migrations
&lt;/h2&gt;

&lt;p&gt;One thing I learned here:&lt;/p&gt;

&lt;p&gt;The correct order is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;copy &lt;code&gt;db.sqlite3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;then run migrations&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Not the other way around.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because migrations should update your &lt;em&gt;existing&lt;/em&gt; database schema if new model changes were added.&lt;/p&gt;

&lt;p&gt;For example, if yesterday my &lt;code&gt;Product&lt;/code&gt; model had:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and today I added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DecimalField&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then running:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;updates the copied database safely.&lt;/p&gt;

&lt;p&gt;That’s exactly what migrations are designed for.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Realization
&lt;/h2&gt;

&lt;p&gt;At some point, I realized SmartDuka started behaving like a real system.&lt;/p&gt;

&lt;p&gt;And once that happens, things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;persistence&lt;/li&gt;
&lt;li&gt;synchronization&lt;/li&gt;
&lt;li&gt;database strategy&lt;/li&gt;
&lt;li&gt;backups&lt;/li&gt;
&lt;li&gt;scalability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;suddenly become important.&lt;/p&gt;

&lt;p&gt;That transition caught me by surprise a little.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I’m Planning Next
&lt;/h2&gt;

&lt;p&gt;Right now I’m still using SQLite during development because it’s simple and fast.&lt;/p&gt;

&lt;p&gt;But I can already see that eventually I’ll need to move toward PostgreSQL, especially because SmartDuka deals with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;transactions&lt;/li&gt;
&lt;li&gt;inventory&lt;/li&gt;
&lt;li&gt;analytics&lt;/li&gt;
&lt;li&gt;credit management&lt;/li&gt;
&lt;li&gt;reporting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At that point, having a shared database instead of manually copying SQLite files will make much more sense.&lt;/p&gt;

&lt;p&gt;But honestly, understanding &lt;em&gt;why&lt;/em&gt; I was recreating my superuser every time was already a huge learning moment.&lt;/p&gt;




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

&lt;p&gt;One thing I’m learning while building real projects is that software development isn’t only about writing features.&lt;/p&gt;

&lt;p&gt;Sometimes the biggest lessons come from workflow problems.&lt;/p&gt;

&lt;p&gt;This was one of them for me.&lt;/p&gt;

&lt;p&gt;If you’re new to Django and you’ve ever wondered:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Why do I keep losing my data even after running migrations?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;hopefully this article saves you a few hours of confusion.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Why Adding a Unique SKU Field Broke My Django Migration</title>
      <dc:creator>Michael Nyawade</dc:creator>
      <pubDate>Sun, 10 May 2026 08:05:18 +0000</pubDate>
      <link>https://dev.to/mnyawade/why-adding-a-unique-sku-field-broke-my-django-migration-3bk8</link>
      <guid>https://dev.to/mnyawade/why-adding-a-unique-sku-field-broke-my-django-migration-3bk8</guid>
      <description>&lt;p&gt;In building projects using Django, one thing that I have realized about database migrations is that things may appear simple until the moment one alters an existing model.&lt;/p&gt;

&lt;p&gt;For example, in developing an inventory management app for my project recently, I experienced a migration problem, which, although small, helped me understand how migrations work within Django and in particular, when adding unique constraints to existing databases.&lt;/p&gt;

&lt;p&gt;I would like to document this problem since someone else may have experienced the same thing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Initial Inventory Model
&lt;/h2&gt;

&lt;p&gt;At the beginning, my &lt;code&gt;Product&lt;/code&gt; model was simple.&lt;br&gt;
&lt;/p&gt;

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


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;buying_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DecimalField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_digits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decimal_places&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;selling_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DecimalField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_digits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decimal_places&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;stock_quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PositiveIntegerField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;low_stock_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PositiveIntegerField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto_now_add&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;After creating the model, I generated and applied migrations:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;I also registered the model in &lt;code&gt;admin.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

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


&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything worked perfectly.&lt;/p&gt;

&lt;p&gt;I created a superuser, logged into the Django admin panel, tested the model, and there were no issues.&lt;/p&gt;




&lt;h2&gt;
  
  
  Updating the Inventory App
&lt;/h2&gt;

&lt;p&gt;As the project evolved, I realized the inventory system needed product categories and SKU support.&lt;/p&gt;

&lt;p&gt;So I updated &lt;code&gt;models.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

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


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;verbose_name_plural&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Categories&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

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


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;related_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;products&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sku&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;buying_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DecimalField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_digits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decimal_places&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;selling_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DecimalField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_digits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decimal_places&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;stock_quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PositiveIntegerField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;low_stock_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PositiveIntegerField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto_now_add&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;profit_per_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selling_price&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buying_price&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_low_stock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stock_quantity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;low_stock_threshold&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;I also updated &lt;code&gt;admin.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.contrib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Product&lt;/span&gt;


&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I created new migrations.&lt;/p&gt;

&lt;p&gt;At this point, I expected everything to work normally.&lt;/p&gt;

&lt;p&gt;Instead, Django threw a migration error.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Error I Encountered
&lt;/h2&gt;

&lt;p&gt;When I ran:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;I got this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;django.db.utils.IntegrityError:
UNIQUE constraint failed: new__inventory_product.sku
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first, I was confused.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;sku&lt;/code&gt; field looked correct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;sku&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So why was the migration failing?&lt;/p&gt;




&lt;h2&gt;
  
  
  What Was Actually Happening
&lt;/h2&gt;

&lt;p&gt;After looking into it more carefully, I realized the issue was related to existing database records.&lt;/p&gt;

&lt;p&gt;Before adding the &lt;code&gt;sku&lt;/code&gt; field, the &lt;code&gt;Product&lt;/code&gt; table already existed in the database.&lt;/p&gt;

&lt;p&gt;When Django attempted to apply the migration, SQLite internally tried to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new version of the table&lt;/li&gt;
&lt;li&gt;Copy the old data into the new table&lt;/li&gt;
&lt;li&gt;Apply the new constraints&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The problem was that the new &lt;code&gt;sku&lt;/code&gt; field had &lt;code&gt;unique=True&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Existing records in the database didn’t have SKU values yet.&lt;/p&gt;

&lt;p&gt;As SQLite tried moving data into the new table structure, duplicate empty values conflicted with the unique constraint.&lt;/p&gt;

&lt;p&gt;That’s why the migration failed.&lt;/p&gt;




&lt;h2&gt;
  
  
  How I Fixed It
&lt;/h2&gt;

&lt;p&gt;Since I was still in the development stage of the project, I decided to reset the database completely.&lt;/p&gt;

&lt;p&gt;Here’s what I did:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Stopped the Development Server
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;CTRL + C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Deleted the SQLite Database
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Deleted Old Migration Files
&lt;/h3&gt;

&lt;p&gt;I deleted the migration files inside the app’s &lt;code&gt;migrations&lt;/code&gt; folder, except:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Recreated Migrations
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Applied Migrations Again
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Recreated the Superuser
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;After that, everything worked correctly.&lt;/p&gt;




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

&lt;p&gt;This experience taught me an important lesson:&lt;/p&gt;

&lt;p&gt;Adding unique fields to existing Django models can break migrations if the database already contains records.&lt;/p&gt;

&lt;p&gt;Even though the code itself may look correct, the migration process also depends on the current database state.&lt;/p&gt;

&lt;p&gt;I also learned that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resetting the database is acceptable during early development&lt;/li&gt;
&lt;li&gt;It is not a good solution for production systems&lt;/li&gt;
&lt;li&gt;Database schema changes should be planned carefully&lt;/li&gt;
&lt;li&gt;Unique constraints require special attention when existing data is involved&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  A Better Production Approach
&lt;/h2&gt;

&lt;p&gt;If this were a production project, deleting the database would obviously be a bad idea.&lt;/p&gt;

&lt;p&gt;A safer approach would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the field without &lt;code&gt;unique=True&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Populate existing rows with unique values&lt;/li&gt;
&lt;li&gt;Then apply the unique constraint in a later migration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That prevents conflicts with existing records.&lt;/p&gt;




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

&lt;p&gt;This wasn’t the biggest bug I’ve encountered in Django, but it was one of those moments that helped me understand migrations better.&lt;/p&gt;

&lt;p&gt;Sometimes small errors teach the most useful lessons.&lt;/p&gt;

&lt;p&gt;If you’re learning Django and run into migration issues after modifying models, don’t panic immediately. In many cases, the problem is related to how existing database records interact with the new schema changes.&lt;/p&gt;

&lt;p&gt;Hopefully this saves someone else a few hours of confusion.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>database</category>
      <category>django</category>
      <category>python</category>
    </item>
  </channel>
</rss>
