Skip to content

Field

A Field attribute represents a single column on a relation. halfORM exposes all columns automatically as Field instances on every relation object.

Two usage patterns:

  • Read — inspect the current constraint value or schema metadata: field.value, field.name, field.py_type, field.is_set(), field.is_not_null().
  • Write — constrain the field to filter rows: field.set(value), field.set((comp, value)).

Constraints are usually set via keyword arguments when instantiating a relation, but .set() is useful when you need a non-= comparator, unaccent, a column-to-column comparison, or an arithmetic expression:

author = Author()
author.last_name.set(('ilike', 'mar%'), unaccent=True)

post = Post()
post.views.set(('>', post.likes))                       # WHERE views > likes
post.views.set(('>=', Expr('2 * "likes"')))             # WHERE views >= 2 * likes

Setting any field to None removes that constraint.

See Learn halfORM in half an hour for a full walkthrough of filtering and comparators.


SQL expressions

Expr

A raw SQL expression for use with :meth:Field.set.

Quoted column identifiers ("col") are automatically prefixed with the relation alias in SELECT queries. String literals use single quotes in SQL, so double-quoted tokens are unambiguously column references.

Use this when you need arithmetic or any SQL expression that column-to-column :meth:Field.set cannot express::

from half_orm.field import Expr

p = Post()
p.views.set(('>=', Expr('2 * "likes"')))       # views >= 2 * likes
p.score.set(Expr('"likes" * 10 + "views"'))    # score = likes*10 + views

Constraint

set(*args, unaccent=False)

Constrain this field, optionally with a custom comparator.

Parameters

value : The constraint value. Pass None to remove the constraint. Pass a (comparator, value) tuple to use an operator other than =. Supported comparators: =, !=, <, <=, >, >=, like, not like, ilike, not ilike, similar to, not similar to, in, not in, is, is not, is distinct from, is not distinct from, and any operator spelled with PostgreSQL's operator characters — including those added by extensions, such as @@, @>, && or pgvector's <->.

The comparator is interpolated into the WHERE clause rather than
bound as a parameter, so anything else raises ``ValueError``.
Never build one by concatenating untrusted input; for a
condition that genuinely needs arbitrary SQL, pass an
:class:`Expr` as the *value*.

Pass a sibling ``Field`` of the **same** relation instance, or an
:class:`Expr` for arbitrary SQL expressions, to compare columns of
the same table without bound parameters.
bool

When True, wraps both sides in PostgreSQL unaccent() before comparing. Requires the unaccent extension.

Examples::

a = Author()
a.last_name.set('Martin')               # last_name = 'Martin'
a.last_name.set(('ilike', 'mar%'))       # last_name ILIKE 'mar%'
a.last_name.set(('ilike', 'mar%'), unaccent=True)
                                        # unaccent(last_name) ILIKE unaccent('mar%')

from half_orm.null import NULL
a.last_name.set(NULL)                   # last_name IS NULL
a.last_name.set(('is not', NULL))        # last_name IS NOT NULL

a.last_name.set(None)                   # removes the constraint

# Column-to-column (same relation instance)
p = Post()
p.views.set(p.likes)                              # views = likes
p.views.set(('>', p.likes))                       # views > likes

# Arbitrary SQL expression
from half_orm.field import Expr
p.views.set(('>=', Expr('2 * "likes"')))          # views >= 2 * likes
p.score.set(Expr('"likes" * 10 + "views"'))       # score = likes*10 + views

Inspection

is_set()

Return True if this field currently carries a constraint.

A field is set after a call to :meth:set with a non-None value, or when the relation was instantiated with a keyword argument for this column. Setting the value back to None clears the constraint.

Example::

a = Author()
a.last_name.is_set()              # False
a.last_name.set('Martin')
a.last_name.is_set()              # True
a.last_name.set(None)
a.last_name.is_set()              # False

value property

The current constraint value, or None if the field is not set.

Use this to read back a value that was set on a row returned by :meth:~half_orm.relation.Relation.ho_get or extracted from an :meth:~half_orm.relation.Relation.ho_select result::

author = Author(last_name='Martin').ho_get()
author.last_name.value    # 'Martin'
author.first_name.value   # 'Alice'

name property

The column name as it appears in the database.

py_type property

The Python type that maps to this column's SQL type.

Array columns (PostgreSQL _type) are returned as typing.List[inner_type]. Unknown SQL types fall back to typing.Any.

Example::

Author().last_name.py_type   # <class 'str'>
Post().tags.py_type          # typing.List[str]  (if tags is text[])

is_not_null()

Return True if the column is declared NOT NULL in the schema.

This reflects the database constraint, not the current value.

Example::

Author().last_name.is_not_null()   # True  (declared NOT NULL)
Author().email.is_not_null()       # True
Post().content.is_not_null()       # False (nullable column)

unaccent property writable

Whether unaccent() is applied to this field's comparison.

Can also be set via the unaccent keyword argument of :meth:set. Requires the PostgreSQL unaccent extension to be installed.


JSON column schema

For json / jsonb columns, the internal structure can be documented directly in the PostgreSQL column comment using an @json block. halfORM parses this block at load time and exposes it as Field.json_schema.

Comment format

The block uses a fenced YAML code block introduced by @json:

COMMENT ON COLUMN blog.post.data IS
'Post metadata.
@json
```yaml
lang:  text    # ISO 639-1 language code
views: integer
tags:  [text]
items:
  - id:   uuid
    name: text
```
';

Type notation inside the block:

Syntax Meaning
field: text scalar — PostgreSQL type name
field: [text] array of scalars
field: followed by - key: type array of objects
field: followed by key: type nested object

YAML comments (#) are supported for inline documentation.

The block is delimited by ```yaml and ```. If the closing ``` is absent, the block extends to the end of the comment.

This information is displayed by repr() under the field line, and is accessible programmatically via Field.json_schema for use in code generators (ho_dataclasses, ho_typeddicts).

json_schema property

Parsed structure from the @json block in the column comment, or None.

Returns the YAML structure as a Python object (dict/list/str) when the column comment contains an @json block::

@json
```yaml
lang: text          # ISO 639-1
views: integer
tags: [text]
items:
  - id: uuid
    name: text
```

Returns None when no @json block is present.