Agent skill
django-development
Comprehensive Django development guidelines for Pantas Green emissions tracking platform. Use when: writing Django models, views, serializers, migrations, ORM queries, templates, or any Python backend code. Triggers: django, model, view, serializer, migration, ORM, queryset, endpoint, API, DRF, emission, carbon, python backend, template, admin
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/development/django-development
SKILL.md
Django Development Guidelines
Quick Reference
| Rule | Description |
|---|---|
| TDD | Scaffold stub → write failing test → implement |
| ORM | Use select_related() and prefetch_related() to avoid N+1 |
| Decimals | Use DecimalField for financial/emissions, never FloatField |
| Business Logic | Keep in models and services, not views |
| Constants | No hardcoded strings - use constants from */constants/*.py |
| DRY Templates | Don't pass redundant context variables to templates |
Before Coding
- BP-1 (MUST) Ask clarifying questions
- BP-2 (SHOULD) Draft and confirm approach for complex work
- BP-3 (SHOULD) List pros/cons if multiple approaches exist
Code Organization & Structure
Import Organization
# Standard library imports
import os
from datetime import datetime
# Third-party imports
from django.db import models
from rest_framework import serializers
# Local imports
from .models import MyModel
from .constants import STATUS_PENDING
Class Method Order
class MyClass:
# 1. __init__ and constructors
def __init__(self): ...
# 2. @classmethod methods
@classmethod
def create(cls): ...
# 3. @staticmethod methods
@staticmethod
def validate(): ...
# 4. @property methods
@property
def name(self): ...
# 5. Public methods
def process(self): ...
# 6. Private methods
def _internal(self): ...
# 7. Magic methods
def __str__(self): ...
File Naming
- Files:
snake_case.py - Classes:
PascalCase - Functions:
snake_case - Constants:
UPPER_SNAKE_CASE
Directory Structure
green/
├── static/
│ ├── js/<model_name>/ # JavaScript files by model
│ └── css/<model_name>/ # CSS files by model
└── templates/
└── <app_name>/
Model Standards
Base Requirements
- Inherit from
CustomModelfor common fields - Use
HistoricalRecordsfor audit trails - Use
DecimalFieldfor financial/emissions calculations (neverFloatField)
Admin Registration Pattern
@admin.register(MyModel)
class MyModelAdmin(GreenImportExportMixin, LocalDatetimeMixin, SimpleHistoryAdmin):
change_list_template = 'admin/impexp_change_list.html'
local_datetimes = ('created_at', 'updated_at')
list_select_related = ('foreign_key_field',) # Optimize queries
class Meta:
permissions = [
('import_mymodel', 'Can import MyModel'),
('export_mymodel', 'Can export MyModel'),
]
Function Quality Standards
- Readability: Function purpose should be immediately clear
- Low Complexity: Minimal nesting, low cyclomatic complexity
- Early Returns: Exit early instead of deep nesting
- Positive Checks: Prefer
if is_validoverif not is_invalid - No Unused Code: Remove unused parameters, variables, and functions
- Descriptive Names: Consistent with existing codebase vocabulary
Database Query Optimization
N+1 Prevention (CRITICAL)
# ❌ BAD - N+1 query problem
for order in Order.objects.all():
print(order.customer.name) # Queries DB for each order
# ✅ GOOD - Single query with JOIN
for order in Order.objects.select_related('customer'):
print(order.customer.name) # No additional queries
# ✅ GOOD - Prefetch for ManyToMany and reverse FK
orders = Order.objects.prefetch_related('items', 'tags')
Bulk Operations
# ❌ BAD - Multiple queries
for id in ids:
obj = Model.objects.get(id=id)
# ✅ GOOD - Single query
objs = Model.objects.filter(id__in=ids)
Circular Join Detection
# ❌ BAD - Circular join pattern (A → B → A)
Model.objects.filter(related__back_to_model__field=value)
# ❌ BAD - Excessive relationship traversal
Model.objects.filter(a__b__c__d__e__field=value)
# ✅ GOOD - Direct path
Model.objects.filter(direct_field=value)
Key indicators of problematic queries:
- Relationship paths that go A → B → A (circular)
- More than 3-4 relationship traversals
- Complex joins when simpler alternatives exist
Constants & String Literals (MANDATORY)
Rule: No Magic Strings
String literals used 3 or more times MUST be defined as constants.
# ❌ BAD - Hardcoded strings
if material_source == 'epd':
...
elif material_source == 'pantas':
...
# ✅ GOOD - Named constants
from .constants import MATERIAL_SOURCE_EPD, MATERIAL_SOURCE_PANTAS
if material_source == MATERIAL_SOURCE_EPD:
...
elif material_source == MATERIAL_SOURCE_PANTAS:
...
Constants File Pattern
# app/constants.py
STATUS_PENDING = 'pending'
STATUS_COMPLETED = 'completed'
STATUS_FAILED = 'failed'
STATUS_CHOICES = [
(STATUS_PENDING, 'Pending'),
(STATUS_COMPLETED, 'Completed'),
(STATUS_FAILED, 'Failed'),
]
# Usage in models
class MyModel(models.Model):
status = models.CharField(choices=STATUS_CHOICES, default=STATUS_PENDING)
Common Patterns to Avoid
| Pattern | Problem | Fix |
|---|---|---|
== 'pending' |
Magic string | Use STATUS_PENDING |
== 'epd' |
Magic string | Use MATERIAL_SOURCE_EPD |
filter(type='scope1') |
Magic string | Use SCOPE_1 constant |
choices=[('a', 'A')] |
No constants | Define choice constants |
View-Template DRY (MANDATORY)
Rule: Don't Pass Redundant Context Variables
If an object is already in context, templates can access its properties directly.
# ❌ BAD - Redundant context variables
def my_view(request):
user = request.user
return render(request, 'template.html', {
'user': user,
'user_name': user.name, # Redundant!
'user_email': user.email, # Redundant!
'is_active': user.is_active, # Redundant!
})
# ✅ GOOD - Let template access properties
def my_view(request):
return render(request, 'template.html', {
'user': request.user,
})
In template:
{{ user.name }}
{{ user.email }}
{{ user.is_active }}
Exceptions (NOT violations)
- Computed values:
"total": calculate_total(items)✅ - Method calls with arguments:
"filtered": obj.get_items(status='active')✅ - Renamed for clarity:
"current_user": request.userwhen template expects specific name ✅
Template Technology Consistency (MANDATORY)
Rule: Use Django Filters, Not JavaScript
Don't duplicate Django template filter functionality in JavaScript.
| JS Pattern | Django Alternative |
|---|---|
new Date().toLocaleString() |
{{ value|date:"format" }} |
toLocaleDateString() |
{{ value|date:"format" }} |
.toLowerCase() |
{{ value|lower }} |
.toUpperCase() |
{{ value|upper }} |
| String truncation | {{ value|truncatechars:N }} |
| Number formatting | {{ value|floatformat }} |
When to Use Django Filters
- Codebase already uses Django filters for same purpose
- Django handles it without JavaScript
- Reduces client-side complexity
Frontend Standards
- No Inline Styles: Use Bootstrap classes
- No Inline JavaScript: Place in
green/static/js/<model_name>/ - Consistent Bootstrap: Use Bootstrap framework for styling
- Template Inheritance: Proper Django template blocks
- Responsive Design: Works on mobile and desktop
Exception Handling & Logging
Exception Logging
# ✅ CORRECT - exc_info=True ONLY in exception handler
try:
result = process_data()
except Exception as e:
logger.error(f"Processing failed: {e}", exc_info=True)
# ✅ CORRECT - No exc_info outside exception block
logger.info(f"Processing started for company {company_id}")
logger.warning(f"Missing optional field: {field_name}")
# ❌ WRONG - exc_info=True outside exception (no stack trace to capture)
logger.info(f"Processing started", exc_info=True)
Log Levels
| Level | Use For |
|---|---|
ERROR |
Exceptions, failures |
WARNING |
Recoverable issues |
INFO |
Significant events |
DEBUG |
Diagnostic details |
Context in Logs
Include relevant IDs: user_id, file_id, company_id
Never log: passwords, tokens, sensitive PII
Testing Standards
Organization
- Tests in
tests/directory within each app - Unit tests:
SimpleTestCase(no DB) - Integration tests:
TestCase(with DB)
Database Mocking
# ✅ Unit tests use Mock, no real DB
from unittest.mock import Mock, patch
class MyTestCase(SimpleTestCase):
@patch('myapp.services.Model.objects.get')
def test_something(self, mock_get):
mock_get.return_value = Mock(name='Test')
...
Test Data
# ❌ BAD - Meaningless test data
user = User(name='foo', age=42)
# ✅ GOOD - Realistic test data
user = User(name='John Smith', age=35)
Assertions
# ❌ WEAK - Ambiguous assertion
self.assertGreaterEqual(count, 0)
# ✅ STRONG - Specific assertion
self.assertEqual(count, 5)
Domain-Specific: Emissions
- Precision: Maintain accuracy in carbon calculations
- Unit Consistency: Consistent units throughout
- Financial Fields: Always use
DecimalField - Audit Trails: Track all calculation changes
- Validation: Validate emission factors and data sources
Performance & Security
Performance
- Test query count for performance-critical code
- Consider memory usage in data processing
- Use Dramatiq for long-running operations
Security
- Never log sensitive information
- Validate file types and sizes for uploads
- Use Django ORM (avoid raw SQL)
Quality Gates
| Gate | Command/Requirement |
|---|---|
| G-1 | ruff check passes |
| G-2 | mypy passes |
| G-3 | python manage.py test passes |
Code Review Checklist
Before submitting code for review, verify:
Code Quality
- Import organization correct (stdlib → third-party → local)
- Class method order follows convention
- No unused imports, variables, or functions
- Early return pattern used (no deep nesting)
Database
- N+1 queries prevented with
select_related/prefetch_related - No circular join patterns
- Bulk operations used where appropriate
Constants (MANDATORY)
- No hardcoded strings used 3+ times
- Existing constants reused from
*/constants/*.py - New constants created for repeated domain-specific strings
View-Template DRY (MANDATORY)
- No redundant context variables in
render()calls - Templates access object properties directly
Template Technology (MANDATORY)
- No JavaScript duplicating Django filter functionality
- Consistent with existing codebase patterns
Testing
- Unit tests use Mock (no DB access)
- Realistic test data (not "foo" or 42)
- Strong assertions (assertEqual vs assertGreaterEqual)
Logging
-
exc_info=Trueonly in exception handlers - No sensitive data in logs
- Includes relevant context IDs
Summary of Coding Rules
| Rule | Level | Description |
|---|---|---|
| BP-1 | MUST | Ask clarifying questions |
| BP-2 | SHOULD | Confirm approach for complex work |
| BP-3 | SHOULD | List pros/cons for multiple approaches |
| C-1 | MUST | Follow TDD workflow |
| C-2 | MUST | Use existing domain vocabulary |
| C-3 | SHOULD | Use Django class-based views |
| C-4 | SHOULD | Prefer simple, composable functions |
| C-6 | MUST | Follow import conventions and PEP 8 |
| C-10 | MUST | Use constants for repeated strings |
| C-11 | MUST | No local imports inside functions |
| D-1 | MUST | Avoid N+1 queries |
| D-2 | SHOULD | Use transactions for atomic operations |
| D-3 | MUST | Create migrations for model changes |
| D-4 | SHOULD | Use Decimal for financial/emissions |
| T-1 | MUST | Tests in tests/ directory |
| T-3 | MUST | Separate unit from integration tests |
| T-7 | MUST | Realistic test data |
| L-1 | MUST | exc_info=True in exception handlers |
| L-4 | MUST | No sensitive data in logs |
| G-1 | MUST | ruff check passes |
| G-2 | MUST | mypy passes |
| G-3 | MUST | Django tests pass |
Version: 2.0.0 - Enhanced with lessons from django-reviewer agent
Didn't find tool you were looking for?