# SM001: not_null_without_default

| Property       | Value                    |
| -------------- | ------------------------ |
| **Rule ID**    | SM001                    |
| **Severity**   | ERROR                    |
| **Name**       | not_null_without_default |
| **Applies to** | All databases            |

## What Triggers This Rule

This rule triggers when a migration contains `AddField` with a field that:

1. Does not allow `NULL` (no `null=True`)
2. Does not have a `default` value
3. Is being added to an existing table

```python
# Triggers SM001
migrations.AddField(
    model_name='user',
    name='email',
    field=models.CharField(max_length=255),  # NOT NULL, no default
)
```

## Why It's Dangerous

When you add a NOT NULL column without a default to an existing table:

### PostgreSQL

1. The database acquires an **ACCESS EXCLUSIVE** lock on the table
2. All existing rows must be updated to add the new column
3. The operation fails because existing rows would have NULL values

### MySQL/MariaDB

1. The table is completely rewritten
2. Write operations are blocked during the rewrite
3. On large tables, this can take minutes to hours

### Real-World Failure Scenario

```
Timeline:
├─ 0:00  Migration starts
├─ 0:00  Table locked (all queries blocked)
├─ 0:01  ERROR: column "email" contains null values
├─ 0:01  Migration rolled back
└─ 0:01  Deployment failed
```

Or worse, if the table is empty in development but has millions of rows in production:

```
Timeline (production):
├─ 0:00   Migration starts
├─ 0:00   Table locked
├─ 0:05   Still rewriting table...
├─ 0:10   Users seeing timeouts...
├─ 0:15   Monitoring alerts firing
├─ 0:20   Table rewrite completes
└─ 0:20   Application recovers
```

## When It's Safe to Ignore

This warning can be safely ignored when:

1. **The table is empty** — No rows need updating
2. **It's a new table** — Created in the same migration
3. **You're using maintenance mode** — No traffic during deployment
4. **The field has a database-level default** — Using `db_default` (Django 5.0+)

## How to Fix It

### Option 1: Add as Nullable First (Recommended)

Split into three migrations:

```python
# Migration 1: Add nullable field
migrations.AddField(
    model_name='user',
    name='email',
    field=models.CharField(max_length=255, null=True),
)
```

```python
# Migration 2: Backfill data
def backfill_emails(apps, schema_editor):
    User = apps.get_model('myapp', 'User')
    User.objects.filter(email__isnull=True).update(email='unknown@example.com')

migrations.RunPython(backfill_emails, migrations.RunPython.noop)
```

```python
# Migration 3: Add NOT NULL constraint
migrations.AlterField(
    model_name='user',
    name='email',
    field=models.CharField(max_length=255),  # Now safe
)
```

### Option 2: Provide a Default Value

```python
migrations.AddField(
    model_name='user',
    name='status',
    field=models.CharField(max_length=20, default='active'),
)
```

### Option 3: Use db_default (Django 5.0+)

```python
migrations.AddField(
    model_name='user',
    name='created_at',
    field=models.DateTimeField(db_default=Now()),
)
```

## How to Suppress

### In Django Settings

```python
SAFE_MIGRATIONS = {
    "DISABLED_RULES": ["SM001"],
}
```

### Using Severity Override

```python
SAFE_MIGRATIONS = {
    "RULE_SEVERITY": {
        "SM001": "WARNING",  # Downgrade to warning
    },
}
```

## See Also

- [Safe Patterns: Adding a NOT NULL Column](../patterns.md#adding-a-not-null-column)
- [SM004: alter_column_type](SM004.md) — Related rule for type changes
