Chapter 13: Django Update Data
1. Quick Overview – The Main Ways to Update Data
| Method | What it does | Speed (relative) | Affects how many rows? | Uses F()? | Signals sent? | Best for |
|---|---|---|---|---|---|---|
| instance.save() | Update one object (full or partial) | Slow (1 query) | 1 | Yes (via attr) | Yes | Forms, single record edits |
| instance.save(update_fields=…) | Update only specified fields | Slightly faster | 1 | Yes | Yes | Performance on large models |
| Model.objects.filter(…).update(…) | Bulk SQL UPDATE – very efficient | Fast (1 query) | Many | Yes (via F()) | No | Mass updates (e.g. approve all) |
| bulk_update(objs, fields=…) | Update many pre-fetched objects in batches | Medium-Fast | Many | No | No | When you already have objects in memory |
| update_or_create() | Update if exists, create if not (single) | Medium | 1 | No | Yes | Config / seed data |
| bulk_create(…, update_conflicts=…) | Bulk upsert (since Django 4.1) | Very fast | Many | Limited | No | Imports with possible duplicates |
2. Method 1: Update One Object – The Classic .save()
Most common in views/forms.
In shell (python manage.py shell):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
from polls.models import Question, Choice # Get existing object q = Question.objects.get(pk=1) # or .filter(...).first() print(q.question_text) # → old value # Change attribute(s) q.question_text = "Best biryani in Hyderabad 2026 edition?" q.pub_date = timezone.now() # update timestamp too # Save → UPDATE SQL happens q.save() # Optional: only update specific fields (faster, less data sent) q.question_text = "Updated again!" q.save(update_fields=['question_text']) # only question_text column touched |
Pro tip: Always use update_fields= when possible — especially on models with many/large fields (TextField, ImageField). Prevents unnecessary writes.
In a view example (e.g. edit question):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
def edit_question(request, question_id): question = get_object_or_404(Question, pk=question_id) if request.method == 'POST': question.question_text = request.POST['new_text'] question.save(update_fields=['question_text']) return redirect('polls:detail', question_id=question.id) ... |
3. Method 2: Atomic Increments – Use F() Expressions (Very Important for Votes!)
You never want to do choice.votes += 1; choice.save() in high-traffic apps — race conditions!
Instead use F() (expression that references current DB value):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
from django.db.models import F # Increment votes by 1 (safe, atomic) Choice.objects.filter(pk=3).update(votes=F('votes') + 1) # Or decrement Question.objects.filter(pk=1).update(some_counter=F('some_counter') - 5) # Multiple fields at once Choice.objects.filter(question_id=1).update( votes=F('votes') + 1, last_voted=timezone.now() ) |
Full voting example (in your vote view):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
def vote(request, question_id): question = get_object_or_404(Question, pk=question_id) try: selected_choice = question.choices.get(pk=request.POST['choice']) except (KeyError, Choice.DoesNotExist): return render(request, 'polls/detail.html', { 'question': question, 'error_message': "You didn't select a choice.", }) # Atomic vote increment Choice.objects.filter(pk=selected_choice.pk).update( votes=F('votes') + 1 ) return redirect('polls:results', question_id=question.id) |
This runs pure SQL: UPDATE … SET votes = votes + 1 WHERE id = … — no race conditions!
4. Method 3: Bulk Update Many Rows – QuerySet.update()
Fastest for mass changes — one SQL statement.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# Set all old questions to "archived" status (assume you added status field) Question.objects.filter(pub_date__lt=timezone.now() - timedelta(days=365)).update( status='archived' ) # Reset votes for all choices in one question Choice.objects.filter(question_id=1).update(votes=0) # Combine with F() Choice.objects.filter(votes__lt=10).update( popularity_score=F('votes') * 2 + 5 ) |
Warning: .update() bypasses .save() → no signals, no custom save logic, no auto_now fields updated.
5. Method 4: Bulk Update from Objects in Memory – bulk_update()
When you already fetched objects and modified them:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
choices = Choice.objects.filter(question_id=1)[:10] # fetched 10 objects for c in choices: c.votes += 2 # or whatever logic # Bulk update only 'votes' field Choice.objects.bulk_update(choices, ['votes'], batch_size=100) |
Pros: Fewer queries than looping .save() Cons: Still needs objects in memory (not pure SQL like .update())
6. Method 5: Update or Create – update_or_create()
Single-row upsert:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# Update if exists, create if not (by unique field) obj, created = Question.objects.update_or_create( question_text="What's your favorite color?", # lookup field defaults={ 'pub_date': timezone.now(), 'category': 'fun' } ) print(created) # False if updated, True if created |
7. Advanced: Bulk Upsert with bulk_create(…, update_conflicts=…) (Django 4.1+)
For importing data that may already exist:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
data = [ Choice(question_id=1, choice_text="Red", votes=5), Choice(question_id=1, choice_text="Blue", votes=3), # ... many more ] Choice.objects.bulk_create( data, update_conflicts=True, unique_fields=['question', 'choice_text'], # what defines uniqueness update_fields=['votes'] # what to update if conflict ) |
Very powerful for CSV imports or sync jobs.
Common Mistakes & Fixes (Real Hyderabad Project Stories)
- Race condition on votes → Forgot F() → use atomic update(F())
- signals not firing → Used .update() or bulk_update when you needed post_save
- updated nothing → Filter returned empty QuerySet → check .count() first
- bulk_update fails → Forgot to specify fields= list
- auto_now not updated → .update() / bulk_update skips them → use F(‘updated_at’) = timezone.now() if needed
Quick Exercise for You
- In shell: Create 3 choices for question #1
- Use F() to increment votes on one choice
- Use .update() to reset votes on all choices for that question
- Try bulk_update on a few modified objects
Tell me:
- “Worked! Show me update in a real form view”
- “How to update related objects (e.g. all choices when question changes)”
- “Explain race conditions deeper with example”
- “Error: Cannot update using F() — help!”
- Or next: “Django Delete Data” or “Forms time!”
You’re handling data like a senior dev already — keep rocking it! 🚀🇮🇳
