====== Django ======
Useful Django snippets, patterns, and quick references.
===== Management Commands =====
==== Create a custom management command ====
# myapp/management/commands/my_command.py
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = "Description of your command"
def add_arguments(self, parser):
parser.add_argument("name", type=str)
parser.add_argument("--dry-run", action="store_true")
def handle(self, *args, **options):
name = options["name"]
if options["dry_run"]:
self.stdout.write(f"Would process: {name}")
return
self.stdout.write(self.style.SUCCESS(f"Done: {name}"))
===== QuerySet Tricks =====
==== Bulk create with ignore conflicts ====
MyModel.objects.bulk_create(
[MyModel(field="value") for _ in range(100)],
ignore_conflicts=True,
)
==== Subquery annotation ====
from django.db.models import OuterRef, Subquery
latest_comment = Comment.objects.filter(
post=OuterRef("pk")
).order_by("-created_at")
Post.objects.annotate(
latest_comment_text=Subquery(latest_comment.values("text")[:1])
)
==== F expressions for atomic updates ====
from django.db.models import F
Product.objects.filter(pk=1).update(stock=F("stock") - 1)
===== Models =====
==== Abstract base model with timestamps ====
from django.db import models
class TimeStampedModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
==== Custom model manager ====
class PublishedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status="published")
class Article(TimeStampedModel):
title = models.CharField(max_length=200)
status = models.CharField(max_length=20, default="draft")
objects = models.Manager() # default
published = PublishedManager()
# Usage: Article.published.all()
===== Views =====
==== APIView with proper error handling (DRF) ====
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class ItemView(APIView):
def get(self, request, pk):
try:
item = Item.objects.get(pk=pk)
except Item.DoesNotExist:
return Response(
{"error": "Item not found"},
status=status.HTTP_404_NOT_FOUND,
)
serializer = ItemSerializer(item)
return Response(serializer.data)
===== Testing =====
==== Test with fixtures and client ====
from django.test import TestCase, Client
from django.urls import reverse
class ArticleViewTest(TestCase):
def setUp(self):
self.client = Client()
self.article = Article.objects.create(
title="Test", status="published"
)
def test_article_detail(self):
url = reverse("article-detail", args=[self.article.pk])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Test")
==== Override settings in tests ====
from django.test import TestCase, override_settings
class MyTest(TestCase):
@override_settings(DEBUG=True, CACHES={"default": {"BACKEND": "django.core.cache.backends.dummy.DummyCache"}})
def test_with_custom_settings(self):
pass
===== Useful One-Liners =====
# Shell plus (needs django-extensions)
python manage.py shell_plus --print-sql
# Show all URLs
python manage.py show_urls
# Reset migrations for an app
python manage.py migrate myapp zero
# Create superuser non-interactively
DJANGO_SUPERUSER_PASSWORD=pass python manage.py createsuperuser --noinput --username admin --email admin@example.com
# Dump data as fixture
python manage.py dumpdata myapp.MyModel --indent 2 > fixture.json
===== Migrations =====
==== Squash migrations ====
python manage.py squashmigrations myapp 0001 0015
==== Create an empty migration (for data migrations) ====
python manage.py makemigrations myapp --empty -n my_data_migration
==== Data migration template ====
from django.db import migrations
def forwards(apps, schema_editor):
MyModel = apps.get_model("myapp", "MyModel")
for obj in MyModel.objects.filter(status="old"):
obj.status = "new"
obj.save(update_fields=["status"])
def backwards(apps, schema_editor):
pass # or reverse the migration
class Migration(migrations.Migration):
dependencies = [
("myapp", "0015_previous"),
]
operations = [
migrations.RunPython(forwards, backwards),
]
===== Signals =====
==== Post-save signal ====
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
===== Django REST Framework =====
==== Serializer with nested write support ====
class AddressSerializer(serializers.ModelSerializer):
class Meta:
model = Address
fields = ["street", "city", "country"]
class UserSerializer(serializers.ModelSerializer):
address = AddressSerializer()
class Meta:
model = User
fields = ["name", "email", "address"]
def create(self, validated_data):
address_data = validated_data.pop("address")
user = User.objects.create(**validated_data)
Address.objects.create(user=user, **address_data)
return user
==== Custom permission ====
from rest_framework.permissions import BasePermission
class IsOwner(BasePermission):
def has_object_permission(self, request, view, obj):
return obj.owner == request.user
==== Pagination ====
# settings.py
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 25,
}
# Or custom pagination
from rest_framework.pagination import CursorPagination
class CreatedAtCursorPagination(CursorPagination):
ordering = "-created_at"
page_size = 50
===== See Also =====
* [[django:links|Django Links]] - Useful Django resources and references