Why are events and fields declared in the Reahl models? and other questions...

27 views
Skip to first unread message

Amirouche Boubekki

unread,
Jan 30, 2017, 10:58:10 AM1/30/17
to Reahl discuss
Héllo,

In the “input widget” example in the paragraph “reahl models use makeup” I can read the following snippet:

class Address(Base):
   
__tablename__ = 'addressbook2_address'

   
id            = Column(Integer, primary_key=True)
   
email_address = Column(UnicodeText)
   
name          = Column(UnicodeText)

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

a) I am not sure what's the purpose of defining validation fields in the model. Isn't validation orthogonal to model representation? For instance, how do you define multiple validation scheme for a single model?
b) There is magic at play here through @exposed decorator. That's a pattern I've never seen before, no problem with that but I was said using the @property decorator for hiding runtime operation is only "maintenance" trick to avoid big refactoring. Is the use of a pseudo @property legit?
c) Can someone use "self" to define the fields? For what purpose? I don't understand the purpose of self in fields method.
d) I don't see the point in using this representation instead of the declarative ORM-like pattern used in Django and others form validation libraries. Why this choice of API?

In the following chapter the events method is presented in “Buttons allow users to act” the first “Augmenting a model with Events”, the following snippet appears:

class Address(Base):
   
__tablename__ = 'addressbook2_address'

   
id            = Column(Integer, primary_key=True)
   
email_address = Column(UnicodeText)
   
name          = Column(UnicodeText)

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

   
def save(self):
       
Session.add(self)
       
    @exposed
   
def events(self, events):

       
events.save = Event(label='Save', action=Action(self.save))

Again the same @exposed decorator is used to declare what seems to be onclick event handlers which seems to be kind of analogous to Django views or Controller in MVC pattern.

e) What's the purpose of Event? What's the purpose of Action?
f) It break the law of separation of concerns, doesn't it?
g) How is used label?

I understand that events handlers are bound to models, is it the only way to go:

h) what happens if instead of executing some processing on a model I want to reach rabbitmq or redis?
i) what happens if I want to ping redis, update one or several models? Where should the code go?

Then the event is referenced in the template/rendering code as follow:

class AddAddressForm(Form):
   
def __init__(self, view):
       
super(AddAddressForm, self).__init__(view, 'add_form')

       
new_address = Address()

       
grouped_inputs = self.add_child(FieldSet(view, legend_text='Add an address'))
       
grouped_inputs.add_child( LabelledBlockInput(TextInput(self, new_address.fields.name)) )
       
grouped_inputs.add_child( LabelledBlockInput(TextInput(self, new_address.fields.email_address)) )

       
self.define_event_handler(new_address.events.save)
       
grouped_inputs.add_child(Button(self, new_address.events.save))

j) How do you declare a event handler that is not bound to a model (cf. previous question)?
k) What mechanism does reahl use to forward the button click event done browser side to Python side? In general how communication between frontend and backend happens?
l) It seems to me browser events are kind of "fire-and-forget" style. Is it possible to have RPC mechanism where clicking on button triggers a method call server side which answers or triggers the desired side effect frontend in the UI?

Other questions:

m) Is it possible to implement a REST service using Reahl?
n) regarding widgets, is it possible to bundle a widget with some routes?
o) again widgets, is it possible to point a widget to re-use a REST interface or events handler?

p) I did read something about client side validation of forms but I can't find it.

Last but not least, last question is it possible to push things to the frontend through websocket or long polling?

Thanks in advance!

Amirouche Boubekki

unread,
Jan 30, 2017, 11:13:05 AM1/30/17
to Reahl discuss
a) is it possible somehow to do something similar to that:

class CommentForm(Form):
   
def __init__(self, view, comment):
       
super(CommentForm, self).__init__(view, 'myform')

       
email_input = TextInput(self, comment.fields.email_address)
       
self.add_child(LabelledBlockInput(email_input))

       
text_input = TextInput(self, comment.fields.text)
       
self.add_child(LabelledBlockInput(text_input))

       
self.add_child(Button(self, self.submit))

    def submit(self, *args, **kwargs):
        redis
.push("commented on stuff")


b) what's the purpose of the view object that is everywhere in the GUI code? What is its type?
c) How can I access the user agent or IP of the user? more generaly is there a notion of request in Reahl?
d) Is it possible to reverse routes based on names like django reverse does?
e) Is it possible to plug custom authentication and custom authorization system?
f) How does Reahl protect against CSRF?
g) Is it possible to connect to two different postgresql database in Reahl?

That's a lot of question I hope you find the time to answer them.


Best regards

Iwan Vosloo

unread,
Jan 31, 2017, 1:51:41 AM1/31/17
to reahl-...@googlegroups.com
Hi Amirouche,

Good questions, let me try answer:

On 30/01/2017 17:58, Amirouche Boubekki wrote:

> a) I am not sure what's the purpose of defining validation fields in the
> model. Isn't validation orthogonal to model representation? For
> instance, how do you define multiple validation scheme for a single model?

We think of Fields as metadata about the model - what you'd call its
.name if you were a human; what security restrictions apply etc.
ValidationConstraints are also added to Fields: the fact that, say, an
Investment's .amount should be an Integer > 0, is metadata, and is a
constraint that should always hold. It is part of the domain.

In many circumstances it also makes sense to say that a certain Field is
also required data for its domain object to be complete. Like an
Investment that does not make sense without an .amount.

However, some ValidationConstraints may be different depending on the
context where they are used. This is sometimes true of whether or not
something is required or not.

For this case, we have special methods on Fields that you can use to get
copies of them, with just certain things modified. For example:
Field.as_required()
http://www.reahl.org/docs/3.2/component/modelinterface.d.html#reahl.component.modelinterface.Field.as_required


Field.as_optional()
http://www.reahl.org/docs/3.2/component/modelinterface.d.html#reahl.component.modelinterface.Field.as_optional

or the more general versions:

Field.as_with_validation_constraint
http://www.reahl.org/docs/3.2/component/modelinterface.d.html#reahl.component.modelinterface.Field.as_with_validation_constraint

Field.as_without_validation_constraint
http://www.reahl.org/docs/3.2/component/modelinterface.d.html#reahl.component.modelinterface.Field.as_without_validation_constraint


That said, you do not HAVE to always put these on actual domain objects.
They just have to be on objects of some sort. You can even put them on a
Widget like a Form for something if you'd like. We sometimes create a
class that acts more like an interface to the domain where it makes
sense like here:
http://www.reahl.org/docs/3.2/domain/systemaccountmodel.d.html#accountmanagementinterface


> b) There is magic at play here through @exposed decorator. That's a
> pattern I've never seen before, no problem with that but I was said
> using the @property decorator for hiding runtime operation is only
> "maintenance" trick to avoid big refactoring. Is the use of a pseudo
> @property legit?

All @properties are basically "derived attributes" of an object.
Meaning, the object does not have an instance attribute holding onto the
result of the property, it needs to execute a method to compute it on
the fly. This is no different. It has no side-effects and should do
nothing other than to compute the .fields of the object in question. I
can't see a problem with that ;-)

> c) Can someone use "self" to define the fields? For what purpose? I
> don't understand the purpose of self in fields method.

Well, it is a method, so has to have a self argument. BUT, yes, you
absolutely do need it in some cases. For example, perhaps you want to be
able to compute whether the currently logged-in user has access to the
specific object instance the field is bound to. How would you do that
without having access to the actual instance?

Our involved security example can be referenced for this, here are such
fields:

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

(For the full example, see:
http://www.reahl.org/docs/3.2/tutorial/accesscontrolinc3.d.html )

> d) I don't see the point in using this representation instead of the
> declarative ORM-like pattern used in Django and others form validation
> libraries. Why this choice of API?

Good question! The trouble is that we want to use our Fields AND and ORM
together. If something like SqlAlchemy declares things like .name and
.email_address in the namespace of the class (as in the precise example
you cite), then we cannot do the same trick for Reahl Fields as well.
The names are already in use by SqlAlchemy declarative.

So, we have to find an approach that does not clash with that.

We do have an alternative in the code (not documented) that can be used
something like this:
https://github.com/reahl/reahl/blob/master/reahl-component/reahl/component_dev/test_field.py#L246

There are some problems with that approach though, which is why we are
not advocating it: often, to define a Field, you need access to the
instance (the self of the previous question), and this class-level
approach does not allow for that.

>
> In the following chapter the events method is presented in “Buttons
> allow users to act” the first “Augmenting a model with Events”
> <http://www.reahl.org/docs/3.2/tutorial/buttonwidgets.d.html>, the
> following snippet appears:
>
> |
>
> classAddress(Base):
> __tablename__='addressbook2_address'
>
> id =Column(Integer,primary_key=True)
> email_address=Column(UnicodeText)
> name =Column(UnicodeText)
>
> @exposed
> deffields(self,fields):
> fields.name=Field(label='Name',required=True)
> fields.email_address=EmailField(label='Email',required=True)
>
> defsave(self):
> Session.add(self)
>
> * @exposed
> defevents(self,events):*
> *events.save=Event(label='Save',action=Action(self.save))*
>
> |
>
> Again the same @exposed decorator is used to declare what seems to be
> onclick event handlers which seems to be kind of analogous to Django
> views or Controller in MVC pattern.
>
> e) What's the purpose of Event
> <http://www.reahl.org/docs/3.2/component/modelinterface.d.html?highlight=event#reahl.component.modelinterface.Event>?
> What's the purpose of Action
> <http://www.reahl.org/docs/3.2/component/modelinterface.d.html?highlight=action#reahl.component.modelinterface.Action>?

An Event is a logical thing that may happen, in response to which the
domain should possibly change.

An Action is some code that can be executed. Actions contain the
necessary code to map incoming data to parameters for method calls, etc.

When you create a button, you specify which Event it triggers when the
user clicks on the Button.

We like to visualise the user interface on a coarse-grained level as
"places" where a user can be. People naturally talk of being in a
certain place in a web app, and about how they can move from one place
to another. You can model these places and how a user gets from one
place to another with something like a state diagram
(http://www.agilemodeling.com/artifacts/stateMachineDiagram.htm )

The terminology of Events and Actions are derived from state diagram
theory. Here is how we use in from Reahl's point of view:
http://www.reahl.org/docs/3.2/tutorial/connectingviews.d.html

So, if an Event is triggered by a user on a specific screen (aka View),
it will first execute the Action attached to it, and then take the user
to another, possibly different View as defined by transitions.



>
> f) It break the law of separation of concerns, doesn't it?

No. By declaring an Event on a domain object, you define a signal that
can be triggered by a user.

If you follow the state diagram approach, each state is a View, and
traditional controller concerns are captured by the state diagram
itself, especially the edges (transitions).

But, the concerns are specifically: what a View looks like vs how you
control transitioning a user between different Views in response to Events.

You do NOT put stuff in an Action that controls the user interface at all.


> g) How is used label?

Again, it is metadata about the Event. We happen to use it as the text
inside the Button to which that Event is attached. But where you declare
the label on the Event you are just saying that "humans may call this
Event X"

>
> I understand that events handlers are bound to models, is it the only
> way to go:
>
> h) what happens if instead of executing some processing on a model I
> want to reach rabbitmq or redis?

Then you write a model which acts as an interface in front of redis or
rabbitmg, with methods that proxy to these services.


> i) what happens if I want to ping redis, update one or several models?
> Where should the code go?

I think you should perhaps provide a concrete example here because the
answer (as it often is with design stuff) will depend. But generically:
then you build a model that reflects the face that you want to update
several other models, use a facade pattern or some such. With a more
concrete example I'd be able to give a better answer.

>
> Then the event is referenced in the template/rendering code as follow:

>
> |
>
> classAddAddressForm(Form):
> def__init__(self,view):
> super(AddAddressForm,self).__init__(view,'add_form')
>
> new_address=Address()
>
> grouped_inputs=self.add_child(FieldSet(view,legend_text='Add an
> address'))
>
> grouped_inputs.add_child(LabelledBlockInput(TextInput(self,new_address.fields.name)))
>
> grouped_inputs.add_child(LabelledBlockInput(TextInput(self,new_address.fields.email_address)))
>
> self.define_event_handler(new_address.events.save)
> grouped_inputs.add_child(Button(self,*new_address.events.save*))
>
> |
>
> j) How do you declare a event handler that is not bound to a model (cf.
> previous question)?

Once again, you don't, but you can decide what you want to "model". It
need not be an address - it can be an XYZInterface, for example.

> k) What mechanism does reahl use to forward the button click event done
> browser side to Python side? In general how communication between
> frontend and backend happens?

It does a POST.

In general, if you click on a button, it POSTs to the Form, executed the
Action attached to that Event and then responds with a redirect (HTTP
302) to the next page (View) where you want the user to end up on.

If there are any exceptions (such as validation exceptions) as a result
of the POST, the database is rolled back, and the user is redirected to
the same View where the button was clicked - but now with validation
errors displayed.

The stuff we do with Ajax can be different, and depends on the context.
There may also be GETs in that case.


> l) It seems to me browser events are kind of "fire-and-forget" style. Is
> it possible to have RPC mechanism where clicking on button triggers a
> method call server side which answers or triggers the desired side
> effect frontend in the UI?

Yes. We have something called a RemoteMethod
(http://www.reahl.org/docs/3.2/web/fw.d.html?highlight=define_transition#remotemethod
) which you can use to define something that can be triggered via an
URL. Exactly what it sends back is also configurable. In fact the basic
POST alluded to above is implemented using this mechanism.


>
> Other questions:
>
> m) Is it possible to implement a REST service using Reahl?

That is not our focus, but it is possible using a RemoteMethod.

> n) regarding widgets
> <http://www.reahl.org/docs/3.2/tutorial/ownwidget.d.html?highlight=widget>,
> is it possible to bundle a widget with some routes?

No, that would not make sense. A View is a page / place on your app and
is uniquely identified by URL. Widgets are reusable chunks of UI that
you can compose particular Views with. If a Widget was linked to a URL,
you could not re-use it to compose different Views.

A UserInterface can also have an URL.

> o) again widgets, is it possible to point a widget to re-use a REST
> interface or events handler?

Yes, again using a RemoteMethod.

>
> p) I did read something about client side validation of forms but I
> can't find it.

Probably here: http://www.reahl.org/docs/3.2/features/validation.d.html

You don't have to do anything special to make this happen. If you use
Fields, their ValidationConstraints will be checked client side and
server side automatically. We think that is quite cool - because you
just say logically how things should be validated, and then it happens
client side with no effort from your end.

>
> Last but not least, last question is it possible to push things to the
> frontend through websocket or long polling?

Not yet I'm sorry. There are still a few other use cases we'd like to
build good abstractions for before we deal with that sort of thing. But
I am sure we will make use of such mechanisms at some point.


I have to run now, will answer your other email a bit later tonight!

Regards
- Iwan

--

Reahl, the Python only web framework / www.reahl.org

Iwan Vosloo

unread,
Feb 1, 2017, 1:03:53 AM2/1/17
to reahl-...@googlegroups.com
Hi Amirouche,

On 30/01/2017 18:13, Amirouche Boubekki wrote:
> a) is it possible somehow to do something similar to that:
>
> |
>
> classCommentForm(Form):
> def__init__(self,view,comment):
> super(CommentForm,self).__init__(view,'myform')
>
> email_input=TextInput(self,comment.fields.email_address)
> self.add_child(LabelledBlockInput(email_input))
>
> text_input=TextInput(self,comment.fields.text)
> self.add_child(LabelledBlockInput(text_input))
>
> self.add_child(Button(self,*self.submit*))
>
> * defsubmit(self,*args,**kwargs):
> redis.push("commented on stuff")*
>
> |

Yes to the idea, but some details should be a bit different:

class CommentForm(Form):
def __init__(self, view, comment):
super(CommentForm, self).__init__(view, 'myform')

email_input=TextInput(self,comment.fields.email_address)
self.add_child(LabelledBlockInput(email_input))

text_input=TextInput(self,comment.fields.text)
self.add_child(LabelledBlockInput(text_input))

# From an implementation point of view, this next call should not
# be necessary but we prefer it if creating a Button breaks if you
# forgot to define how its Event should be handled, so...
self.add_event_handler(self.events.submit)

self.add_child(Button(self, self.events.submit)

@exposed
def events(self, events):
events.submit = Event(Action(self.submit))

def submit(self):
redis.push("commented on stuff")

>
> b) what's the purpose of the view object that is everywhere in the GUI
> code? What is its type?

A View represents what a user sees as a page in the app. Users navigate
between Views and each View is identified by a URL. If you think about
it, not much different from any MVC concept of a View.

Menus can give you links to navigate to other Views, of you can be
transitioned from one View to another in response to having clicked on a
button. (Refer to:
http://www.reahl.org/docs/3.2/tutorial/connectingviews.d.html )

Generally, .define_view creates UrlBoundView instances:
http://www.reahl.org/docs/3.2/web/fw.d.html#reahl.web.fw.UrlBoundView

You can also create your own to deal with arguments parsed from the URL
of the View: http://www.reahl.org/docs/3.2/tutorial/parameterised.d.html

> c) How can I access the user agent or IP of the user? more generaly is
> there a notion of request in Reahl?

Yes, there is, but we'd prefer to keep it hidden because we are trying
to work on a high level of abstraction: we want to be above concepts
dealing with web implementation details.

You can get the current request by accessing the session context, and
then accessing the request:

request = WebExecutionContext.get_context().request #this would be an
instance of webob.Request
print(request.client_addr)


> d) Is it possible to reverse routes based on names like django reverse
> <https://docs.djangoproject.com/en/1.10/topics/http/urls/#reverse-resolution-of-urls>
> does?

We have a totally different take on URLs. Firstly, with Reahl you build
UserInterfaces, and each View on a UserInterface has its own URL
*relative to* the UserInterface. You can graft one UserInterface onto
its own URL relative to another UserInterface as well. And... the idea
is that when you create a UserInterface you do not now the contexts in
which it will be used - much like when you write a function and it
contains no knowledge of where it will be called from.

This is a bit more of an advanced concept. Some of it is explained here:
http://www.reahl.org/docs/3.2/tutorial/loggingin.d.html

Either way: in keeping with trying to be on a level of abstraction ABOVE
web implementation concepts, we rather want to program with the notion
of a Bookmark. Bookmarks are aware of the relative nature of our URLs.
Bookmarks are explained here:
http://www.reahl.org/docs/3.2/tutorial/connectingviews.d.html#navigation-under-the-user-s-control-bookmarks

In http://www.reahl.org/docs/3.2/web/fw.d.html#reahl.web.fw.Bookmark a
couple of ways are shown for how to obtain a Bookmark to a particular
View (whether the View exists yet, or not).

So, in essence I think we address similar concerns as Django, but in a
totally different way.

> e) Is it possible to plug custom authentication and custom authorization
> system?

Well, you could roll your own completely, as is done in the example
here: http://www.reahl.org/docs/3.2/tutorial/loggingin.d.html

The stuff we have in reahl.domain.systemaccountmodel is sort of done
with the idea that you can have other kinds of SystemAccount - other
that EmailAndPasswordSystemAccount but actually doing that will require
some more work.


> f) How does Reahl protect against CSRF?

Not yet. It is on our TODO list though. The idea is to do something
similar to what Django does with a token except we will build this into
the implementation of a Form so you would not need to know about it or
do anything special to enable it. That said...we've not done that yet.


> g) Is it possible to connect to two different postgresql database in Reahl?
>

In theory yes, but I suspect it will require more work. Since we sit on
top of SQLAlchemy it would depend on how you set up your Session and you
you map different model objects to databases. In SqlAlchemy there are
basically two ways of doing this:

With different model objects in different databases:

http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#enabling-two-phase-commit

or with horizontal sharding:
http://docs.sqlalchemy.org/en/latest/orm/extensions/horizontal_shard.html

That said - we have not implemented anything to use these capabilities
of SqlAlchemy.

Perhaps important to note is that Reahl manages transactions on the
programmer's behalf.


> That's a lot of question I hope you find the time to answer them.

No problem. Answers above are not all my own words ;-) I had some help!

-Iwan

Amirouche Boubekki

unread,
Feb 1, 2017, 5:29:24 AM2/1/17
to Reahl discuss
Hi Iwan and all,

Thanks for your answers. I will need time to process them and maybe reply.

I think what you do is a noble cause I hope you succeed in what you try to achieve.

Best regards,

Amirouche
Reply all
Reply to author
Forward
0 new messages