Define a schema
Subclass BaseSchema from polynom.schema.schema, set entity_name to the table name, and list the columns in fields. Set namespace_name to choose the relational namespace. Decorate the class with polynom_schema from polynom.schema.schema_registry so an application can create the table.
Each schema includes _entry_id, a string UUID stored as a primary key column. PolyNOM adds it to the declared fields. Although DataModel defines RELATIONAL, DOCUMENT, and GRAPH, the schema and query generators in 0.4.0 implement relational mappings.
Fields and constraints
The field constructors in polynom.schema.field take a database column name and, for ordinary and primary key fields, an instance of a PolyNOM type adapter.
| Class | Arguments and behavior |
|---|---|
Field(db_field_name, polytype, nullable=True, default=None, unique=False, python_field_name=None, previous_name=None) |
Defines an ordinary column. python_field_name changes the model attribute name. previous_name can identify a renamed field to the migrator. Give the column a type-compatible literal default. |
PrimaryKeyField(db_field_name, polytype, unique=False, python_field_name=None, previous_name=None) |
Marks a non-null column as part of the primary key. Additional primary key fields join the automatic _entry_id in the generated key. Query.get() still looks up the first primary key field, which is normally _entry_id. |
ForeignKeyField(db_field_name, referenced_schema, referenced_db_field_name="_entry_id", nullable=True, unique=False, python_field_name=None, previous_name=None) |
Defines a foreign key and uses the referenced field’s type. Pass a registered schema class and use the default _entry_id when referring to its generated key. |
Register referenced schemas before application startup. PolyNOM orders the schemas by their foreign key dependencies. Cycles raise an error during startup. In 0.4.0, the foreign key constructor does not reliably pass the unique and python_field_name arguments through to the base field. Use a column whose database and Python names match for this field type, and manage an additional uniqueness constraint explicitly if needed.
Type adapters
Import type adapters from polynom.schema.polytypes. These adapters describe a SQL column and handle conversions between the mapped attribute and the Prism value.
| Adapter | Python attribute | SQL column |
|---|---|---|
TinyInt(), SmallInt(), Integer(), BigInt() |
int |
TINYINT, SMALLINT, INTEGER, BIGINT |
Boolean() |
bool |
BOOLEAN |
Real(), Double() |
float |
REAL, DOUBLE |
Decimal(precision=64, scale=32) |
decimal.Decimal |
DECIMAL(precision, scale) |
VarChar(length), Text() |
str |
VARCHAR(length), TEXT |
Date(), Time(precision=0), Timestamp(precision=0) |
datetime.date, datetime.time, datetime.datetime |
DATE, TIME, TIMESTAMP |
Json() |
dict |
JSON serialized into TEXT |
PolyEnum(python_enum) |
A member of the supplied enum.Enum class |
Member name stored in TEXT |
File() |
bytes |
FILE |
Geometry() |
A Shapely geometry | GeoJSON serialized into TEXT |
The database enforces type and column constraints. Change-log serialization converts decimal values to floating-point numbers, so a logged decimal may lose precision.
Map a model
Import BaseModel from polynom.model.model. Set the model’s schema and implement a constructor that accepts its Python field names as keyword arguments. Accept _entry_id=None and pass it to the base constructor. A new model gets a generated UUID, and a loaded model receives the UUID from its row.
Call Model.query(session) to create a query for its schema. Use FlexModel.from_schema(schema) when you want a model class created dynamically from a schema. The resulting class accepts its fields as keyword arguments.
Use the polynom_model decorator from polynom.model.model_registry to register a model by its fully qualified name. Registration is optional when other code uses the class directly.
Relationships
Relationship(target_model, back_populates=None, cascade=None) from polynom.model.relationship describes a reference to another model instance. Pass a model class or the fully qualified name of a registered model. Its target_model property resolves the class. Assigning an instance validates the model type and can maintain a reverse reference. A reverse attribute named by back_populates must exist on the target. It can hold a single model or a list.
Declare a ForeignKeyField for the persisted link, and set its ID when creating or updating a row. Setting a relationship attribute updates in-memory references. It neither writes the foreign key field nor queries the database when read. Retrieve related rows explicitly with their schema queries.
This example constructs and links two models in memory. It does not require a server or create tables.
from polynom.model.model import BaseModel, FlexModel
from polynom.model.relationship import Relationship
from polynom.schema.field import Field, ForeignKeyField
from polynom.schema.polytypes import VarChar
from polynom.schema.schema import BaseSchema
class AuthorSchema(BaseSchema):
entity_name = "authors"
fields = [Field("name", VarChar(80), nullable=False)]
class BookSchema(BaseSchema):
entity_name = "books"
fields = [
Field("title", VarChar(120), nullable=False),
ForeignKeyField("author_id", AuthorSchema, nullable=False),
]
class Author(BaseModel):
schema = AuthorSchema()
def __init__(self, name, _entry_id=None):
super().__init__(_entry_id)
self.name = name
self.books = []
class Book(BaseModel):
schema = BookSchema()
author = Relationship(Author, back_populates="books")
def __init__(self, title, author_id, _entry_id=None):
super().__init__(_entry_id)
self.title = title
self.author_id = author_id
author = Author("Octavia Butler")
book = Book("Kindred", author._entry_id)
book.author = author
assert book.author is author
assert book in author.books
assert book.author_id == author._entry_id
BookRow = FlexModel.from_schema(BookSchema())
row = BookRow(title=book.title, author_id=book.author_id)
print(row.title)
Cascade behavior
The relationship and session code recognizes save-update and all when adding related objects, and delete-orphan when detaching or replacing references in certain tracked cases. These operate on Python objects in a session. They require the relationship and reverse references to be configured and the relevant objects to belong to the session. Database cascade constraints have separate behavior.
The lower-level Cascade helper in polynom.schema.cascade can record list membership with track_initial_state() and report removed elements with detect_orphans(). It does not schedule database operations by itself. Sessions and queries