Skip to content
Back to skills

Model Entity Validator

ASecurity

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.

  • 6 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 28, 2026
ai-agentspythongodjangogitdatabase

Security analysis

A100/100

Scanned May 28, 2026

npx -y skills add smicolon/ai-kit --skill model-entity-validator --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Model Entity Validator?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Model Entity Validator
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/smicolon-model-entity-validator/badge)](https://www.skillsdirectory.com/skills/smicolon-model-entity-validator)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
---
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.

```python
# ❌ WRONG - Repeating fields
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()

# ✅ CORRECT - Inherit from BaseModel
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:

```python
# Search for BaseModel in (in order):
# 1. core/models.py
# 2. shared/models.py
# 3. common/models.py
# 4. {app}/base.py
```

**If BaseModel found:** Suggest inheritance (Step 2a)
**If BaseModel NOT found:** Create BaseModel first (Step 2b)

### Step 2a: BaseModel Exists - Suggest Inheritance

When seeing:
```python
class Product(models.Model):
    name = models.CharField(max_length=255)
```

Fix to:
```python
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:

```python
# core/models.py
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:
> ```python
> import core.models as _core_models
>
> class YourModel(_core_models.BaseModel):
>     # Your fields here - DO NOT add id, created_at, updated_at, is_deleted
> ```

### Step 3: Detect Duplicate Fields

If seeing a model with explicit id/timestamp fields that inherits from BaseModel:

```python
# ❌ WRONG - Duplicate fields
class User(_core_models.BaseModel):
    id = models.UUIDField(...)  # Already in BaseModel!
    created_at = models.DateTimeField(...)  # Already in BaseModel!
    email = models.EmailField()
```

Remove them:
```python
# ✅ CORRECT - Only custom fields
class User(_core_models.BaseModel):
    email = models.EmailField(unique=True)
```

### Step 4: Validate Indexes

Check if appropriate indexes exist:

```python
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']),  # Suggest for unique lookups
            models.Index(fields=['name']),  # Suggest for search
        ]
```

## Complete Model Examples

### Standard Model

```python
# products/models.py
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)

```python
# users/models.py
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)

```python
# users/models.py
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:

```python
import core.models as _core_models

class Product(_core_models.BaseModel):
    name = models.CharField(max_length=255)

    objects = models.Manager()  # All records
    active = _core_models.ActiveManager()  # Excludes soft-deleted

# Usage
Product.active.all()  # Only non-deleted
Product.objects.all()  # All including deleted
```

## Validation Checklist

When reviewing a model, check:

1. ✅ Inherits from `BaseModel` (or has explicit required fields for special cases)
2. ✅ Does NOT duplicate fields from BaseModel
3. ✅ Uses absolute import: `import core.models as _core_models`
4. ✅ Has appropriate `db_table` name (singular or plural, snake_case)
5. ✅ Has indexes on frequently queried fields
6. ✅ Foreign keys have `on_delete` specified
7. ✅ Has docstring explaining the model
8. ✅ 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

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.