From 958cde4bafac04b68db168af259de8a11e19222e Mon Sep 17 00:00:00 2001 From: Daniel Tsvetkov Date: Fri, 5 Jun 2020 12:38:25 +0200 Subject: [PATCH] Building all routes by default --- bootstrap/webapp/app.py | 1 + bootstrap/webapp/routes/__init__.py | 0 oshipka/webapp/views.py | 140 +++++++++++++++++++++------- vm_gen/templates/_hooks.py | 109 ++++++++++++++++++++++ vm_gen/templates/routes_py | 47 ++++++++++ vm_gen/vm_gen.py | 37 ++++++-- 6 files changed, 292 insertions(+), 42 deletions(-) create mode 100644 bootstrap/webapp/routes/__init__.py create mode 100644 vm_gen/templates/_hooks.py create mode 100644 vm_gen/templates/routes_py diff --git a/bootstrap/webapp/app.py b/bootstrap/webapp/app.py index 0b2192c..9b95cd6 100644 --- a/bootstrap/webapp/app.py +++ b/bootstrap/webapp/app.py @@ -1,5 +1,6 @@ from flask import render_template +from webapp.routes import * from oshipka.webapp import app diff --git a/bootstrap/webapp/routes/__init__.py b/bootstrap/webapp/routes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/oshipka/webapp/views.py b/oshipka/webapp/views.py index 6570d74..5248f70 100644 --- a/oshipka/webapp/views.py +++ b/oshipka/webapp/views.py @@ -19,24 +19,44 @@ def get_instance(model_view, uuid): return instance -def list_view(model_view, template, template_ctx_func=None): - template = template if template else "{}/list.html".format(model_view.model_name) - +def list_view(model_view, template_func=None, template_ctx_func=None, args_process_func=None, + list_func=None, post_list_func=None, **kwargs): def inner(): - instances = model_view.model.query.all() + serialized_args = request.args + serialized_args = args_process_func(serialized_args) if args_process_func else serialized_args + instances = None + if list_func is not None: + instances = list_func(model_view, serialized_args) + if instances is None: + instances = model_view.model.query.all() + if post_list_func is not None: + instances = post_list_func(instances) + template = template_func(instances) if template_func else None + if not template: + template = "{}/list.html".format(model_view.model_name) template_ctx = template_ctx_func(instances) if template_ctx_func else {} return render_template(template, instances=instances, **template_ctx) return inner -def get_view(model_view, template, template_ctx_func=None): - template = template if template else "{}/get.html".format(model_view.model_name) - +def get_view(model_view, template_func=None, template_ctx_func=None, args_process_func=None, + get_func=None, post_get_func=None, **kwargs): def inner(uuid): - instance = get_instance(model_view, uuid) - if not instance: - return redirect(request.referrer or url_for('home')) + serialized_args = request.args + serialized_args = args_process_func(serialized_args) if args_process_func else serialized_args + instance = None + if get_func is not None: + instance = get_func(model_view, uuid, serialized_args) + if instance is not None: + instance = instance[0] + if instance is None: + instance = get_instance(model_view, uuid) + if post_get_func is not None: + instance = post_get_func(instance) + template = template_func(instance) if template_func else None + if not template: + template = "{}/get.html".format(model_view.model_name) template_ctx = template_ctx_func(instance) if template_ctx_func else {} return render_template(template, instance=instance, **template_ctx) @@ -47,69 +67,117 @@ def serialize_form(): return dict(filter(lambda k: not k[0].startswith("__"), dict(request.form).items())) -def update_view(model_view, template, pre_process_func=None, post_process_func=None, **kwargs): - template = template if template else "{}/edit.html".format(model_view.model_name) - +def update_view(model_view, template_func=None, template_ctx_func=None, args_process_func=None, + should_update_func=None, post_add=None, post_commit=None, **kwargs): def inner(uuid): instance = get_instance(model_view, uuid) if not instance: return redirect(request.referrer or url_for('home')) if request.method == "GET": - return render_template(template, instance=instance) + template = template_func(instance) if template_func else None + if not template: + template = "{}/edit.html".format(model_view.model_name) + template_ctx = template_ctx_func(instance) if template_ctx_func else {} + return render_template(template, instance=instance, **template_ctx) serialized_form = serialize_form() _next = serialized_form.pop('_next') if '_next' in serialized_form else None - serialized_form = pre_process_func(serialized_form, instance) if pre_process_func else serialized_form + serialized_form = args_process_func(serialized_form) if args_process_func else serialized_form + if should_update_func is not None: + should_update, msg = should_update_func(instance, serialized_form) + else: + should_update, msg = True, "" + if not should_update: + if msg: + flash(msg) + return redirect(_next or request.referrer or url_for('home')) for k, v in serialized_form.items(): setattr(instance, k, v) + db.session.add(instance) + if post_add is not None: + post_add(instance) + db.session.commit() - post_process_func(instance) if post_process_func else instance - flash("Updated {}:{}".format(model_view.model_name, uuid)) + if post_commit is not None: + post_commit(instance) + flash("Updated {}:{}".format(model_view.model_name, id)) return redirect(_next or request.referrer or url_for('home')) return inner -def create_view(model_view, template, template_ctx_func=None, post_add=None, post_create=None): - template = template if template else "{}/create.html".format(model_view.model_name) - +def create_view(model_view, template_func=None, template_ctx_func=None, args_process_func=None, + should_func=None, post_add=None, post_commit=None): def inner(): if request.method == "GET": + template = template_func() if template_func else None + if not template: + template = "{}/create.html".format(model_view.model_name) template_ctx = template_ctx_func() if template_ctx_func else {} return render_template(template, **template_ctx) serialized_form = serialize_form() _next = serialized_form.pop('_next') if '_next' in serialized_form else None - + serialized_form = args_process_func(serialized_form) if args_process_func else serialized_form serialized_form['uuid'] = str(uuid4()) + + if should_func is not None: + should_create, msg = should_func(serialized_form) + else: + should_create, msg = True, "" + if not should_create: + if msg: + flash(msg) + return redirect(_next or request.referrer or url_for('home')) instance = model_view.model(**serialized_form) db.session.add(instance) if post_add is not None: post_add(instance) db.session.commit() - if post_create is not None: - post_create(instance) + if post_commit is not None: + post_commit(instance) flash("Created {}".format(model_view.model_name)) return redirect(_next or request.referrer or url_for('home')) return inner -def delete_view(model_view): +def delete_view(model_view, template_func=None, template_ctx_func=None, args_process_func=None, + should_func=None, post_delete=None, post_commit=None, **kwargs): def inner(uuid): instance = get_instance(model_view, uuid) if not instance: return redirect(request.referrer or url_for('home')) if request.method == "GET": - return render_template("delete_instance.html", instance=instance, - model_name=model_view.model_name) + template = template_func(instance) if template_func else None + if not template: + template = "delete_instance.html" + template_ctx = template_ctx_func(instance) if template_ctx_func else {} + return render_template(template, + instance=instance, + model_name=model_view.model_name, + **template_ctx) serialized_form = serialize_form() + serialized_form = args_process_func(serialized_form) if args_process_func else serialized_form _next = serialized_form.pop('_next') if '_next' in serialized_form else None + if should_func is not None: + should_delete, msg = should_func(instance, serialized_form) + else: + should_delete, msg = True, "" + if not should_delete: + if msg: + flash(msg) + return redirect(_next or request.referrer or url_for('home')) db.session.delete(instance) + if post_delete is not None: + post_delete(instance) + db.session.commit() + if post_commit is not None: + post_commit(instance) flash("Deleted {}:{}".format(model_view.model_name, uuid)) return redirect(_next or request.referrer or url_for('home')) @@ -126,36 +194,36 @@ class ModelView(object): self.model_name = camel_case_to_snake_case(model.__name__) self.model_name_pl = p.plural(self.model_name) - def register_create(self, create_template=None, **kwargs): + def register_create(self, **kwargs): url = '/{}/create'.format(self.model_name_pl) self.app.add_url_rule(url, methods=["GET", "POST"], endpoint='create_{}'.format(self.model_name), - view_func=create_view(self, create_template, **kwargs)) + view_func=create_view(self, **kwargs)) - def register_list(self, list_template=None, **kwargs): + def register_list(self, **kwargs): url = '/{}'.format(self.model_name_pl) self.app.add_url_rule(url, 'list_{}'.format(self.model_name), - list_view(self, list_template, **kwargs)) + list_view(self, **kwargs)) - def register_get(self, retrieve_template=None, **kwargs): + def register_get(self, **kwargs): url = '/{}/'.format(self.model_name_pl) self.app.add_url_rule(url, 'get_{}'.format(self.model_name), - get_view(self, retrieve_template, **kwargs)) + get_view(self, **kwargs)) - def register_update(self, update_template=None, **kwargs): + def register_update(self, **kwargs): url = '/{}//edit'.format(self.model_name_pl) self.app.add_url_rule(url, methods=["GET", "POST"], endpoint='update_{}'.format(self.model_name), - view_func=update_view(self, update_template, **kwargs)) + view_func=update_view(self, **kwargs)) - def register_delete(self): + def register_delete(self, **kwargs): url = '/{}//delete'.format(self.model_name_pl) self.app.add_url_rule(url, methods=["GET", "POST"], endpoint='delete_{}'.format(self.model_name), - view_func=delete_view(self)) + view_func=delete_view(self, **kwargs)) def catch_flash(f): diff --git a/vm_gen/templates/_hooks.py b/vm_gen/templates/_hooks.py new file mode 100644 index 0000000..a3e24fc --- /dev/null +++ b/vm_gen/templates/_hooks.py @@ -0,0 +1,109 @@ +def get_template_func(instance): + return None + + +def get_templ_ctx_func(instance): + rv = dict() + return rv + + +def get_func(model_view, uuid, serialized_args): + """Should return a list of one element (or [None] if element is none) or None if you want default behaviour""" + return None + + +def post_get_func(instance): + return instance + + +def list_template_func(instances): + return None + + +def list_templ_ctx_func(instances): + rv = dict() + return rv + + +def list_func(model_view, serialized_args): + """Should return a list elements or None if you want default behaviour""" + return None + + +def post_list_func(instances): + return instances + + +def create_template_func(): + return None + + +def create_templ_ctx_func(): + rv = dict() + return rv + + +def create_args_process_func(serialized_form): + return serialized_form + + +def create_should_func(serialized_form): + return True, "" + + +def create_post_add_func(instance): + pass + + +def create_post_commit_func(instance): + pass + + +def update_template_func(instance): + return None + + +def update_templ_ctx_func(instance): + rv = dict() + return rv + + +def update_args_process_func(serialized_form): + return serialized_form + + +def update_should_func(serialized_form): + return True, "" + + +def update_post_add_func(instance): + pass + + +def update_post_commit_func(instance): + pass + + +def delete_template_func(instance): + return None + + +def delete_templ_ctx_func(instance): + rv = dict() + return rv + + +def delete_args_process_func(serialized_form): + return serialized_form + + +def delete_should_func(instance, serialized_form): + return True, "" + + +def delete_post_delete_func(instance): + pass + + +def delete_post_commit_func(instance): + pass diff --git a/vm_gen/templates/routes_py b/vm_gen/templates/routes_py new file mode 100644 index 0000000..8d68dc5 --- /dev/null +++ b/vm_gen/templates/routes_py @@ -0,0 +1,47 @@ +""" +!!!AUTOGENERATED: DO NOT EDIT!!! + +Edit the hooks in webapp/routes/[[ name|camel_to_snake ]]_hooks.py instead +""" + +from oshipka.webapp import app +from oshipka.webapp.views import ModelView +from webapp.models import [[ name ]] +from webapp.routes.[[ name|camel_to_snake ]]_hooks import * + +ModelView(app, [[name]]).register_get( + template_func=get_template_func, + template_ctx_func=get_templ_ctx_func, + get_func=get_func, + post_get_func=post_get_func, +) +ModelView(app, [[name]]).register_list( + template_func=list_template_func, + template_ctx_func=list_templ_ctx_func, + list_func=list_func, + post_list_func=post_list_func, +) +ModelView(app, [[name]]).register_create( + template_func=create_template_func, + template_ctx_func=create_templ_ctx_func, + args_process_func=create_args_process_func, + should_func=create_should_func, + post_add=create_post_add_func, + post_commit=create_post_commit_func, +) +ModelView(app, [[name]]).register_update( + template_func=update_template_func, + template_ctx_func=update_templ_ctx_func, + args_process_func=update_args_process_func, + should_func=update_should_func, + post_add=update_post_add_func, + post_commit=update_post_commit_func, +) +ModelView(app, [[name]]).register_delete( + template_func=delete_template_func, + template_ctx_func=delete_templ_ctx_func, + args_process_func=delete_args_process_func, + should_func=delete_should_func, + post_add=delete_post_delete_func, + post_commit=delete_post_commit_func, +) diff --git a/vm_gen/vm_gen.py b/vm_gen/vm_gen.py index a64eee7..d60efd4 100644 --- a/vm_gen/vm_gen.py +++ b/vm_gen/vm_gen.py @@ -1,4 +1,5 @@ import os +import shutil import sys import autopep8 @@ -61,6 +62,20 @@ def process_model(view_model): f.write(model) +def process_routes(view_model): + template = env.get_template('routes_py') + model = autopep8.fix_code(template.render(**view_model), options=pep_options) + _model_name = view_model.get('name') + model_name_snake = camel_case_to_snake_case(_model_name.split('.yaml')[0]) + filename = "{}.py".format(model_name_snake) + with open(os.path.join(ROUTES_PATH, filename), 'w+') as f: + f.write(model) + route_hooks_filename = os.path.join(ROUTES_PATH, "{}_hooks.py".format(model_name_snake)) + if not os.path.exists(route_hooks_filename): + src_path = os.path.join(VM_TEMPLATES_PATH, "_hooks.py") + shutil.copy(src_path, route_hooks_filename) + + def process_html_templates(view_model): _model_name_snake = camel_case_to_snake_case(view_model.get('name')) model_dir = os.path.join(HTML_TEMPLATES_PATH, _model_name_snake) @@ -76,7 +91,8 @@ def process_html_templates(view_model): def main(): view_model_names = os.listdir(VIEW_MODELS_PATH) - all_imports = ['from oshipka.persistance import db'] + all_model_imports = ['from oshipka.persistance import db'] + all_route_imports = [] for view_model_name in view_model_names: with open(os.path.join(VIEW_MODELS_PATH, view_model_name), 'r') as stream: try: @@ -84,15 +100,23 @@ def main(): for view_model in view_models: view_model = enrich_view_model(view_model) process_model(view_model) + process_routes(view_model) view_model_name = view_model.get('name', '') - all_imports.append('from webapp.models.{} import {}'.format( - camel_case_to_snake_case(view_model_name), view_model_name)) + model_snake_name = camel_case_to_snake_case(view_model_name) + all_model_imports.append('from webapp.models.{} import {}'.format( + model_snake_name, view_model_name)) process_html_templates(view_model) + all_route_imports.append('from webapp.routes.{} import *'.format( + model_snake_name + )) except yaml.YAMLError as e: breakpoint() - all_imports = autopep8.fix_code('\n'.join(all_imports), options=pep_options) + all_model_imports = autopep8.fix_code('\n'.join(all_model_imports), options=pep_options) + all_route_imports = autopep8.fix_code('\n'.join(all_route_imports), options=pep_options) with open(os.path.join(MODELS_PATH, "__init__.py"), 'w+') as f: - f.write(all_imports) + f.write(all_model_imports) + with open(os.path.join(ROUTES_PATH, "__init__.py"), 'w+') as f: + f.write(all_route_imports) if __name__ == "__main__": @@ -104,7 +128,8 @@ if __name__ == "__main__": WEBAPP_PATH = os.path.join(basepath, "webapp") VIEW_MODELS_PATH = os.path.join(WEBAPP_PATH, "view_models") MODELS_PATH = os.path.join(WEBAPP_PATH, "models") - for d in [VIEW_MODELS_PATH,MODELS_PATH ]: + ROUTES_PATH = os.path.join(WEBAPP_PATH, "routes") + for d in [VIEW_MODELS_PATH, MODELS_PATH, ROUTES_PATH, ]: os.makedirs(d, exist_ok=True) HTML_TEMPLATES_PATH = os.path.join(WEBAPP_PATH, "templates")