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.
from surrealdb import Null, NullTypeSource: null.py
How NONE and NULL convert
| 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
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.
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)Null
FalseThe 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
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.
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")False False False
no nicknamerepr(Null) is Null. The object is immutable: setting an attribute on it raises AttributeError.
Field types that accept NONE or NULL
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
An open end of a
RangeisNull.Range(None, BoundExcluded(10))storesNullas its start, and a range read back hasNullthere too.In
use(),Nonekeeps the current namespace or database, andNullclears it:db.use(database=Null)leaves the connection with no database selected.
Pydantic models
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.
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)TrueSee also
Python values for every value class
Value types for how every value converts
NONE and NULL in SurrealQL