Metadata-Version: 2.1
Name: SQLAlchemy-Api-Handler
Version: 0.9.6
Summary: Adds SQLAlchemy support to your Flask application for handle apis.
Home-page: https://github.com/betagouv/sqlalchemy-api-handler
Author: Erwan Ledoux
Author-email: lawanledoux@gmail.com
Maintainer: Ledoux
Maintainer-email: lawanledoux@gmail.com
License: MPL-2.0
Project-URL: Code, https://github.com/betagouv/sqlalchemy-api-handler
Project-URL: Issue tracker, https://github.com/betagouv/sqlalchemy-api-handler/issues
Description: sqlalchemy-api-handler
        ======================
        
        SQLAlchemy-Api-Handler is an extension that adds support for handling apis with sqlalchemy. It helps to handle models with :
           - humanized ids once it is jsonified,
           - throwing api errors for some casting of value during the save time,
           - dictification of the model objects into jsonified ones.
           - It also gives an activate method to help you better handle offline operational transforms, based on the PostgreSQL-Audit Activity model.
        
        [![CircleCI](https://circleci.com/gh/betagouv/sqlalchemy-api-handler/tree/master.svg?style=svg)](https://circleci.com/gh/betagouv/sqlalchemy-api-handler/tree/master)
        
        
        Installing
        ----------
        
        Install and update using `pip`:
        
        ```bash
          $ pip install -U SQLAlchemy-Api-Handler
        ```
        
        A Simple Example
        ----------------
        
        Suppose a request POST /users {'email': 'marx.foo@plop.fr', name: 'Marx Foo'} :
        
        ```python
            from flask import Flask, jsonify, request
            from sqlalchemy_api_handler import ApiHandler
        
            app = Flask(__name__)
            app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///example.sqlite'
            db = SQLAlchemy(app)
            ApiHandler.set_db(db)
        
            class User(ApiHandler, db.Model):
                email = db.Column(db.String, unique=True, nullable=False)
                name = db.Column(db.String, unique=True, nullable=False)
        
            @app.route('/users', methods=['POST'])
            def post_user():
              user = User(**request.form)
              ApiHandler.save(user)
              return jsonify(as_dict(user))
        ```
        
        The success result will have stored a user object at, let's say id = 32,
        and so will fetch an object at humanized id = humanize(32), ie
        
        ```
          {'id': 'EA', 'email': 'marx.foo@plop.fr', name: 'Marx Foo'}
        ```
        
        Playing with nesting data
        -------------------------
        
        Suppose a request GET /offers
        
        ```python
            from flask import Flask, jsonify, request
            from sqlalchemy.orm import relationship
            from sqlalchemy_api_handler import ApiHandler
        
            app = Flask(__name__)
            app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///example.sqlite'
            db = SQLAlchemy(app)
            ApiHandler.set_db(db)
        
            class Venue(ApiHandler, db.Model):
                address = db.Column(db.String, unique=True, nullable=False)
                name = db.Column(db.String, unique=True, nullable=False)
        
            class Offer(ApiHandler, db.Model):
                name = db.Column(db.String, unique=True, nullable=False)
                venueId = db.Column(db.BigInteger,
                             db.ForeignKey('venue.id'),
                             nullable=False,
                             index=True)
                venue = relationship('Venue',
                                     foreign_keys=[venueId],
                                     backref='offers')
        
            class Stock(ApiHandler, db.Model):
                available = db.Column(db.Integer, nullable=False)
                offerId = db.Column(db.BigInteger,
                                 db.ForeignKey('offer.id'),
                                 index=True,
                                 nullable=False)
                offer = relationship('Offer',
                                     foreign_keys=[offerId],
                                     backref='stocks')
        
            venue = Venue(address='Somewhere I belong', name='MyVenue')
            offer = Offer(name='MyOffer')
            stock = Stock(available=10)
            stock.offer = offer
            offer.venue = venue
            ApiHandler.save(stock)
        
            offer_includes = [
              'stocks',
              {
                'key': 'venue',
                'includes': [
                  '-address'
                ]
              }
            ]
        
            @app.route('/offers', methods=['GET'])
            def get_offers():
              offers = Offer.query.all()
              return jsonify(as_dict(offers, includes=offer_includes))
        ```
        
        The success will return
        
        ```
          [
            {
              'id': 'AE',
              'name': 'MyOffer',
              'stocks': [
                {
                  'available': 10,
                  'id': 'AE'
                }
              ],
              'venue': {
                'name': 'MyVenue'
              }
            }
          ]
        ```
        
        
        Activity
        -----
        
        If you need to manage operation transforms of your entities (typically with a collaborative app working offline) :
        
        
        ```python
            from flask import Flask, jsonify, request
            from sqlalchemy.orm import relationship
            from sqlalchemy_api_handler import ApiHandler
            from sqlalchemy_api_handler.mixins import ActivityMixin, \
                                                      HasActivitiesMixin
        
            app = Flask(__name__)
            app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///example.sqlite'
            db = SQLAlchemy(app)
            ApiHandler.set_db(db)
        
            versioning_manager.init(db.Model)
            class Activity(ActivityMixin,
                           ApiHandler,
                           versioning_manager.activity_cls):
                __table_args__ = {'extend_existing': True}
        
                id = versioning_manager.activity_cls.id
            ApiHandler.set_activity(Activity)
        
            class Venue(ApiHandler,
                        db.Model,
                        HasActivitiesMixin):
                address = db.Column(db.String, unique=True, nullable=False)
                name = db.Column(db.String, unique=True, nullable=False)
        
            @app.route('/__activities__', methods=['POST'])
            def create_activities():
                activities = [Activity(**a) for a in request.json]
                ApiHandler.activate(*activities)
                return jsonify([as_dict(activity) for activity in activities]), 201
        ```
        
        The request POST /__activities__ helps you to sync your backend with the last state of pushed activities. For example, if the data posted is :
        ```
        [
          {
            "dateCreated": "2020-08-05T08:34:06.415000Z" ,
            "entityIdentifier": "4039fb61-f085-43c4-a2ed-e5e97c5dcebc",
            "modelName": "Venue",
            "patch": { "address": "22 rue de la Loire", "name": "MyVenue" }
          },
          {
            "dateCreated": "2020-08-06T09:34:06.415000Z" ,
            "entityIdentifier": "4039fb61-f085-43c4-a2ed-e5e97c5dcebc",
            "modelName": "Venue",
            "patch": { "name": "MyVenueChanged" }
          }
        ]
        ```
        
        It will then end to create a venue instance:
        ```python
        venue = Venue.query.filter_by(name='MyVenueChanged').first()
        print(as_dict(venue, includes=['__activities__']))
        ```
        
        ```
        {
          '__activities__': [
            {
              'dateCreated': '2020-08-05T08:34:06.415000Z',
              'entityIdentifier': '4039fb61-f085-43c4-a2ed-e5e97c5dcebc',
              'id': 'BA',
              'modelName': 'Venue',
              'patch': {
                'address': '22 rue de la Loire',
                'name': 'MyVenue'
              },
              'verb': 'insert'
            },
            {
              'dateCreated': '2020-08-06T09:34:06.415000Z',
              'entityIdentifier': '4039fb61-f085-43c4-a2ed-e5e97c5dcebc',
              'id': 'BF',
              'modelName': 'Venue',
              'patch': {
                'name': 'MyVenueChanged'
              },
              'verb': 'update'
            }
          ],
          'activityIdentifier': '2020-08-05T08:34:06.415000Z',
          'address': "22 rue de la Loire",
          'id': 'AE',
          'name': 'MyVenueChanged'
        }
        ```
        
        
        
        Links
        -----
        
        -   Releases: https://pypi.org/project/SQLAlchemy-Api-Handler/
        -   Code: https://github.com/betagouv/sqlalchemy-api-handler
        -   Issue tracker: https://github.com/betagouv/sqlalchemy-api-handler/issues
        
        - Flask: https://betagouvprojects.com/p/flask/
        - SQLAlchemy: https://www.sqlalchemy.org
        - pip: https://pip.pypa.io/en/stable/quickstart/
        
        Deploy
        ------
        
        First, make sure that the deploy environment is started:
        
        ```bash
          ./sqlaah start
        ```
        
        In a second tab, then:
        
        2. Change the __version__ into sqlalchemy_api_handler/__init__.py
        
        3. Pre publish:
        
        ```bash
          ./sqlaah prepublish
        ```
        
        4. Publish:
        
        ```bash
          ./sqlaah publish
        ```
        
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.*
Description-Content-Type: text/markdown
