DEV Community

Andrés Baamonde Lozano
Andrés Baamonde Lozano

Posted on

Building an archetype to my python apps

I have built a lot of python webapps, APIs with flask. But didnt have a good aproximation for made it, really maintanible.

This weekend i have been working on a new archetype, im not sure that it will be better than my current archetype but i share it here. Anyone can comment if thinks that is not a good aproximation or if have any improvement.

If i dont comment some part of the archetype and you want an why i choose that way of implementation just comment below and i will answer as soon as possible.

Folders

A quick description:

  • api: api endpoints.
  • cfgs: server configurations folder, at this moment only one.
  • host: Here will be all server configurations.
  • web: jinja2 cgi clasic web, static files like css and JS.
├── app │ ├── api │ ├── cfgs │ ├── host │ └── web ├── docs └── resources └── docker └── app // sites available file 
Enter fullscreen mode Exit fullscreen mode

Run file and configuration loader

└── app ├── api ├── cfg_loader.py ├── cfgs ├── host ├── main.py // main file that launch server └── web 
Enter fullscreen mode Exit fullscreen mode

main.py file will import server and will run it only if it is runned as a script, else will expose app for CGI apache servers.

import os import json from host.server import Server from cfg_loader import CfgLoader cfg = CfgLoader( resource_path = "cfgs", debug= __name__ == "__main__", root_path=os.path.dirname(os.path.abspath(__file__))) app = Server(cfg) if __name__ == "__main__": app.run() 
Enter fullscreen mode Exit fullscreen mode

cfg_loader.py file isnt interesting at all, it only loads cfg depending of the runing environment.

Host

└── app └── host └── server.py // host, receives api and web routes and manages cfg 
Enter fullscreen mode Exit fullscreen mode

Server file manages flask app, it will import api and web dependencies and delegates routes registration. It only manages server configuration like templates routes. If any service is needed by views or web it will be injected from here.

import os from flask import Flask from api.my_api import MyApi from web.web_routes import WebRoutes class Server(Flask): def __init__(self, cfg): super(Server, self).__init__( __name__, template_folder=os.path.abspath(cfg.templates_folder), static_url_path=os.path.abspath(cfg.static_folder)) self.cfg = cfg self.web_routes = WebRoutes(cfg) self.api_routes = MyApi(cfg) self.api_routes.register(self) self.web_routes.register(self) def run(self): super(Server, self).run( host=self.cfg.host, port=self.cfg.port, debug=self.cfg.debug) 
Enter fullscreen mode Exit fullscreen mode

Api

The app Api at this moment has only uri versioning, at this point im not sure that it will not be the best choice, if anyone knows a py library for this problem any apport is welcomed. Because blueprints are not the best choice, at least in my opinion.

└── app └── api ├── blueprints // Api blueprints │ ├── base_blueprint.py │ ├── v1 // Api blueprint version 1 │ │ ├── api_blueprint.py │ │ └── my_view.py │ └── v2 // Api blueprint version 2 │ ├── api_blueprint.py │ └── my_view_v2.py ├── errors // Errors, error handlers │ ├── handlers.py │ └── validation_error.py ├── my_api.py └── responses // api responses 
Enter fullscreen mode Exit fullscreen mode

The funcitionality of these files is quite intuitive, my_api registers all api endpoints, default responses and error handlers.

Error handlers has not much interest because it are implemented like in flask documentation, a note for that is that i consider a good practice used as much as possible the Built-in Exceptions. Because using it will avoid unnecesary classes.

Base blueprint only has a generic implementation for reuse on child blueprints that implementation is a refactor for classic CRUD operations.

Now i will show the my_api implementation, as a commented previously this class has the responsibility of registering api routes.

from flask import Flask from .blueprints.v1.api_blueprint import ApiBlueprint as V1 from .blueprints.v2.api_blueprint import ApiBlueprint as V2 class MyApi(object): def __init__(self, cfg, api_basepath="/api"): self.cfg = cfg self.api_basepath = api_basepath def register(self, app: Flask): #self.response_class = DefaultResponse  # Register api with blueprints and views  app.register_blueprint(V1(self.api_basepath)) app.register_blueprint(V2(self.api_basepath)) # Register error handler  #self.register_error_handler(InvalidUsage, self.handle_invalid_usage) 
Enter fullscreen mode Exit fullscreen mode

Web

└── app └── web ├── static // JS/CSS files ├── templates // html jinja2 templates │ └── index.html └── web_routes.py // registers web routes on app 
Enter fullscreen mode Exit fullscreen mode

Web only has static files and templates, web_routes.py will register routing and template rendering functions, if a service is needed it will be provided on constructor.

from flask import Flask, render_template class WebRoutes(object): def __init__(self, cfg): self.cfg = cfg self.routes = [ ("/", self.index, ['GET']) ] def register(self, app: Flask): for (path, func, methods) in self.routes: app.add_url_rule(path, view_func=func, methods=methods) def index(self): return render_template('index.html') 
Enter fullscreen mode Exit fullscreen mode

That´s all for now! if any of you wants more posts, detailed implementations or more information/improvements to this archetype. Place a comment below for this rookie developer :D

Refs

Top comments (0)