Python Best Practices
Practical patterns for writing clean, maintainable Python — especially in Django projects.
Use Type Hints
Type hints catch bugs before runtime and improve editor support:
from typing import Optional
def get_post_by_slug(slug: str) -> Optional["Post"]:
try:
return Post.objects.get(slug=slug)
except Post.DoesNotExist:
return None
For Django models, use __future__ annotations to avoid circular imports:
from __future__ import annotations
from django.db import models
class Doc(models.Model):
subject: models.ForeignKey[Subject]
Prefer f-strings
# Good
message = f"Created {count} posts in {elapsed:.2f}s"
# Avoid
message = "Created {} posts in {:.2f}s".format(count, elapsed)
message = "Created %d posts in %.2fs" % (count, elapsed)
Context Managers for Resources
from contextlib import contextmanager
@contextmanager
def timer(label: str):
start = time.time()
yield
elapsed = time.time() - start
print(f"{label}: {elapsed:.2f}s")
# Usage
with timer("Database seed"):
seed_all_content()
Dataclasses Over Dicts
When you're passing structured data around, use dataclasses:
from dataclasses import dataclass
@dataclass
class SeedResult:
created: int
updated: int
errors: list[str]
@property
def total(self) -> int:
return self.created + self.updated
Path Handling with pathlib
from pathlib import Path
content_dir = Path("blog/content")
for md_file in content_dir.glob("*.md"):
text = md_file.read_text(encoding="utf-8")
slug = md_file.stem # filename without extension
Enumerated Choices
Django's TextChoices and IntegerChoices are cleaner than raw constants:
class Post(models.Model):
class Status(models.IntegerChoices):
DRAFT = 0, "Draft"
PUBLISHED = 1, "Published"
status = models.IntegerField(choices=Status.choices, default=Status.DRAFT)
# Usage
Post.objects.filter(status=Post.Status.PUBLISHED)
Guard Clauses
Flatten nested logic with early returns:
# Instead of deeply nested if/else
def process_request(request):
if not request.user.is_authenticated:
return redirect("login")
if request.method != "POST":
return render(request, "form.html")
if not form.is_valid():
return render(request, "form.html", {"form": form})
# Happy path — no nesting
form.save()
return redirect("success")