From String Fields to Real Architecture — Fixing Audit Logging in Django

# webdev# ai# programming# python
From String Fields to Real Architecture — Fixing Audit Logging in DjangoArslon Erkinov

Today I closed Day 17 — Part 1 of my AI Phishing Defense Platform. The system was “working”… but...

Today I closed Day 17 — Part 1 of my AI Phishing Defense Platform.

The system was “working”… but architecturally wrong.
The Problem
Audit logs were storing API keys as plain strings.

That breaks:

  • relational integrity
  • analytics joins
  • usage aggregation
  • long-term scalability

It looked like this:
api_key = models.CharField(max_length=64)

That’s not production-grade.

The Refactor

I replaced it with:

api_key = models.ForeignKey(
APIKey,
null=True,
blank=True,
on_delete=models.SET_NULL,
)

This allowed:

  • proper usage aggregation
  • accurate plan tracking
  • clean JOIN queries
  • future-ready analytics

What Broke

  • As expected, migrations and existing logic exploded:
  • ForeignKey assignment errors
  • IntegrityError on status_code
  • Rate limit filters pointing to wrong fields
  • Audit logger passing strings instead of instances
  • This is normal when upgrading architecture.

What Was Fixed
• Correct FK assignment
• Refactored log_audit_event
• Unified rate limit logic
• Clean APIUsage logging
• Plan-aware audit entries

Now the system tracks:

  • Anonymous
  • Free
  • Pro
  • Status codes
  • Daily usage
  • Endpoint analytics

Lesson

  • Don’t build demos.
  • Build systems that survive refactoring.
  • Architecture debt always shows up later.

Day 17 is about backend maturity.
Tomorrow: analytics expansion layer.