Giter Site home page Giter Site logo

flask-servicelayer's Introduction

LDAPOM Model

This package provides some classes to build a Service layer and expose an API that interacts with the model. The first idea is to remove all logic of the routes and model of the Flask application, and put it in the service layer. The second goal is to provide a common API that can be use to manipulate a model regardless of its storage backend.

Sorry… What?

A common Flask organisation consists in separating model (SQLAlchemy classes, for instance), and routes. When the application grows, you can also use Blueprints. One Blueprint by model for instance :

app/ # Flask main application
    __init__.py
    customer/ # Customer Blueprint
        __init__.py
        forms.py # User forms
        models.py # User model
        views.py # User views
        templates/ # User templates
            ...
    product/ # Product Blueprint
        __init__.py
        forms.py
        models.py
        views.py
        templates/
            ...
    templates/ # Common templates
        layout.html
        macros.html
    config.py
    filters.py
    ...

That's great. But where do you put the logic ? "Yeah… It depends…". "Sometimes in the model, sometimes in the views…". You see what I mean, right ? Problem is, as the application grows, it became impossible to maintain, because you just doesn't know where the logic is done.

First, "model layer" is supposed to be only data storage manipulation. But putting the logic in the "view layer" really is bad idea. What if you want to add a REST API to you application ? You just copy paste all the code from your classic route to make the new API routes ?

Finally, if you want to replace SQL database by LDAP, you should be able to do it without modifying anything but the model. So you can't put any logic in "models.py". So, please, stop using SQLAlchemy methods in your views.

This package gives you a solution.

Installation

pip install flask-servicelayer

Example

The package provides two main classes, one to build a Flask SQLAlchemy-based service, and one to build a LDAPom-model-based service.

SQLAlchemyService

See how the route became clean when all the logic and the SQLAlchemy specifics code are put in a service layer.

app/product/services.py :

from flask.ext.servicelayer import SQLAlchemyService

from .models import Product
from .. import db

class ProductService(SQLAlchemyService):
    __model__ = Product
    __db__ = db

app/product/views.py:

products = ProductService()

@product.route("/")
def index():
    return render_template('product/list.html', products=products.all())

@product.route("/new", methods=['GET', 'POST'])
def new():
    form = ProductForm()
    if form.validate_on_submit():
        products.create(**{field.name: field.data for field in form})
        return redirect(url_for('.index'))
    return render_template('product/new.html', form=form)

@product.route("/delete/<int:id>")
def delete(id):
    products.delete(products.get_or_404(id))
    return redirect(url_for('.index'))

LDAPOMService

And see how customer views are really looking like product views, even if the Customer model is based on LDAPOM-model. Do you see any complex retrieve functions in the views?

app/customer/services.py:

from flask.ext.servicelayer import LDAPOMService

from .. import ldap
from .models import Customer

class CustomerService(LDAPOMService):
    __model__ = Customer
    __ldap__ = ldap

app/customer/views.py:

customers = CustomerService()

@customer.route("/")
def index():
    return render_template('customer/list.html', customers=customers.all())

@customer.show(/<id>)
def show(id):
    customer = customers.get_or_404(id)
    return render_template("customer/show.html", customer=customer)

Tips

Instantiation

Depending on how you have structure the rest of your application, you can instantiate a service object when you need it, or instantiate on object of each service at the beginning of each request, and store it in g to use it everywhere. Personally, I like to instantiate each service in the before_request method of its Blueprint. For instance, in app/customer/__init__.py:

from flask import Blueprint, g

domain = Blueprint("customer", __name__, template_folder='templates')

from .services import CustomerService

@customer.before_app_request
def before_request():
    if 'ldap' in g:
        g.customers = CustomerService(g.ldap)

from . import views

LDAP Cache

The package provides an LDAPOMCachedService class. This class inherits LDAPOMService and can be use exactly the same way. The only difference is that all(), get() and find() methods are cached within the service object to avoid doing a new LDAP request every time. This is very powerful when your service object is put in the global var g to be used everywhere during your request.

Licence

This code is under WTFPL. Just do what the fuck you want with it.

The idea is based on Matt Wright's article How do I Structure My Flask Application. Matt, if you read this, thank you!

flask-servicelayer's People

Contributors

maethor avatar opencoderx 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

opencoderx eneudy

flask-servicelayer's Issues

Can't instantiate abstract class with abstract methods create

It appears the create method is defined as an abstractmethod on the base service class but not implemented in either the SQLAlchemyService or LDAPOMService services. This results in a TypeError when trying to instantiate a service based off of these due to create not being defined in any of the sub classes.

It seems like either create should be defined in both SQLAlchemyService and LDAPOMService or made to not be an abstractmethod.

regner:~/workspace $ python manage.py update_capsuleers
Traceback (most recent call last):
  File "manage.py", line 7, in <module>
    from evepilots.capsuleers.scripts import UpdateExistingCapsuleers, LoadCapsuleers
  File "/home/ubuntu/workspace/evepilots/capsuleers/scripts.py", line 5, in <module>
    from evepilots.capsuleers.utils import update_capsuleers, load_capsuleers_file
  File "/home/ubuntu/workspace/evepilots/capsuleers/utils.py", line 9, in <module>
    from evepilots.services import capsuleers
  File "/home/ubuntu/workspace/evepilots/services.py", line 5, in <module>
    capsuleers = CapsuleerService()
TypeError: Can't instantiate abstract class CapsuleerService with abstract methods create

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.