DRY (Don’t Repeat Yourself)
Avoid code duplication by abstracting recurring logic into reusable components.
Django naturally encourages DRY with tools like the ORM, forms, and admin. Example: Using Django REST Framework (DRF) serializers for model validation:
from rest_framework import serializers
from .models import Expense, Category, Balance
class ExpenseSerializer(serializers.ModelSerializer):
class Meta:
model = Expense
fields = ['id', 'user', 'category', 'amount', 'description', 'date']
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['id', 'name', 'description', 'user']
class BalanceSerializer(serializers.ModelSerializer):
class Meta:
model = Balance
fields = ['id', 'total_balance']
KISS (Keep It Simple, Stupid)
Favor simplicity over cleverness. Readable code is better than smart code.
Django’s generic class-based views (CBVs) let you write concise and standardized logic for common CRUD operations:
from rest_framework import generics
from .models import Expense
from .serializers import ExpenseSerializer
class ExpenseListView(generics.ListCreateAPIView):
queryset = Expense.objects.all()
serializer_class = ExpenseSerializer
YAGNI (You Ain’t Gonna Need It)
Don’t implement features until they’re actually needed.
Start small. Add complexity only when justified by real-world needs.
No custom managers or over-engineered profile logic — just what you need.from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField(unique=True)
def __str__(self):
return self.usernameSeparation of Concerns (SoC)
Assign distinct responsibilities to different parts of your application.
Django’s MTV (Model-Template-View) architecture supports SoC by design.
Example: Offloading business logic to models/services instead of views:# models.py
class User(AbstractUser):
def get_total_expenses(self):
return self.expense_set.aggregate(Sum('amount'))['amount__sum'] or 0# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
from .models import User
class UserTotalExpensesView(APIView):
def get(self, request, user_id):
user = get_object_or_404(User, id=user_id)
total_expenses = user.get_total_expenses()
return Response({'total_expenses': total_expenses})SOLID Principles
A set of five principles that promote robust, scalable object-oriented design.
S - Single Responsibility Principle (SRP)
Each class should do one thing only.
O - Open/Closed Principle (OCP)class Category(AbstractModel):
name = models.CharField(max_length=100, unique=True)
description = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE)Code should be open for extension but closed for modification.
You can add new middleware by subclassing without changing existing code.class BaseMiddleware:
def process_request(self, request):
raise NotImplementedError
class AuthMiddleware(BaseMiddleware):
def process_request(self, request):
if not request.user.is_authenticated:
return HttpResponse('Unauthorized', status=401)L - Liskov Substitution Principle (LSP)
Subtypes should be replaceable for their parent types without breaking the app.
I - Interface Segregation Principle (ISP)class Notification:
def send(self):
raise NotImplementedError
class EmailNotification(Notification):
def send(self):
print("Sending email")
class SMSNotification(Notification):
def send(self):
print("Sending SMS")
def notify(notification: Notification):
notification.send()Don't force classes to implement unused methods.
class CreateMixin:
def create(self, request, *args, **kwargs):
return JsonResponse({'message': 'Create not implemented'}, status=405)
class ReadMixin:
def read(self, request, *args, **kwargs):
return JsonResponse({'message': 'Read not implemented'}, status=405)
class MyView(CreateMixin, ReadMixin, View):
def read(self, request, *args, **kwargs):
return JsonResponse({'message': 'Reading data'})Split responsibilities using mixins to keep classes focused.
D - Dependency Inversion Principle (DIP)
Depend on abstractions, not concrete implementations.
Conclusion# services.py
class PaymentService:
def process_payment(self):
raise NotImplementedError
class StripePaymentService(PaymentService):
def process_payment(self):
print("Processing Stripe payment")
# views.py
class PaymentView(View):
def __init__(self, payment_service: PaymentService):
self.payment_service = payment_service
def post(self, request, *args, **kwargs):
self.payment_service.process_payment()
return JsonResponse({'message': 'Payment successful'})By applying these principles:
- Your Django code stays modular and easier to test.
- Maintenance becomes smoother as features grow.
- Teams can onboard faster and contribute with confidence.
Stick to the basics, avoid premature optimization, and let Django’s clean design do the heavy lifting.