Good Code
The good version chooses field types that match the data, links the author through a real relationship, and pushes uniqueness and publication rules into the database.
Lesson 02
Use precise fields, validation intent, and database constraints so the model protects real domain rules.
from django.conf import settings
from django.db import models
from django.db.models import Q
class Review(models.Model):
class Status(models.TextChoices):
DRAFT = "draft", "Draft"
PUBLISHED = "published", "Published"
title = models.CharField(max_length=120)
slug = models.SlugField(max_length=140)
status = models.CharField(
max_length=20,
choices=Status.choices,
default=Status.DRAFT,
)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
published_at = models.DateTimeField(null=True, blank=True)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["author", "slug"],
name="unique_review_slug_per_author",
),
models.CheckConstraint(
condition=Q(status="draft") | Q(published_at__isnull=False),
name="published_reviews_have_date",
),
]from django.db import models
class Review(models.Model):
title = models.TextField(null=True, blank=True)
slug = models.CharField(max_length=255, null=True, blank=True)
status = models.CharField(max_length=255, null=True, blank=True)
author_id = models.IntegerField()
published_at = models.DateTimeField(null=True, blank=True)The good version chooses field types that match the data, links the author through a real relationship, and pushes uniqueness and publication rules into the database.
The bad version stores almost everything as optional text or raw IDs. It leaves important invariants to scattered application code.