commit ae3a566f68f9ed17462d421b520f3810593be7cf Author: Martin Date: Mon Apr 18 20:56:24 2016 +0200 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0bd28be --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.pyc +config.py +venv/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e894657 --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# Photowall API 2 + +Provides a REST API to Photowall database. + +## Installation + +`$ pip install -r requirements.txt` + +## Configuration + +First copy config.sample.py to config.py. This file will be read by the application on startup. + +List of available configuration options: + +| Name | Description | +| ---- | ----------- | +| `SQLALCHEMY_DATABASE_URI` | Database connection string. e.g: `postgresql://user:password@localhost/photowall` + +## Getting started + +Starting the application: + +`$ python run.py` \ No newline at end of file diff --git a/api/__init__.py b/api/__init__.py new file mode 100644 index 0000000..bc27867 --- /dev/null +++ b/api/__init__.py @@ -0,0 +1,29 @@ +from flask import Flask, jsonify +from flask_sqlalchemy import SQLAlchemy +from api.helpers import JSONEncoder + +app = Flask(__name__) +app.config.from_object('config') + +# enable this when json encoding works +# app.json_encoder = JSONEncoder + +def on_404(e): + return jsonify(dict(error='Not found')), 404 + +app.errorhandler(404)(on_404) + +#SQLAlchemy +db = SQLAlchemy(app) + +from api.views import designers +app.register_blueprint(designers.mod) + +@app.route("/") +def index(): + data = { + "info": { + "api_version": "0.0.1" + } + } + return jsonify(data) diff --git a/api/helpers/__init__.py b/api/helpers/__init__.py new file mode 100644 index 0000000..a3edbf2 --- /dev/null +++ b/api/helpers/__init__.py @@ -0,0 +1 @@ +from json import JSONEncoder, JsonSerializer \ No newline at end of file diff --git a/api/helpers/json.py b/api/helpers/json.py new file mode 100644 index 0000000..2539148 --- /dev/null +++ b/api/helpers/json.py @@ -0,0 +1,46 @@ +from flask.json import JSONEncoder as BaseJSONEncoder + + +class JSONEncoder(BaseJSONEncoder): + """Custom :class:`JSONEncoder` which respects objects that include the + :class:`JsonSerializer` mixin. + """ + def default(self, obj): + if isinstance(obj, JsonSerializer): + return obj.to_json() + return super(JSONEncoder, self).default(obj) + + +class JsonSerializer(object): + """A mixin that can be used to mark a SQLAlchemy model class which + implements a :func:`to_json` method. The :func:`to_json` method is used + in conjuction with the custom :class:`JSONEncoder` class. By default this + mixin will assume all properties of the SQLAlchemy model are to be visible + in the JSON output. Extend this class to customize which properties are + public, hidden or modified before being being passed to the JSON serializer. + """ + + __json_public__ = None + __json_hidden__ = None + __json_modifiers__ = None + + def get_field_names(self): + for p in self.__mapper__.iterate_properties: + yield p.key + + def to_json(self): + field_names = self.get_field_names() + + public = self.__json_public__ or field_names + hidden = self.__json_hidden__ or [] + modifiers = self.__json_modifiers__ or dict() + + rv = dict() + for key in public: + rv[key] = getattr(self, key) + for key, modifier in modifiers.items(): + value = getattr(self, key) + rv[key] = modifier(value, self) + for key in hidden: + rv.pop(key, None) + return rv \ No newline at end of file diff --git a/api/models/__init__.py b/api/models/__init__.py new file mode 100644 index 0000000..ed340fe --- /dev/null +++ b/api/models/__init__.py @@ -0,0 +1 @@ +from designer import Designer \ No newline at end of file diff --git a/api/models/designer.py b/api/models/designer.py new file mode 100644 index 0000000..39cc683 --- /dev/null +++ b/api/models/designer.py @@ -0,0 +1,9 @@ +from api import db +from api.helpers import JsonSerializer + +class Designer(JsonSerializer, db.Model): + __tablename__ = 'designers' + + designerid = db.Column('designerid', db.Integer, primary_key=True) + name = db.Column(db.String(100), nullable=False) + path = db.Column(db.String(100), nullable=False) \ No newline at end of file diff --git a/api/views/__init__.py b/api/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/views/designers.py b/api/views/designers.py new file mode 100644 index 0000000..dc7febb --- /dev/null +++ b/api/views/designers.py @@ -0,0 +1,41 @@ +from flask import Blueprint, request, jsonify +from api import db +from api.models import Designer + +mod = Blueprint('designers', __name__, url_prefix='/designers') + +@mod.route("/", methods=["GET"]) +def list(): + designers = Designer.query.all() + return jsonify(designers=[d.to_json() for d in designers]) + +@mod.route("/", methods=["POST"]) +def create(): + data = request.json + designer = Designer(**data) + db.session.add(designer) + db.session.commit() + return jsonify(designer.to_json()) + + +@mod.route("/", methods=["GET"]) +def show(id): + designer = Designer.query.get_or_404(id) + return jsonify(designer.to_json()) + +@mod.route("/", methods=["PUT"]) +def update(id): + designer = Designer.query.get_or_404(id) + data = request.json + for k, v in data.iteritems(): + setattr(designer, k, v) + db.session.add(designer) + db.session.commit() + return jsonify(designer.to_json()) + +@mod.route("/", methods=["DELETE"]) +def delete(id): + designer = Designer.query.get_or_404(id) + db.session.delete(designer) + db.session.commit() + return jsonify({"status": "ok"}) \ No newline at end of file diff --git a/config.sample.py b/config.sample.py new file mode 100644 index 0000000..98e0b48 --- /dev/null +++ b/config.sample.py @@ -0,0 +1,2 @@ +SQLALCHEMY_DATABASE_URI = 'postgresql://user:password@host/db_name' +SQLALCHEMY_TRACK_MODIFICATIONS = True \ No newline at end of file diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..5720efa --- /dev/null +++ b/manage.py @@ -0,0 +1,27 @@ +from flask.ext.script import Manager, prompt_bool +from api import app, db +from api.models import Designer + +manager = Manager(app) + +@manager.command +def initdb(): + db.create_all() + print 'All tables created' + +@manager.command +def dropdb(): + if prompt_bool("Are you sure you want to lose all your data"): + db.drop_all() + print 'All tables dropped' + +@manager.command +def seed(): + print 'Adding sample data %s' % db.engine.url + designer = Designer(name="Martin", path='/martin', username='martin', password='secret') + db.session.add(designer) + db.session.commit() + + +if __name__ == '__main__': + manager.run() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e478c08 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,11 @@ +Flask==0.10.1 +Flask-SQLAlchemy==2.1 +Flask-Script==2.0.5 +Jinja2==2.8 +MarkupSafe==0.23 +SQLAlchemy==1.0.12 +Werkzeug==0.11.8 +argparse==1.2.1 +itsdangerous==0.24 +psycopg2==2.6.1 +wsgiref==0.1.2 diff --git a/run.py b/run.py new file mode 100644 index 0000000..0996ddb --- /dev/null +++ b/run.py @@ -0,0 +1,2 @@ +from api import app +app.run(debug=True) \ No newline at end of file