The quick and easy generative Webframework for python. Completely rewritten from scratch. Ready to go!
pip install pythononwheels
Follow on twitter for updates: Follow @pythononwheels You can check the current state on github: Star . This is also the one serving this site.
PythonOnWheels is a generative, Rapid Application Development, non intrusive web framework for python. With PythonOnWheels you need no extra tools to start. Everything from DB to Webserver and template engine is included. But you are not forced to use them and can go raw or include your own tools whenever you want.
With PythonOnWheels you simply add a class decorator like @relation.has_many("comments")
to the model (in this example a Post model) and every Post can have comments. It will
be automatically mapped to the DB (SQLite, Postgres, MySQL, MariaDb, Oracle, MSSQL ...) and to all related
comment
Models.
@relation.has_many("comments")
class Post(Base):
# Alll your model code below here ..
.....
Supported relation types are:
@relation.has_many("comments")
(decorated class has many comments.)@relation.many_to_many("comments")
(decorated class has many to many with comments)@relation.belongs_to("comments")
(decorated class belongs to comments.)@relation.one_to_one("comments")
(decorated class has one to one with comments)@relation.tree()
(decorated class has adjacence list (is a tree))For details see sqlalchemy relation documentation
@relation.setup_schema()
decorator will map this schema to a vaild
sqlalchemy (or specific NoSQL) column definition set. comments
model.
This is all done for you with the @relation.has_many("comments")
@relation.has_many("comments")
@relation.setup_schema()
class Post(Base):
#
# Schema definition with the new (cerberus) schema style
# which offer you immediate validation
#
schema = {
# string sqltypes can be TEXT or UNICODE or nothing
'author': {'type': 'string', 'maxlength' : 35},
'title' : {'type': 'string', "required" : True},
'text' : {'type': 'string'},
'votes' : {'type': 'integer'},
'status': {'type': 'string', "allowed" : ["backlog", "wip", "done"] },
}
# init
def __init__(self, **kwargs):
self.init_on_load(**kwargs)
# your methods down here
With PythonOnWheels you simply add a class decorator like @app.add_rest_routes("basename")
to your handler and you get all the typical REST routes mapped to the according CRUD methods of your
handler class.
By the way: this is what generate_handler
generates for you.
@app.add_rest_routes("comment")
class commentHandler(BaseHandler):
#
# every pow handler automatically gets these RESTful routes
# thru the @app.add_rest_routes() decorator.
#
# 1 GET /comment #=> index
# 2 GET /comment/1 #=> show
# 3 GET /comment/new #=> new
# 4 GET /comment/1/edit #=> edit
# 5 GET /comment/page/1 #=> page
# 6 GET /comment/search #=> search
# 7 PUT /comment/1 #=> update
# 8 POST /comment #=> create
# 9 DELETE /comment/1 #=> destroy
#
# standard supported http methods are:
# SUPPORTED_METHODS = ("GET", "HEAD", "POST", "DELETE", "PATCH", "PUT", "OPTIONS")
# you can overwrite any of those directly or leave the
# @add_rest_routes out to have a basic handler.
#
def show(self, id=None):
m=Comment()
res=m.find_one(Comment.id==id)
self.success(message="Comment show", data=res.json_dump())
def index(self):
m=Comment()
res = m.find_all(as_json=True)
self.success(message="Comment, index", data=res)
def page(self, page=0):
m=Comment()
page_size=myapp["page_size"]
if database["type"] == "sqlite":
limit=page_size
else:
limit=(page*page_size)+page_size
res = m.find_all(as_json=True,
limit=limit,
offset=page*page_size
)
self.success(message="Comment page: #" +str(page), data=res )
@tornado.web.authenticated
def edit(self, id=None):
self.success(message="Comment, edit id: " + str(id))
@tornado.web.authenticated
def new(self):
self.success("Comment, new")
@tornado.web.authenticated
def create(self):
self.success(message="Comment, create")
@tornado.web.authenticated
def update(self, id=None):
self.success("Comment, update id: " + str(id))
@tornado.web.authenticated
def destroy(self, id=None):
self.success("Comment, destroy id: " + str(id))
Additionally you can set direct routes by simply adding the class
decorator: @app.add_route("/")
. This will then call the default
HTTP methods of your handler (GET=get, PUT=put ...)
@app.add_route("/")
class TestHandler(BaseHandler):
# on HTTP GET this method will be called.
def get(self):
self.write("Hello world!")
You can also specify more precisely which method to call for each HTTP VERB using the
dispatch
parameter.
@app.add_route("/test/([0-9]+)*", dispatch={"get" : "test"})
class TestHandler(BaseHandler):
# on HTTP GET this method will be called.
def test(self, index=None):
self.write(index)
@app.add_route("/test/([0-9]+)*", dispatch={"get" : "test"})
to add a direct route: matching the regular expression
:
/test/([0-9+])
and then calling the given method of your handler class. The regex
group ([0-9+])
will be handed as the first parameter to test(self, index)
generate_app
generate_models script
generate_migrations script
generate_handlers
(handler are controllers)@has_many(), @has_one(), @one_to_one(), @tree()
@app.add_rest_routes("base_route")
@app.add_route("route")
generate_migration, update_db
model.validate() => executes cerberus validator