> Full SurrealDB documentation index: https://surrealdb.com/docs/llms.txt

# Null

Null and NullType in the Python SDK: SurrealDB NULL as a Python value, separate from None for NONE, keeping NULL fields when a record is read and written back, testing with is Null, field types that accept NONE or NULL, and NullType in pydantic models.

SurrealDB has two values for a field that holds nothing. `NONE` means that the field is not there, and `NULL` means that the field is there and holds a null value. Python has only `None`, so the SDK uses `None` for `NONE` and the `Null` object for `NULL`. This page documents `Null` and how the two values behave when you read and write records.

```python title="Import"
from surrealdb import Null, NullType
```

**Source:** [null.py](https://github.com/surrealdb/surrealdb.py/blob/main/src/surrealdb/data/types/null.py)

---

## How NONE and NULL convert {#conversion}

| Python | SurrealDB | Read back as |
|---|---|---|
| `None` | `NONE` | `None`. A field that is `NONE` is not in the record at all. |
| `Null` | `NULL` | `Null` |

`Null` is the only instance of `NullType`. Calling `NullType()` returns the same object, and copying or pickling `Null` gives `Null` again, so a test with `is Null` always works.

---

## Reading and writing records {#read-modify-write}

A `NULL` field reads as `Null`, so a record that you read, change and write back keeps the field as `NULL`. A field set to `None` is sent as `NONE`, which removes the field from the record.

```python
from surrealdb import Null, RecordID, Surreal

with Surreal("ws://localhost:8000") as db:
    db.signin({"username": "root", "password": "secret"})
    db.use("surrealdb", "docs")

    tobie = RecordID("person", "tobie")
    db.create(tobie, {"name": "Tobie", "nickname": Null})

    person = db.select(tobie).execute()
    person["name"] = "Tobie Morgan"
    saved = db.update(tobie, person)
    print(saved["nickname"])

    saved = db.update(tobie, {"name": "Tobie", "nickname": None})
    print("nickname" in saved)
```

```text title="Output"
Null
False
```

The first `update()` writes the record back with its `NULL` field. The second sends `None` for the field, so the stored record no longer has a `nickname`.

---

## Testing for `Null` {#testing}

`Null` is falsy, like `None`, so `if not value:` treats both as empty. `Null` is not equal to `None`, so a test with `is None` or `== None` does not find a `NULL` field. Where a field can hold either value, test for both.

```python
from surrealdb import Null

print(bool(Null), Null == None, Null is None)

nickname = Null
if nickname is None or nickname is Null:
    print("no nickname")
```

```text title="Output"
False False False
no nickname
```

`repr(Null)` is `Null`. The object is immutable: setting an attribute on it raises `AttributeError`.

---

## Field types that accept NONE or NULL {#field-types}

A schemafull field accepts only the empty value that its type names. `option<T>` accepts `NONE`, and a union with `null` accepts `NULL`.

| Field type | `None` (`NONE`) | `Null` (`NULL`) |
|---|---|---|
| `string` | Refused | Refused |
| `option<string>` | Accepted, the field is left out | Refused |
| `string \| null` | Refused | Accepted |
| `option<string \| null>` | Accepted, the field is left out | Accepted |

A refused value raises a `ServerError` that names the field and the type it expected.

---

## Other places where `Null` appears {#other-uses}

- An open end of a [`Range`](/docs/reference/python/api/values/range.md) is `Null`. `Range(None, BoundExcluded(10))` stores `Null` as its start, and a range read back has `Null` there too.
- In [`use()`](/docs/reference/python/api/core/surreal.md#use), `None` keeps the current namespace or database, and `Null` clears it: `db.use(database=Null)` leaves the connection with no database selected.

---

## Pydantic models {#pydantic}

Pydantic has no built-in schema for `NullType`. To read a field that can hold `NULL` into a model, add `NullType` to the field's annotation and allow arbitrary types on the model. Without `NullType` in the annotation, a `NULL` value fails validation for a field such as `str | None`.

```python
from pydantic import BaseModel, ConfigDict
from surrealdb import Null, NullType

class Person(BaseModel):
    model_config = ConfigDict(arbitrary_types_allowed=True)

    name: str
    nickname: str | NullType | None = None

person = Person(name="Tobie", nickname=Null)
print(person.nickname is Null)
```

```text title="Output"
True
```

---

## See also

- [Python values](/docs/reference/python/api/values.md) for every value class
- [Value types](/docs/reference/python/concepts/value-types.md) for how every value converts
- [NONE and NULL](/docs/reference/query-language/language-primitives/data-types/none-and-null.md) in SurrealQL
