Giter VIP home page Giter VIP logo

pow_devel's Introduction

Pow logo

Just do your thing!

A python web framework, build on the Ruby on Rails principles.

Generators for models, controllers, scaffolding views and database migrations. Validation, convention over configuration..and much more. Focus on your thing instead of the boilerplate.

Hello World

@app.make_routes()
class HelloHandler(BaseHandler):
    @route(r'/hello', dispatch=["get"])
    def hello(self):
        self.write("Hello world!")

Installation:

pip install -U pythononwheels

Everything you need on board. Batteries included.

PythonOnWheels is a generative, Rapid Application Development, non intrusive web framework for python. 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 easily include the modules and tools of your choice tools whenever you want.

Based on a very Strong foundation:

Probably the most simple SQL relations out there!

Based on sqlalchemy. With PythonOnWheels you simply add a class decorator like

@relation.has_many("comments")
class Post(Base):
    # All your Post model code below here ..
    .....

to your SQL 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. DB Migrations are created automatically in the background.

supported relation types are:

  • has_many("comments")(decorated class has many comments.)
  • many_to_many("comments")(decorated class has many to many with comments)
  • belongs_to("comments")(decorated class belongs to comments.)
  • one_to_one("comments")(decorated class has one to one with comments)
  • tree() (decorated class has adjacence list (is a tree)

All pow models (SQL or NoSQL) use a cerberus schema as definition.

This means you have validation on board for every model and you can easily switch from SQL to NoSQL

  • the @relation.setup_schema() decorator will map this schema to a vaild sqlalchemy (or specific NoSQL) column definition set.
  • SQL only: model will also automatically get all the right Foreign_Keys and python attributes to create a has_many relationship to the 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

Probably the most simple RESTrouting out there! One decorator. Done!

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 when you use the --rest parameter:

@app.add_rest_routes("rest_test")
class RestTest(BaseHandler):
    # 
    # every pow handler automatically gets these RESTful routes
    # when you add the : app.add_rest_routes() decorator.
    #
    # 1  GET    /resttest                           #=> list
    # 2  GET    /resttest/<uuid:identifier>         #=> show
    # 3  GET    /resttest/new                       #=> new
    # 4  GET    /resttest/<uuid:identifier>/edit    #=> edit 
    # 5  GET    /resttest/page/<uuid:identifier>    #=> page
    # 6  GET    /resttest/search                    #=> search
    # 7  PUT    /resttest/<uuid:identifier>         #=> update
    # 8  PUT    /resttest                           #=> update (You have to send the id as json payload)
    # 9  POST   /resttest                           #=> create
    # 10 DELETE /resttest/<uuid:identifier>         #=> destroy
    # ...

Routing: RegEx and Flask like routes included .

You can set routes by simply adding a class decorator to the handler class or decorate methods directly.

@route("/", dispatch=["get"])

PythonOnWheels will then call the index method of your handler if the route and the HTTP method matches.

Example for Flask like routing:

@app.make_method_routes()
class HelloHandler(BaseHandler):
    @route(r'/hello/<int:identifier>', dispatch=["get"])
    def hello(self, identifier=None):
        self.write("Hello world! " + str(identifier))

For Regex routes:

@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)

Model Validation on board with cerberus schemas.

All Model Schemas are Cerberus schemas automatically. So thats easy.

    model.validate() => executes cerberus validator

And finally: a super easy workflow. Quick to start, all the basics on board and easy to expand: generative approach (but not noisy)

  • generate_app script
  • generate_models script (You probably want to store some data)
  • Optionally generate_migrations script (only needed for SQL DBs)
  • generate_handlers (aka controllers to define your logic and API)
  • start the server (python server.py) done

The vision:

If you want start to develop your web-application and focus on the App, instead of the frameworks, you are in the right place. PythonOnWheels feels right if you do not recognize that you use it.

Enjoy!

See getting started or go to the documentation

pow_devel's People

Contributors

kilohertz avatar smoy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

pow_devel's Issues

Add default schema URL / Endpoint for model REST API

So you can access the schema as well

API /modelname/schema
Returns JSON

Example JSON Rsponse for a blog article model:

call:

localhost:8080/article/shema

response:

schema = {
        'title'          :   { 'type' : 'string', 'maxlength' : 255},
        'teaser'         :   { 'type' : 'string' },
        'text'           :   { 'type' : 'string'},
        'tags'           :   { 'type' : 'list', "default" : [] },
        'comments'       :   { 'type' : 'list', "default" : [] },
        'featured'       :   { 'type' : 'boolean', "default" : False },
        'blog'           :   { 'type' : 'boolean', "default" : False },  
        "blog_link"      :   { 'type' : 'string', "default" : "" },
        "votes"          :   { "type" : "integer", "default" : 0 },
        "author"         :   { "type" : "dict" }, #author 
        "images"         :   { "type" : "list"  }, #list of images uploaded for this article   
        "featured_image" :   { "type" : "string", "default" : "" },
        "read_time"      :   { "type" : "string"},
        "published"      :   { "type" : "boolean"},
        "published_date" :   { "type" : "datetime", "default" : datetime.datetime(1999,1,1,23,59)},
        "voter_ips"      :   { "type" : "list", "default" : [] },
	 "twitter_card"	 :   { "type" : "boolean", "default" : False }
    }

fwfgr

Restful API Authentication

Hi,

I want to know what is the best option to implement authentication in pythononwheels restful api ???

2 primary keys issue

Hello, please help me to figure out what's doing on, my original table consists of:

+------------+---------------+------+-----+---------+-------+
| Field      | Type          | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no     | int           | NO   | PRI | NULL    |       |
| birth_date | date          | NO   |     | NULL    |       |
| first_name | varchar(14)   | NO   |     | NULL    |       |
| last_name  | varchar(16)   | NO   |     | NULL    |       |
| gender     | enum('M','F') | NO   |     | NULL    |       |
| hire_date  | date          | NO   |     | NULL    |       |
+------------+---------------+------+-----+---------+-------+

But when i'm creating model, view and handler, my model looks like this

>>> from employees.models.sql.employees import Employees
setup_schema:employees
>>> t = Employees()
>>> t
id                  : None (primary key) 
created_at          : None
last_updated        : None
emp_no              : None (primary key)
birth_date          : None
first_name          : None
last_name           : None
hire_date           : None

Why i have 3 extra fields and 2 primary keys even if i'm using

_use_pow_schema_attrs = False

?
And why i don't have

gender     | enum('M','F')

field?
Could someone help me to make it right, thank you.

here is my model employees.py

#
# Model Employees
#
from enum import Enum
from sqlalchemy import Column, Integer, String, Boolean, Sequence
from sqlalchemy import BigInteger, Date, DateTime, Float, Numeric, Unicode, Text
from employees.lib.powlib import relation
from employees.database.sqldblib import Base
from employees.lib.powlib import PowBaseMeta
from sqlalchemy import schema
from sqlalchemy.sql.sqltypes import NULLTYPE
import enum
from sqlalchemy import Integer

# @relation.has_many("<plural_other_models>")
@relation.setup_sql_schema()
class Employees(Base, metaclass=PowBaseMeta):

    #
    # cerberus style schema
    #
    schema = {
        'emp_no':   {'type': 'integer',
                     "sql":
                     {
                         "primary_key": True,
                         "default": NULLTYPE,
                         "unique": True,
                         "nullable": True
                     }, },
        'birth_date':   {'type': 'datetime', "sqltype": "date"},
        'first_name':   {'type': 'string', 'maxlength': 14},
        'last_name':   {'type': 'string', 'maxlength': 16},
        'gender':   {'type': 'string', "allowed": ["M", "F"],  "sqltype": "enum"},
        'hire_date':   {'type': 'datetime', "sqltype": "date"}

    }


    # if you want to define a custom tablename for this model:
    __tablename__ = "employees"


# if you dont want to use the pow schema extension
_use_pow_schema_attrs = False

# define class attributes/variables here that should be included in to_dict()
# conversion and also handed to the encoders but that are NOT part of the schema.
include_attributes = []

# Add sqlalchemy table_args here. Add "autoload" : True for database reflection
__table_args__ = {"extend_existing": True}

#
# init
#


def __init__(self, **kwargs):
    self.setup_instance_values()
    self.init_on_load(**kwargs)
    #
    # your model's methods down here
    #


Rework TinyBaseModel _return_find() -> dict_result_to_object()

what: Behaviour for lists vs single results in the methods above
class: models.tinydb.TinyBaseModel

currently:
return element only for one result
return list for more than one result.

problem:
results in many if len ==1 ... in the methods

Should change to:
Always return list.

tune paginator to change scope automatically

should work like the one at google.
first display
1,2,3,4,... Last

if you click on 4 for example it should not only go to page 4 but also display

3,4,5,6...last

and so forth. if you are at page 100 (from lets say 1000) it should display

99, 100, 101, 102 ... last

MS SQL Server: Change Default Schema

Hi,

To make a query in SQL Server to specific table i have and intermediate scheme to stableish the access (dbname.schema_name.table_name), how do i set the schema to query that table????

I know to connect to db you use the connection string to stablish the connection to databe, only left to set the schema name to get the table, how i do that???

add a generate_view scaffolding

Command idea:

generate_view -n name -b <base>

will generate a basic tornado template with the following basic structure:

{% extends ../<base> %}

{% block include_js %}
    // all needed js libs for this view go here
{% end %}

{% block view_js %}
     function view_docready(){
        // everything that needs to be executed in docready for this view
       // goes here.
     }
{% end %}

{% block include_css %}
    // all css includes for this view here

{% end %}

{% block css %}
   // all css for this view here
{% end %}

Sio everything will be niocely embedded in a base template and you do not pollute the css/js includes or <style> / <script> blocks but keep them locally in every view.

Dash 1.6.0 needs defined (max) core components..

ERROR: dash 1.6.0 has requirement dash-core-components==1.5.0, but you'll have dash-core-components 1.8.0 which is incompatible.
ERROR: dash 1.6.0 has requirement dash-html-components==1.0.1, but you'll have dash-html-components 1.0.2 which is incompatible.
ERROR: dash 1.6.0 has requirement dash-renderer==1.2.0, but you'll have dash-renderer 1.2.4 which is incompatible.
ERROR: dash 1.6.0 has requirement dash-table==4.5.0, but you'll have dash-table 4.6.0 which is incompatible.

Need to adapt the requirements_dash file.
Still works, though.

Include Werkzeug routing

make the app.add_route decorator accept werkzeug style routes as well.
Scope: This will only affect the regex.
Usage stays the same.

app.add_route( route_regex, dispatch={} )
Current usage:

@app.add_route("/thanks/*", dispatch={"get": "_get"} )
@app.add_route("/thanks/([0-9]+)*", dispatch={"get": "testme"})
class ThanksHandler(BaseHandler):
    def _get(self):
        print("  .. in _get" )
        self.render("thanks.tmpl")

    def testme(self, index=0 ):
        print("  .. in testme: index = " + str(index))
        self.render("thanks.tmpl", index=index)

-->regex groups will be handed over to the handler mathod as parameters

New Option: will include Werkzeug routres as well
@app.add_route("/thanks/<int:year>", dispatch={"get": "testme"})
=> Rule("/thanks/<int:year>", endpoint="testme")
@app.add_route("/thanks/<int:year>/<int:month>/<int:day>/<slug>", dispatch={"get": "testme"})
=> Rule('/thanks<int:year>/<int:month>/<int:day>/<slug>', endpoint="testme")

The compiled Rule will be added to the tornado routes with the accordiung dispathc dict.
See application.add_route()

fin_route = Rule.compile()
# cls is the handler class
# dispatch is the dispatch dict from app.add_route() decorator.

route_tuple = (fin_route,cls, dispatch)
handlers.append((route_tuple,pos))

see: http://werkzeug.pocoo.org/docs/0.14/routing/

Add add_route decorator for methods

Use the class decorator add_route style for methods as well.
Of course the method name can be left out then.
See: add_mroute(...) below... shold be called add_route as well in the real implementation.

sample:

@app.add_route('/handler1/classdec', dispatch={"get" : "new_test"})
class Handler1(PowHandler):
    #
    # on HTTP GET this method will be called. See dispatch parameter.
    #
    def _get_method(self, testval=0):
        """
            just a simple hanlder sceleton. Adapt to your needs
        """ 
        out_dict = {"testval" : str(testval)}
        self.success(message="I got testval:", data=out_dict, format="json", raw_data=True)
        #self.write("I got testval:" + str(testval))
    
    @add_mroute('/handler1/funcdec', dispatch=["get"])
    def new_test(self):
        """
            method to test a function decotrator
        """

Make View Framework optional (Bootstrap4, SemanticUI)

Semantic is great but more people prefer bootstrap since it is more widely used.

Bootstrap4 has switched from alpha to beta as of Aug 10th 2017. So this is the default option for the future.

separate the stubs and template files by extension
Bootstrap = .bs4
semanticUI = .sui

after generating these will all be called .tmpl but include their correct templates which are still named
base.bs4
vs
base .sui

Auto Default value for lists not working

So lists need an explicit "default" : [] in the model definition:

schema = {
        'title':    { 'type': 'string', 'maxlength' : 35},
        'text' :    { 'type': 'string'},
        'tags' :    { 'type': 'list', "default" : [] },
        "votes" :   { "type" : "integer", "default" : 0 }   
}

see line:

'tags' : { 'type': 'list', "default" : [] },

Error while running demo

Followed exact steps from quick start demo and getting this error (windows and ubuntu the same error)

Traceback (most recent call last):
File "C:\Work\pow\todo\server.py", line 16, in
from todo.lib.application import Application, log_handler
File "c:\Work\pow\todo\lib\application.py", line 12, in
from todo.database.sqldblib import Base, Session, engine
File "c:\Work\pow\todo\database\sqldblib.py", line 42, in
metadata = MetaData(engine)
File "C:\Work\pow\todo\lib\site-packages\sqlalchemy\sql\schema.py", line 5231, in init
raise exc.ArgumentError(
sqlalchemy.exc.ArgumentError: expected schema argument to be a string, got <class 'sqlalchemy.engine.base.Engine'>.

Can't generate migrations

Following your example in the screen cast I can't seem to generate migrations.

vagrant@precise64:~/pow_devel/weblog$ python generate_controller.py post
generate_controller: post
-- created controller controllers/PostController.py
-- generating TestCase... .//tests/controllers/TestPostController.py...(created)
generated_controller in(0:00:00.000720)

vagrant@precise64:~/pow_devel/weblog$ python generate_scaffold.py post
{'model': 'None', 'force': False, 'template': "/${context.get('template')}"}
generate_scaffold for model: post
-- created scaffold views/Post_list.tmpl
-- created scaffold views/Post_show.tmpl
-- created scaffold views/Post_create.tmpl
-- created scaffold views/Post_edit.tmpl
-- created scaffold views/Post_message.tmpl
generated_scaffold in(0:00:00.001537)

vagrant@precise64:~/pow_devel/weblog$ python generate_migration.py post
2013-02-24 19:07:56,700 INFO sqlalchemy.engine.base.Engine PRAGMA table_info("apps")
2013-02-24 19:07:56,700 INFO sqlalchemy.engine.base.Engine ()
2013-02-24 19:07:56,701 INFO sqlalchemy.engine.base.Engine PRAGMA foreign_key_list("apps")
2013-02-24 19:07:56,701 INFO sqlalchemy.engine.base.Engine ()
2013-02-24 19:07:56,702 INFO sqlalchemy.engine.base.Engine PRAGMA index_list("apps")
2013-02-24 19:07:56,702 INFO sqlalchemy.engine.base.Engine ()
generated_migration in(0:00:00.000040)

vagrant@precise64:~/pow_devel/weblog$ ls migrations/
00001_app.py 00002_versions.py init.py

simplify the generators.

  1. review the cold (pretty old and ugly)
    1.1 4. a #TODO tag if things need to be done and are not done immediately.
  2. clean it up:
    2.1. use ONE stub file where Tags act as placeholders that will be replaced be the generator
    instead of 'really' concatenate the result_string by opening parts stubs and adding strings to that.
    2.2. DRY things out.
  3. Docstring the functions

replace all evals with getattr

example: from BaseController.py (ln. 175-178):

def redirect(self, action, **kwargs):
        """ sets the given action and executes it so that all prerequisites are correct """
        self.setCurrentAction(action)
        return eval("self." + action + "(**kwargs)")

should be replaced by:

def redirect(self, action, **kwargs):
        """ sets the given action and executes it so that all prerequisites are correct """
        self.setCurrentAction(action)
        return getattr(self, action)(**kwargs)

A more secure and pythonic solution.

thanks to microkernel (user from the www.python-forum.de)
see post: http://www.python-forum.de/viewtopic.php?f=9&t=29801

reliable check for file uploads

for demo blog picture upload a reliable helper method has to be created which
checks whether a multipart form contains file data or not.

in case someone edits a post which had an image before but no new one is
chosen the form is type multipart, it contains the according field (of type file input)
but the req.body does not contain the file data

Invalid Python comparison operator syntax used in auto-generated ../lib/application.py

Immediately after generating a brand new application using the generate_app command, the new application Tornado server server.py script fails to run due to a syntax error in the auto-generated lib/application.py script. It is evident that there is a syntax error in the auto-generated script as this is an invalid Python statement.

Here is the output

(sharept_env) myusr@mydesktop:~/scripts/python/web/app/app$ python server.py
Traceback (most recent call last):
  File "server.py", line 16, in <module>
    from app.lib.application import Application, log_handler
  File "/home/myusr/scripts/python/web/app/app/lib/application.py", line 96
    if current_role :=  required_role != "any" and self.current_user.role != required_role:
                     ^
SyntaxError: invalid syntax

Line 96 should probably be changed to:

if required_role != "any" and self.current_user.role != required_role:

The above modification does allow the web server to start up. I just installed pythononwheels 0.925.1 only a few minutes ago (testing out your app). Unfortunately, as-is and without the above modification, this bug makes running a brand new application impossible. My Python version is 3.7.9, but this seems to be irrelevant as the issue is not version-related, rather a syntax issue. Thank you for looking into this problem. This bug must have been introduced in a recent commit, as this would prevent any new user from running a newly-created application no matter the Python version or platform.

HTTPS Protocol

Question, what is the best way to set the ssl certificates, what i guess is to add like an ssl_options with their respective paths in the config.py file and then in the server.py add the ssl options

reference: https://stackoverflow.com/questions/13471115/how-to-handle-a-request-with-https-protocol-in-tornado/13472397
http_server = tornado.httpserver.HTTPServer(applicaton, ssl_options={
"certfile": os.path.join(data_dir, "mydomain.crt"),
"keyfile": os.path.join(data_dir, "mydomain.key"),
})

or this is already implemented ?

Dash integration with pythononwheels is broken due to Werkzeug

With pythononwheels==0.925.51, the dash tutorial is broken due to unpinned dependency on Werkzeug

cited on plotly/dash#1992

Nothing specific with pythononwheels; however, for someone to follow the current dash on pythononwheels will fail.

In order to workaround the problem, I have to pip install a compatible version

pip install Werkzeug==2.0.3

Observer for Models

create Observeration instances for Models.

standard:
Modelname_observer.py

  1. shall be automatically included by the models
  2. according methods are executed after model method.
    convention:
    Model->create calls Model_observer->create()
  3. Handling idea:
    via getattribute
    check if observer is there, check for according method, execute if there.

Add uikit3 support as an option to bootstrap4

uikit3 is very clean an simple.
Nice results.

Offering two view options doesnt turn to be negative since client side programming is not core part of the framework.
Users may skip that completely and got for progressive approaches (vue, react...)

Validation for Model Attributes

shall be transparently available as externel validation module/class.

method:
getattribute in model checks whether or not there is a
validation configured for this attribute. If so, it calls the validation method before
setting the attribute

Add basic Authentication as an extension module

as a plugin in ext/auth
but as an intermediate solution realized as a decorator.

so:
@authenticate
def action(self):
example ...

will make an action executable only if a user is logged on.

Veeerrry simple but enough for Beta1.

A more sophisticated Authentication will follow in Beta2.

Role Based Authorisation might be left to someone else ....

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.