What changed in version 6.1


To upgrade a production system, install the new system in a new virtualenv, then migrate your database:

reahl migratedb etc

A new way of exposing Fields and Events

Reahl’s Fields (or Events) can be defined on a normal model object.

The usual way of defining extra semantics for an attribute of instances of a particular class is to define the attribute in class scope in a special way, as is done by SqlAlchemy, Django ORM, etc.

Since a Reahl programmer could use those tools as well for the same attribute on a given class, the same plan cannot also be implemented for designating an attribute as being a Reahl Field, for example.

Initially, this was accomplished using the exposed() decorator, but we felt this is a very unfamiliar way of defining such behaviour.

In this release, the exposed() decorator is deprecated in favour of a new plan that is more aligned with how well known frameworks work.

Previously exposed() was used to define attributes in a instance-side method:

class Address(Base):

    email_address = Column(UnicodeText) # For persistence using SqlAlchemy
    name          = Column(UnicodeText) # For persistence using SqlAlchemy

    def fields(self, fields):
        fields.name = Field(label='Name', required=True)
        fields.email_address = EmailField(label='Email', required=True)

The new ExposedNames is now used to create a namespace where attributes can be defined just for Reahl:

class Address(Base):

    email_address = Column(UnicodeText) # For persistence using SqlAlchemy
    name          = Column(UnicodeText) # For persistence using SqlAlchemy

    fields = ExposedNames()
    fields.name = lambda i: Field(label='Name', required=True)
    fields.email_address = lambda i: EmailField(label='Email', required=True)

Note in the above that attributes are not just assigned an instance of Field though. Instead assign a callable that will instantiate the necessary Field when called.

This is done to allow the use of instance-side data for Fields, for example:

class Address(Base):


    fields = ExposedNames()
    fields.date_of_birth = lambda i: EmailField(label=f'Date {i.name} was born', required=True)

Another advantage of this new approach is that, when used for Event’s, the defined Event names can be used as-is without an instance to declare transitions:

class Address(Base):

    events = ExposedNames()
    events.save = lambda i: Event(label='Save', action=Action(i.save))


class AddressBookUI(UserInterface):
    def assemble(self):
        home = self.define_view('/', title='Address book', page=AddressBookPage.factory())
        self.define_transition(Address.events.save, home, home)

Bug fixes

  • Listing and checking out of examples did not work correctly (#354).

  • Fixed incorrect handling of timeouts (#383).

  • Changed DateField to correctly use dayfirst based on the current locale. The default is now an empty string instead of the current date. (#194)

  • Fixed in-memory tables that were not created on the fly when using sqlite (#366).

Updated dependencies

Some dependencies on thirdparty python packages have been loosened to include a higher max version:

  • babel between 2.1 and 2.11

  • Pillow between 2.5 and 3.9

  • alembic between 0.9.6 and 1.8

  • beautifulsoup4 between 4.6 and 4.11

  • docutils between 0.14 and 0.19

  • lxml between 4.2 and 4.9

  • plotly between 5.1.0 and 5.11

  • prompt_toolkit between 2.0.10 and 3.0

  • selenium between 2.42 and 4.7

  • tzlocal between 2.0 and 4.2

  • watchdog between 0.8.3 and 2.2

  • wrapt between 1.11.0 and 1.14

  • setuptools should now be 51.0.0 or higher

The dependency on plotly has been upped to version 5.11.0.

The dependency on pip has been upped to minimum version 21.1 in order to work correctly for projects using pyproject.toml.

Some javascript dependencies were updated to newer versions:

  • Jquery to 3.6.1

  • Jquery-ui to 1.13.2

  • jquery-validate to 1.19.5

  • js-cookie to 3.0.1

  • bootstrap to 4.6.2

  • underscore to 1.13.6

  • plotlyjs to 2.16.4