Django Idioms and Patterns
Django rewards convention, the ORM, and "batteries included" design. Idiomatic Django = DRY, model-centric, well-tested.
Scope: Django-specific patterns. For Python:
@.gemini/skills/python-idioms/SKILL.md.
Models
Fat models, thin views — business logic in models/managers, not views.
Custom managers for common queries:
class TaskManager(models.Manager): def active(self): return self.filter(status='active') class Task(models.Model): objects = TaskManager()Metaclass for ordering, constraints, indexes.
Views
Class-based views for CRUD, function-based for simple/custom:
class TaskListView(LoginRequiredMixin, ListView): model = Task queryset = Task.objects.active() paginate_by = 25DRF serializers for API boundaries:
class TaskSerializer(serializers.ModelSerializer): class Meta: model = Task fields = ['id', 'title', 'priority', 'created_at'] read_only_fields = ['id', 'created_at']
ORM
select_related/prefetch_relatedto avoid N+1 queries.F()andQ()expressions for complex queries.- Never raw SQL unless ORM can't express it (and then use parameterized queries).
Migrations
- One migration per logical change. Squash when history grows.
- Data migrations in separate migration files from schema changes.
RunPythonwithreverse_codefor reversibility.
Testing
pytest-djangooverunittest.TestCase:@pytest.mark.django_db def test_create_task(): task = Task.objects.create(title='Test', priority='high') assert task.title == 'Test'Factory Boy for test data (not fixtures).
Formatting and Static Analysis
| Tool | Purpose | Command |
|---|---|---|
ruff |
Formatting + linting | ruff format . && ruff check . |
mypy + django-stubs |
Type checking | mypy . |
bandit |
Security | bandit -r . |
Related
- Python Idioms @.gemini/skills/python-idioms/SKILL.md
- Database Design Principles @.gemini/skills/database-design-principles/SKILL.md