Skip to content

Latest commit

 

History

History
178 lines (123 loc) · 4.88 KB

items.rst

File metadata and controls

178 lines (123 loc) · 4.88 KB

Items

An item is a persisted object with one or more attributes that make up a schema.

Attributes

Axiom comes with the usual suspects of attributes:

  • text for (Unicode) text
  • bytes for bytes
  • integer for integer values
  • pointXdecimal (X varying from 1 to 10) for decimals of varying precision
  • ieee754_double for floating point values
  • timestamp for absolute points in time

It also comes with a few slightly more advanced ones:

  • reference which allows you to store a reference to any other Axiom item (not just those of a particular type)
  • textlist which allows you to store lists of (Unicode) text in one field
  • path which allows you to store both relative and absolute paths
  • inmemory which allows you to store non-persisted/non-persistable state on the item instance

All of these will be covered throughout the book.

Creating items and accessing attributes

Creating an item class is done by subclassing axiom.item.Item, and assigning at least one attribute from axiom.attributes to a name in the class body:

.. literalinclude:: person.py
   :language: python

.. testsetup::

    from person import Person

You can then create instances of that item by calling the Item class and specifying the attributes as keyword arguments. You can access the attributes on the item just like regular attributes:

>>> alice = Person(name=u"Alice")
>>> assert alice.name == u"Alice"

Static, strong typing

Axiom item attributes are not just statically typed but also strongly typed. For example, you can't assign a bytestring to a text attribute:

>>> Person(name="a bytestring")
Traceback (most recent call last):
   ...
ConstraintError: attribute [Person.name = text()] must be (unicode string without NULL bytes); not 'str'
>>> alice = Person(name=u"Alice")
>>> alice.name = "another bytestring"
Traceback (most recent call last):
   ...
ConstraintError: attribute [Person.name = text()] must be (unicode string without NULL bytes); not 'str'

Defaults and unspecified values

You're allowed to not specify an attribute, which sets it to None:

>>> anonymous = Person()
>>> assert anonymous.name is None

If you don't want to allow None as a value, set allowNone=False on the attribute:

.. literalinclude:: coin.py
    :language: python

.. testsetup::

    from coin import Coin
    import decimal

>>> Coin()
Traceback (most recent call last):
   ...
TypeError: attribute [Coin.value = money()] must not be None
>>> quarter = Coin(value=decimal.Decimal("0.25"))

You can also have default values. For example, we could have a petting zoo with bunnies, and bunnies start out having been petted zero times:

.. literalinclude:: bunny.py
    :language: python

.. testsetup::

    from bunny import Bunny

>>> thumper = Bunny()
>>> assert thumper.timesPetted == 0  # Aww :-(
>>> thumper.timesPetted += 1
>>> assert thumper.timesPetted == 1  # Yay :-)

Sometimes a default value isn't enough, and you need a default value factory that gets called when the item gets created. Let's recite the alphabet:

.. literalinclude:: alphabet.py
    :language: python

.. testsetup::

    from alphabet import Letter

>>> a, b, c = Letter(), Letter(), Letter()
>>> assert a.value == "a"
>>> assert b.value == "b"
>>> assert c.value == "c"

Type names

Axiom item classes have a "type name", which is the unique name used to identify instances of it in an Axiom store, and distinguish them from other item classes. If you don't explicitly specify a type name, one is automatically generated based on the name of the class and the module it's in. These are put into lower case and concatenated with underscores. This is done because the type name will be a SQLite table name, and therefore has to be a valid SQL identifier.

>>> Bunny.typeName
'bunny_bunny'

The module is called bunny. In this documentation, there are no modules above it; if your module is in a package, it could be something like 'top_mid_leaf_class'.

There are some benefits to specifying an explicit type name. For example, you will be able to change the structure of your package, or move items to different, independent packages.

You can specify the type name using the typeName class attribute:

.. testsetup::

    from axiom import attributes, item

>>> class MobileBunny(item.Item):
...     typeName = "mobile_bunny"
...     timesPetted = attributes.integer()
>>> MobileBunny.typeName
'mobile_bunny'

Note

There's no ubiquitous standard for type names. These will map to SQL tables, so whatever your preference is for those might win out.