Поля (Fields)¶
Поля определяют типы колонок и их поведение. Каждое поле — это дескриптор Python, который управляет валидацией, сериализацией и DDL-генерацией.
Скалярные поля¶
Integer / BigInteger / SmallInteger¶
from backend.base.system.dotorm.dotorm.fields import (
Integer, BigInteger, SmallInteger
)
class Product(DotModel):
__table__ = "products"
quantity: int = Integer(default=0)
price_cents: int = BigInteger()
sort_order: int = SmallInteger(default=0)
| Поле | PostgreSQL | Python |
|---|---|---|
Integer |
INTEGER |
int |
BigInteger |
BIGINT |
int |
SmallInteger |
SMALLINT |
int |
Char / Text¶
from backend.base.system.dotorm.dotorm.fields import Char, Text
class User(DotModel):
__table__ = "users"
name: str = Char(max_length=255, required=True) # VARCHAR(255) NOT NULL
login: str = Char(max_length=100, unique=True) # VARCHAR(100) UNIQUE
bio: str = Text() # TEXT
Boolean¶
is_active: bool = Boolean(default=True) # BOOLEAN DEFAULT true
is_deleted: bool = Boolean(default=False)
Datetime / Date / Time¶
from datetime import datetime, timezone
from backend.base.system.dotorm.dotorm.fields import Datetime, Date, Time
class Task(DotModel):
__table__ = "tasks"
due_date: date = Date() # DATE
reminder_time: time = Time() # TIME
# default — callable, который вернёт значение во время сохранения.
# Строка "now" не сработает — нужен именно lambda с datetime.now().
created_at: datetime = Datetime(default=lambda: datetime.now(timezone.utc))
updated_at: datetime = Datetime(default=lambda: datetime.now(timezone.utc))
Float / Decimal¶
from backend.base.system.dotorm.dotorm.fields import Float, Decimal
weight: float = Float() # FLOAT
price: float = Decimal(precision=10, scale=2) # NUMERIC(10, 2)
JSONField¶
from backend.base.system.dotorm.dotorm.fields import JSONField
metadata: list | dict = JSONField(default={}) # JSONB DEFAULT '{}'
tags: list | dict = JSONField(default=[]) # JSONB DEFAULT '[]'
asyncpg и JSONB
asyncpg не десериализует JSONB автоматически. DotORM делает json.loads() при чтении, если значение пришло строкой.
Selection¶
Поле с ограниченным набором значений:
from backend.base.system.dotorm.dotorm.fields import Selection
class Chat(DotModel):
__table__ = "chats"
chat_type: str = Selection(
options=[
("direct", "Личный"),
("group", "Группа"),
("channel", "Канал"),
("record", "Чат записи"),
],
default="group",
required=True,
)
В PostgreSQL это VARCHAR с валидацией на уровне ORM. Значения хранятся как строки ("direct", "group", ...).
Параметры полей¶
Все поля наследуют общие параметры от базового Field:
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
required |
bool |
False |
NOT NULL в DDL |
default |
any |
None |
Значение по умолчанию |
unique |
bool |
False |
UNIQUE constraint |
index |
bool |
False |
Создать индекс |
store |
bool |
True |
Хранить в БД (False для computed) |
readonly |
bool |
False |
Не обновлять через API |
name: str = Char(
max_length=255,
required=True, # NOT NULL
unique=True, # UNIQUE
index=True, # CREATE INDEX
)
created_at: datetime = Datetime(
default="now",
readonly=True, # нельзя изменить через API
)
Поля связей¶
Many2one (FK)¶
Внешний ключ — ссылка на одну запись другой таблицы:
from backend.base.system.dotorm.dotorm.fields import Many2one
class ChatMessage(DotModel):
__table__ = "chat_messages"
# FK → chats.id
chat_id: "Chat" = Many2one["Chat"](
relation_table="chats",
required=True,
)
# FK → users.id (nullable)
author_user_id: "User" = Many2one["User"](
relation_table="users",
)
При чтении:
msg = await ChatMessage.get(1)
msg.chat_id # int (FK value) — при обычном чтении
msg.chat_id.name # str — при чтении с nested fields
One2many¶
Обратная связь — список дочерних записей:
from backend.base.system.dotorm.dotorm.fields import One2many
class Chat(DotModel):
__table__ = "chats"
messages: list["ChatMessage"] = One2many["ChatMessage"](
relation_table="chat_messages", # таблица дочерних записей
relation_table_field="chat_id", # FK в дочерней таблице
)
One2many не создаёт колонку
One2many — виртуальное поле. Оно не генерирует колонку в БД, а только определяет связь для чтения.
Many2many¶
Связь многие-ко-многим через промежуточную таблицу:
from backend.base.system.dotorm.dotorm.fields import Many2many
class User(DotModel):
__table__ = "users"
role_ids: list["Role"] = Many2many["Role"](
relation_table="roles", # целевая таблица
many2many_table="user_roles", # промежуточная таблица
column1="user_id", # FK на текущую модель
column2="role_id", # FK на целевую модель
)
Промежуточная таблица создаётся автоматически:
CREATE TABLE IF NOT EXISTS user_roles (
user_id INTEGER REFERENCES users(id),
role_id INTEGER REFERENCES roles(id),
PRIMARY KEY (user_id, role_id)
);
One2one¶
Связь один-к-одному: