diff --git a/README.md b/README.md index f161738..3d4d615 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,11 @@ Bra länk: https://scotch.io/tutorials/build-a-crud-web-app-with-python-and-flask-part-two + +export FLASK_CONFIG=development +export FLASK_APP=run.py + +Init DB +flask db init +flask db migrate + flask db upgrade diff --git a/app/__init__.py b/app/__init__.py index a360ae6..4bf2b0c 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -3,17 +3,23 @@ from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate from config import app_config + db = SQLAlchemy() def create_app(config_name): - app = Flask(__name__, instance_relative_config=True) - app.config.from_object(app_config[config_name]) - app.config.from_pyfile('config.py') - db.init_app(app) - from app import models - migrate = Migrate(app, db) + app = Flask(__name__, instance_relative_config=True) + app.config.from_object(app_config[config_name]) + app.config.from_pyfile('config.py') + db.init_app(app) + migrate = Migrate(app, db) - @app.route('/') - def hello_world(): - return 'Hello, World!' + from app import models - return app + from .api import api as api_blueprint + app.register_blueprint(api_blueprint) + + + @app.route('/') + def hello_world(): + return 'Hello, World!' + + return app diff --git a/app/api/__init__.py b/app/api/__init__.py new file mode 100644 index 0000000..9262d62 --- /dev/null +++ b/app/api/__init__.py @@ -0,0 +1,4 @@ +from flask import Blueprint + +api = Blueprint('api', __name__) +from . import views diff --git a/app/api/api.py b/app/api/api.py new file mode 100644 index 0000000..838c8bc --- /dev/null +++ b/app/api/api.py @@ -0,0 +1,16 @@ +from . import api +from ..model import Gas, Car +from flask import jsonify + + +@api.route('/dummy') +def dummy(): + return 'dummy, World!' + +@api.route('/cars', methods=['GET', 'POST']) +def list_cars(): + cars = Car.query.all() + result = [d.__dict__ for d in cars] + return jsonify(result=result) + + diff --git a/app/api/views.py b/app/api/views.py new file mode 100644 index 0000000..93c8201 --- /dev/null +++ b/app/api/views.py @@ -0,0 +1,50 @@ +from . import api +from ..models import Gas, Car +from flask import jsonify, request, abort +from .. import db + + +@api.route('/dummy') +def dummy(): + return 'dummy, World!' + + +@api.route('/cars', methods=['GET', 'POST']) +def list_cars(): + cars = Car.query.first() + return jsonify(result=cars.get_public()) + + +@api.route('/gas', methods=['GET', 'POST']) +def list_gas(): + gas = Gas.query.first() + return jsonify(result=gas.get_public()) + + +@api.route('/gas/add', methods=['POST']) +def add_gas(): + if not request.json: + abort(400) + if not 'price' in request.json: + abort(400) + if not 'volume' in request.json: + abort(400) + if not 'milage' in request.json: + abort(400) + if not 'car' in request.json: + abort(400) + + price = request.json['price'] + volume = request.json['volume'] + milage = request.json['milage'] + car = request.json['car'] + car_id = Car.query.get_or_404(car) + gas = Gas(price=price, volume=volume, milage=milage, car_id=car_id) + db.session.add(gas) + db.session.commit() + + + + return jsonify(result="OK") + + diff --git a/app/models.py b/app/models.py index 76ee162..16bb96d 100644 --- a/app/models.py +++ b/app/models.py @@ -1,19 +1,75 @@ from app import db -from datetime import datetime +from datetime import datetime, date -class Gas(db.Model): - """ - Create a gas table - """ - __tablename__ = 'gas' +class AutoSerialize(object): + 'Mixin for retrieving public fields of model in json-compatible format' + __public__ = None - id = db.Column(db.Integer, primary_key=True) - price = db.Column(db.Integer) - volume = db.Column(db.Integer) - milage = db.Column(db.Integer) - date = db.Column(db.DateTime, default=datetime.utcnow) - winterTyre = db.Column(db.Boolean) + def get_public(self, exclude=(), extra=()): + "Returns model's PUBLIC data for jsonify" + data = {} + keys = self._sa_instance_state.attrs.items() + public = self.__public__ + extra if self.__public__ else extra + for k, field in keys: + if public and k not in public: continue + if k in exclude: continue + value = self._serialize(field.value) + if value: + data[k] = value - def __ref__(self): - return ''.format(self.id) + return data + + @classmethod + def _serialize(cls, value, follow_fk=False): + if type(value) in (datetime, date): + ret = value.isoformat() + elif hasattr(value, '__iter__'): + ret = [] + for v in value: + ret.append(cls._serialize(v)) + elif AutoSerialize in value.__class__.__bases__: + ret = value.get_public() + else: + ret = value + + return ret + + +class Gas(db.Model, AutoSerialize): + """ + Create a gas table + """ + + __table_args__ = {'extend_existing': True} + __tablename__ = 'gas' + __public__ = ('price', 'volume', 'milage', 'date', + 'wintertyre', 'car', 'id') + + id = db.Column(db.Integer, primary_key=True) + price = db.Column(db.Integer) + volume = db.Column(db.Integer) + milage = db.Column(db.Integer) + date = db.Column(db.DateTime, default=datetime.utcnow) + winterTyre = db.Column(db.Boolean) + car_id = db.Column(db.Integer, db.ForeignKey('car.id')) + + def __ref__(self): + return ''.format(self.id) + + +class Car(db.Model, AutoSerialize): + """ + Create a table of cars + """ + + __tablename__ = 'car' + __public__ = ('name', 'id') + + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(50)) + cars = db.relationship('Gas', backref='car', + lazy='dynamic') + + def __ref__(self): + return ''.format(self.id) diff --git a/migrations/versions/6ea64b2c52be_added_cars_table.py b/migrations/versions/6ea64b2c52be_added_cars_table.py new file mode 100644 index 0000000..1dd5150 --- /dev/null +++ b/migrations/versions/6ea64b2c52be_added_cars_table.py @@ -0,0 +1,36 @@ +"""Added cars table + +Revision ID: 6ea64b2c52be +Revises: 46b4c05512b3 +Create Date: 2017-08-23 20:43:41.807113 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '6ea64b2c52be' +down_revision = '46b4c05512b3' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('car', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=50), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.add_column(u'gas', sa.Column('car_id', sa.Integer(), nullable=True)) + op.create_foreign_key(None, 'gas', 'car', ['car_id'], ['id']) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'gas', type_='foreignkey') + op.drop_column(u'gas', 'car_id') + op.drop_table('car') + # ### end Alembic commands ### diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..67ee445 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,15 @@ +alembic==0.9.5 +click==6.7 +Flask==0.12.2 +Flask-Migrate==2.1.0 +Flask-SQLAlchemy==2.2 +itsdangerous==0.24 +Jinja2==2.9.6 +Mako==1.0.7 +MarkupSafe==1.0 +mysqlclient==1.3.10 +python-dateutil==2.6.1 +python-editor==1.0.3 +six==1.10.0 +SQLAlchemy==1.1.13 +Werkzeug==0.12.2