Skip to main content
Back to Notes

What HIPAA Compliance Actually Means in Django Code

DjangoHealthcareHIPAASecurityPython

When I built an automation platform for a healthcare organization managing multiple practices, "HIPAA compliance" wasn't just a checkbox — it shaped every technical decision. Here's what it actually looks like in code.

What HIPAA Requires (That Developers Miss)

Most developers know about data encryption. Fewer think about:

  • Audit trails — every read and write of patient data must be logged with who, when, and what changed
  • Minimum necessary access — users should only see data they need for their specific role
  • Automatic session timeout — idle sessions must expire
  • Encrypted data at rest and in transit — not just HTTPS, but database-level encryption for PHI fields

Audit Logging with Django Signals

The most important pattern: log every change to patient data automatically.

from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver

@receiver(post_save, sender=PatientRecord)
def log_patient_record_change(sender, instance, created, **kwargs):
    AuditLog.objects.create(
        user=get_current_user(),  # middleware sets this
        action="created" if created else "updated",
        model="PatientRecord",
        object_id=str(instance.id),
        timestamp=timezone.now(),
        ip_address=get_current_ip(),
    )

I use a middleware that stores the current request user in thread-local storage so signals can access it without being passed explicitly.

Field-Level Encryption for PHI

Store protected health information encrypted, not in plaintext:

from django_cryptography.fields import encrypt

class PatientRecord(models.Model):
    # Encrypted at the database level
    date_of_birth = encrypt(models.DateField())
    ssn_last_four = encrypt(models.CharField(max_length=4))
    diagnosis_notes = encrypt(models.TextField(blank=True))

    # Non-PHI fields — no encryption needed
    practice_id = models.ForeignKey(Practice, on_delete=models.PROTECT)
    created_at = models.DateTimeField(auto_now_add=True)

Django-cryptography handles key management and transparently encrypts/decrypts. The raw database never contains readable PHI.

Role-Based Access with Django Permissions

class PatientRecordViewSet(viewsets.ModelViewSet):
    def get_queryset(self):
        user = self.request.user

        # Admins see all records in their practice
        if user.has_perm("records.view_all"):
            return PatientRecord.objects.filter(practice=user.practice)

        # Staff only see records assigned to them
        return PatientRecord.objects.filter(
            practice=user.practice,
            assigned_staff=user,
        )

Never trust the frontend to filter data — always enforce at the queryset level.

Handling Celery Tasks with PHI

Never pass patient data directly into task arguments — task arguments are stored in Redis in plaintext:

# BAD — PHI sits in Redis queue
sync_patient.delay(patient_name="John Doe", dob="1990-01-01", ssn="123")

# GOOD — pass only the ID, fetch data inside the task
sync_patient.delay(patient_id="a1b2c3")

@celery_app.task
def sync_patient(patient_id: str):
    patient = PatientRecord.objects.get(id=patient_id)
    # Now data is fetched securely, not stored in queue

The Mindset Shift

HIPAA compliance isn't a feature you add at the end — it's a constraint that shapes your architecture from day one. Every time I model data, I ask: is this PHI? Who should see it? What happens if it leaks?

The systems that fail compliance audits aren't usually doing something obviously wrong. They're doing something reasonable that wasn't thought through: logging too much, caching things they shouldn't, or assuming the ORM handles security automatically.

It doesn't. You have to be deliberate.