Skip to content

Commit

Permalink
added
Browse files Browse the repository at this point in the history
  • Loading branch information
kmjbyrne committed Dec 27, 2020
1 parent 0160d85 commit b46eefe
Show file tree
Hide file tree
Showing 28 changed files with 177 additions and 444 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ python:
install:
- pip install -r requirements.txt
script:
- nosetests tests
- true
52 changes: 52 additions & 0 deletions docs/basic.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
Basic Usage
============

Lets start with a basic usage example and illustrate the core features Flask Atomic offers.

Architect Blueprint
++++++++++++++++++++

The Architect Blueprint is itself yes, you guessed it a Flask Blueprint. It looks, smells and
sounds just the same as a normal Flask Blueprint but does a little magic for you.

All that's needed to start is a SQLAlchemy Model to get things started.


.. code-block:: python
class SomeModel(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Datetime, default=datetime.now())
username = db.Column(db.String(50))
alias = db.Column(db.String(50))
When we have models like this we typically will have a GET route, POST route maybe DELETE and
PUT depending on the application. Typically, these routes will have similar behaviours across
different application: define a route, take some data, create a model, save to database.

.. code-block:: python
from flask_atomic import Architect
from .models import SomeModel
monitor_blueprint = Architect(SomeModel)
app = Flask(__name__)
app.register_blueprint(monitor_blueprint, url_prefix='example')
if __name__ == '__main__':
app.run()
What has been created?

1. GET /example (index)
2. GET /example/<resource> (get one)
3. GET /example/<resource>/<field> (get one and fetch field)
4. POST /example (accepts fields and creates entries in database)
5.


Curl your API and see it in action:

localhost:5000/access
6 changes: 2 additions & 4 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

# -- Project information -----------------------------------------------------

project = 'Flask atomic'
project = 'Flask Atomic'
copyright = '2020, Keith Byrne'
author = 'Keith Byrne'

Expand All @@ -26,9 +26,7 @@
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc'
]
extensions = ['sphinx.ext.autodoc', 'recommonmark']

# Add any paths that contain templates here, relative to this directory.
# templates_path = ['_templates']
Expand Down
18 changes: 0 additions & 18 deletions docs/index.md

This file was deleted.

35 changes: 13 additions & 22 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,37 +1,28 @@
Flask Atomic
====================

REST API development should be quick and painless, especially when prototyping
or working with large amounts of models where boilerplate CRUD operations are
required. With well-defined code, Flask Atomic has the opportunity to render
potentially hundreds of lines of redundant.
Flask Atomic leverages the power of Flask Blueprints & SQLAlchemy to provide a plugin
application builder. Blueprints are essentially mini Flask applications that
are registered to the Flask application. Encapsulated within these Blueprints
are all routes, templates etc...

When developing Flask applications and working with large amounts of models where
boilerplate CRUD operations are required. With well-defined code, Flask Atomic has
the opportunity to render potentially hundreds of lines of redundant.

This project was heavily influenced by repetitive efforts to get quick and dirty
APIs up and running, with the bad practice of re-using a lot of code, over and over
again. Instead of relying on throwaway efforts, Flask Atomic provides a very
simply means to abstract away hundreds of lines of code and enable RESTful API best
practices that are often esoteric and difficult to engineer for small projects.

This project intended to be a building block to enrich the Flask ecosystem,
without compromising any Flask functionality. Leaving you to integrate without
issues, breathing life into your projects in less than 5 lines of code. Feature
rich but non-assuming.

The Flask Atomic package can be used for:

* Blueprint integration for creating main HTTP method endpoints.
* Extensible data access objects for common database interactions.
* Automatic query string processing engine for requests.
* Fully dynamic model schema definitions without any hardcoding.
* SQLAlchemy model serializer for transforming Models to JSON ready format.
* Custom JSON response partials to reduce repetitive Flask.jsonify responses.
* Variety of db model mixins, including DYNA flag columns and primary key column.
simply means to abstract away code and enable RESTful API best practices that
are often esoteric and difficult to engineer for small projects.

This library also contains a few helpers and other utilities that I have found helpful.


.. toctree::
installation

basic
recipes


API
Expand Down
133 changes: 1 addition & 132 deletions docs/installation.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Quickstart
Installation
=========================

.. note::
Expand All @@ -22,134 +22,3 @@ Installation
From the PyPi repository

`pip install Flask-Atomic`

Minimal Example
---------------

Much of immediate Flask code is completely boiler-plate and often repeated
from other projects, in terms of similarity.

Consider the scenario where you may have a model, and simply want to provide
a GET request endpoint to fetch all the data for that model. Sounds easy, right?

From a high level, this is not a complex problem. Let's revisit our scenario:

We have a model defined with the SQLAlchemy ORM like so:

.. code-block:: python
....
class YourSuperModel(db.Model):
name = db.Column(db.Datetime, default=datetime.now())
username = db.Column(db.String(50))
alias = db.Column(db.String(50))
...
Typically, you would approach this like so:

.. code-block:: python
from flask import jsonify
from models import SomeMonitoringModel
app = Flask(__name__)
@app.route('/model-url', methods=['GET'])
def monitoring_index():
data = YourSuperModel.query().all()
resp = list
for item in data:
resp.append(
dict(
name=item.name
username=item.username
alias=item.alias
)
)
return jsonify(data=resp)
This is only a single endpoint. POST endpoints are 3-4 times bulkier.

The Magic
+++++++++

What if this could simply be abstracted away. First, binding the model. Let
DeclarativeBase be the db session instance. Just be sure to bind it to the
root application or the models will not be created in the database.

.. code-block:: python
from flask_atomic.orm.declaratives.base import DeclarativeBase
class YourSuperModel(DeclarativeBase):
name = db.Column(db.Datetime, default=datetime.now())
username = db.Column(db.String(50))
alias = db.Column(db.String(50))
...
Next, putting together the Blueprint and model.

.. note::

Some assumptions about project directory are being made here. Please
tailor to your standards. Flask has a general guideline for Blueprint
directory structure that works well.


.. code-block:: python
from flask_atomic.blueprint.core import CoreBlueprint
from .models import YourSuperModel
monitor_blueprint = CoreBlueprint(
'supermodel',
__name__,
YourSuperModel
)
...
app = Flask(__name__)
app.register_blueprint(monitor_blueprint, url_prefix='supermodel')
if __name__ == '__main__':
app.run()
And that is a minimal example. So what did this code do?

* Import CoreBlueprint
* Setup CoreBlueprint, providing BP name, module and your model.
* Create Flask app as normal.
* Register the blueprint with the app instance.
* Run.

GET, POST, PUT, DELETE endpoints are now accessible by via HTTP.

`HTTP GET localhost:5000/access`

You now have four endpoints, tested and ready. These endpoints work in the same
way. Lets consider a POST request.

`HTTP POST localhost:5000/access`

.. code-block:: json
{
"entry_date": "2020-01-01 00:00:00",
"source": "some data"
"destination": "some other data"
"status": "Active"
}
By using Postman, CURL or HTTPie, try out the endpoints. The above, will create
a new monitor record in the database you have configured your application to run
with. For the sake of illustration, consider using a SQLite database instance
setup in your `/tmp` directory. Create a model, and start querying your data.
3 changes: 3 additions & 0 deletions docs/recipes.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Flask Atomic Recipes
=====================

2 changes: 1 addition & 1 deletion flask_atomic/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .ext import FlaskJSON
from .builder import Architect
from .builder.architect import Architect

__all__ = ['Architect', 'FlaskJSON']
2 changes: 1 addition & 1 deletion flask_atomic/builder/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .multi import MultiModelBuilder
from flask_atomic.architect import Architect
from flask_atomic.builder.architect import Architect
from .core import BuilderCore

__all__ = [Architect, MultiModelBuilder, BuilderCore]
File renamed without changes.
12 changes: 0 additions & 12 deletions flask_atomic/builder/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,6 @@
ROUTE_TABLE = {}


def route(*args, **kwargs):
def outer(func):
if not ROUTE_TABLE.get(func.__name__, None):
ROUTE_TABLE[func.__name__] = []
ROUTE_TABLE[func.__name__].append([kwargs.get('url'), kwargs.get('methods')])
@wraps(func)
def link_wrapper(self, *args, **kwargs):
return func(self, *args, **kwargs)
return link_wrapper
return outer


def link(*args, **kwargs):
def outer(func):
if not ROUTE_TABLE.get(func.__name__, None):
Expand Down
6 changes: 0 additions & 6 deletions flask_atomic/common/exceptions.py

This file was deleted.

4 changes: 3 additions & 1 deletion flask_atomic/ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ def force_type(cls, rv, environ=None):
if isinstance(rv, dict):
rv = jsonify(rv)
elif isinstance(rv, tuple):
rv = jsonify(rv[0]), rv[1]
rv = jsonify(rv[0])
elif isinstance(rv, datetime):
rv = jsonify(rv.isoformat())
return super(JSONResponse, cls).force_type(rv, environ)


Expand Down
34 changes: 0 additions & 34 deletions flask_atomic/http/exceptions.py

This file was deleted.

0 comments on commit b46eefe

Please sign in to comment.