# Write Operations — Spec

## Overview

Add write operations to Cendry: `save`, `create`, `delete`. This is the v2 scope — the library is currently read-only.

## Operations

### `ctx.save(instance)`

Upsert — insert or update. Uses Firestore's `set()`.

```python
city = City(name="SF", state="CA", ..., id="SF")
ctx.save(city)
```

- If `id` is set, writes to that document ID.
- If `id` is None, Firestore auto-generates an ID and sets it on the instance.
- Uses the type handler system for serialization (serialize via handlers, aliases, enum conversion).

### `ctx.create(instance)`

Insert only — raises if the document already exists.

```python
city = City(name="SF", state="CA", ..., id="SF")
ctx.create(city)  # raises if "SF" already exists
```

### `ctx.delete(instance)` / `ctx.delete(ModelClass, doc_id)`

Delete a document.

```python
ctx.delete(city)              # by instance
ctx.delete(City, "SF")        # by class + ID
```

### Async variants

All operations available on `AsyncCendry` as `async def`.

## Serialization

Write operations use `to_dict(instance, by_alias=True)` to serialize — Firestore stores aliased field names. The type handler `serialize` methods are called for custom types. Enums are converted by value/name. Sets/tuples are converted to lists.

## Validation

- `save` and `create` raise `CendryError` if the instance has required fields set to `None`.
- `delete` by instance raises `CendryError` if `id` is `None`.

## Subcollections

`parent=` parameter works the same as read operations.

## Topics to brainstorm

- Batch writes (`ctx.save_many`, `ctx.delete_many`)
- Transactions
- `ctx.update(instance, **fields)` for partial updates
- Optimistic locking / preconditions
