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.