Module reahl.component.modelinterface

Facilities to govern user input and output, as well as what access the current user has to model objects.

' Copyright 2018, 2022 Reahl Software Services (Pty) Ltd. All rights reserved.

@startuml
!include ./../../../base.iuml

title Fields and Events 

note as N1
  A Field controls the attribute of a model object.
  A Field can have ValidationConstraints and AccessRights. 
  An Event is a kind of Field representing something the user can trigger.
  Events are made to do something by linking them to Actions.
  Actions wrap a python callable
  An AccessRights is linked to one Action to determine whether the 
  Field it belongs to is writeable; AccessRights is linked to another
  another Action to determine if it is readable.
end note


callable <<python>>
attribute <<python>>
Exception <<python>>

Event --up-|> Field
Event -- Action
Action --> callable : "function or object method"

Field --> attribute : domain_value
Field --> ModelObject : "bound to"
ModelObject -right-> attribute: "has attribute"
Field -right- "*" ValidationConstraint
Field --- AccessRights
AccessRights -left- Action : readable
AccessRights -left- Action : writable

ValidationConstraint --up-|> Exception


@enduml

Fields

Field

class reahl.component.modelinterface.Field(default=None, required=False, required_message=None, label=None, readable=None, writable=None, disallowed_message=None, min_length=None, max_length=None)

Bases: object

A Field represents something which can be input by a User.

A Field is responsible for transforming user input from a string into a Python object which that string represents. Different kinds of Fields marshall input to different kinds of Python object. This (base) class does no marshalling itself, the parsed Python object is just the input string as given. Subclasses override this behaviour.

A Field also manages the validation of such input, based on a list of individual instances of ValidationConstraint added to the Field.

The final parsed value of a Field is set as an attribute on a Python object to which the Field is bound. (See also ExposedNames).

Parameters:
  • default – The default (parsed) value if no user input is given.

  • required – If True, indicates that input is always required for this Field.

  • required_message – See error_message of RequiredConstraint.

  • label – A text label by which to identify this Field to a user.

  • readable – A callable that takes one argument (this Field). It is executed to determine whether the current user is allowed to see this Field. Returns True if the user is allowed, else False.

  • writable – A callable that takes one argument (this Field). It is executed to determine whether the current user is allowed supply input for this Field. Returns True if the user is allowed, else False.

  • disallowed_message – An error message to be displayed when a user attempts to supply input to this Field when it is not writable for that user. (See error_message of ValidationConstraint.)

  • min_length – The minimum number of characters allowed in the user supplied input.

  • max_length – The maximum number of characters allowed in the user supplied input.

Changed in version 4.0: Added min_length and max_length kwargs.

entered_input_type

alias of str

property initial_value

partial(func, *args, **keywords) - new function with partial application of the given arguments and keywords.

property input_status

partial(func, *args, **keywords) - new function with partial application of the given arguments and keywords.

property validation_error

partial(func, *args, **keywords) - new function with partial application of the given arguments and keywords.

property user_input

partial(func, *args, **keywords) - new function with partial application of the given arguments and keywords.

property parsed_input

partial(func, *args, **keywords) - new function with partial application of the given arguments and keywords.

make_required(required_message)

Forces this Field to be required, using required_message as an error message.

as_required(required_message=None)

Returns a new Field which is exactly like this one, except that the new Field is required, using required_message.

make_optional()

Forces this Field to be optional (not required).

as_optional()

Returns a new Field which is exactly like this one, except that the new Field is optional.

without_validation_constraint(validation_constraint_class)
Returns a new Field which is exactly like this one, except that the new Field does not include

a ValidationConstraint of the class given as validation_constraint_class.

Changed in version 4.0: Changed name to be consistent with conventions for with_ methods.

with_validation_constraint(validation_constraint)
Returns a new Field which is exactly like this one, except that the new Field also includes

the ValidationConstraint given as validation_constraint.

Changed in version 4.0: Changed name to be consistent with conventions for with_ methods.

with_label(label)
Returns a new Field which is exactly like this one, except that its label is set to

the given text.

Added in version 5.0.

with_discriminator(discriminator)

Returns a new Field which is exactly like this one, except that its name is mangled to include the given discriminator in order to prevent name clashes. Name clashes can occur when two Fields with the same name on different objects are used on the same Form.

Added in version 5.0.

parse_input(unparsed_input)

Override this method on a subclass to specify how that subclass transforms the unparsed_input (a string) into a representative Python object.

unparse_input(parsed_value)

Override this method on a subclass to specify how that subclass transforms a given Python object (parsed_value) to a string that represents it to a user.

from_input(unparsed_input, ignore_validation=False, ignore_access=False)

Sets the value of this Field from the given unparsed_input.

as_input()

Returns the value of this Field as a string.

add_validation_constraint(validation_constraint)

Adds the given validation_constraint to this Field. All ValidationConstraints added to the Field are used in order, to validate input supplied to the Field.

ExposedNames

class reahl.component.modelinterface.ExposedNames

Bases: object

Use ExposedNames to create a namespace on a class for the purpose of declaring

all the Field or all the Event instances bound to an instance of that class.

To create a namespace, assign an instance of ExposedNames to a class attribute named for the needed namespace (ie, fields). For each Field/Event needed, assign a callable to an attributes on the ExposedNames.

The callable will be passed a single argument: the instance of the class it will be bound to.. It should return a Field or Event instance.

For example:

class Person:
    def __init__(self, name):
        self.name = name
        self.age = 0

    fields = ExposedNames()
    fields.age = lambda i: IntegerField(name='Age of %s' % i.name)

    events = ExposedNames()
    events.submit = lambda i: Event(Action(i.submit))

    def submit(self):
        pass

This is similar to how SqlAlchemy or Django ORM declare columns on a class which are used to save corresponding attributes of an instance to columns in a database table.

For example, with SQLAlchemy you could have:

class Person(Base):
   id = Column(Integer, primary_key=True)
   name = Column(String)
   age = Column(Integer)

ExposedNames is different in that it allows you to declare your Fields or Events in a namespace of their own. ExposedNames can thus be used in conjunction with, say, SQLAlchemy on the same instance:

class Person(Base):
   id = Column(Integer)
   name = Column(String)
   age = Column(Integer)

   fields = ExposedNames()
   fields.name = lambda i: Field(label='Name')
   fields.age = lambda i: IntegerField(label='Age')

p = Person()
p.name = 'Jane'
p.age = 25

Session.save(p)  # Saves Jane/25 to database with some auto generated id

assert p.fields.age.as_input() == '25'

p.fields.age.from_input('28')  
assert p.age == 28  # Which means since SqlAlchemy will save the age attribute, it will now save 28 to the database

Added in version 6.1.

FieldIndex

class reahl.component.modelinterface.FieldIndex(storage_object)

Bases: object

A named collection of Field instances applicable to an object.

Programmers should not construct this class, an instance is automatically created when accessing a ExposedNames class attribute on an instance. (See ExposedNames )

When used in conjuction with Fields declared on a ExposedNames, construction of an individual Field is delayed until it is accessed on the the FieldIndex:

def create_field(i):
    print('creating')
    return Field()

class Person:
    fields = ExposedNames()
    fields.name = create_field
    fields.age = create_field

person.fields.name # prints 'creating'
person.fields.name # does not create it again
person.fields.age  # prints 'creating'

Changed in version 6.1: Deprecated: an instance of this class is also passed to methods marked as @exposed. (See ExposedDecorator )

Changed in version 7.0: Removed: the use of @exposed has been removed (See ExposedNames )

CurrentUser

class reahl.component.modelinterface.CurrentUser

Bases: Field

A Field whose value is always set to the party of the account currently logged in.

parse_input(unparsed_input)

Override this method on a subclass to specify how that subclass transforms the unparsed_input (a string) into a representative Python object.

unparse_input(parsed_value)

Override this method on a subclass to specify how that subclass transforms a given Python object (parsed_value) to a string that represents it to a user.

EmailField

class reahl.component.modelinterface.EmailField(default=None, required=False, required_message=None, label=None, readable=None, writable=None)

Bases: Field

A Field representing a valid email address. Its parsed value is the given string.

PasswordField

class reahl.component.modelinterface.PasswordField(default=None, required=False, required_message=None, label=None, writable=None, min_length=6, max_length=20)

Bases: Field

A Field representing a password. Its parsed value is the given string, but the user is not allowed to see its current value.

BooleanField

class reahl.component.modelinterface.BooleanField(default=None, required=False, required_message=None, label=None, readable=None, writable=None, true_value=None, false_value=None)

Bases: ChoiceField

A Field that can only have one of two parsed values: True or False. The string representation of each of these values are given in true_value or false_value, respectively. (These default to ‘on’ and ‘off’ initially.)

parse_input(unparsed_input)

Override this method on a subclass to specify how that subclass transforms the unparsed_input (a string) into a representative Python object.

unparse_input(parsed_value)

Override this method on a subclass to specify how that subclass transforms a given Python object (parsed_value) to a string that represents it to a user.

IntegerField

class reahl.component.modelinterface.IntegerField(default=None, required=False, required_message=None, label=None, readable=None, writable=None, min_value=None, max_value=None)

Bases: Field

A Field that yields an integer.

Parameters:
  • min_value – The minimum value allowed as valid input.

  • max_value – The maximum value allowed as valid input.

(For other arguments, see Field.)

parse_input(unparsed_input)

Override this method on a subclass to specify how that subclass transforms the unparsed_input (a string) into a representative Python object.

NumericField

class reahl.component.modelinterface.NumericField(default=None, precision=2, required=False, required_message=None, label=None, readable=None, writable=None, min_value=None, max_value=None)

Bases: Field

A Field that yields any number that has a decimal point followed by digits that show the fractional part.

Parameters:
  • precision – The number of decimal digits allowed.

  • min_value – The minimum value allowed as valid input.

  • max_value – The maximum value allowed as valid input.

(For other arguments, see Field.)

Added in version 5.2.

parse_input(unparsed_input)

Override this method on a subclass to specify how that subclass transforms the unparsed_input (a string) into a representative Python object.

unparse_input(parsed_value)

Override this method on a subclass to specify how that subclass transforms a given Python object (parsed_value) to a string that represents it to a user.

JsonField

class reahl.component.modelinterface.JsonField(default=None, required=False, required_message=None, label=None, readable=None, writable=None, disallowed_message=None, min_length=None, max_length=None)

Bases: Field

A field that parses a JSON formatted string to a python dictionary object.

Added in version 5.2.

parse_input(unparsed_input)

Override this method on a subclass to specify how that subclass transforms the unparsed_input (a string) into a representative Python object.

unparse_input(parsed_value)

Override this method on a subclass to specify how that subclass transforms a given Python object (parsed_value) to a string that represents it to a user.

DateField

class reahl.component.modelinterface.DateField(default=None, date_format='medium', min_value=None, max_value=None, required=False, required_message=None, label=None, readable=None, writable=None)

Bases: Field

A Field that can parse a Python Date from a given user input string. The input string need not conform to strict format – a DateField does its best to parse what is given. The names of months, days, etc that may be typed by a user are parsed according to the current language in use.

Parameters:

(For other arguments, see Field.)

Changed in version 6.1: Added date_format keyword

parse_input(unparsed_input)

Override this method on a subclass to specify how that subclass transforms the unparsed_input (a string) into a representative Python object.

unparse_input(parsed_value)

Override this method on a subclass to specify how that subclass transforms a given Python object (parsed_value) to a string that represents it to a user.

Inputting predefined values

ChoiceField

class reahl.component.modelinterface.ChoiceField(grouped_choices, default=None, required=False, required_message=None, label=None, readable=None, writable=None)

Bases: Field

A Field that only allows the value of one of the given Choice instances as input.

Parameters:

grouped_choices – A list (or callable that will return a list) of Choice or ChoiceGroup instances from which a user should choose a single Choice. If a callable, it will be invoked at the last possible time to ensure an up-to-date list is obtained.

(For other arguments, see Field.)

Changed in version 6.1: Kwarg grouped_choices changed to also be able to be a callable to delay the fetching of choices.

parse_input(unparsed_input)

Override this method on a subclass to specify how that subclass transforms the unparsed_input (a string) into a representative Python object.

MultiChoiceField

class reahl.component.modelinterface.MultiChoiceField(grouped_choices, default=None, required=False, required_message=None, label=None, readable=None, writable=None)

Bases: ChoiceField

A Field that allows a selection of values from the given Choice instances as input.

entered_input_type

alias of list

parse_input(unparsed_inputs)

Override this method on a subclass to specify how that subclass transforms the unparsed_input (a string) into a representative Python object.

unparse_input(parsed_values)

Override this method on a subclass to specify how that subclass transforms a given Python object (parsed_value) to a string that represents it to a user.

Choice

class reahl.component.modelinterface.Choice(value, field)

Bases: object

One possible Choice to be allowed as input for a ChoiceField.

Parameters:
  • value – The Python value represented by this Choice.

  • field – A Field able to marshall the value of this Choice. The label of this Field is used as a string to represent this Choice to a user.

ChoiceGroup

class reahl.component.modelinterface.ChoiceGroup(label, choices)

Bases: object

Different Choice instances can be grouped together. User interface machinery can use this information for display purposes.

Parameters:
  • label – A name for the group to display.

  • choices – The list of choices in this ChoiceGroup.

Inputting files

FileField

class reahl.component.modelinterface.FileField(allow_multiple=False, default=None, required=False, required_message=None, label=None, readable=None, writable=None, max_size_bytes=None, accept=None, max_files=None)

Bases: Field

A Field that can accept one or more files as input.

Parameters:
  • allow_multiple – Set to True to allow more than one file to be input at a time.

  • max_size_bytes – The maximim size of file allowed, in bytes.

  • accept – The accepted type of file, as specified via mime type.

  • max_files – Specifies the maximum number of files the user is allowed to input.

(For other arguments, see Field.)

parse_input(unparsed_value)

Override this method on a subclass to specify how that subclass transforms the unparsed_input (a string) into a representative Python object.

unparse_input(parsed_value)

Override this method on a subclass to specify how that subclass transforms a given Python object (parsed_value) to a string that represents it to a user.

UploadedFile

class reahl.component.modelinterface.UploadedFile(filename, contents, mime_type)

Bases: object

Represents a file that was input by a user. The contents of the file is represented as bytes, because knowing what the encoding is is a tricky issue. The user only sits in front of the browser and selects files on their filesystem and hits ‘upload’. Those files can be binary or text. If text, they may or may not be in the same encoding as their system’s preferred encoding. If binary, their browser may guess their content type correctly or may not, and if we go and decode them with i.e UTF-8, the system could break with UnicodeDecodeError on jpegs and the like.

Changed in version 3.0: UploadedFile is now constructed with the entire contents of the uploaded file instead of with a file-like object as in 2.1.

filename

The name of the file

mime_type

The mime type of the file

property size

The size of the UploadedFile contents.

open()

A contextmanager for reading the file contents (as bytes).

For example:

with uploaded_file.open() as contents:
    print(contents.read())

Constraints

ValidationConstraint

class reahl.component.modelinterface.ValidationConstraint(error_message=None)

Bases: Exception

A ValidationConstraint is responsible for checking that a given value is deemed valid input. Each ValidationConstraint only checks one aspect of the validity of a given Field. The ValidationConstraint first checks a string sent as user input, but can afterwards also validate the resultant Python object which was created based on such string input.

Parameters:

error_message – The error message shat should be shown to a user if input failed this ValidationConstraint. This error_message is a string containing a PEP-292 template. Attributes of the ValidationConstraint can be referred to by name in variable references of this template string.

validate_input(unparsed_input)

Override this method to provide the custom logic of how this ValidationConstraint should check the given string for validity. If validation fails, this method should raise self.

validate_parsed_value(parsed_value)

Override this method to provide the custom logic of how this ValidationConstraint should check the given Python object after it was created from initial input in string form. If validation fails, this method should raise self.

property parameters

Override this property to supply parameters for this ValidationConstraint. Parameters are used by user interface mechanics outside the scope of this module for implementation reasons.

property message

The message to display to a user if this ValidationConstraint is failed by some input.

property label

The textual label displayed to users for the Field to which this ValidationConstraint is linked.

property value

The current value which failed validation.

RemoteConstraint

class reahl.component.modelinterface.RemoteConstraint(error_message=None)

Bases: ValidationConstraint

A ValidationConstraint which can only be executed on the server. Create a subclass of this class and override validate_input and validate_parsed_value.

Parameters:

error_message – (See ValidationConstraint)

RequiredConstraint

class reahl.component.modelinterface.RequiredConstraint(dependency_expression='*', error_message=None)

Bases: ValidationConstraint

The presence of this ValidationConstraint on a Field indicates that the Field is required.

Parameters:
  • dependency_expression – RequiredConstraint is conditional upon this jquery selector expression matching more than 0 elements. By default this matches all elements, making the RequiredConstraint non-conditional.

  • error_message – (See ValidationConstraint)

Changed in version 5.0: Renamed selector_expression to dependency_expression.

property parameters

Override this property to supply parameters for this ValidationConstraint. Parameters are used by user interface mechanics outside the scope of this module for implementation reasons.

validate_input(unparsed_input)

Override this method to provide the custom logic of how this ValidationConstraint should check the given string for validity. If validation fails, this method should raise self.

EqualToConstraint

class reahl.component.modelinterface.EqualToConstraint(other_field, error_message=None)

Bases: ComparingConstraint

A ValidationConstraint that requires the value of its Field to be equal to the value input into other_field.

Parameters:
  • other_field – The Field whose value must be equal to the Field to which this ValidationConstraint is attached.

  • error_message – (See ValidationConstraint)

GreaterThanConstraint

class reahl.component.modelinterface.GreaterThanConstraint(other_field, error_message=None)

Bases: ComparingConstraint

A ValidationConstraint that requires the value of its Field to be greater than the value input into other_field (the > operator is used for the comparison).

Parameters:

SmallerThanConstraint

class reahl.component.modelinterface.SmallerThanConstraint(other_field, error_message=None)

Bases: ComparingConstraint

A ValidationConstraint that requires the value of its Field to be smaller than the value input into other_field (the < operator is used for the comparison).

Parameters:

MinLengthConstraint

class reahl.component.modelinterface.MinLengthConstraint(min_length, error_message=None)

Bases: ValidationConstraint

A ValidationConstraint that requires length of what the user typed to be at least min_length characters long.

Parameters:
  • min_length – The minimum allowed length of the input.

  • error_message – (See ValidationConstraint)

property parameters

Override this property to supply parameters for this ValidationConstraint. Parameters are used by user interface mechanics outside the scope of this module for implementation reasons.

validate_input(unparsed_input)

Override this method to provide the custom logic of how this ValidationConstraint should check the given string for validity. If validation fails, this method should raise self.

MaxLengthConstraint

class reahl.component.modelinterface.MaxLengthConstraint(max_length, error_message=None)

Bases: ValidationConstraint

A ValidationConstraint that requires length of what the user typed to not be more than max_length characters long.

Parameters:
  • max_length – The maximum allowed length of the input.

  • error_message – (See ValidationConstraint)

property parameters

Override this property to supply parameters for this ValidationConstraint. Parameters are used by user interface mechanics outside the scope of this module for implementation reasons.

validate_input(unparsed_input)

Override this method to provide the custom logic of how this ValidationConstraint should check the given string for validity. If validation fails, this method should raise self.

PatternConstraint

class reahl.component.modelinterface.PatternConstraint(pattern, error_message=None)

Bases: ValidationConstraint

A ValidationConstraint that requires unparsed input to match the supplied regex.

Parameters:

Changed in version 6.1: Arg pattern changed to also be able to be a callable to delay the computation of the pattern.

property parameters

Override this property to supply parameters for this ValidationConstraint. Parameters are used by user interface mechanics outside the scope of this module for implementation reasons.

validate_input(unparsed_input)

Override this method to provide the custom logic of how this ValidationConstraint should check the given string for validity. If validation fails, this method should raise self.

AllowedValuesConstraint

class reahl.component.modelinterface.AllowedValuesConstraint(allowed_values, error_message=None)

Bases: PatternConstraint

A PatternConstraint that only allows unparsed input equal to one of a list of allowed_values.

Parameters:
  • allowed_values – A list containing the strings values to be allowed.

  • error_message – (See ValidationConstraint)

Changed in version 6.1: Arg allowed_values changed to also be able to be a callable to delay the fetching of values to be allowed.

IntegerConstraint

class reahl.component.modelinterface.IntegerConstraint(error_message=None)

Bases: PatternConstraint

A PatternConstraint that only allows input that represent a valid integer.

Parameters:

error_message – (See ValidationConstraint)

validate_input(unparsed_input)

Override this method to provide the custom logic of how this ValidationConstraint should check the given string for validity. If validation fails, this method should raise self.

MinValueConstraint

class reahl.component.modelinterface.MinValueConstraint(min_value, error_message=None)

Bases: ValidationConstraint

A ValidationConstraint that requires its parsed input to be greater than or equal to a supplied min_value. (To do the comparison, the >= operator is used on the parsed value.)

Parameters:
validate_parsed_value(parsed_value)

Override this method to provide the custom logic of how this ValidationConstraint should check the given Python object after it was created from initial input in string form. If validation fails, this method should raise self.

MaxValueConstraint

class reahl.component.modelinterface.MaxValueConstraint(max_value, error_message=None)

Bases: ValidationConstraint

A ValidationConstraint that requires its parsed input to be smaller than or equal to a supplied max_value. (To do the comparison, the <= operator is used on the parsed value.)

Parameters:
validate_parsed_value(parsed_value)

Override this method to provide the custom logic of how this ValidationConstraint should check the given Python object after it was created from initial input in string form. If validation fails, this method should raise self.

Events and Actions

Event

class reahl.component.modelinterface.Event(label=None, action=None, readable=None, writable=None, disallowed_message=None, **event_argument_fields)

Bases: Field

An Event can be triggered by a user. When an Event occurs, the action of the Event is executed.

Parameters:
  • label – (See Field)

  • action – The Action to execute when this Event occurs.

  • readable – (See Field)

  • writable – (See Field)

  • disallowed_message – (See Field)

  • event_argument_fields – Keyword arguments given in order to specify the names of the arguments this Event should have. The value to each keyword argument is a Field governing input to that Event argument.

from_input(unparsed_input, ignore_validation=False, ignore_access=False)

Sets the value of this Field from the given unparsed_input.

fire()

Fire this event - which executes the Action of the event.

with_arguments(**event_arguments)

Returns a new Event exactly like this one, but with argument values as given.

with_returned_argument(event_argument_name)

Returns a new Event exactly like this one, but indicates that the action of this event will return a value for the given argument name.

Added in version 6.1.

parse_input(unparsed_input)

Override this method on a subclass to specify how that subclass transforms the unparsed_input (a string) into a representative Python object.

unparse_input(parsed_value)

Override this method on a subclass to specify how that subclass transforms a given Python object (parsed_value) to a string that represents it to a user.

Action

class reahl.component.modelinterface.Action(declared_method, arg_names=[], kwarg_name_map={})

Bases: AdaptedMethod

An Action which is supplied to an Event is executed when that Event occurs. Executing the Action means executing its declared_method.

Parameters:
  • declared_method – The method to be called when executing this Action.

  • arg_names – A list of the names of Event arguments to send to declared_method as positional arguments (in the order listed).

  • kwarg_name_map – A dictionary specifying which keyword arguments to send to declared_method when called. The dictionary maps each name of an Event argument that needs to be sent to the name of the keyword argument as which it should be sent.

Allowed

class reahl.component.modelinterface.Allowed(allowed)

Bases: Action

An Action that always returns the (boolean) value of allowed with which it was constructed.

Not

class reahl.component.modelinterface.Not(action)

Bases: Action

An Action which returns the boolean inverse of the result of another action.

secured

reahl.component.modelinterface.secured

An alias for SecuredDeclaration

SecuredDeclaration

class reahl.component.modelinterface.SecuredDeclaration(read_check=None, write_check=None)

Bases: object

A decorator for marking a method as being @secured. Marking a method as @secured, causes a wrapper to be placed around the original method. The wrapper checks the access rights of the current user before each call to the method to ensure unauthorised users cannot call the wrapped method.

When such a @secured method is used as the declared_method of an Action, the Action derives its access constraints directly from the @secured method.

Parameters:
  • read_check – A callable with signature matching that of the @secured method. It should return True to indicate that the current user may be aware of the method, else False. User interface machinery could use this info to determine what to show to a user, what to grey out, or what to hide completely, depending on who the current user is.

  • write_check – A callable with signature matching that of the @secured method. It should return True to indicate that the current user may execute the method, else False.