Part 6 · 2 chapters · ~12 min
Django End to End
Projects and apps, settings, models and migrations, the admin, URL routing, views and templates, forms and validation, authentication and permissions, Django REST Framework serializers and viewsets, Celery tasks, async views, and FastAPI as the alternative for API-only services.
10
Models, migrations and the admin
code
# ledger/models.py
from django.db import models
class Account(models.Model):
owner = models.ForeignKey("auth.User", on_delete=models.PROTECT, related_name="accounts")
currency = models.CharField(max_length=3)
balance_kobo = models.BigIntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
constraints = [models.CheckConstraint(condition=models.Q(balance_kobo__gte=0), name="balance_non_negative")]
python manage.py makemigrations && python manage.py migrate # review the generated SQL: sqlmigrate ledger 0001
# ledger/admin.py: a back-office UI for free
@admin.register(Account)
class AccountAdmin(admin.ModelAdmin):
list_display = ("id", "owner", "currency", "balance_kobo"); search_fields = ("owner__email",); readonly_fields = ("balance_kobo",)THE DJANGO REQUEST CYCLE
from the WSGI/ASGI server to your view and back
swipe the figure sideways, or tap expand for full screen
1/5
the server
Gunicorn (WSGI) or Uvicorn (ASGI) receives the HTTP request and calls Django's application object (wsgi.py or asgi.py).
WSGI or ASGI server calls DjangoProdstack course part 2
11
DRF, atomic transfers, Celery and FastAPI
code
# an atomic, race-free debit with select_for_update (OS part 3, Concurrency part 2)
from django.db import transaction
from django.db.models import F
@transaction.atomic
def transfer(from_id, to_id, amount):
a, b = Account.objects.select_for_update().filter(id__in=[from_id, to_id]).order_by("id") # lock in id order
src, dst = (a, b) if a.id == from_id else (b, a)
if src.balance_kobo < amount: raise InsufficientFunds()
Account.objects.filter(id=src.id).update(balance_kobo=F("balance_kobo") - amount)
Account.objects.filter(id=dst.id).update(balance_kobo=F("balance_kobo") + amount)
transaction.on_commit(lambda: send_receipt.delay(src.id, amount)) # Celery task only after commit
# DRF: a serializer and a viewset
class TransferSerializer(serializers.Serializer):
amount_kobo = serializers.IntegerField(min_value=1); to = serializers.CharField()
# FastAPI equivalent, for API-only services: hints drive validation and OpenAPI docs
@app.post("/transfers", status_code=201)
async def create(t: TransferIn, idempotency_key: str = Header()) -> TransferOut: ...Django or FastAPI? Django when you want the ORM, admin, auth, migrations and security defaults in one coherent package (most products); FastAPI for small async API services, ML model serving, or teams that prefer assembling components (SQLAlchemy, Alembic) themselves.