| name | model-entity-validator |
| description | This skill should be used when the user asks to "create a model", "add a Django model", "create database table", "add entity", "define schema", or when writing class definitions inheriting from models.Model. Validates BaseModel inheritance pattern. |
Model/Entity Validator
Enforces Smicolon's BaseModel inheritance pattern for all Django models.
Activation Triggers
This skill activates when:
- Creating new model files
- Modifying existing models
- Mentioning "model", "database", "schema", "table"
- Writing class inheriting from
models.Model
- Running migrations
- Discussing data structure
Core Principle: BaseModel Inheritance
NEVER repeat UUID/timestamp fields. All models inherit from BaseModel.
class User(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_deleted = models.BooleanField(default=False)
email = models.EmailField()
import core.models as _core_models
class User(_core_models.BaseModel):
email = models.EmailField(unique=True)
Validation Process
Step 1: Check if BaseModel Exists
Before any action, check if the project has a BaseModel:
If BaseModel found: Suggest inheritance (Step 2a)
If BaseModel NOT found: Create BaseModel first (Step 2b)
Step 2a: BaseModel Exists - Suggest Inheritance
When seeing:
class Product(models.Model):
name = models.CharField(max_length=255)
Fix to:
import core.models as _core_models
class Product(_core_models.BaseModel):
"""Product model - inherits id, timestamps, soft delete from BaseModel."""
name = models.CharField(max_length=255)
price = models.DecimalField(max_digits=10, decimal_places=2)
class Meta:
db_table = 'products'
Step 2b: BaseModel NOT Found - Create It First
If no BaseModel exists, create it:
import uuid
from django.db import models
class BaseModel(models.Model):
"""
Abstract base model providing:
- UUID primary key
- Automatic timestamps (created_at, updated_at)
- Soft delete support (is_deleted)
All models MUST inherit from this class.
"""
id = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
editable=False
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_deleted = models.BooleanField(default=False)
class Meta:
abstract = True
ordering = ['-created_at']
def soft_delete(self) -> None:
"""Soft delete the record."""
self.is_deleted = True
self.save(update_fields=['is_deleted', 'updated_at'])
def restore(self) -> None:
"""Restore a soft-deleted record."""
self.is_deleted = False
self.save(update_fields=['is_deleted', 'updated_at'])
class ActiveManager(models.Manager):
"""Manager that excludes soft-deleted records."""
def get_queryset(self):
return super().get_queryset().filter(is_deleted=False)
Report to developer:
BaseModel Created
Created core/models.py with BaseModel. All models should now inherit from it:
import core.models as _core_models
class YourModel(_core_models.BaseModel):
Step 3: Detect Duplicate Fields
If seeing a model with explicit id/timestamp fields that inherits from BaseModel:
class User(_core_models.BaseModel):
id = models.UUIDField(...)
created_at = models.DateTimeField(...)
email = models.EmailField()
Remove them:
class User(_core_models.BaseModel):
email = models.EmailField(unique=True)
Step 4: Validate Indexes
Check if appropriate indexes exist:
class Product(_core_models.BaseModel):
name = models.CharField(max_length=255)
sku = models.CharField(max_length=100, unique=True)
class Meta:
db_table = 'products'
indexes = [
models.Index(fields=['sku']),
models.Index(fields=['name']),
]
Complete Model Examples
Standard Model
from django.db import models
import core.models as _core_models
class Product(_core_models.BaseModel):
"""Product model."""
name = models.CharField(max_length=255)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
sku = models.CharField(max_length=100, unique=True, db_index=True)
is_active = models.BooleanField(default=True)
class Meta:
db_table = 'products'
verbose_name = 'Product'
verbose_name_plural = 'Products'
indexes = [
models.Index(fields=['name']),
models.Index(fields=['is_active', 'is_deleted']),
]
def __str__(self):
return self.name
User Model (with AbstractUser)
import uuid
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
"""
Custom User model with UUID and timestamps.
Note: For User, override AbstractUser directly since it has its own
ID handling. Add the standard fields manually.
"""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_deleted = models.BooleanField(default=False)
email = models.EmailField(unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
db_table = 'users'
Through Model (Many-to-Many)
import core.models as _core_models
class UserOrganization(_core_models.BaseModel):
"""Through model for user-organization relationship."""
user = models.ForeignKey('User', on_delete=models.CASCADE)
organization = models.ForeignKey('Organization', on_delete=models.CASCADE)
role = models.CharField(max_length=50)
class Meta:
db_table = 'user_organizations'
unique_together = [['user', 'organization']]
Custom Managers
BaseModel provides soft delete. Add a custom manager for convenience:
import core.models as _core_models
class Product(_core_models.BaseModel):
name = models.CharField(max_length=255)
objects = models.Manager()
active = _core_models.ActiveManager()
Product.active.all()
Product.objects.all()
Validation Checklist
When reviewing a model, check:
- ✅ Inherits from
BaseModel (or has explicit required fields for special cases)
- ✅ Does NOT duplicate fields from BaseModel
- ✅ Uses absolute import:
import core.models as _core_models
- ✅ Has appropriate
db_table name (singular or plural, snake_case)
- ✅ Has indexes on frequently queried fields
- ✅ Foreign keys have
on_delete specified
- ✅ Has docstring explaining the model
- ✅ Has
__str__ method for admin display
Behavior
Proactive enforcement:
- Check if BaseModel exists FIRST
- Suggest inheritance instead of field duplication
- Remove duplicate fields automatically
- Create BaseModel if missing
- Explain WHY inheritance is better
Never:
- Add duplicate id/timestamp fields to models
- Let developers repeat base fields
- Ignore existing BaseModel in the project