diff --git a/.gitignore b/.gitignore index 3bae2c6..179bb33 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ inventory/static/uploads/* *.sqlite3 alembic.ini alembic/ -*.egg-info/ \ No newline at end of file +*.egg-info/ +test-app/ \ No newline at end of file diff --git a/crudkit/api/flask_api.py b/crudkit/api/flask_api.py index e69de29..94e7d3f 100644 --- a/crudkit/api/flask_api.py +++ b/crudkit/api/flask_api.py @@ -0,0 +1,29 @@ +from flask import Blueprint, jsonify, request + +def generate_crud_blueprint(model, service): + bp = Blueprint(model.__name__.lower(), __name__) + + @bp.get('/') + def list_items(): + items = service.list() + return jsonify([item.as_dict() for item in items]) + + @bp.get('/') + def get_item(id): + item = service.get(id) + return jsonify(item.as_dict()) + + @bp.post('/') + def create_item(): + obj = service.create(request.json) + return jsonify(obj.as_dict()) + + @bp.patch('/') + def update_item(id): + obj = service.update(id, request.json) + return jsonify(obj.as_dict()) + + @bp.delete('/') + def delete_item(id): + service.delete(id) + return '', 204 diff --git a/crudkit/core/base.py b/crudkit/core/base.py index e69de29..4618a85 100644 --- a/crudkit/core/base.py +++ b/crudkit/core/base.py @@ -0,0 +1,11 @@ +from sqlalchemy import Column, Integer, DateTime, func +from sqlalchemy.orm import declarative_mixin + +@declarative_mixin +class CRUDMixin: + id = Column(Integer, primary_key=True) + created_at = Column(DateTime, default=func.now()) + updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) + + def as_dict(self): + return {c.name: getattr(self, c.name) for c in self.__table__.columns} diff --git a/crudkit/core/service.py b/crudkit/core/service.py index e69de29..ca58dcf 100644 --- a/crudkit/core/service.py +++ b/crudkit/core/service.py @@ -0,0 +1,33 @@ +from typing import Type, TypeVar, Generic +from sqlalchemy.orm import Session + +T = TypeVar("T") + +class CRUDService(Generic[T]): + def __init__(self, model: Type[T], session: Session): + self.model = model + self.session = session + + def get(self, id: int) -> T: + return self.session.get(self.model, id) + + def list(self, limit=100, offset=0) -> list[T]: + return self.session.query(self.model).offset(offset).limit(limit).all() + + def create(self, data: dict) -> T: + obj = self.model(**data) + self.session.add(obj) + self.session.commit() + return obj + + def update(self, id: int, data: dict) -> T: + obj = self.get(id) + for k, v in data.items(): + setattr(obj, k, v) + self.session.commit() + return obj + + def delete(self, id: int): + obj = self.get(id) + self.session.delete(obj) + self.session.commit() diff --git a/crudkit/ui/fragments.py b/crudkit/ui/fragments.py index e69de29..db169d0 100644 --- a/crudkit/ui/fragments.py +++ b/crudkit/ui/fragments.py @@ -0,0 +1,13 @@ +from jinja2 import Environment, FileSystemLoader +import os + +FRAGMENT_PATH = os.path.join(os.path.dirname(__file__), 'templates') +env = Environment(loader=FileSystemLoader(FRAGMENT_PATH)) + +def render_field(field_name, value): + template = env.get_template('field.html') + return template.render(field_name=field_name, value=value) + +def render_table(objects): + template = env.get_template('table.html') + return template.render(objects=objects) diff --git a/crudkit/ui/templates/field.html b/crudkit/ui/templates/field.html new file mode 100644 index 0000000..d6904dc --- /dev/null +++ b/crudkit/ui/templates/field.html @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/crudkit/ui/templates/table.html b/crudkit/ui/templates/table.html new file mode 100644 index 0000000..8abd5e0 --- /dev/null +++ b/crudkit/ui/templates/table.html @@ -0,0 +1,8 @@ + + + {% for field in objects[0].__table__.columns %}{% endfor %} + + {% for obj in objects %} + {% for field in obj.__table__columns %}{% endfor %} + {% endfor %} +
{{ field.name }}
{{ getattr(obj, field.name) }}
\ No newline at end of file