Chapter 12: Django Insert Data
1. The Four Main Ways to Insert Data (Quick Overview Table)
| Method | Use Case | Speed (relative) | Signals sent? | Validation? | Returns PKs? | Best for |
|---|---|---|---|---|---|---|
| save() on instance | Single object, full control | Slow (1 query) | Yes | Yes | Yes | Normal CRUD, forms |
| Model.objects.create() | Single object, one-liner | Slow (1 query) | Yes | Yes | Yes | Quick single inserts |
| get_or_create() | Upsert (get if exists, create if not) | Medium | Yes | Yes | Yes | Seed data, avoid duplicates |
| bulk_create() | Many objects at once | Very fast (1 query) | No | No | Optional | Imports, migrations, tests |
2. Method 1: The Classic – Create Instance + .save()
Most basic & explicit way.
In python manage.py shell:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
from polls.models import Question, Choice from django.utils import timezone # Step 1: Create object in memory (not in DB yet) q = Question( question_text="What's your favorite place for biryani in Hyderabad?", pub_date=timezone.now() ) # Step 2: Save to database → INSERT happens here q.save() print(q.pk) # → 1 (or whatever next ID is) print(q) # → What's your favorite place... |
Add a related Choice:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
c1 = Choice( question=q, # link to parent choice_text="Paradise", votes=0 ) c1.save() c2 = Choice(question=q, choice_text="Bawarchi", votes=0) c2.save() |
When to use: When you need to do extra logic before/after save (e.g. set slug, send email, resize image).
Pro tip: Override .save() in model if you want auto-logic (e.g. auto-slugify).
3. Method 2: One-Liner – objects.create()
Same as above but shorter & safer (handles save automatically).
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# Single question q = Question.objects.create( question_text="Best Python web framework in 2026?", pub_date=timezone.now() ) # With related object Choice.objects.create( question=q, choice_text="Django (obviously!)", votes=5 ) Choice.objects.create(question=q, choice_text="FastAPI", votes=3) |
Advantages:
- Atomic (create or fail)
- Returns the saved object with PK
Common mistake: Forgetting to assign ForeignKey → IntegrityError.
4. Method 3: Smart Upsert – get_or_create()
Very useful for seed data, config tables, or avoiding duplicates.
Returns tuple: (object, created) where created is boolean.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# Get or create a question – won't duplicate if text matches q, created = Question.objects.get_or_create( question_text="What's the capital of Telangana?", defaults={'pub_date': timezone.now()} ) print(created) # True first time, False next time print(q.pk) # For choice (scoped to question) choice, created = Choice.objects.get_or_create( question=q, choice_text="Hyderabad", defaults={'votes': 0} ) |
Warning: get_or_create() is not atomic in high-concurrency (rare race condition possible). For critical cases use select_for_update() or database constraints.
Best for: Loading countries, tags, initial users, settings.
5. Method 4: The Speed King – bulk_create() (Bulk Insert)
Insert many rows in one SQL query — 10–100× faster than looping .save() or .create().
Perfect for:
- Importing CSV/JSON data
- Test fixtures
- Data migrations
- Seeding large lists
Example: Create 5 questions at once.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
questions_data = [ Question( question_text="Favorite biryani spot?", pub_date=timezone.now() ), Question( question_text="Best movie of 2025?", pub_date=timezone.now() ), Question(question_text="Django or FastAPI in 2026?", pub_date=timezone.now()), Question(question_text="Favorite Hyderabad metro station?", pub_date=timezone.now()), Question(question_text="Rainy season plans?", pub_date=timezone.now()), ] # Bulk insert – returns list of objects with PKs filled (Django 2.2+ default) created_questions = Question.objects.bulk_create(questions_data) print(len(created_questions)) # 5 print(created_questions[0].pk) # actual ID from DB # Now bulk create choices for the first question choices_data = [ Choice(question=created_questions[0], choice_text="Paradise", votes=0), Choice(question=created_questions[0], choice_text="Bawarchi", votes=0), Choice(question=created_questions[0], choice_text="Pista House", votes=0), ] Choice.objects.bulk_create(choices_data, ignore_conflicts=True) # skip duplicates if any |
Important options in 6.0:
- ignore_conflicts=True → skips rows that violate unique constraints (very useful)
- update_conflicts + update_fields → upsert style (newer Django)
- return_defaults=True → fills auto fields like created_at if using db_default
Limitations:
- No pre_save / post_save signals
- No full model validation
- No .save() overrides called
- PKs assigned only if database supports returning IDs (SQLite/PostgreSQL/MySQL yes)
Pro tip: For huge imports (10k+ rows), combine with transaction.atomic() or disable constraint checking temporarily.
6. Quick Bonus: Insert via Admin, Fixtures, or Data Migrations
- Admin: Already doing this — easiest for manual entry
- Fixtures (JSON/YAML): python manage.py loaddata initial_data.json
- Data migrations: Create empty migration → python manage.py makemigrations –empty polls → add RunPython with bulk_create inside
Example data migration snippet:
|
0 1 2 3 4 5 6 7 8 9 10 11 |
def seed_questions(apps, schema_editor): Question = apps.get_model('polls', 'Question') Question.objects.bulk_create([...]) class Migration(migrations.Migration): operations = [migrations.RunPython(seed_questions)] |
Recap & Your Next Steps
You now know all major ways to insert data — from single elegant .create() to blazing-fast bulk_create().
Try this right now:
- Open shell
- Run the bulk_create() example above
- Go to /admin/ → see your new questions & choices
- Visit /polls/ → see them in your template if wired up
Tell me:
- “It worked! Show me how to insert from CSV file”
- “Explain signals & why bulk_create skips them”
- “How to handle bulk upsert (update if exists)?”
- “Error: NOT NULL constraint failed — help!”
- Or next topic: “Django Forms” or “Full voting flow”
You’re moving like a pro now — data is flowing! 🚀 Keep it up, boss! 🇮🇳
