Array Field
The openviper.db.fields module provides a PostgreSQL-native
ArrayField for OpenViper models. It stores homogeneous lists of a
scalar type using PostgreSQL’s ARRAY column type and falls back to
JSON serialisation on other databases.
Overview
PostgreSQL-native -
INTEGER[],VARCHAR[],UUID[], etc.Fallback JSON backend - stores arrays as JSON text on non-PostgreSQL databases (SQLite, MySQL, MariaDB, MSSQL, Oracle).
Type-safe element coercion - each element is converted through the base field’s
to_python()/to_db()methods.Size constraint - optional
sizeparameter caps the maximum number of elements at validation time.Auto-instantiation - pass a Field class (
IntegerField) or an instance (IntegerField()); classes are instantiated with defaults.
Installation
ArrayField is part of the core distribution. No extra packages are
required. Import it directly:
from openviper.db.fields import ArrayField
Basic Usage
Define a model with array fields:
from openviper.db import Model
from openviper.db.fields import AutoField, CharField, IntegerField, UUIDField, ArrayField
class Article(Model):
id = AutoField(primary_key=True)
title = CharField(max_length=200)
tags = ArrayField(CharField(max_length=50))
scores = ArrayField(IntegerField, null=True)
source_entry_ids = ArrayField(UUIDField, null=True)
base_field accepts either a Field instance (for custom options) or
a Field class (auto-instantiated with defaults):
# Instance - custom max_length propagates to each element
tags = ArrayField(CharField(max_length=100))
# Class - auto-instantiated as IntegerField()
scores = ArrayField(IntegerField)
All standard Field keyword arguments (null, default, db_index,
unique, help_text, etc.) are supported:
flags = ArrayField(BooleanField, default=list, null=True)
rankings = ArrayField(IntegerField, size=10)
notes = ArrayField(TextField, db_index=True)
Parameters
base_fieldA
Fieldinstance or class describing the element type. Instances allow custom options (e.g.CharField(max_length=50)); classes are auto-instantiated with defaults (e.g.IntegerFieldbecomesIntegerField()).sizeOptional
int. Maximum number of elements. Enforced byvalidate().None(default) means no limit.
All other keyword arguments (null, blank, unique, default,
db_column, db_index, help_text) are forwarded to the base
Field constructor.
Database Column Types
PostgreSQL
On PostgreSQL, ArrayField maps to the native ARRAY type. The
element type is derived from the base field:
Other Databases
On non-PostgreSQL databases (SQLite, MySQL, MariaDB, MSSQL, Oracle),
arrays are stored as JSON-encoded TEXT columns.
Reading and Writing
# Create
article = await Article.objects.create(
title="ArrayField guide",
tags=["python", "orm", "database"],
scores=[95, 87, 92],
)
# Read - values are automatically deserialised
article = await Article.objects.get(id=1)
article.tags # ["python", "orm", "database"]
article.scores # [95, 87, 92]
# Update
article.tags = ["python", "orm"]
article.scores = [100, 99]
await article.save()
# Filter - use __contains for array membership
results = await Article.objects.filter(tags__contains="python")
Validation
ArrayField.validate() enforces:
Type check - value must be a
listortuple.Null check -
Noneis rejected whennull=False(the default).Size constraint - if
sizeis set,len(value)must not exceed it.Element validation - each element is passed through the base field’s
validate()method.
field = ArrayField(IntegerField, size=5, null=False)
field.name = "scores"
field.validate([1, 2, 3]) # OK
field.validate(None) # raises ValueError
field.validate("not a list") # raises ValueError
field.validate([1, 2, 3, 4, 5, 6]) # raises ValueError (exceeds size=5)
Backend Selection
The backend is selected automatically based on the configured database:
PostgreSQL connection URL detected: uses
PostgresArrayBackendwith nativeARRAYcolumns.All other databases: uses
FallbackJsonBackendwith JSONTEXTcolumns.
The backend is cached after first selection. Use reset_backend() in
tests to force re-evaluation:
from openviper.db.fields import reset_array_backend
# In test teardown
reset_array_backend()
API Reference
- class ArrayField(base_field, size=None, **kwargs)
PostgreSQL-native array field storing homogeneous lists.
- Parameters:
base_field – A Field instance (e.g.
IntegerField()) or class (e.g.IntegerField). Classes are auto-instantiated with defaults.size – Maximum number of elements.
Nonemeans no limit.kwargs – Standard Field keyword arguments (
null,default,db_index, etc.).
- property db_column_type
Returns the DDL column type string. On PostgreSQL this is the base type with
[]appended (e.g.INTEGER[]). On other databases this returnsTEXT.
- to_python(value) list | None
Convert a database value to a Python list. Accepts lists, tuples, JSON strings, and
None. Each element is coerced through the base field’sto_python().
- to_db(value) list | str | None
Prepare a Python list for database storage. On PostgreSQL the list is returned as-is. On other databases the list is JSON-encoded. Each element is first coerced through the base field’s
to_db().
- validate(value) None
Validate the array value. Checks type, null constraint, size constraint, and validates each element through the base field.
- get_sa_type() sqlalchemy.types.TypeEngine
Return the SQLAlchemy column type.
ARRAYon PostgreSQL,Texton other databases.
- class PostgresArrayBackend
PostgreSQL backend using native
ARRAYcolumns.
- class FallbackJsonBackend
Fallback backend storing arrays as JSON text on non-PostgreSQL databases.
- get_array_backend() BaseArrayBackend
Return the appropriate backend based on the configured database. The result is cached; call
reset_array_backend()to force re-evaluation.
- reset_array_backend()
Clear the cached backend. Useful in test teardown.