A Ruby on Rails workflow for Python

Klaas (khz)
9 min readFeb 8, 2019

When I saw the Ruby on Rails intro by DHH I was literally flashed. It included all I ever wanted to develop web applications in a way, where you can really leave out all the boilerplate and focus on the Application you want to build. It didn’t only include the parts I (and probably most developers) just needed but the main change was the workflow.

You just generate and scaffold the aspects like models, controllers and views you need and if you stick to a little convention (e.g. using the same names) everything works without any further configuration.

It just flows, It just works!

I Think this workflow is ideal for small and medium sized projects

Because it makes it easy to get started quickly with your idea and app and do not have to worry about installing and configuring a Webserver, a Database, an authentication package, the right ORM .. etc, beforehand.

Of course it must be easy to use your own modules or access the underlying modules directly later on.

I’m a python guy, so this is where PythonOnWheels comes into play

The only drawback for me with Rails was that I did not really got a connection to Ruby. Since I could not find something similar in the Python world, I made a small python framework named PythonOnWheels that integrates a solid set of python modules and glues them together to give a fluid, Rails like workflow. To me this easiness and fluent flow of Rails perfectly fit’s for Python since Python also takes away a lot of boilerplate from you by design and conventions. It’s even more important what you don’t have to do than what you can do in an easy way. (No protected, public, private, no getters, setters, no strict/static typing forced …)

You can write a pretty neat app, say a twitter client in a handful of lines in Python. The goal was to be able to write a WebApp this easy with PythonOnWheels

There are a lot of superb frameworks for python out there. The minimal ones, like e.g. Flask and the big ones like e.g. Django. But with the minimal you have a lot of manual work when you need something more that the first route and with the big ones you have a lot to configure and setup to go.

So what is PythonOnWheels:

PythonOnWheels (PoW) is a generative Model-View-Controller web framework for python. Based on the principles, conventions and workflow of Ruby on Rails. It sits right in the middle between minimal and big frameworks. It includes what probably most developers want for small and mid sized projects. It’s OpenSource and Free! (MIT License)

The idea is to include and seamlessly integrate everything you need for small / medium projects and glue it to make it nice and handy.

based on a strong, well known and well documented foundation

  • python 3.x
  • tornado webserver
  • SQL Databases: SQLAlchemy ORM (SQLite, MySQL, PostgreSQL, MS-SQL, Oracle, and more ..)
  • Automatic Database Migrations onboard (based on alembic)
  • NoSQL Databases : tinyDB, MongoDB, ElasticSearch (in progress)
  • cerberus schemas and validation for models on board
  • Models support serialisation/deserialisation to JSON, XML and CSV
  • views: tornado templates
  • views css Frameworks: bootstrap4 and semanticUI
  • plotly Dash integration (pandas)for Data visualisation
  • oauth2 authentication (Twitter, Facebook, google, also tested with Stackoverflow)

Some excerpts of what PythonOnWheels adds

All PoW models SQL andNoSQL use the same schema definition.

Actually we use dictionaries which are cerberus schemas to define the models. This schema definition is not only super simple but it also means you have validation on board for every model and you can easily switch from SQL to NoSQL. So you don’t have to worry how to define SQlite model schemas or what it was precisely for MongoDB.

A dictionary is pretty common and easy to remember. This stub below is even generated for you so you just adapt it to your needs. Read more: Working with models

Probably the most simple SQL relations out there!

Based on SQLAlchemy.

If you are working with SQL Databases you probably want to make some relations after defining the models. This is usually not so easy not much fun and error prone if your focus is not DB management but an App.

This is again a point where PythonOnWheels takes away the pain from you. In the example below we relate the Task model to the Subtask model simply by adding the @relation.has_many decorator to the Task class. That’s it.

Behind the scenes PythonOnWheels generates SQLAlchemy models and the appropriate alembic migrations for you. You simply fire the migration and there you go. Read more here: working with SQL relations

Probably the most simple REST routing out there!

After scaffolding your models you’ll probably want to make them accessible. The most common use case is that you want the CRUD actions and a REST API. You scaffold a REST handler and simple add the @app.add_rest_routes decorator and that’s it. The following methods and routes are automatically created for you. Read more: Generating REST Handlers

By the way: if you give it the same name as the model they will automatically be linked together. This also works for the views later on.

Flask/Werkzeug like and RegEx routing (Tornado)

Since PoW is based on Tornado we have the RegEx routing on board. I also added direct support for Flask/Werkzeug routing since I think this style is really easy and fluent to use:

Automatic Views Scaffolding for CRUD actions

If you use the same name, the generate_scaffold script automatically generates bootstrap4 or semanticUI views for you. They automatically adapt to the correct model fields and types. Below are examples for the list and new views. Views are by default Tornado templates which give you a mustache like, simple but powerful template language.

BTW: The tags field will be automatically converted to a list, since we defined it to be of that type in the model schema.

Installation is just

pip install -U pythononwheels

Then generate your first app

generate_app -n <appname> -p <path>

I recommend that you create a virtualenv and install the requirements:

virtualenv <path>

cd into <path> and activate the virtualenv (source bin/activate)

pip install -r requirements.txt

Start the Server.

python server.py

Point your browser to http://localhost:8080

Creating a model is as easy just:

python generate_model.py -n todo -t tinydb

Of course you can choose any name. As types you can choose sql (any SQLAlchemy supported SQL DB), tinydb, mongodb or elastic. This will generate the following output

 — — — — — — — — — — — — — — — — — — — — 
generating model: todo
— — — — — — — — — — — — — — — — — — — —
model_class_name: Todo
model_name_plural: todos
… generated: tinydb DB Model
— — — — — — — — — — — — — — — — — — — —
… in : /Users/khz/development/todo/models/tinydb/todo.py

So we can find our new model in the models/tinydb directory of our application. It looks just as shown above.

Let’s play around and save some Todos

Open a python prompt and import the model and create an instance

>>> from todo.models.tinydb.todo import Todo
… setting it up for tinyDB: /Users/khz/development/todo/tiny.db
>>> t=Todo()
>>>

Let’s set a title and text

>>> t.title=”A new todo”
>>> t.text=”I need to finish this”

Let’s inspect our model

>>> t
{ ‘_uuid’: ‘’,
‘created_at’: datetime.datetime(2019, 2, 8, 21, 35, 13, 730304),
‘id’: ‘’,
‘last_updated’: datetime.datetime(2019, 2, 8, 21, 35, 13, 730304),
‘tags’: [],
‘text’: ‘I need to finish this’,
‘title’: ‘A new todo’,
‘votes’: 0}

You can see that PythonOnWheels (just like Rails) added the attributes _uuid, id, last_updated and created_at. These make data handling a lot easier later and are handled automatically for you.

Now lets save our Todo to the Database

>>> t.upsert()
insert, new eid: 1

Yai, we persisted our Todo. The upsert() method will automatically insert or update whether the model is already in the DB or not.

Finally let’s query the Database to see if it worked

>>> r=t.find_all()
>>> for elem in r:
print(elem.title)
A new todo

Models have a lot of methods that are self explanatory. Like find_all(), find_first(), find_one(), delete(), upsert().. Read more: Working with Models.

Now let’s create a REST API

Creating a RETS API is almost as easy since the workflow is always the same in PythonOnWheels. So you probably guess it. We will generate a handler and add the REST routes to it. Let’ start

Generate a REST handler

python generate_handler.py -n todo -t tinydb --rest

Two conventions are important here. One is that you give the handler the same name as the model (todo) and the other is that you give it a hint of which Database type the model is. This is due to the fact that you can actually use different Databases in the same app in PoW. The — rest parameter makes sure that PoW generates a handler for us that already supports all CRUD actions and exposes the according routes for them.

CamelCased handler name: Todo
— — — — — — — — — — — — — — — — — — —
generating handler: Todo
— — — — — — — — — — — — — — — — — — —
… REST Handler
… created: /Users/khz/development/todo/handlers/todo.py
— — — — — — — — — — — — — — — — — — —

So you can see that the handler was generated for us in the handlers directory. We will not look at it in detail here. If you want to see more, just read here: Working with handlers. For now this

Let’s finally test our REST API

start the server again

python server.py

We will use requests to test the API now. Open a python prompt

>>> import requests
>>> r=requests.get(“http://localhost:8080/todo")
>>> r.json()
{‘status’: 200, ‘message’: ‘todo, index’, ‘data’: ‘{“title”: “A new todo”, “text”: “I need to finish this”, “tags”: [], “votes”: 0, “id”: “5d6f7a4d-5e3d-4c08-bf62-e385deb45061”, “_uuid”: “5d6f7a4d-5e3d-4c08-bf62-e385deb45061”, “created_at”: “2019–02–08 21:42:13”, “last_updated”: “2019–02–08 21:43:14”}’, ‘next’: None, ‘prev’: None}
>>>

Cool. We have a working API running.

What happened is that the request for the route /todo called the todo handler (by convention) and executed the list action (method). If no special accept format is requested it will respond with the default format, which is JSON.

Summary

I hope you see that the workflow is really fluent and focussing on the output, hiding all the boilerplate and config behind the scenes.

It is really important to notice that we have not configured anything and created a working application with a model that stores data in a NoSQL Database. To add an API was not harder at all. Just scaffold, use the same name. Done. There you go with your working REST API.

Check the PoW sample apps:

  • medium.py => a blogging system / medium clone made for self hosting. It uses the brilliant medium-editor (tweaked a little) and the superb mediumish.css: I actually also use this to host the PythonOnWheels homepage.
  • Plotly Dash Integration => Integration of a Dash app in PythonOnWheels: Really nice and simple integration of Dash. If your focus is visualisation and you need to have a framework for DB access and authentication around, check this out. Just a generate_dash and you are good to go. Get your data in a pandas dataframe and use the great plotly Dash library to visualize it, with crossfilters, callbacks and all it offers.

Read the short hands-on tutorial

You can read the short hands-on tutorial to make a small todo app with a NoSQL DB (TinyDB), a REST API and view scaffolding.

Or watch the same as a 10 Minute intro video.

Hope you like the idea and the flow of PoW

--

--

Klaas (khz)

11 to 1 pm spare time software developer. Mostly python.