Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PyConZA 2014: "Introduction to building web applications with Reahl" by Iwan Vosloo & Craig Sparks

Pycon ZA
October 06, 2014

PyConZA 2014: "Introduction to building web applications with Reahl" by Iwan Vosloo & Craig Sparks

Reahl is a full-featured web framework with a twist: with Reahl you write a web application purely in Python. HTML, JavaScript, and all those cumbersome web technologies are hidden away from you.

The first application developed in the online Reahl tutorial includes just enough functionality to give someone a feel for how Reahl is different and interesting. Some topics there fall slightly outside of the scope of Reahl itself because several other Python tools are used, such as virtualenv, Python eggs and SqlAlchemy.

This tutorial is for those who are interested to get that first very basic taste of Reahl with help nearby, and do not feel like following the online tutorial alone at home. We will develop the first example of the online tutorial to give you a solid feel for the approach taken by Reahl, while introducing a number of other development tools that should probably be used by any Python developer. Topics covered are:

* Using virtualenv
* Developing Python eggs
* Persistence using sqlalchemy / elixir
* Using basic widgets in Reahl
* Input and validation using Reahl
* Reacting to user-initiated events using Reahl

Pycon ZA

October 06, 2014
Tweet

More Decks by Pycon ZA

Other Decks in Programming

Transcript

  1. Overview • A taste • Installation • Running examples •

    “Hello world!” explained • What we’re going to build • A skeleton with Widgets • Aside: models and ORM • Getting input • What’s next?
  2. for the running tabbed panel example: http://www.reahl.org/examples/tabbedpanel for source code

    http://www.reahl.org/docs/3.0/features/tabbedpanel.d.html A taste
  3. Running examples | Housekeeping cd tabbedpanel reahl setup -- develop

    -N reahl-control createdbuser etc reahl-control createdb etc reahl-control createdbtables etc reahl-control dropdb etc
  4. Hello world | Project metadata <project type="egg"> <deps purpose="run"> <egg

    name="reahl-component"/> <egg name="reahl-web"/> <egg name="reahl-sqlalchemysupport"/> <egg name="reahl-web-declarative"/> </deps> </project> hello/ .reahlproject etc/ hello.py
  5. hello/ .reahlproject etc/ hello.py Hello world | The application from

    reahl.web.fw import UserInterface from reahl.web.ui import TwoColumnPage, P class HelloPage(TwoColumnPage): def __init__(self, view): super(HelloPage, self).__init__(view) self.main.add_child(P(view, text='Hello World!')) class HelloUI(UserInterface): def assemble(self): self.define_view('/', title='Home', page=HelloPage.factory())
  6. hello/ .reahlproject etc/ hello.py Hello world | Configuration reahl-control listconfig

    --missing --files --info etc from hello import HelloUI web.site_root = HelloUI web.config.py
  7. Hello world | Running it cd hello reahl setup --

    develop -N reahl-control dropdb etc reahl-control createdbuser etc reahl-control createdb etc reahl-control createdbtables etc reahl serve etc
  8. Hello world | Factories self.define_view('/', title='Home', page=HelloPage.factory()) class HelloPage(TwoColumnPage): def

    __init__(self, view): ... Factories are created before a View becomes available – think of them as delayed constructors with the first argument missing
  9. Hello world | Request cycle .assemble() UserInterface construct View and

    all its Widgets render as HTML Factories created Correct factory used to create View
  10. A skeleton | AddressBookPanel class AddressBookPanel(Panel): def __init__(self, view): super(AddressBookPanel,

    self).__init__(view) self.add_child(H(view, 1, text='Addresses')) for address in Session.query(Address).all(): self.add_child(AddressBox(view, address)) self.add_child(AddAddressForm(view))
  11. A skeleton | AddressBox class AddressBox(Widget): def __init__(self, view, address):

    super(AddressBox, self).__init__(view) address_text = '%s: %s' % \ (address.name, address.email_address) self.add_child(P(view, text=address_text))
  12. A skeleton | AddAddressForm class AddAddressForm(Form): def __init__(self, view): super(AddAddressForm,

    self).__init__(view, 'add_form') grouped_inputs = InputGroup(view, label_text='Add an address') self.add_child(grouped_inputs) # + lots of stuff to come
  13. Models & ORM | The ORM problem Address Name Email

    John Doe [email protected] Jane Doe [email protected] class Address(object): def __init__(self, name, email): self.name = name self.email = email # more methods here ...
  14. Models & ORM | Address from sqlalchemy import Column, Integer,

    UnicodeText from reahl.sqlalchemysupport import Session, Base class Address(Base): __tablename__ = 'addressbook2_address' id = Column(Integer, primary_key=True) email_address = Column(UnicodeText) name = Column(UnicodeText) def save(self): Session.add(self)
  15. Models & ORM | AddressBox class AddressBox(Widget): def __init__(self, view,

    address): super(AddressBox, self).__init__(view) address_text = '%s: %s' % \ (address.name, address.email_address) self.add_child(P(view, text=address_text))
  16. User input | Fields and Inputs Address name email save()

    LabelledBlockInput TextInput EmailField
  17. User Input | @exposed Fields 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)
  18. User Input | Inputs using Fields class AddAddressForm(Form): def __init__(self,

    view): super(AddAddressForm, self).__init__(view, 'add_form') grouped_inputs = InputGroup(view, label_text='Add an address') self.add_child(grouped_inputs) address = Address() name_input = TextInput(self, address.fields.name) grouped_inputs.add_child(LabelledBlockInput(name_input)) email_input = TextInput(self, address.fields. email_address) grouped_inputs.add_child(LabelledBlockInput(email_input))
  19. User Input | @exposed Events class Address(Base): # some irrelevant

    code not shown here def save(self): Session.add(self) @exposed def events(self, events): events.save = Event(label='Save', action=Action(self.save))
  20. User Input | Buttons using Events class AddAddressForm(Form): def __init__(self,

    view): super(AddAddressForm, self).__init__(view, 'add_form') grouped_inputs = InputGroup(view, label_text='Add an address') self.add_child(grouped_inputs) address = Address() self.define_event_handler(address.events.save) grouped_inputs.add_child(Button(self, address.events.save))
  21. What’s next? • Fields with access control restrictions • Transitioning

    between different Views • Enabling re-use via Slots • Views that differ depending on the URL • Events with arguments • What happened to AJAX? • Internationalisation • Database schema migration