{ "info": { "author": "Peter M. Elias", "author_email": "petermelias@gmail.com", "bugtrack_url": null, "classifiers": [ "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "# Flask EasyMode\n[![Build Status](https://travis-ci.org/petermelias/flask-easymode.png?branch=master)](https://travis-ci.org/petermelias/flask-easymode) [![Coverage Status](https://coveralls.io/repos/petermelias/flask-easymode/badge.png?branch=master)](https://coveralls.io/r/petermelias/flask-easymode?branch=master) [![Downloads](https://pypip.in/d/flask-easymode/badge.png)](https://crate.io/packages/flask-easymode) [![Downloads](https://pypip.in/v/flask-easymode/badge.png)](https://crate.io/packages/flask-easymode)\n\n## Motivation\n\nThis package was mainly created to consolidate and provide an API for common \npatterns that come up when using Flask for standard web applications.\n\nPrimarily it focuses on web API patterns, and view <--> model data handling \npatterns that generally do not change very often and can be very repetitive\nat times.\n\n## Design\n\nThis extension uses Blinker heavily to provide abstract plug points and assume\nnothing about the application of any of the features provided by the API.\nGenerally speaking, you are expected to register Blinker recipient functions\nin order to define your application specific behavior.\n\nI highly recommend using ```@signal.connect``` and ```@signal.connect_via```\nto register generic and increasingly specific handlers. This has the pleasant\neffect of being efficient, lightweight, and decoupled.\n\nI have made an effort to modularize and break up the various features so that\nthis package still follows the \"opt-in\" philosophy of Flask and its extensions.\n\nFor this reason, ```init_app()``` does almost nothing until you start enabling specific\nfeatures which will start touching config variables and the application context stack.\n\nMuch of the functionality is contained within mixins, helpers, and decorators which\nmust be imported after a feature has been enabled. Obviously there are certain\ncomponents that are usable directly and are not tied to a specific feature.\n\nFor example, the some of the mixins provided are mainly to assist with common\nmodel class operations that come up on almost every project. These mixins\ncan just be plainly imported and used separately from even Flask itself.\n\nThat said, do not try and use decorators and/or helpers that depend on \noptionally-enabled features until you have enabled them. In some cases \nthey will simply ignore you, in other cases they will throw errors.\n\n\n## Features\n\n* View Function Dependency-Injection of Models (by far the coolest feature)\n* Fully Automatic XHR API Mode (error handling, data serialization, and more)\n* Totally Abstract CRUD Mixins (examples using SQLAlchemy in Wiki)\n* Useful View Helper functions such as ```redirect_self()``` and ```redirect_next()```\n\n## Examples\n\n### View Function DI\n\nThis example uses SQLAlchemy to look up our hypothetical user. Understand that the point\nof using Signals here is that you can attach any lookup system you want. Totally abstract.\n\n```python\n\n# setup app\nem = EasyMode()\nem.init_app(app)\nem.enable_injection(app)\nem.add_injectable(User) # DO NOT FORGET TO DO THIS\n\n# Sadly, EasyMode cannot magically know where you like to keep your injectable models\n# If you have a class you want ID'd by an alternate name, do this:\n\nem.add_injectable(User, alt='Uzer')\n\n# Keep in mind if you load the same class many times with multiple \n# alternate names, they will all trigger loading of that same class\n# if present during the actual injection phase. In some unusual cases\n# this is actually desireable behavior.\n\n# inherit user from Injectable mixin\nclass User(object, Injectable):\n\t\n\tdef __init__(self, name):\n\t\tself.name = name\n\n# subscribe database lookup to injection signal\n@object_injected.connect_via(User)\ndef lookup_user(cls, conditions, **kwargs)\n\tquery = session.query(cls)\n\tfor attr, value in conditions:\n\t\tquery.filter(getattr(cls, attr)==value)\n\treturn query.first()\n\t# or redis\n\t# or cassandra\n\t# or a floppy disk\n\n# setup routes\n@app.route('/user/edit/')\n@inject('user')\ndef user_edit():\n\treturn g.user.name # user is automagically assigned to g object (unless you turn that off)\n\n# make a request\nwith app.test_client() as c:\n\tprint c.get('/user/edit/petermelias') # petermelias\n```\n\nSuper cool. Imagine all the code you can delete now because you don't have to lookup models\nfrom your ```view_args```.\n\nFor anybody who is wondering, ```request.form``` is also checked for dependencies, so you can post params\nas well and they will get picked up and injected right alongside url params. Merge behavior of course.\n\n...and as of recently ```request.get_json()``` is ALSO checked for incoming parameters and merged as well. Trifecta.\n\nIf you don't like the auto-g-assignment, do this instead:\n\n```python\n@app.route('/user/edit/')\n@inject('user', as_args=True)\ndef user_edit(user):\n\treturn user.name # injects as parameter instead of global, sexy I know.\n```\n\nJust when you thought it was over... you can specify unlimited conditions.\n\n```python\n@app.route('/users/list///')\n@inject('user')\ndef users_list():\n\tprint users # [User, User, User] all matching the parameters assuming your lookup function actually does that.\n\n```\n\nYou can use arbitrarily long class names, the only rule is that the route params must be separated\nby underscores between every word.\n\n```python\nclass PharoahOfEgypt(object, Injectable): pass\n\n@app.route('/pharoah/worship/')\n@inject('pharoah_of_egypt')\ndef worship_pharoah():\n\tp = g.pharoah_of_egypt\n```\n\n### Full Auto XHR\n\nThis is really useful when you're communicating with a JavaScript frontend.\n\n```python\n# app\nem = EasyMode()\nem.init_app(app)\nem.enable_xhr(app)\n\n@app.route('/some-xhr-endpoint')\n@xhr_api()\ndef xhr_endpoint():\n\tg.xhr.data['some-data'] = 'that I want to send to the client'\n\n\tflash('A super important message')\n\tflash('A huge problem', 'error')\n\n\t# you're not blind, there is no return statement necessary in this mode\n\nwith app.app_context() as c:\n\tr = c.get('/some-xhr-endpoint', headers=[('X-Requested-With', 'XMLHttpRequest')])\n\tprint r.data\n\n\t'''\n\t{\n\t\t\"data\": {\n\t\t\t\"some-data\": \"that I want to send to the client\"\n\t\t},\n\t\t\"messages\": [\n\t\t\t[\"message\", \"A super important messsage\"],\n\t\t\t[\"error\", \"A huge problem\"]\n\t\t]\n\t}\n\t'''\n```\n\nThere are many options and behavior notes for the XHR API but they are for specific\nuse cases that will be documented in the Wiki.\n\n### Abstract CRUD Interface\n\nMixins and signals for attaching CRUD handlers. Also includes the Injectable mixin\nand a bundled one if you want the super bonus 5-pack. See the CRUDI mixin.\n\n```python\n\nclass User(CRUD): pass\n\n# or if you want Injectable too\n# class User(CRUDI): pass\n\n# generic handling function for ALL models\n@object_created.connect\ndef model_created(cls, o, **kwargs):\n\tdata = kwargs.get('data')\n\tif data:\n\t\to.update(**data) # update function comes from CRUD\n\n# user specific handling function\n@object_created.connect_via(User)\ndef user_created(cls, user, **kwargs):\n\tuser.initialize_account()\n\tuser.send_welcome_email()\n\tfacebook.report_signup_to_nsa(user)\n\n# generic model reading function using SQLAlchemy\n@object_read.connect\ndef read_model(model, _many=False, **kwargs):\n\tr = None\n\tquery = session.query(model)\n\tfilters = kwargs.get('f')\n\n\t[query.filter(f) for f in filters if filters]\n\n\tif _many:\n\t\tr = query.all()\n\telse:\n\t\tr = query.first()\n\treturn r\n\n@object_deleted.connect\ndef delete_object(o, **kwargs):\n\tsession.delete(o) # owned\n\n# and in our view...\n\n@app.route('/user/lookup/')\n@inject('user', as_args=True)\ndef lookup_user(user):\n\tuser.update(**request.form.to_dict()) # because who needs validation anyway?\n\tuser.delete() # or this user for that matter (comes from CRUD)\n\n\tsome_other_user = User.read(filters=[User.name=='Mike', User.age>=12]) # read comes from CRUD\n\tuser_list = User.read_many(filters=[User.age>=21]) # as does read_many (shortcuts _many=True)\n \n\treturn jsonify(**some_other_user.as_dict) # as_dict and as_json come from CRUD\n\n\t# you can either attach your own as_dict / as_json functions or use the defaults\n\t# in future versions.\n\t# bear in mind that if you use the built in ones, you will need to define a \n\t# _updateable and _readable property on your CRUD classes. This will not be\n\t# necessary in the next release.\n\n```\n\nAs you can see this is both a fun, concise and robust way to set up data handling since it requires\nthe bare minimum amount of code to build an efficient framework for loading and modifying data.\n\nIn otherwords, no ActiveRecord bullshit here.\n\nAlso, keep in mind that ALL signalling uses ```**kwargs.``` You can make your handlers as simple and/or\nas complex as needed. This library tries to assume the minimum level of convenience for the\nconsumer and uses private kwarg keys internally to avoid polluting your public API-space.\n\nFork, extend, merge, repeat!\n\n## Tests\n\n```python\nnosetests\n```", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://github.com/petermelias/flask-easymode", "keywords": null, "license": "MIT", "maintainer": null, "maintainer_email": null, "name": "flask-easymode", "package_url": "https://pypi.org/project/flask-easymode/", "platform": "any", "project_url": "https://pypi.org/project/flask-easymode/", "project_urls": { "Download": "UNKNOWN", "Homepage": "http://github.com/petermelias/flask-easymode" }, "release_url": "https://pypi.org/project/flask-easymode/0.0.17/", "requires_dist": null, "requires_python": null, "summary": "Make Flask development even easier", "version": "0.0.17" }, "last_serial": 913598, "releases": { "0.0.10": [ { "comment_text": "", "digests": { "md5": "bc1fe3694dffaa7a7bc1d0185bc8132b", "sha256": "1ddaaee741dfa86774e7873453ca8082e2370dd17f46fbf833c61a4e30ee5fda" }, "downloads": -1, "filename": "flask-easymode-0.0.10.tar.gz", "has_sig": false, "md5_digest": "bc1fe3694dffaa7a7bc1d0185bc8132b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10084, "upload_time": "2013-09-26T22:46:29", "url": "https://files.pythonhosted.org/packages/0f/ef/e986c6ae5cc3c04304928361c668babec8dc5e401e9047861b649ab75422/flask-easymode-0.0.10.tar.gz" } ], "0.0.11": [ { "comment_text": "", "digests": { "md5": "52e0704e3ffd75e036695a72752fb780", "sha256": "1495ca02740ab7f6cda7c2efe63f54dff46a9c08c932a7404835a21b3a29d0e2" }, "downloads": -1, "filename": "flask-easymode-0.0.11.tar.gz", "has_sig": false, "md5_digest": "52e0704e3ffd75e036695a72752fb780", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10489, "upload_time": "2013-10-16T23:03:28", "url": "https://files.pythonhosted.org/packages/0c/7e/67cc45edc565717ca0614a95be896009206691e08e55eea93f95f3e1b29c/flask-easymode-0.0.11.tar.gz" } ], "0.0.12": [ { "comment_text": "", "digests": { "md5": "4d62c727fa8daef97560605917f0610b", "sha256": "1aaa1ab4e78a01d6d34cfaf1300a7e496cecaf3c0eb9d50ec23bd62097e097c8" }, "downloads": -1, "filename": "flask-easymode-0.0.12.tar.gz", "has_sig": false, "md5_digest": "4d62c727fa8daef97560605917f0610b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14436, "upload_time": "2013-10-23T20:52:45", "url": "https://files.pythonhosted.org/packages/31/0f/4b795c5864c1a0ef8ad50e2a1893bc5d4803434f76dee9c529c099203a87/flask-easymode-0.0.12.tar.gz" } ], "0.0.13": [ { "comment_text": "", "digests": { "md5": "80c547d8b89f196f637b60bc8bd8e8f6", "sha256": "e25f7aac6d374c265192879108acf6e396836db1463c82223ed57f5870b1d775" }, "downloads": -1, "filename": "flask-easymode-0.0.13.tar.gz", "has_sig": false, "md5_digest": "80c547d8b89f196f637b60bc8bd8e8f6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14442, "upload_time": "2013-10-23T20:58:50", "url": "https://files.pythonhosted.org/packages/de/1b/146ac3a80a450407c48f2d586867bd18ad2a3e583bf183c1a7786bbc29e3/flask-easymode-0.0.13.tar.gz" } ], "0.0.14": [ { "comment_text": "", "digests": { "md5": "41e85d72fb83969e014ff717715e2e3c", "sha256": "a09f4809bbd40aa091235c51e7142c4a91e99f674995c75c2c08fc1d664c9b88" }, "downloads": -1, "filename": "flask-easymode-0.0.14.tar.gz", "has_sig": false, "md5_digest": "41e85d72fb83969e014ff717715e2e3c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14468, "upload_time": "2013-11-01T18:18:07", "url": "https://files.pythonhosted.org/packages/2f/74/56cb1349c650e32956f7de3d0a6f7e4c15df3d1e7c81381a0507d431f88f/flask-easymode-0.0.14.tar.gz" } ], "0.0.15": [ { "comment_text": "", "digests": { "md5": "3518d2383a20579055065f1125933b6d", "sha256": "96b67b41cdf76e8cd25c32aaed679d0b2b5db6dcbd8d6451a9d367ebb25818c2" }, "downloads": -1, "filename": "flask-easymode-0.0.15.tar.gz", "has_sig": false, "md5_digest": "3518d2383a20579055065f1125933b6d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14477, "upload_time": "2013-11-03T05:29:46", "url": "https://files.pythonhosted.org/packages/fc/e7/4e20af5dc2a666a3dc6bec816e484b13ace83267c575db85ba4a6e2d57a5/flask-easymode-0.0.15.tar.gz" } ], "0.0.16": [ { "comment_text": "", "digests": { "md5": "bbac5f6df9e3e678dea92292989a8a68", "sha256": "3e9c6982cbeee2fc005ead526deadd2b4143d431ccd443b243885c56cc87fa36" }, "downloads": -1, "filename": "flask-easymode-0.0.16.tar.gz", "has_sig": false, "md5_digest": "bbac5f6df9e3e678dea92292989a8a68", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14489, "upload_time": "2013-11-03T05:31:31", "url": "https://files.pythonhosted.org/packages/f2/99/9648925984f77ccde092ad3100c104cf1e4e0101b9e49cfb5c4e4e6012da/flask-easymode-0.0.16.tar.gz" } ], "0.0.17": [ { "comment_text": "", "digests": { "md5": "cfe1e9cd906f91aff02f02d4bcd01630", "sha256": "b0e3177bc5588e0caad8e8da8899d25dbab883b1d4c6354e76ac6ceb112bf11b" }, "downloads": -1, "filename": "flask-easymode-0.0.17.tar.gz", "has_sig": false, "md5_digest": "cfe1e9cd906f91aff02f02d4bcd01630", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14593, "upload_time": "2013-11-07T13:32:24", "url": "https://files.pythonhosted.org/packages/21/47/bd2eeb141de4bbeb2af40eb038951032c3b47d7a432fbfd34c1b33d1e5b0/flask-easymode-0.0.17.tar.gz" } ], "0.0.3": [ { "comment_text": "", "digests": { "md5": "ed6ee2e20cb297be4b6885899169f6af", "sha256": "1d357d04cf4af2f1119c6da6ca7b5d975e1975a1ac0b101dec3561bbca766c05" }, "downloads": -1, "filename": "flask-easymode-0.0.3.tar.gz", "has_sig": false, "md5_digest": "ed6ee2e20cb297be4b6885899169f6af", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13049, "upload_time": "2013-09-11T14:58:19", "url": "https://files.pythonhosted.org/packages/d8/d9/9e90b70ffee483f786a7c337b823b7b43d6fbe1e736cbfd6e94ff9725ca0/flask-easymode-0.0.3.tar.gz" } ], "0.0.4": [ { "comment_text": "", "digests": { "md5": "55b81daa3b05a76a4b39322260c0ff4a", "sha256": "4249103d6cb85fa2286bf0bec543e21c3c1ed098e1789a47bd671db4005ed860" }, "downloads": -1, "filename": "flask-easymode-0.0.4.tar.gz", "has_sig": false, "md5_digest": "55b81daa3b05a76a4b39322260c0ff4a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13033, "upload_time": "2013-09-14T00:07:17", "url": "https://files.pythonhosted.org/packages/62/a9/a968dcb3b8f7bd870e2f10df87ddd6840c26cc645d16defb56725b3d6993/flask-easymode-0.0.4.tar.gz" } ], "0.0.5": [ { "comment_text": "", "digests": { "md5": "26959812fb118eba0d0c02d34175558f", "sha256": "1ddbbde57c0164037daa821dbed92c61a5232dcb028d9a025e0c9c3c6cc0bcd8" }, "downloads": -1, "filename": "flask-easymode-0.0.5.tar.gz", "has_sig": false, "md5_digest": "26959812fb118eba0d0c02d34175558f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12916, "upload_time": "2013-09-14T00:21:17", "url": "https://files.pythonhosted.org/packages/51/44/752472e24bbd9c73dc7f34ab8b8c2cf273d13142ffd113bd4736139b4029/flask-easymode-0.0.5.tar.gz" } ], "0.0.6": [ { "comment_text": "", "digests": { "md5": "b0daf79d58b9e24c9410d5746de75110", "sha256": "3a914f0c7ef5ece1b250dbd90ce01ba7e68df98068bbcd6989283e0de679683b" }, "downloads": -1, "filename": "flask-easymode-0.0.6.tar.gz", "has_sig": false, "md5_digest": "b0daf79d58b9e24c9410d5746de75110", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12929, "upload_time": "2013-09-15T08:03:23", "url": "https://files.pythonhosted.org/packages/c0/8d/98f97da6e84cf1422dca9f23704ffe5a2e50bfdc6ce9f34db20f8b457c0a/flask-easymode-0.0.6.tar.gz" } ], "0.0.7": [ { "comment_text": "", "digests": { "md5": "0ed9013ace5c3f02efa1352510a3fba8", "sha256": "edd9df5cc269f6691cab3f3d4096265c06ae478d459ba406d060f3ac3e5c474f" }, "downloads": -1, "filename": "flask-easymode-0.0.7.tar.gz", "has_sig": false, "md5_digest": "0ed9013ace5c3f02efa1352510a3fba8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12948, "upload_time": "2013-09-15T20:54:04", "url": "https://files.pythonhosted.org/packages/5e/70/abc8e49ee5302634a5af1360f7d975360a90255b8509d0ef76622d7e19c9/flask-easymode-0.0.7.tar.gz" } ], "0.0.8": [ { "comment_text": "", "digests": { "md5": "daa0294ab6501253f7597c5d1210f5a5", "sha256": "2154de6dbc924c822a1da2f15178c0a1b08ff545b07fe5a66f1fa98bbadad331" }, "downloads": -1, "filename": "flask-easymode-0.0.8.tar.gz", "has_sig": false, "md5_digest": "daa0294ab6501253f7597c5d1210f5a5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13487, "upload_time": "2013-09-15T21:28:15", "url": "https://files.pythonhosted.org/packages/ac/23/4333c2898e632157e9be2d96fb265d60ef96660ad0647523e04d23a590bf/flask-easymode-0.0.8.tar.gz" } ], "0.0.9": [ { "comment_text": "", "digests": { "md5": "01dcbfcf67742434aa182cedf4a0e831", "sha256": "eb97905334fe85900a09fd5ee07333eb22a599d534fddf42fddf630b08731796" }, "downloads": -1, "filename": "flask-easymode-0.0.9.tar.gz", "has_sig": false, "md5_digest": "01dcbfcf67742434aa182cedf4a0e831", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9973, "upload_time": "2013-09-26T19:23:29", "url": "https://files.pythonhosted.org/packages/fc/62/9766b3938f35dea2a71a9d015b2eb93cb89b257553dd556a76d81d009b46/flask-easymode-0.0.9.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "cfe1e9cd906f91aff02f02d4bcd01630", "sha256": "b0e3177bc5588e0caad8e8da8899d25dbab883b1d4c6354e76ac6ceb112bf11b" }, "downloads": -1, "filename": "flask-easymode-0.0.17.tar.gz", "has_sig": false, "md5_digest": "cfe1e9cd906f91aff02f02d4bcd01630", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14593, "upload_time": "2013-11-07T13:32:24", "url": "https://files.pythonhosted.org/packages/21/47/bd2eeb141de4bbeb2af40eb038951032c3b47d7a432fbfd34c1b33d1e5b0/flask-easymode-0.0.17.tar.gz" } ] }